diff --git a/run.py b/run.py index 645a463..7aca838 100644 --- a/run.py +++ b/run.py @@ -35,9 +35,6 @@ from voicevox_engine.preset.preset_manager import PresetManager from voicevox_engine.setting.model import CorsPolicyMode from voicevox_engine.setting.setting_manager import USER_SETTING_PATH, SettingHandler -from voicevox_engine.tts_pipeline.style_bert_vits2_tts_engine import ( - StyleBertVITS2TTSEngine, -) from voicevox_engine.tts_pipeline.tts_engine import TTSEngineManager from voicevox_engine.user_dict.user_dict_manager import UserDictionary from voicevox_engine.utility.path_utility import ( @@ -334,150 +331,159 @@ def read_cli_arguments(envs: Envs) -> CLIArgs: def main() -> None: """AivisSpeech Engine を実行する""" - multiprocessing.freeze_support() - - envs = read_environment_variables() - - if envs.output_log_utf8: - set_output_log_utf8() - - args = read_cli_arguments(envs) - - if args.output_log_utf8: - set_output_log_utf8() - - # Sentry によるエラートラッキングを開始 - # ref: https://docs.sentry.io/platforms/python/integrations/fastapi/ - sentry_sdk.init( - dsn="https://ebdf5cc288b3ab31a262186329ff3a95@o4508551725383680.ingest.us.sentry.io/4508555159470080", - release=f"AivisSpeech-Engine@{__version__}", - environment="development" if __version__ == "latest" else "production", - # Set traces_sample_rate to 1.0 to capture 100% - # of transactions for tracing. - traces_sample_rate=1.0, - _experiments={ - # Set continuous_profiling_auto_start to True - # to automatically start the profiler on when - # possible. - "continuous_profiling_auto_start": True, - }, - ) - - # 起動時の可能な限り早い段階で実行結果をキャッシュしておくのが重要 - generate_user_agent("GPU" if args.use_gpu is True else "CPU") - - logger.info(f"AivisSpeech Engine version {__version__}") - logger.info(f"Engine root directory: {engine_root()}") - logger.info(f"User data directory: {get_save_dir()}") - - core_manager = initialize_cores( - use_gpu=args.use_gpu, - voicelib_dirs=args.voicelib_dirs, - voicevox_dir=args.voicevox_dir, - runtime_dirs=args.runtime_dirs, - cpu_num_threads=args.cpu_num_threads, - enable_mock=args.enable_mock, - load_all_models=args.load_all_models, - ) - # tts_engines = make_tts_engines_from_cores(core_manager) - # assert len(tts_engines.versions()) != 0, "音声合成エンジンがありません。" + try: + multiprocessing.freeze_support() + + envs = read_environment_variables() + if envs.output_log_utf8: + set_output_log_utf8() + + args = read_cli_arguments(envs) + if args.output_log_utf8: + set_output_log_utf8() + + # Sentry によるエラートラッキングを開始 + # ref: https://docs.sentry.io/platforms/python/integrations/fastapi/ + sentry_sdk.init( + dsn="https://ebdf5cc288b3ab31a262186329ff3a95@o4508551725383680.ingest.us.sentry.io/4508555159470080", + release=f"AivisSpeech-Engine@{__version__}", + environment="development" if __version__ == "latest" else "production", + # Set traces_sample_rate to 1.0 to capture 100% + # of transactions for tracing. + traces_sample_rate=1.0, + _experiments={ + # Set continuous_profiling_auto_start to True + # to automatically start the profiler on when + # possible. + "continuous_profiling_auto_start": True, + }, + ) - # AivmManager を初期化 - aivm_manager = AivmManager(get_save_dir() / "Models") + # 起動時の可能な限り早い段階で実行結果をキャッシュしておくのが重要 + generate_user_agent("GPU" if args.use_gpu is True else "CPU") - # StyleBertVITS2TTSEngine を通常の TTSEngine の代わりに利用 - tts_engines = TTSEngineManager() - tts_engines.register_engine( - StyleBertVITS2TTSEngine(aivm_manager, args.use_gpu, args.load_all_models), - MOCK_VER, - ) + logger.info(f"AivisSpeech Engine version {__version__}") + logger.info(f"Engine root directory: {engine_root()}") + logger.info(f"User data directory: {get_save_dir()}") - cancellable_engine: CancellableEngine | None = None - if args.enable_cancellable_synthesis: - cancellable_engine = CancellableEngine( - init_processes=args.init_processes, + core_manager = initialize_cores( use_gpu=args.use_gpu, voicelib_dirs=args.voicelib_dirs, voicevox_dir=args.voicevox_dir, runtime_dirs=args.runtime_dirs, cpu_num_threads=args.cpu_num_threads, enable_mock=args.enable_mock, + load_all_models=args.load_all_models, ) + # tts_engines = make_tts_engines_from_cores(core_manager) + # assert len(tts_engines.versions()) != 0, "音声合成エンジンがありません。" - setting_loader = SettingHandler(args.setting_file) - settings = setting_loader.load() + # AivmManager を初期化 + aivm_manager = AivmManager(get_save_dir() / "Models") - # 複数方式で指定可能な場合、優先度は上から「引数」「環境変数」「設定ファイル」「デフォルト値」 + # ごく稀に style_bert_vits2_tts_engine.py (が依存する onnxruntime) のインポート自体に失敗し + # 例外が発生する環境があるようなので、例外をキャッチしてエラーログに出力できるよう、敢えてルーター初期化時にインポートする + from voicevox_engine.tts_pipeline.style_bert_vits2_tts_engine import ( + StyleBertVITS2TTSEngine, + ) - cors_policy_mode = select_first_not_none( - [args.cors_policy_mode, settings.cors_policy_mode] - ) + # StyleBertVITS2TTSEngine を通常の TTSEngine の代わりに利用 + tts_engines = TTSEngineManager() + tts_engines.register_engine( + StyleBertVITS2TTSEngine(aivm_manager, args.use_gpu, args.load_all_models), + MOCK_VER, + ) - setting_allow_origins = None - if settings.allow_origin is not None: - setting_allow_origins = settings.allow_origin.split(" ") - allow_origin = select_first_not_none_or_none( - [args.allow_origins, setting_allow_origins] - ) + cancellable_engine: CancellableEngine | None = None + if args.enable_cancellable_synthesis: + cancellable_engine = CancellableEngine( + init_processes=args.init_processes, + use_gpu=args.use_gpu, + voicelib_dirs=args.voicelib_dirs, + voicevox_dir=args.voicevox_dir, + runtime_dirs=args.runtime_dirs, + cpu_num_threads=args.cpu_num_threads, + enable_mock=args.enable_mock, + ) + + setting_loader = SettingHandler(args.setting_file) + settings = setting_loader.load() + + # 複数方式で指定可能な場合、優先度は上から「引数」「環境変数」「設定ファイル」「デフォルト値」 + + cors_policy_mode = select_first_not_none( + [args.cors_policy_mode, settings.cors_policy_mode] + ) - if envs.env_preset_path is not None and len(envs.env_preset_path) != 0: - env_preset_path = Path(envs.env_preset_path) - else: - env_preset_path = None - default_preset_path = get_save_dir() / "presets.yaml" - preset_path = select_first_not_none( - [args.preset_file, env_preset_path, default_preset_path] - ) - preset_manager = PresetManager(preset_path) + setting_allow_origins = None + if settings.allow_origin is not None: + setting_allow_origins = settings.allow_origin.split(" ") + allow_origin = select_first_not_none_or_none( + [args.allow_origins, setting_allow_origins] + ) - user_dict = UserDictionary() + if envs.env_preset_path is not None and len(envs.env_preset_path) != 0: + env_preset_path = Path(envs.env_preset_path) + else: + env_preset_path = None + default_preset_path = get_save_dir() / "presets.yaml" + preset_path = select_first_not_none( + [args.preset_file, env_preset_path, default_preset_path] + ) + preset_manager = PresetManager(preset_path) - engine_manifest = load_manifest(engine_manifest_path()) + user_dict = UserDictionary() - library_manager = LibraryManager( - # get_save_dir() / "installed_libraries", - # AivisSpeech では利用しない LibraryManager によるディレクトリ作成を防ぐため、get_save_dir() 直下を指定 - get_save_dir(), - engine_manifest.supported_vvlib_manifest_version, - engine_manifest.brand_name, - engine_manifest.name, - engine_manifest.uuid, - ) + engine_manifest = load_manifest(engine_manifest_path()) - if args.disable_mutable_api: - disable_mutable_api = True - else: - disable_mutable_api = envs.disable_mutable_api - - root_dir = select_first_not_none([args.voicevox_dir, engine_root()]) - character_info_dir = root_dir / "resources" / "character_info" - # NOTE: ENGINE v0.19 以前向けに後方互換性を確保する - if not character_info_dir.exists(): - character_info_dir = root_dir / "speaker_info" - - # ASGI に準拠した AivisSpeech Engine アプリケーションを生成する - app = generate_app( - tts_engines, - aivm_manager, - core_manager, - setting_loader, - preset_manager, - user_dict, - engine_manifest, - library_manager, - cancellable_engine, - character_info_dir, - cors_policy_mode, - allow_origin, - disable_mutable_api=disable_mutable_api, - ) + library_manager = LibraryManager( + # get_save_dir() / "installed_libraries", + # AivisSpeech では利用しない LibraryManager によるディレクトリ作成を防ぐため、get_save_dir() 直下を指定 + get_save_dir(), + engine_manifest.supported_vvlib_manifest_version, + engine_manifest.brand_name, + engine_manifest.name, + engine_manifest.uuid, + ) + + if args.disable_mutable_api: + disable_mutable_api = True + else: + disable_mutable_api = envs.disable_mutable_api + + root_dir = select_first_not_none([args.voicevox_dir, engine_root()]) + character_info_dir = root_dir / "resources" / "character_info" + # NOTE: ENGINE v0.19 以前向けに後方互換性を確保する + if not character_info_dir.exists(): + character_info_dir = root_dir / "speaker_info" + + # ASGI に準拠した AivisSpeech Engine アプリケーションを生成する + app = generate_app( + tts_engines, + aivm_manager, + core_manager, + setting_loader, + preset_manager, + user_dict, + engine_manifest, + library_manager, + cancellable_engine, + character_info_dir, + cors_policy_mode, + allow_origin, + disable_mutable_api=disable_mutable_api, + ) + + # 起動処理にのみに要したメモリを開放 + gc.collect() - # 起動処理にのみに要したメモリを開放 - gc.collect() + # AivisSpeech Engine サーバーを起動 + # NOTE: デフォルトは ASGI に準拠した HTTP/1.1 サーバー + uvicorn.run(app, host=args.host, port=args.port, log_config=LOGGING_CONFIG) - # AivisSpeech Engine サーバーを起動 - # NOTE: デフォルトは ASGI に準拠した HTTP/1.1 サーバー - uvicorn.run(app, host=args.host, port=args.port, log_config=LOGGING_CONFIG) + except Exception as ex: + logger.error(f"Unexpected error occurred during engine startup:", exc_info=ex) + raise ex if __name__ == "__main__": diff --git a/voicevox_engine/aivm_manager.py b/voicevox_engine/aivm_manager.py index 8f10187..24a8d9e 100644 --- a/voicevox_engine/aivm_manager.py +++ b/voicevox_engine/aivm_manager.py @@ -457,8 +457,7 @@ def get_installed_aivm_infos( ).start() except Exception as ex: # 非同期タスクの開始に失敗しても起動に影響を与えないよう、ログ出力のみ行う - logger.warning(f"Failed to start async update task:") - logger.warning(ex) + logger.warning(f"Failed to start async update task:", exc_info=ex) return self._installed_aivm_infos @@ -510,9 +509,9 @@ async def fetch_latest_version(aivm_info: AivmInfo) -> None: except httpx.TimeoutException as ex: logger.warning( - f"Timeout while fetching model info for {aivm_info.manifest.uuid} from AivisHub:" + f"Timeout while fetching model info for {aivm_info.manifest.uuid} from AivisHub:", + exc_info=ex, ) - logger.warning(ex) except Exception as ex: # エラーが発生しても起動に影響を与えないよう、ログ出力のみ行う # - httpx.RequestError: ネットワークエラーなど @@ -520,9 +519,9 @@ async def fetch_latest_version(aivm_info: AivmInfo) -> None: # - StopIteration: model_files に AIVMX が存在しない # - ValueError: Version.parse() が失敗 logger.warning( - f"Failed to fetch model info for {aivm_info.manifest.uuid} from AivisHub:" + f"Failed to fetch model info for {aivm_info.manifest.uuid} from AivisHub:", + exc_info=ex, ) - logger.warning(ex) # 全モデルの更新タスクを作成 assert self._installed_aivm_infos is not None @@ -581,8 +580,7 @@ def install_aivm(self, file: BinaryIO) -> None: aivm_metadata = aivmlib.read_aivmx_metadata(file) aivm_manifest = aivm_metadata.manifest except aivmlib.AivmValidationError as ex: - logger.error(f"AIVMX file is invalid.") - logger.error(ex) + logger.error(f"AIVMX file is invalid:", exc_info=ex) raise HTTPException( status_code=422, detail=f"指定された AIVMX ファイルの形式が正しくありません。({ex})", @@ -657,8 +655,7 @@ def install_aivm_from_url(self, url: str) -> None: response.raise_for_status() logger.info(f"Downloaded AIVMX file from {url}.") except httpx.HTTPError as ex: - logger.error(f"Failed to download AIVMX file from {url}:") - logger.error(ex) + logger.error(f"Failed to download AIVMX file from {url}:", exc_info=ex) raise HTTPException( status_code=500, detail=f"AIVMX ファイルのダウンロードに失敗しました。({ex})", diff --git a/voicevox_engine/app/routers/aivm_models.py b/voicevox_engine/app/routers/aivm_models.py index 4badeb5..eaa55ec 100644 --- a/voicevox_engine/app/routers/aivm_models.py +++ b/voicevox_engine/app/routers/aivm_models.py @@ -6,9 +6,6 @@ from voicevox_engine.aivm_manager import AivmManager from voicevox_engine.model import AivmInfo -from voicevox_engine.tts_pipeline.style_bert_vits2_tts_engine import ( - StyleBertVITS2TTSEngine, -) from voicevox_engine.tts_pipeline.tts_engine import LATEST_VERSION, TTSEngineManager from ..dependencies import VerifyMutabilityAllowed @@ -21,6 +18,12 @@ def generate_aivm_models_router( ) -> APIRouter: """音声合成モデル管理 API Router を生成する""" + # ごく稀に style_bert_vits2_tts_engine.py (が依存する onnxruntime) のインポート自体に失敗し + # 例外が発生する環境があるようなので、例外をキャッチしてエラーログに出力できるよう、敢えてルーター初期化時にインポートする + from voicevox_engine.tts_pipeline.style_bert_vits2_tts_engine import ( + StyleBertVITS2TTSEngine, + ) + router = APIRouter( prefix="/aivm_models", tags=["音声合成モデル管理"],