Skip to content

Commit

Permalink
Fix: エンジン起動時に発生した例外もログファイルに保存されるよう調整
Browse files Browse the repository at this point in the history
また今まで logger.error(ex) としていた箇所に関して、exc_info=ex に例外を渡した方が Traceback が出て良いことに気づき、そうした
  • Loading branch information
tsukumijima committed Jan 2, 2025
1 parent 86b6bbb commit 02111f7
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 139 deletions.
258 changes: 132 additions & 126 deletions run.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down Expand Up @@ -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__":
Expand Down
17 changes: 7 additions & 10 deletions voicevox_engine/aivm_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -510,19 +509,19 @@ 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: ネットワークエラーなど
# - KeyError: レスポンスのJSONに必要なキーが存在しない
# - 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
Expand Down Expand Up @@ -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})",
Expand Down Expand Up @@ -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})",
Expand Down
9 changes: 6 additions & 3 deletions voicevox_engine/app/routers/aivm_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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=["音声合成モデル管理"],
Expand Down

0 comments on commit 02111f7

Please sign in to comment.