|
Download database_schema.md from Gaykar/AIService: direct link, hf CLI and curl.
- Browser
- Download file 3.59 kB
-
https://huggingface.co/spaces/Gaykar/AIService/resolve/main/database_schema.md
- Command line
-
hf download hf://spaces/Gaykar/AIService/database_schema.md
-
curl -L -o database_schema.md https://huggingface.co/spaces/Gaykar/AIService/resolve/main/database_schema.md
3.59 kB
| # Database Schema β Voice Chat (STT/TTS) System | |
| ## 1. `users` | |
| | Column | Type | Notes | | |
| |---|---|---| | |
| | id | PK | | | |
| --- | |
| ## 2. `threads` | |
| Represents a conversation session β groups multiple messages together. | |
| | Column | Type | Notes | | |
| |---|---|---| | |
| | id | PK | internal DB primary key | | |
| | client_thread_id | UUID | client-generated thread identifier β `Column(UUID(as_uuid=True), default=uuid.uuid4, unique=True, index=True, nullable=False)`. This is what the client sends and what's used to build the `thread_abc`-equivalent folder name in the S3 path below β decoupled from `id` so the internal PK never has to be exposed to or generated by the client. | | |
| | user_id | FK β users.id | | | |
| | created_at | timestamp | session start | | |
| --- | |
| ## 3. `messages` | |
| One row per audio event (STT input turn or TTS output turn). The `type` column determines which direction the conversion runs. | |
| | Column | Type | Notes | | |
| |---|---|---| | |
| | id | PK | internal DB primary key | | |
| | client_msg_id | UUID | client-generated message identifier β `Column(UUID(as_uuid=True), default=uuid.uuid4, unique=True, index=True, nullable=False)`. The client generates this *before* uploading, uses it as the S3 filename (`msg_<client_msg_id>.wav`), and sends it along with the DB write β this is what enables direct client β S3 presigned-URL uploads, bypassing your backend for the audio bytes entirely. | | |
| | thread_id | FK β threads.id | internal join β resolved server-side by looking up the row matching the client's `client_thread_id` | | |
| | user_id | FK β users.id | denormalized β avoids a join for ownership checks | | |
| | type | enum: `stt` \| `tts` | direction of conversion | | |
| | audio_storage_path | text | S3/Supabase path β built from `client_thread_id` + `client_msg_id`, see layout below | | |
| | text_content | text | transcript (STT output) or source text (TTS input) | | |
| | created_at | timestamp | for ordering turns within a thread | | |
| **How `type` works:** | |
| - `type = stt` β `audio_storage_path` is the input, `text_content` is the model output (transcript) | |
| - `type = tts` β `text_content` is the input, `audio_storage_path` is the model output (generated audio) | |
| **Why two IDs per row (`id` vs. `client_*_id`):** | |
| `id` is the internal relational PK β small, sequential, used for all server-side joins and never exposed to or generated by the client. `client_thread_id` / `client_msg_id` are what the client actually generates, sends, and uses to name S3 objects β this lets the client build a valid, unique S3 path and request a presigned upload URL *before* your backend has even created a DB row, without ever needing to know or influence the internal PK. | |
| --- | |
| ## S3 / Supabase Bucket Layout | |
| ``` | |
| bucket/ | |
| βββ user_id_123/ | |
| βββ stt/ | |
| β βββ {client_thread_id}/ | |
| β βββ msg_{client_msg_id}.wav β input (spoken query) | |
| β | |
| βββ tts/ | |
| βββ {client_thread_id}/ | |
| βββ msg_{client_msg_id}.wav β output (spoken response) | |
| ``` | |
| - The folder/file names in this path are the client-generated `client_thread_id` and `client_msg_id` β not the internal `id` columns. | |
| - `audio_storage_path` in the `messages` table stores this exact path, so it can be used directly for S3 reads/writes without reconstructing it. | |
| - Split by `stt/` and `tts/` at the top level (not by thread) so each pipeline can have independent lifecycle/retention rules. | |
| - `{client_thread_id}/` nested underneath keeps all audio for one conversation together for easy per-thread cleanup or export. | |
|  |