From e0a798dc0dc9d24ecd010b594836607360fb57bc Mon Sep 17 00:00:00 2001 From: James Pine Date: Mon, 16 Mar 2026 06:18:46 -0700 Subject: [PATCH] add release skills, backfill changelog, wire CI to use changelog for GitHub releases - Backfill CHANGELOG.md from all 17 GitHub releases (was stale at v0.1.0) - Add draft-release-notes and release-bump agent skills - Extract release notes from CHANGELOG.md in release CI instead of hardcoded placeholder - Remove stale PATCH_NOTES.md, mlx-test/, move PROJECT_STATUS to docs/notes - Reorganize API reference docs from unknown/ to named groups - Update openapi.json --- .agents/skills/release-bump/SKILL.md | 4 +- .github/workflows/release.yml | 35 +- .../health_health_get.mdx | 0 .../docs/api-reference/general/meta.json | 4 + .../{unknown => general}/root__get.mdx | 0 .../generate_speech_generate_post.mdx | 0 .../get_audio_audio__generation_id__get.mdx | 0 .../docs/api-reference/generation/meta.json | 8 + .../transcribe_audio_transcribe_post.mdx | 0 ...eration_history__generation_id__delete.mdx | 0 ...generation_history__generation_id__get.mdx | 0 .../get_stats_history_stats_get.mdx | 0 .../list_history_history_get.mdx | 0 .../docs/api-reference/history/meta.json | 9 + docs/content/docs/api-reference/meta.json | 2 +- ...gress_models_progress__model_name__get.mdx | 0 .../get_model_status_models_status_get.mdx | 0 .../load_model_models_load_post.mdx | 0 .../docs/api-reference/models/meta.json | 10 + ...er_model_download_models_download_post.mdx | 0 .../unload_model_models_unload_post.mdx | 0 ...ple_profiles__profile_id__samples_post.mdx | 0 .../create_profile_profiles_post.mdx | 0 ...e_profile_profiles__profile_id__delete.mdx | 0 ...le_profiles_samples__sample_id__delete.mdx | 0 .../get_profile_profiles__profile_id__get.mdx | 0 ...ples_profiles__profile_id__samples_get.mdx | 0 .../list_profiles_profiles_get.mdx | 0 .../docs/api-reference/profiles/meta.json | 13 + ...date_profile_profiles__profile_id__put.mdx | 0 .../docs/api-reference/unknown/meta.json | 27 - docs/openapi.json | 1442 ++++++++++++++++- 32 files changed, 1512 insertions(+), 42 deletions(-) rename docs/content/docs/api-reference/{unknown => general}/health_health_get.mdx (100%) create mode 100644 docs/content/docs/api-reference/general/meta.json rename docs/content/docs/api-reference/{unknown => general}/root__get.mdx (100%) rename docs/content/docs/api-reference/{unknown => generation}/generate_speech_generate_post.mdx (100%) rename docs/content/docs/api-reference/{unknown => generation}/get_audio_audio__generation_id__get.mdx (100%) create mode 100644 docs/content/docs/api-reference/generation/meta.json rename docs/content/docs/api-reference/{unknown => generation}/transcribe_audio_transcribe_post.mdx (100%) rename docs/content/docs/api-reference/{unknown => history}/delete_generation_history__generation_id__delete.mdx (100%) rename docs/content/docs/api-reference/{unknown => history}/get_generation_history__generation_id__get.mdx (100%) rename docs/content/docs/api-reference/{unknown => history}/get_stats_history_stats_get.mdx (100%) rename docs/content/docs/api-reference/{unknown => history}/list_history_history_get.mdx (100%) create mode 100644 docs/content/docs/api-reference/history/meta.json rename docs/content/docs/api-reference/{unknown => models}/get_model_progress_models_progress__model_name__get.mdx (100%) rename docs/content/docs/api-reference/{unknown => models}/get_model_status_models_status_get.mdx (100%) rename docs/content/docs/api-reference/{unknown => models}/load_model_models_load_post.mdx (100%) create mode 100644 docs/content/docs/api-reference/models/meta.json rename docs/content/docs/api-reference/{unknown => models}/trigger_model_download_models_download_post.mdx (100%) rename docs/content/docs/api-reference/{unknown => models}/unload_model_models_unload_post.mdx (100%) rename docs/content/docs/api-reference/{unknown => profiles}/add_profile_sample_profiles__profile_id__samples_post.mdx (100%) rename docs/content/docs/api-reference/{unknown => profiles}/create_profile_profiles_post.mdx (100%) rename docs/content/docs/api-reference/{unknown => profiles}/delete_profile_profiles__profile_id__delete.mdx (100%) rename docs/content/docs/api-reference/{unknown => profiles}/delete_profile_sample_profiles_samples__sample_id__delete.mdx (100%) rename docs/content/docs/api-reference/{unknown => profiles}/get_profile_profiles__profile_id__get.mdx (100%) rename docs/content/docs/api-reference/{unknown => profiles}/get_profile_samples_profiles__profile_id__samples_get.mdx (100%) rename docs/content/docs/api-reference/{unknown => profiles}/list_profiles_profiles_get.mdx (100%) create mode 100644 docs/content/docs/api-reference/profiles/meta.json rename docs/content/docs/api-reference/{unknown => profiles}/update_profile_profiles__profile_id__put.mdx (100%) delete mode 100644 docs/content/docs/api-reference/unknown/meta.json diff --git a/.agents/skills/release-bump/SKILL.md b/.agents/skills/release-bump/SKILL.md index a266494a..8b52f87a 100644 --- a/.agents/skills/release-bump/SKILL.md +++ b/.agents/skills/release-bump/SKILL.md @@ -119,6 +119,6 @@ Given current version `X.Y.Z`: ## Notes -- This skill does not create a GitHub Release. That happens when the tag is pushed and CI runs, or the user can create it manually via `gh release create`. +- When the tag is pushed, the release CI (`.github/workflows/release.yml`) automatically extracts the matching version section from `CHANGELOG.md` and uses it as the GitHub Release body. No manual copy-paste needed. - The release commit message is controlled by `.bumpversion.cfg` (`Bump version: X.Y.Z -> A.B.C`). Do not override it. -- If the user wants to update the GitHub Release body with the changelog narrative after pushing, they can use: `gh release edit vX.Y.Z --notes-file <(sed -n '/## \[X.Y.Z\]/,/## \[/p' CHANGELOG.md | head -n -1)` +- If you need to manually update the GitHub Release body after the fact: `gh release edit vX.Y.Z --notes-file <(sed -n '/## \[X.Y.Z\]/,/## \[/p' CHANGELOG.md | head -n -1)` diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b24862d8..8165de0f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -123,6 +123,29 @@ jobs: p12-file-base64: ${{ secrets.APPLE_CERTIFICATE }} p12-password: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} + - name: Extract release notes from CHANGELOG.md + id: changelog + shell: bash + run: | + # Get the version from the tag (strip leading 'v') + VERSION="${GITHUB_REF_NAME#v}" + + # Extract the section for this version from CHANGELOG.md + # Matches from "## [X.Y.Z]" until the next "## [" heading + NOTES=$(sed -n "/^## \[${VERSION}\]/,/^## \[/{/^## \[${VERSION}\]/d;/^## \[/d;p;}" CHANGELOG.md) + + # Fall back to a placeholder if the version isn't in the changelog + if [ -z "$(echo "$NOTES" | tr -d '[:space:]')" ]; then + NOTES="See the assets below to download and install this version." + fi + + # Use multiline output syntax + { + echo "notes<> "$GITHUB_OUTPUT" + - uses: tauri-apps/tauri-action@v0.6 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -139,17 +162,7 @@ jobs: projectPath: tauri tagName: v__VERSION__ releaseName: "voicebox v__VERSION__" - releaseBody: | - ## What's Changed - See the assets below to download and install this version. - - ### Installation - - **macOS (Apple Silicon)**: Download the `aarch64.dmg` file - uses MLX for fast native inference - - **macOS (Intel)**: Download the `x64.dmg` file - uses PyTorch - - **Windows**: Download the `.msi` installer - - **Linux**: Compile from source (see README) - - The app includes automatic updates - future updates will be installed automatically. + releaseBody: ${{ steps.changelog.outputs.notes }} releaseDraft: true prerelease: false args: ${{ matrix.args }} diff --git a/docs/content/docs/api-reference/unknown/health_health_get.mdx b/docs/content/docs/api-reference/general/health_health_get.mdx similarity index 100% rename from docs/content/docs/api-reference/unknown/health_health_get.mdx rename to docs/content/docs/api-reference/general/health_health_get.mdx diff --git a/docs/content/docs/api-reference/general/meta.json b/docs/content/docs/api-reference/general/meta.json new file mode 100644 index 00000000..8d98f90d --- /dev/null +++ b/docs/content/docs/api-reference/general/meta.json @@ -0,0 +1,4 @@ +{ + "title": "General", + "pages": ["root__get", "health_health_get"] +} diff --git a/docs/content/docs/api-reference/unknown/root__get.mdx b/docs/content/docs/api-reference/general/root__get.mdx similarity index 100% rename from docs/content/docs/api-reference/unknown/root__get.mdx rename to docs/content/docs/api-reference/general/root__get.mdx diff --git a/docs/content/docs/api-reference/unknown/generate_speech_generate_post.mdx b/docs/content/docs/api-reference/generation/generate_speech_generate_post.mdx similarity index 100% rename from docs/content/docs/api-reference/unknown/generate_speech_generate_post.mdx rename to docs/content/docs/api-reference/generation/generate_speech_generate_post.mdx diff --git a/docs/content/docs/api-reference/unknown/get_audio_audio__generation_id__get.mdx b/docs/content/docs/api-reference/generation/get_audio_audio__generation_id__get.mdx similarity index 100% rename from docs/content/docs/api-reference/unknown/get_audio_audio__generation_id__get.mdx rename to docs/content/docs/api-reference/generation/get_audio_audio__generation_id__get.mdx diff --git a/docs/content/docs/api-reference/generation/meta.json b/docs/content/docs/api-reference/generation/meta.json new file mode 100644 index 00000000..b7c68dd7 --- /dev/null +++ b/docs/content/docs/api-reference/generation/meta.json @@ -0,0 +1,8 @@ +{ + "title": "Generation", + "pages": [ + "generate_speech_generate_post", + "transcribe_audio_transcribe_post", + "get_audio_audio__generation_id__get" + ] +} diff --git a/docs/content/docs/api-reference/unknown/transcribe_audio_transcribe_post.mdx b/docs/content/docs/api-reference/generation/transcribe_audio_transcribe_post.mdx similarity index 100% rename from docs/content/docs/api-reference/unknown/transcribe_audio_transcribe_post.mdx rename to docs/content/docs/api-reference/generation/transcribe_audio_transcribe_post.mdx diff --git a/docs/content/docs/api-reference/unknown/delete_generation_history__generation_id__delete.mdx b/docs/content/docs/api-reference/history/delete_generation_history__generation_id__delete.mdx similarity index 100% rename from docs/content/docs/api-reference/unknown/delete_generation_history__generation_id__delete.mdx rename to docs/content/docs/api-reference/history/delete_generation_history__generation_id__delete.mdx diff --git a/docs/content/docs/api-reference/unknown/get_generation_history__generation_id__get.mdx b/docs/content/docs/api-reference/history/get_generation_history__generation_id__get.mdx similarity index 100% rename from docs/content/docs/api-reference/unknown/get_generation_history__generation_id__get.mdx rename to docs/content/docs/api-reference/history/get_generation_history__generation_id__get.mdx diff --git a/docs/content/docs/api-reference/unknown/get_stats_history_stats_get.mdx b/docs/content/docs/api-reference/history/get_stats_history_stats_get.mdx similarity index 100% rename from docs/content/docs/api-reference/unknown/get_stats_history_stats_get.mdx rename to docs/content/docs/api-reference/history/get_stats_history_stats_get.mdx diff --git a/docs/content/docs/api-reference/unknown/list_history_history_get.mdx b/docs/content/docs/api-reference/history/list_history_history_get.mdx similarity index 100% rename from docs/content/docs/api-reference/unknown/list_history_history_get.mdx rename to docs/content/docs/api-reference/history/list_history_history_get.mdx diff --git a/docs/content/docs/api-reference/history/meta.json b/docs/content/docs/api-reference/history/meta.json new file mode 100644 index 00000000..d0ea138d --- /dev/null +++ b/docs/content/docs/api-reference/history/meta.json @@ -0,0 +1,9 @@ +{ + "title": "History", + "pages": [ + "list_history_history_get", + "get_generation_history__generation_id__get", + "delete_generation_history__generation_id__delete", + "get_stats_history_stats_get" + ] +} diff --git a/docs/content/docs/api-reference/meta.json b/docs/content/docs/api-reference/meta.json index 78943f50..bcce6902 100644 --- a/docs/content/docs/api-reference/meta.json +++ b/docs/content/docs/api-reference/meta.json @@ -1,5 +1,5 @@ { "title": "API Reference", "defaultOpen": true, - "pages": ["unknown"] + "pages": ["general", "profiles", "generation", "history", "models"] } diff --git a/docs/content/docs/api-reference/unknown/get_model_progress_models_progress__model_name__get.mdx b/docs/content/docs/api-reference/models/get_model_progress_models_progress__model_name__get.mdx similarity index 100% rename from docs/content/docs/api-reference/unknown/get_model_progress_models_progress__model_name__get.mdx rename to docs/content/docs/api-reference/models/get_model_progress_models_progress__model_name__get.mdx diff --git a/docs/content/docs/api-reference/unknown/get_model_status_models_status_get.mdx b/docs/content/docs/api-reference/models/get_model_status_models_status_get.mdx similarity index 100% rename from docs/content/docs/api-reference/unknown/get_model_status_models_status_get.mdx rename to docs/content/docs/api-reference/models/get_model_status_models_status_get.mdx diff --git a/docs/content/docs/api-reference/unknown/load_model_models_load_post.mdx b/docs/content/docs/api-reference/models/load_model_models_load_post.mdx similarity index 100% rename from docs/content/docs/api-reference/unknown/load_model_models_load_post.mdx rename to docs/content/docs/api-reference/models/load_model_models_load_post.mdx diff --git a/docs/content/docs/api-reference/models/meta.json b/docs/content/docs/api-reference/models/meta.json new file mode 100644 index 00000000..915f0285 --- /dev/null +++ b/docs/content/docs/api-reference/models/meta.json @@ -0,0 +1,10 @@ +{ + "title": "Models", + "pages": [ + "get_model_status_models_status_get", + "load_model_models_load_post", + "unload_model_models_unload_post", + "trigger_model_download_models_download_post", + "get_model_progress_models_progress__model_name__get" + ] +} diff --git a/docs/content/docs/api-reference/unknown/trigger_model_download_models_download_post.mdx b/docs/content/docs/api-reference/models/trigger_model_download_models_download_post.mdx similarity index 100% rename from docs/content/docs/api-reference/unknown/trigger_model_download_models_download_post.mdx rename to docs/content/docs/api-reference/models/trigger_model_download_models_download_post.mdx diff --git a/docs/content/docs/api-reference/unknown/unload_model_models_unload_post.mdx b/docs/content/docs/api-reference/models/unload_model_models_unload_post.mdx similarity index 100% rename from docs/content/docs/api-reference/unknown/unload_model_models_unload_post.mdx rename to docs/content/docs/api-reference/models/unload_model_models_unload_post.mdx diff --git a/docs/content/docs/api-reference/unknown/add_profile_sample_profiles__profile_id__samples_post.mdx b/docs/content/docs/api-reference/profiles/add_profile_sample_profiles__profile_id__samples_post.mdx similarity index 100% rename from docs/content/docs/api-reference/unknown/add_profile_sample_profiles__profile_id__samples_post.mdx rename to docs/content/docs/api-reference/profiles/add_profile_sample_profiles__profile_id__samples_post.mdx diff --git a/docs/content/docs/api-reference/unknown/create_profile_profiles_post.mdx b/docs/content/docs/api-reference/profiles/create_profile_profiles_post.mdx similarity index 100% rename from docs/content/docs/api-reference/unknown/create_profile_profiles_post.mdx rename to docs/content/docs/api-reference/profiles/create_profile_profiles_post.mdx diff --git a/docs/content/docs/api-reference/unknown/delete_profile_profiles__profile_id__delete.mdx b/docs/content/docs/api-reference/profiles/delete_profile_profiles__profile_id__delete.mdx similarity index 100% rename from docs/content/docs/api-reference/unknown/delete_profile_profiles__profile_id__delete.mdx rename to docs/content/docs/api-reference/profiles/delete_profile_profiles__profile_id__delete.mdx diff --git a/docs/content/docs/api-reference/unknown/delete_profile_sample_profiles_samples__sample_id__delete.mdx b/docs/content/docs/api-reference/profiles/delete_profile_sample_profiles_samples__sample_id__delete.mdx similarity index 100% rename from docs/content/docs/api-reference/unknown/delete_profile_sample_profiles_samples__sample_id__delete.mdx rename to docs/content/docs/api-reference/profiles/delete_profile_sample_profiles_samples__sample_id__delete.mdx diff --git a/docs/content/docs/api-reference/unknown/get_profile_profiles__profile_id__get.mdx b/docs/content/docs/api-reference/profiles/get_profile_profiles__profile_id__get.mdx similarity index 100% rename from docs/content/docs/api-reference/unknown/get_profile_profiles__profile_id__get.mdx rename to docs/content/docs/api-reference/profiles/get_profile_profiles__profile_id__get.mdx diff --git a/docs/content/docs/api-reference/unknown/get_profile_samples_profiles__profile_id__samples_get.mdx b/docs/content/docs/api-reference/profiles/get_profile_samples_profiles__profile_id__samples_get.mdx similarity index 100% rename from docs/content/docs/api-reference/unknown/get_profile_samples_profiles__profile_id__samples_get.mdx rename to docs/content/docs/api-reference/profiles/get_profile_samples_profiles__profile_id__samples_get.mdx diff --git a/docs/content/docs/api-reference/unknown/list_profiles_profiles_get.mdx b/docs/content/docs/api-reference/profiles/list_profiles_profiles_get.mdx similarity index 100% rename from docs/content/docs/api-reference/unknown/list_profiles_profiles_get.mdx rename to docs/content/docs/api-reference/profiles/list_profiles_profiles_get.mdx diff --git a/docs/content/docs/api-reference/profiles/meta.json b/docs/content/docs/api-reference/profiles/meta.json new file mode 100644 index 00000000..05469ed8 --- /dev/null +++ b/docs/content/docs/api-reference/profiles/meta.json @@ -0,0 +1,13 @@ +{ + "title": "Profiles", + "pages": [ + "list_profiles_profiles_get", + "create_profile_profiles_post", + "get_profile_profiles__profile_id__get", + "update_profile_profiles__profile_id__put", + "delete_profile_profiles__profile_id__delete", + "get_profile_samples_profiles__profile_id__samples_get", + "add_profile_sample_profiles__profile_id__samples_post", + "delete_profile_sample_profiles_samples__sample_id__delete" + ] +} diff --git a/docs/content/docs/api-reference/unknown/update_profile_profiles__profile_id__put.mdx b/docs/content/docs/api-reference/profiles/update_profile_profiles__profile_id__put.mdx similarity index 100% rename from docs/content/docs/api-reference/unknown/update_profile_profiles__profile_id__put.mdx rename to docs/content/docs/api-reference/profiles/update_profile_profiles__profile_id__put.mdx diff --git a/docs/content/docs/api-reference/unknown/meta.json b/docs/content/docs/api-reference/unknown/meta.json deleted file mode 100644 index ddac4fd3..00000000 --- a/docs/content/docs/api-reference/unknown/meta.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "title": "Endpoints", - "pages": [ - "root__get", - "health_health_get", - "list_profiles_profiles_get", - "create_profile_profiles_post", - "get_profile_profiles__profile_id__get", - "update_profile_profiles__profile_id__put", - "delete_profile_profiles__profile_id__delete", - "add_profile_sample_profiles__profile_id__samples_post", - "get_profile_samples_profiles__profile_id__samples_get", - "delete_profile_sample_profiles_samples__sample_id__delete", - "generate_speech_generate_post", - "list_history_history_get", - "get_generation_history__generation_id__get", - "delete_generation_history__generation_id__delete", - "get_stats_history_stats_get", - "transcribe_audio_transcribe_post", - "get_audio_audio__generation_id__get", - "load_model_models_load_post", - "unload_model_models_unload_post", - "get_model_progress_models_progress__model_name__get", - "get_model_status_models_status_get", - "trigger_model_download_models_download_post" - ] -} diff --git a/docs/openapi.json b/docs/openapi.json index 1a196e6c..594afd87 100644 --- a/docs/openapi.json +++ b/docs/openapi.json @@ -1 +1,1441 @@ -{"openapi":"3.1.0","info":{"title":"voicebox API","description":"Production-quality Qwen3-TTS voice cloning API","version":"0.1.0"},"servers":[{"url":"http://localhost:8000","description":"Local development server"}],"paths":{"/":{"get":{"summary":"Root","description":"Root endpoint.","operationId":"root__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/health":{"get":{"summary":"Health","description":"Health check endpoint.","operationId":"health_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthResponse"}}}}}}},"/profiles":{"get":{"summary":"List Profiles","description":"List all voice profiles.","operationId":"list_profiles_profiles_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/VoiceProfileResponse"},"type":"array","title":"Response List Profiles Profiles Get"}}}}}},"post":{"summary":"Create Profile","description":"Create a new voice profile.","operationId":"create_profile_profiles_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceProfileCreate"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceProfileResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/profiles/{profile_id}":{"get":{"summary":"Get Profile","description":"Get a voice profile by ID.","operationId":"get_profile_profiles__profile_id__get","parameters":[{"name":"profile_id","in":"path","required":true,"schema":{"type":"string","title":"Profile Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceProfileResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"summary":"Update Profile","description":"Update a voice profile.","operationId":"update_profile_profiles__profile_id__put","parameters":[{"name":"profile_id","in":"path","required":true,"schema":{"type":"string","title":"Profile Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceProfileCreate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceProfileResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"summary":"Delete Profile","description":"Delete a voice profile.","operationId":"delete_profile_profiles__profile_id__delete","parameters":[{"name":"profile_id","in":"path","required":true,"schema":{"type":"string","title":"Profile Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/profiles/{profile_id}/samples":{"post":{"summary":"Add Profile Sample","description":"Add a sample to a voice profile.","operationId":"add_profile_sample_profiles__profile_id__samples_post","parameters":[{"name":"profile_id","in":"path","required":true,"schema":{"type":"string","title":"Profile Id"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_add_profile_sample_profiles__profile_id__samples_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProfileSampleResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"summary":"Get Profile Samples","description":"Get all samples for a profile.","operationId":"get_profile_samples_profiles__profile_id__samples_get","parameters":[{"name":"profile_id","in":"path","required":true,"schema":{"type":"string","title":"Profile Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ProfileSampleResponse"},"title":"Response Get Profile Samples Profiles Profile Id Samples Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/profiles/samples/{sample_id}":{"delete":{"summary":"Delete Profile Sample","description":"Delete a profile sample.","operationId":"delete_profile_sample_profiles_samples__sample_id__delete","parameters":[{"name":"sample_id","in":"path","required":true,"schema":{"type":"string","title":"Sample Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/generate":{"post":{"summary":"Generate Speech","description":"Generate speech from text using a voice profile.","operationId":"generate_speech_generate_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerationRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/history":{"get":{"summary":"List History","description":"List generation history with optional filters.","operationId":"list_history_history_get","parameters":[{"name":"profile_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Profile Id"}},{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Search"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HistoryListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/history/{generation_id}":{"get":{"summary":"Get Generation","description":"Get a generation by ID.","operationId":"get_generation_history__generation_id__get","parameters":[{"name":"generation_id","in":"path","required":true,"schema":{"type":"string","title":"Generation Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HistoryResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"summary":"Delete Generation","description":"Delete a generation.","operationId":"delete_generation_history__generation_id__delete","parameters":[{"name":"generation_id","in":"path","required":true,"schema":{"type":"string","title":"Generation Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/history/stats":{"get":{"summary":"Get Stats","description":"Get generation statistics.","operationId":"get_stats_history_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/transcribe":{"post":{"summary":"Transcribe Audio","description":"Transcribe audio file to text.","operationId":"transcribe_audio_transcribe_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_transcribe_audio_transcribe_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TranscriptionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/audio/{generation_id}":{"get":{"summary":"Get Audio","description":"Serve generated audio file.","operationId":"get_audio_audio__generation_id__get","parameters":[{"name":"generation_id","in":"path","required":true,"schema":{"type":"string","title":"Generation Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/models/load":{"post":{"summary":"Load Model","description":"Manually load TTS model.","operationId":"load_model_models_load_post","parameters":[{"name":"model_size","in":"query","required":false,"schema":{"type":"string","default":"1.7B","title":"Model Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/models/unload":{"post":{"summary":"Unload Model","description":"Unload TTS model to free memory.","operationId":"unload_model_models_unload_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/models/progress/{model_name}":{"get":{"summary":"Get Model Progress","description":"Get model download progress via Server-Sent Events.","operationId":"get_model_progress_models_progress__model_name__get","parameters":[{"name":"model_name","in":"path","required":true,"schema":{"type":"string","title":"Model Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/models/status":{"get":{"summary":"Get Model Status","description":"Get status of all available models.","operationId":"get_model_status_models_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelStatusListResponse"}}}}}}},"/models/download":{"post":{"summary":"Trigger Model Download","description":"Trigger download of a specific model.","operationId":"trigger_model_download_models_download_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelDownloadRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"Body_add_profile_sample_profiles__profile_id__samples_post":{"properties":{"file":{"type":"string","format":"binary","title":"File"},"reference_text":{"type":"string","title":"Reference Text"}},"type":"object","required":["file","reference_text"],"title":"Body_add_profile_sample_profiles__profile_id__samples_post"},"Body_transcribe_audio_transcribe_post":{"properties":{"file":{"type":"string","format":"binary","title":"File"},"language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language"}},"type":"object","required":["file"],"title":"Body_transcribe_audio_transcribe_post"},"GenerationRequest":{"properties":{"profile_id":{"type":"string","title":"Profile Id"},"text":{"type":"string","maxLength":5000,"minLength":1,"title":"Text"},"language":{"type":"string","pattern":"^(en|zh)$","title":"Language","default":"en"},"seed":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Seed"},"model_size":{"anyOf":[{"type":"string","pattern":"^(1\\.7B|0\\.6B)$"},{"type":"null"}],"title":"Model Size","default":"1.7B"}},"type":"object","required":["profile_id","text"],"title":"GenerationRequest","description":"Request model for voice generation."},"GenerationResponse":{"properties":{"id":{"type":"string","title":"Id"},"profile_id":{"type":"string","title":"Profile Id"},"text":{"type":"string","title":"Text"},"language":{"type":"string","title":"Language"},"audio_path":{"type":"string","title":"Audio Path"},"duration":{"type":"number","title":"Duration"},"seed":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Seed"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","profile_id","text","language","audio_path","duration","seed","created_at"],"title":"GenerationResponse","description":"Response model for voice generation."},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"HealthResponse":{"properties":{"status":{"type":"string","title":"Status"},"model_loaded":{"type":"boolean","title":"Model Loaded"},"model_downloaded":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Model Downloaded"},"model_size":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model Size"},"gpu_available":{"type":"boolean","title":"Gpu Available"},"vram_used_mb":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Vram Used Mb"}},"type":"object","required":["status","model_loaded","gpu_available"],"title":"HealthResponse","description":"Response model for health check."},"HistoryListResponse":{"properties":{"items":{"items":{"$ref":"#/components/schemas/HistoryResponse"},"type":"array","title":"Items"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["items","total"],"title":"HistoryListResponse","description":"Response model for history list."},"HistoryResponse":{"properties":{"id":{"type":"string","title":"Id"},"profile_id":{"type":"string","title":"Profile Id"},"profile_name":{"type":"string","title":"Profile Name"},"text":{"type":"string","title":"Text"},"language":{"type":"string","title":"Language"},"audio_path":{"type":"string","title":"Audio Path"},"duration":{"type":"number","title":"Duration"},"seed":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Seed"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","profile_id","profile_name","text","language","audio_path","duration","seed","created_at"],"title":"HistoryResponse","description":"Response model for history entry (includes profile name)."},"ModelDownloadRequest":{"properties":{"model_name":{"type":"string","title":"Model Name"}},"type":"object","required":["model_name"],"title":"ModelDownloadRequest","description":"Request model for triggering model download."},"ModelStatus":{"properties":{"model_name":{"type":"string","title":"Model Name"},"display_name":{"type":"string","title":"Display Name"},"downloaded":{"type":"boolean","title":"Downloaded"},"size_mb":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Size Mb"},"loaded":{"type":"boolean","title":"Loaded","default":false}},"type":"object","required":["model_name","display_name","downloaded"],"title":"ModelStatus","description":"Response model for model status."},"ModelStatusListResponse":{"properties":{"models":{"items":{"$ref":"#/components/schemas/ModelStatus"},"type":"array","title":"Models"}},"type":"object","required":["models"],"title":"ModelStatusListResponse","description":"Response model for model status list."},"ProfileSampleResponse":{"properties":{"id":{"type":"string","title":"Id"},"profile_id":{"type":"string","title":"Profile Id"},"audio_path":{"type":"string","title":"Audio Path"},"reference_text":{"type":"string","title":"Reference Text"}},"type":"object","required":["id","profile_id","audio_path","reference_text"],"title":"ProfileSampleResponse","description":"Response model for profile sample."},"TranscriptionResponse":{"properties":{"text":{"type":"string","title":"Text"},"duration":{"type":"number","title":"Duration"}},"type":"object","required":["text","duration"],"title":"TranscriptionResponse","description":"Response model for transcription."},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"VoiceProfileCreate":{"properties":{"name":{"type":"string","maxLength":100,"minLength":1,"title":"Name"},"description":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Description"},"language":{"type":"string","pattern":"^(en|zh)$","title":"Language","default":"en"}},"type":"object","required":["name"],"title":"VoiceProfileCreate","description":"Request model for creating a voice profile."},"VoiceProfileResponse":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"language":{"type":"string","title":"Language"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","name","description","language","created_at","updated_at"],"title":"VoiceProfileResponse","description":"Response model for voice profile."}}}} \ No newline at end of file +{ + "openapi": "3.1.0", + "info": { + "title": "voicebox API", + "description": "Production-quality Qwen3-TTS voice cloning API", + "version": "0.1.0" + }, + "servers": [ + { + "url": "http://localhost:8000", + "description": "Local development server" + } + ], + "paths": { + "/": { + "get": { + "summary": "Root", + "description": "Root endpoint.", + "operationId": "root__get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "tags": [ + "General" + ] + } + }, + "/health": { + "get": { + "summary": "Health", + "description": "Health check endpoint.", + "operationId": "health_health_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HealthResponse" + } + } + } + } + }, + "tags": [ + "General" + ] + } + }, + "/profiles": { + "get": { + "summary": "List Profiles", + "description": "List all voice profiles.", + "operationId": "list_profiles_profiles_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/VoiceProfileResponse" + }, + "type": "array", + "title": "Response List Profiles Profiles Get" + } + } + } + } + }, + "tags": [ + "Profiles" + ] + }, + "post": { + "summary": "Create Profile", + "description": "Create a new voice profile.", + "operationId": "create_profile_profiles_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/VoiceProfileCreate" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/VoiceProfileResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "tags": [ + "Profiles" + ] + } + }, + "/profiles/{profile_id}": { + "get": { + "summary": "Get Profile", + "description": "Get a voice profile by ID.", + "operationId": "get_profile_profiles__profile_id__get", + "parameters": [ + { + "name": "profile_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Profile Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/VoiceProfileResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "tags": [ + "Profiles" + ] + }, + "put": { + "summary": "Update Profile", + "description": "Update a voice profile.", + "operationId": "update_profile_profiles__profile_id__put", + "parameters": [ + { + "name": "profile_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Profile Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/VoiceProfileCreate" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/VoiceProfileResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "tags": [ + "Profiles" + ] + }, + "delete": { + "summary": "Delete Profile", + "description": "Delete a voice profile.", + "operationId": "delete_profile_profiles__profile_id__delete", + "parameters": [ + { + "name": "profile_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Profile Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "tags": [ + "Profiles" + ] + } + }, + "/profiles/{profile_id}/samples": { + "post": { + "summary": "Add Profile Sample", + "description": "Add a sample to a voice profile.", + "operationId": "add_profile_sample_profiles__profile_id__samples_post", + "parameters": [ + { + "name": "profile_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Profile Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/Body_add_profile_sample_profiles__profile_id__samples_post" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProfileSampleResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "tags": [ + "Profiles" + ] + }, + "get": { + "summary": "Get Profile Samples", + "description": "Get all samples for a profile.", + "operationId": "get_profile_samples_profiles__profile_id__samples_get", + "parameters": [ + { + "name": "profile_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Profile Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ProfileSampleResponse" + }, + "title": "Response Get Profile Samples Profiles Profile Id Samples Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "tags": [ + "Profiles" + ] + } + }, + "/profiles/samples/{sample_id}": { + "delete": { + "summary": "Delete Profile Sample", + "description": "Delete a profile sample.", + "operationId": "delete_profile_sample_profiles_samples__sample_id__delete", + "parameters": [ + { + "name": "sample_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Sample Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "tags": [ + "Profiles" + ] + } + }, + "/generate": { + "post": { + "summary": "Generate Speech", + "description": "Generate speech from text using a voice profile.", + "operationId": "generate_speech_generate_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GenerationRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GenerationResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "tags": [ + "Generation" + ] + } + }, + "/history": { + "get": { + "summary": "List History", + "description": "List generation history with optional filters.", + "operationId": "list_history_history_get", + "parameters": [ + { + "name": "profile_id", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Profile Id" + } + }, + { + "name": "search", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Search" + } + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 50, + "title": "Limit" + } + }, + { + "name": "offset", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 0, + "title": "Offset" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HistoryListResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "tags": [ + "History" + ] + } + }, + "/history/{generation_id}": { + "get": { + "summary": "Get Generation", + "description": "Get a generation by ID.", + "operationId": "get_generation_history__generation_id__get", + "parameters": [ + { + "name": "generation_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Generation Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HistoryResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "tags": [ + "History" + ] + }, + "delete": { + "summary": "Delete Generation", + "description": "Delete a generation.", + "operationId": "delete_generation_history__generation_id__delete", + "parameters": [ + { + "name": "generation_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Generation Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "tags": [ + "History" + ] + } + }, + "/history/stats": { + "get": { + "summary": "Get Stats", + "description": "Get generation statistics.", + "operationId": "get_stats_history_stats_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "tags": [ + "History" + ] + } + }, + "/transcribe": { + "post": { + "summary": "Transcribe Audio", + "description": "Transcribe audio file to text.", + "operationId": "transcribe_audio_transcribe_post", + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/Body_transcribe_audio_transcribe_post" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TranscriptionResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "tags": [ + "Generation" + ] + } + }, + "/audio/{generation_id}": { + "get": { + "summary": "Get Audio", + "description": "Serve generated audio file.", + "operationId": "get_audio_audio__generation_id__get", + "parameters": [ + { + "name": "generation_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Generation Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "tags": [ + "Generation" + ] + } + }, + "/models/load": { + "post": { + "summary": "Load Model", + "description": "Manually load TTS model.", + "operationId": "load_model_models_load_post", + "parameters": [ + { + "name": "model_size", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "1.7B", + "title": "Model Size" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "tags": [ + "Models" + ] + } + }, + "/models/unload": { + "post": { + "summary": "Unload Model", + "description": "Unload TTS model to free memory.", + "operationId": "unload_model_models_unload_post", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "tags": [ + "Models" + ] + } + }, + "/models/progress/{model_name}": { + "get": { + "summary": "Get Model Progress", + "description": "Get model download progress via Server-Sent Events.", + "operationId": "get_model_progress_models_progress__model_name__get", + "parameters": [ + { + "name": "model_name", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Model Name" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "tags": [ + "Models" + ] + } + }, + "/models/status": { + "get": { + "summary": "Get Model Status", + "description": "Get status of all available models.", + "operationId": "get_model_status_models_status_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ModelStatusListResponse" + } + } + } + } + }, + "tags": [ + "Models" + ] + } + }, + "/models/download": { + "post": { + "summary": "Trigger Model Download", + "description": "Trigger download of a specific model.", + "operationId": "trigger_model_download_models_download_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ModelDownloadRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "tags": [ + "Models" + ] + } + } + }, + "components": { + "schemas": { + "Body_add_profile_sample_profiles__profile_id__samples_post": { + "properties": { + "file": { + "type": "string", + "format": "binary", + "title": "File" + }, + "reference_text": { + "type": "string", + "title": "Reference Text" + } + }, + "type": "object", + "required": [ + "file", + "reference_text" + ], + "title": "Body_add_profile_sample_profiles__profile_id__samples_post" + }, + "Body_transcribe_audio_transcribe_post": { + "properties": { + "file": { + "type": "string", + "format": "binary", + "title": "File" + }, + "language": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Language" + } + }, + "type": "object", + "required": [ + "file" + ], + "title": "Body_transcribe_audio_transcribe_post" + }, + "GenerationRequest": { + "properties": { + "profile_id": { + "type": "string", + "title": "Profile Id" + }, + "text": { + "type": "string", + "maxLength": 5000, + "minLength": 1, + "title": "Text" + }, + "language": { + "type": "string", + "pattern": "^(en|zh)$", + "title": "Language", + "default": "en" + }, + "seed": { + "anyOf": [ + { + "type": "integer", + "minimum": 0.0 + }, + { + "type": "null" + } + ], + "title": "Seed" + }, + "model_size": { + "anyOf": [ + { + "type": "string", + "pattern": "^(1\\.7B|0\\.6B)$" + }, + { + "type": "null" + } + ], + "title": "Model Size", + "default": "1.7B" + } + }, + "type": "object", + "required": [ + "profile_id", + "text" + ], + "title": "GenerationRequest", + "description": "Request model for voice generation." + }, + "GenerationResponse": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "profile_id": { + "type": "string", + "title": "Profile Id" + }, + "text": { + "type": "string", + "title": "Text" + }, + "language": { + "type": "string", + "title": "Language" + }, + "audio_path": { + "type": "string", + "title": "Audio Path" + }, + "duration": { + "type": "number", + "title": "Duration" + }, + "seed": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Seed" + }, + "created_at": { + "type": "string", + "format": "date-time", + "title": "Created At" + } + }, + "type": "object", + "required": [ + "id", + "profile_id", + "text", + "language", + "audio_path", + "duration", + "seed", + "created_at" + ], + "title": "GenerationResponse", + "description": "Response model for voice generation." + }, + "HTTPValidationError": { + "properties": { + "detail": { + "items": { + "$ref": "#/components/schemas/ValidationError" + }, + "type": "array", + "title": "Detail" + } + }, + "type": "object", + "title": "HTTPValidationError" + }, + "HealthResponse": { + "properties": { + "status": { + "type": "string", + "title": "Status" + }, + "model_loaded": { + "type": "boolean", + "title": "Model Loaded" + }, + "model_downloaded": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Model Downloaded" + }, + "model_size": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Model Size" + }, + "gpu_available": { + "type": "boolean", + "title": "Gpu Available" + }, + "vram_used_mb": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "title": "Vram Used Mb" + } + }, + "type": "object", + "required": [ + "status", + "model_loaded", + "gpu_available" + ], + "title": "HealthResponse", + "description": "Response model for health check." + }, + "HistoryListResponse": { + "properties": { + "items": { + "items": { + "$ref": "#/components/schemas/HistoryResponse" + }, + "type": "array", + "title": "Items" + }, + "total": { + "type": "integer", + "title": "Total" + } + }, + "type": "object", + "required": [ + "items", + "total" + ], + "title": "HistoryListResponse", + "description": "Response model for history list." + }, + "HistoryResponse": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "profile_id": { + "type": "string", + "title": "Profile Id" + }, + "profile_name": { + "type": "string", + "title": "Profile Name" + }, + "text": { + "type": "string", + "title": "Text" + }, + "language": { + "type": "string", + "title": "Language" + }, + "audio_path": { + "type": "string", + "title": "Audio Path" + }, + "duration": { + "type": "number", + "title": "Duration" + }, + "seed": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Seed" + }, + "created_at": { + "type": "string", + "format": "date-time", + "title": "Created At" + } + }, + "type": "object", + "required": [ + "id", + "profile_id", + "profile_name", + "text", + "language", + "audio_path", + "duration", + "seed", + "created_at" + ], + "title": "HistoryResponse", + "description": "Response model for history entry (includes profile name)." + }, + "ModelDownloadRequest": { + "properties": { + "model_name": { + "type": "string", + "title": "Model Name" + } + }, + "type": "object", + "required": [ + "model_name" + ], + "title": "ModelDownloadRequest", + "description": "Request model for triggering model download." + }, + "ModelStatus": { + "properties": { + "model_name": { + "type": "string", + "title": "Model Name" + }, + "display_name": { + "type": "string", + "title": "Display Name" + }, + "downloaded": { + "type": "boolean", + "title": "Downloaded" + }, + "size_mb": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "title": "Size Mb" + }, + "loaded": { + "type": "boolean", + "title": "Loaded", + "default": false + } + }, + "type": "object", + "required": [ + "model_name", + "display_name", + "downloaded" + ], + "title": "ModelStatus", + "description": "Response model for model status." + }, + "ModelStatusListResponse": { + "properties": { + "models": { + "items": { + "$ref": "#/components/schemas/ModelStatus" + }, + "type": "array", + "title": "Models" + } + }, + "type": "object", + "required": [ + "models" + ], + "title": "ModelStatusListResponse", + "description": "Response model for model status list." + }, + "ProfileSampleResponse": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "profile_id": { + "type": "string", + "title": "Profile Id" + }, + "audio_path": { + "type": "string", + "title": "Audio Path" + }, + "reference_text": { + "type": "string", + "title": "Reference Text" + } + }, + "type": "object", + "required": [ + "id", + "profile_id", + "audio_path", + "reference_text" + ], + "title": "ProfileSampleResponse", + "description": "Response model for profile sample." + }, + "TranscriptionResponse": { + "properties": { + "text": { + "type": "string", + "title": "Text" + }, + "duration": { + "type": "number", + "title": "Duration" + } + }, + "type": "object", + "required": [ + "text", + "duration" + ], + "title": "TranscriptionResponse", + "description": "Response model for transcription." + }, + "ValidationError": { + "properties": { + "loc": { + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "integer" + } + ] + }, + "type": "array", + "title": "Location" + }, + "msg": { + "type": "string", + "title": "Message" + }, + "type": { + "type": "string", + "title": "Error Type" + } + }, + "type": "object", + "required": [ + "loc", + "msg", + "type" + ], + "title": "ValidationError" + }, + "VoiceProfileCreate": { + "properties": { + "name": { + "type": "string", + "maxLength": 100, + "minLength": 1, + "title": "Name" + }, + "description": { + "anyOf": [ + { + "type": "string", + "maxLength": 500 + }, + { + "type": "null" + } + ], + "title": "Description" + }, + "language": { + "type": "string", + "pattern": "^(en|zh)$", + "title": "Language", + "default": "en" + } + }, + "type": "object", + "required": [ + "name" + ], + "title": "VoiceProfileCreate", + "description": "Request model for creating a voice profile." + }, + "VoiceProfileResponse": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "name": { + "type": "string", + "title": "Name" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Description" + }, + "language": { + "type": "string", + "title": "Language" + }, + "created_at": { + "type": "string", + "format": "date-time", + "title": "Created At" + }, + "updated_at": { + "type": "string", + "format": "date-time", + "title": "Updated At" + } + }, + "type": "object", + "required": [ + "id", + "name", + "description", + "language", + "created_at", + "updated_at" + ], + "title": "VoiceProfileResponse", + "description": "Response model for voice profile." + } + } + }, + "tags": [ + { + "name": "General", + "description": "Root and health check endpoints" + }, + { + "name": "Profiles", + "description": "Voice profile management" + }, + { + "name": "Generation", + "description": "Speech generation, transcription, and audio retrieval" + }, + { + "name": "History", + "description": "Generation history and statistics" + }, + { + "name": "Models", + "description": "Model loading, unloading, and status management" + } + ] +} \ No newline at end of file