From 96ea82d60a8bae290eda5530bfee909705381aca Mon Sep 17 00:00:00 2001 From: tsukumi Date: Tue, 26 Nov 2024 04:36:15 +0900 Subject: [PATCH] =?UTF-8?q?Refactor:=20FastAPI=20=E3=81=AB=E3=82=88?= =?UTF-8?q?=E3=81=A3=E3=81=A6=E8=87=AA=E5=8B=95=E7=94=9F=E6=88=90=E3=81=95?= =?UTF-8?q?=E3=82=8C=E3=82=8B=20API=20=E3=83=89=E3=82=AD=E3=83=A5=E3=83=A1?= =?UTF-8?q?=E3=83=B3=E3=83=88=E3=81=AE=E6=95=B4=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 「その他」に分類される API エンドポイントがあまりに多すぎたほか、サマリーのない API も多かったため、API ドキュメント上で見栄えよく重要な API をすぐ把握できるよう改良した --- voicevox_engine/app/application.py | 8 ++-- voicevox_engine/app/routers/aivm_models.py | 9 +++- voicevox_engine/app/routers/character.py | 18 +++++--- voicevox_engine/app/routers/morphing.py | 14 +++--- voicevox_engine/app/routers/preset.py | 14 +++--- voicevox_engine/app/routers/tts_pipeline.py | 48 +++++++++++++++------ voicevox_engine/app/routers/user_dict.py | 11 ++++- 7 files changed, 84 insertions(+), 38 deletions(-) diff --git a/voicevox_engine/app/application.py b/voicevox_engine/app/application.py index 9b520417..a17d4a41 100644 --- a/voicevox_engine/app/application.py +++ b/voicevox_engine/app/application.py @@ -87,9 +87,6 @@ def _get_core_characters(version: str | None) -> list[CoreCharacter]: generate_tts_pipeline_router(tts_engines, preset_manager, cancellable_engine) ) app.include_router(generate_morphing_router(tts_engines, aivm_manager)) - app.include_router( - generate_preset_router(preset_manager, verify_mutability_allowed) - ) app.include_router(generate_character_router(resource_manager, aivm_manager)) if engine_manifest.supported_features.manage_library: app.include_router( @@ -97,13 +94,16 @@ def _get_core_characters(version: str | None) -> list[CoreCharacter]: ) # generate_aivm_models_router() は AivisSpeech Engine 独自追加ルーター app.include_router(generate_aivm_models_router(aivm_manager, verify_mutability_allowed)) # noqa # fmt: skip + app.include_router( + generate_preset_router(preset_manager, verify_mutability_allowed) + ) app.include_router(generate_user_dict_router(user_dict, verify_mutability_allowed)) - app.include_router(generate_engine_info_router(core_version_list, engine_manifest)) app.include_router( generate_setting_router( setting_loader, engine_manifest.brand_name, verify_mutability_allowed ) ) + app.include_router(generate_engine_info_router(core_version_list, engine_manifest)) app.include_router(generate_portal_page_router(engine_manifest.name)) app = configure_openapi_schema( diff --git a/voicevox_engine/app/routers/aivm_models.py b/voicevox_engine/app/routers/aivm_models.py index 35f7220e..f7177b74 100644 --- a/voicevox_engine/app/routers/aivm_models.py +++ b/voicevox_engine/app/routers/aivm_models.py @@ -23,11 +23,12 @@ def generate_aivm_models_router( @router.get( "", - response_description="インストールした音声合成モデルの情報", + summary="インストール済みのすべての音声合成モデルの情報を取得する", + response_description="インストール済みのすべての音声合成モデルの情報", ) def get_installed_aivm_infos() -> dict[str, AivmInfo]: """ - インストールした音声合成モデルの情報を返します。 + インストール済みのすべての音声合成モデルの情報を返します。 """ return aivm_manager.get_installed_aivm_infos() @@ -36,6 +37,7 @@ def get_installed_aivm_infos() -> dict[str, AivmInfo]: "/install", status_code=204, dependencies=[Depends(verify_mutability)], + summary="音声合成モデルをインストールする", ) def install_aivm( file: Annotated[ @@ -65,6 +67,8 @@ def install_aivm( @router.get( "/{aivm_uuid}", + summary="指定された音声合成モデルの情報を取得する", + response_description="指定された音声合成モデルの情報", ) def get_aivm_info( aivm_uuid: Annotated[str, Path(description="音声合成モデルの UUID")] @@ -79,6 +83,7 @@ def get_aivm_info( "/{aivm_uuid}/uninstall", status_code=204, dependencies=[Depends(verify_mutability)], + summary="音声合成モデルをアンインストールする", ) def uninstall_aivm( aivm_uuid: Annotated[str, Path(description="音声合成モデルの UUID")] diff --git a/voicevox_engine/app/routers/character.py b/voicevox_engine/app/routers/character.py index fd63ae7e..b5789ded 100644 --- a/voicevox_engine/app/routers/character.py +++ b/voicevox_engine/app/routers/character.py @@ -39,16 +39,19 @@ def generate_character_router( aivm_manager: AivmManager, ) -> APIRouter: """キャラクター情報 API Router を生成する""" - router = APIRouter(tags=["その他"]) + router = APIRouter(tags=["話者情報"]) - @router.get("/speakers") + @router.get( + "/speakers", + summary="話者情報の一覧を取得する", + ) def speakers( core_version: Annotated[ str | SkipJsonSchema[None], Query(description="AivisSpeech Engine ではサポートされていないパラメータです (常に無視されます) 。"), ] = None, # fmt: skip # noqa ) -> list[Speaker]: - """喋れるキャラクターの情報の一覧を返します。""" + """話者情報の一覧を返します。""" # AivisSpeech Engine では常に AivmManager から Speaker を取得する return aivm_manager.get_speakers() """ @@ -56,7 +59,10 @@ def speakers( return _characters_to_speakers(characters) """ - @router.get("/speaker_info") + @router.get( + "/speaker_info", + summary="UUID で指定された話者の情報を取得する", + ) def speaker_info( resource_baseurl: Annotated[str, Depends(_get_resource_baseurl)], speaker_uuid: str, @@ -67,8 +73,8 @@ def speaker_info( ] = None, # fmt: skip # noqa ) -> SpeakerInfo: """ - UUID で指定された喋れるキャラクターの情報を返します。 - 画像や音声はresource_formatで指定した形式で返されます。 + UUID で指定された話者の情報を返します。 + 画像や音声は resource_format で指定した形式で返されます。 """ # AivisSpeech Engine では常に AivmManager から SpeakerInfo を取得する return aivm_manager.get_speaker_info(speaker_uuid) diff --git a/voicevox_engine/app/routers/morphing.py b/voicevox_engine/app/routers/morphing.py index 8ccfff9c..01c9ff84 100644 --- a/voicevox_engine/app/routers/morphing.py +++ b/voicevox_engine/app/routers/morphing.py @@ -51,13 +51,13 @@ def morphable_targets( ] = None, # fmt: skip # noqa ) -> list[dict[str, MorphableTargetInfo]]: """ - 指定されたベーススタイルに対してエンジン内の各キャラクターがモーフィング機能を利用可能か返します。 - モーフィングの許可/禁止は `/speakers `の `speaker.supported_features.synthesis_morphing` に記載されています。 - プロパティが存在しない場合は、モーフィングが許可されているとみなします。 - 返り値のスタイル ID は string 型なので注意。 + 指定されたベーススタイルに対してエンジン内の各キャラクターがモーフィング機能を利用可能か返します。
+ モーフィングの許可/禁止は `/speakers `の `speaker.supported_features.synthesis_morphing` に記載されています。
+ プロパティが存在しない場合は、モーフィングが許可されているとみなします。
+ 返り値のスタイル ID は string 型なので注意。
AivisSpeech Engine では話者ごとに発声タイミングが異なる関係で実装不可能なため (動作こそするが聴くに耐えない) 、 全ての話者でモーフィングが禁止されています。 - """ + """ # noqa characters = aivm_manager.get_characters() try: morphable_targets = get_morphable_targets(characters, base_style_ids) @@ -93,8 +93,8 @@ def _synthesis_morphing( ] = None, # fmt: skip # noqa ) -> FileResponse: """ - 指定された 2 種類のスタイルで音声を合成、指定した割合でモーフィングした音声を得ます。 - モーフィングの割合は `morph_rate` で指定でき、0.0 でベースのスタイル、1.0 でターゲットのスタイルに近づきます。 + 指定された 2 種類のスタイルで音声を合成、指定した割合でモーフィングした音声を得ます。
+ モーフィングの割合は `morph_rate` で指定でき、0.0 でベースのスタイル、1.0 でターゲットのスタイルに近づきます。
AivisSpeech Engine では話者ごとに発声タイミングが異なる関係で実装不可能なため (動作こそするが聴くに耐えない) 、 常に 400 Bad Request を返します。 """ diff --git a/voicevox_engine/app/routers/preset.py b/voicevox_engine/app/routers/preset.py index c01fc12b..d31c1377 100644 --- a/voicevox_engine/app/routers/preset.py +++ b/voicevox_engine/app/routers/preset.py @@ -18,15 +18,16 @@ def generate_preset_router( preset_manager: PresetManager, verify_mutability: VerifyMutabilityAllowed ) -> APIRouter: """プリセット API Router を生成する""" - router = APIRouter(tags=["その他"]) + router = APIRouter(tags=["プリセット"]) @router.get( "/presets", + summary="エンジンが保持しているプリセットの設定を取得する", response_description="プリセットのリスト", ) def get_presets() -> list[Preset]: """ - エンジンが保持しているプリセットの設定を返します + エンジンが保持しているプリセットの設定を返します。 """ try: presets = preset_manager.load_presets() @@ -38,6 +39,7 @@ def get_presets() -> list[Preset]: @router.post( "/add_preset", + summary="新しいプリセットを追加する", response_description="追加したプリセットのプリセットID", dependencies=[Depends(verify_mutability)], ) @@ -50,7 +52,7 @@ def add_preset( ] ) -> int: """ - 新しいプリセットを追加します + 新しいプリセットを追加します。 """ try: id = preset_manager.add_preset(preset) @@ -62,6 +64,7 @@ def add_preset( @router.post( "/update_preset", + summary="既存のプリセットを更新する", response_description="更新したプリセットのプリセットID", dependencies=[Depends(verify_mutability)], ) @@ -74,7 +77,7 @@ def update_preset( ] ) -> int: """ - 既存のプリセットを更新します + 既存のプリセットを更新します。 """ try: id = preset_manager.update_preset(preset) @@ -86,6 +89,7 @@ def update_preset( @router.post( "/delete_preset", + summary="既存のプリセットを削除する", status_code=204, dependencies=[Depends(verify_mutability)], ) @@ -93,7 +97,7 @@ def delete_preset( id: Annotated[int, Query(description="削除するプリセットのプリセットID")] ) -> None: """ - 既存のプリセットを削除します + 既存のプリセットを削除します。 """ try: preset_manager.delete_preset(id) diff --git a/voicevox_engine/app/routers/tts_pipeline.py b/voicevox_engine/app/routers/tts_pipeline.py index bcac90d0..380600c5 100644 --- a/voicevox_engine/app/routers/tts_pipeline.py +++ b/voicevox_engine/app/routers/tts_pipeline.py @@ -213,7 +213,7 @@ def accent_phrases( @router.post( "/mora_data", tags=["クエリ編集"], - summary="アクセント句から音高・音素長を得る", + summary="アクセント句から音高・音素長を得る (AivisSpeech Engine では常にダミーの値が返されます)", ) def mora_data( accent_phrases: list[AccentPhrase], @@ -230,7 +230,7 @@ def mora_data( @router.post( "/mora_length", tags=["クエリ編集"], - summary="アクセント句から音素長を得る", + summary="アクセント句から音素長を得る (AivisSpeech Engine では常にダミーの値が返されます)", ) def mora_length( accent_phrases: list[AccentPhrase], @@ -247,7 +247,7 @@ def mora_length( @router.post( "/mora_pitch", tags=["クエリ編集"], - summary="アクセント句から音高を得る", + summary="アクセント句から音高を得る (AivisSpeech Engine では常にダミーの値が返されます)", ) def mora_pitch( accent_phrases: list[AccentPhrase], @@ -286,6 +286,9 @@ def synthesis( Query(description="AivisSpeech Engine ではサポートされていないパラメータです (常に無視されます) 。"), ] = None, # fmt: skip # noqa ) -> FileResponse: + """ + 指定されたスタイル ID に紐づく音声合成モデルを用いて音声合成を行います。 + """ version = core_version or LATEST_VERSION engine = tts_engines.get_engine(version) wave = engine.synthesize_wave( @@ -536,7 +539,7 @@ def frame_synthesis( }, } }, - tags=["その他"], + tags=["音声合成"], summary="base64エンコードされた複数のwavデータを一つに結合する", ) def connect_waves(waves: list[str]) -> FileResponse: @@ -564,7 +567,7 @@ def connect_waves(waves: list[str]) -> FileResponse: @router.post( "/validate_kana", - tags=["その他"], + tags=["クエリ作成"], summary="テキストが AquesTalk 風記法に従っているか判定する", responses={ 400: { @@ -589,13 +592,18 @@ async def validate_kana( detail=ParseKanaBadRequest(err).model_dump(), ) - @router.post("/initialize_speaker", status_code=204, tags=["その他"]) + @router.post( + "/initialize_speaker", + status_code=204, + tags=["音声合成モデル管理"], + summary="指定されたスタイル ID に紐づく音声合成モデルをロードする", + ) def initialize_speaker( style_id: Annotated[StyleId, Query(alias="speaker")], skip_reinit: Annotated[ bool, Query( - description="既に初期化済みのスタイルの再初期化をスキップするかどうか" + description="既にロード済みの音声合成モデルの再ロードをスキップするかどうか" ), ] = False, core_version: Annotated[ @@ -604,14 +612,18 @@ def initialize_speaker( ] = None, # fmt: skip # noqa ) -> None: """ - 指定されたスタイルを初期化します。 - 実行しなくても他のAPIは使用できますが、初回実行時に時間がかかることがあります。 + 指定されたスタイル ID に紐づく音声合成モデルをロードします。 + 実行しなくても他の API は使用できますが、初回実行時に時間がかかることがあります。 """ version = core_version or LATEST_VERSION engine = tts_engines.get_engine(version) engine.initialize_synthesis(style_id, skip_reinit=skip_reinit) - @router.get("/is_initialized_speaker", tags=["その他"]) + @router.get( + "/is_initialized_speaker", + tags=["音声合成モデル管理"], + summary="指定されたスタイル ID に紐づく音声合成モデルがロードされているかを確認する", + ) def is_initialized_speaker( style_id: Annotated[StyleId, Query(alias="speaker")], core_version: Annotated[ @@ -620,20 +632,30 @@ def is_initialized_speaker( ] = None, # fmt: skip # noqa ) -> bool: """ - 指定されたスタイルが初期化されているかどうかを返します。 + 指定されたスタイル ID に紐づく音声合成モデルがロードされているかどうかを返します。 """ version = core_version or LATEST_VERSION engine = tts_engines.get_engine(version) return engine.is_synthesis_initialized(style_id) - @router.get("/supported_devices", tags=["その他"]) + @router.get( + "/supported_devices", + tags=["音声合成モデル管理"], + summary="このビルドでサポートされている、音声合成モデルの推論デバイスを取得する", + ) def supported_devices( core_version: Annotated[ str | SkipJsonSchema[None], Query(description="AivisSpeech Engine ではサポートされていないパラメータです (常に無視されます) 。"), ] = None, # fmt: skip # noqa ) -> SupportedDevicesInfo: - """対応デバイスの一覧を取得します。""" + """ + このビルドでサポートされている、音声合成モデルの推論デバイスを返します。
+ 通常、下記の値が返されます。true であっても実際に推論デバイスが利用可能とは限りません。 + - Windows: `{"cpu": true, "cuda": false, "dml": true}` + - macOS: `{"cpu": true, "cuda": false, "dml": false}` + - Linux: `{"cpu": true, "cuda": true, "dml": false}` + """ version = core_version or LATEST_VERSION supported_devices = tts_engines.get_engine(version).supported_devices if supported_devices is None: diff --git a/voicevox_engine/app/routers/user_dict.py b/voicevox_engine/app/routers/user_dict.py index 10b40c69..9c5f3b1f 100644 --- a/voicevox_engine/app/routers/user_dict.py +++ b/voicevox_engine/app/routers/user_dict.py @@ -26,6 +26,7 @@ def generate_user_dict_router( @router.get( "/user_dict", + summary="ユーザー辞書に登録されている単語の一覧を取得する", response_description="単語のUUIDとその詳細", ) def get_user_dict_words() -> dict[str, UserDictWord]: @@ -42,7 +43,12 @@ def get_user_dict_words() -> dict[str, UserDictWord]: status_code=500, detail="辞書の読み込みに失敗しました。" ) - @router.post("/user_dict_word", dependencies=[Depends(verify_mutability)]) + @router.post( + "/user_dict_word", + dependencies=[Depends(verify_mutability)], + summary="ユーザー辞書に言葉を追加する", + response_description="追加した言葉のUUID", + ) def add_user_dict_word( surface: Annotated[str, Query(description="言葉の表層形")], pronunciation: Annotated[str, Query(description="言葉の発音(カタカナ)")], @@ -100,6 +106,7 @@ def add_user_dict_word( "/user_dict_word/{word_uuid}", status_code=204, dependencies=[Depends(verify_mutability)], + summary="ユーザー辞書に登録されている言葉を更新する", ) def rewrite_user_dict_word( surface: Annotated[str, Query(description="言葉の表層形")], @@ -159,6 +166,7 @@ def rewrite_user_dict_word( "/user_dict_word/{word_uuid}", status_code=204, dependencies=[Depends(verify_mutability)], + summary="ユーザー辞書に登録されている言葉を削除する", ) def delete_user_dict_word( word_uuid: Annotated[str, Path(description="削除する言葉のUUID")] @@ -179,6 +187,7 @@ def delete_user_dict_word( "/import_user_dict", status_code=204, dependencies=[Depends(verify_mutability)], + summary="他のユーザー辞書をインポートする", ) def import_user_dict_words( import_dict_data: Annotated[