Files
VRCT/src-python/errors.py
misyaguziya b0cf8bf335 feat: Implement unified error handling system
- Added `errors.py` to define a centralized error management system with error codes and metadata.
- Created `VRCTError` class for generating standardized error responses.
- Introduced `error_handling_migration_guide.md` to document migration patterns for existing error handling to the new system.
- Updated error handling patterns in the codebase to utilize the new error management system.
2026-01-02 19:38:06 +09:00

695 lines
25 KiB
Python

# src-python/errors.py
"""
統一エラー管理システム
すべてのエラーを一元管理し、エンドポイントとエラーコードの対応を明確にする。
"""
from typing import Any, Optional, Dict
from enum import Enum
class ErrorCode(str, Enum):
"""エラーコード定数
命名規則: カテゴリ_具体的な内容
"""
# ============================================================================
# デバイス関連エラー (DEVICE_*)
# ============================================================================
DEVICE_NO_MIC = "DEVICE_NO_MIC"
DEVICE_NO_SPEAKER = "DEVICE_NO_SPEAKER"
# ============================================================================
# 翻訳関連エラー (TRANSLATION_*)
# ============================================================================
TRANSLATION_ENGINE_LIMIT = "TRANSLATION_ENGINE_LIMIT"
TRANSLATION_VRAM_CHAT = "TRANSLATION_VRAM_CHAT"
TRANSLATION_VRAM_MIC = "TRANSLATION_VRAM_MIC"
TRANSLATION_VRAM_SPEAKER = "TRANSLATION_VRAM_SPEAKER"
TRANSLATION_VRAM_ENABLE = "TRANSLATION_VRAM_ENABLE"
TRANSLATION_DISABLED_VRAM = "TRANSLATION_DISABLED_VRAM"
# ============================================================================
# 音声認識関連エラー (TRANSCRIPTION_*)
# ============================================================================
TRANSCRIPTION_VRAM_MIC = "TRANSCRIPTION_VRAM_MIC"
TRANSCRIPTION_VRAM_SPEAKER = "TRANSCRIPTION_VRAM_SPEAKER"
TRANSCRIPTION_SEND_DISABLED_VRAM = "TRANSCRIPTION_SEND_DISABLED_VRAM"
TRANSCRIPTION_RECEIVE_DISABLED_VRAM = "TRANSCRIPTION_RECEIVE_DISABLED_VRAM"
# ============================================================================
# ウェイトダウンロード関連エラー (WEIGHT_*)
# ============================================================================
WEIGHT_CTRANSLATE2_DOWNLOAD = "WEIGHT_CTRANSLATE2_DOWNLOAD"
WEIGHT_WHISPER_DOWNLOAD = "WEIGHT_WHISPER_DOWNLOAD"
# ============================================================================
# バリデーションエラー (VALIDATION_*)
# ============================================================================
VALIDATION_MIC_THRESHOLD = "VALIDATION_MIC_THRESHOLD"
VALIDATION_SPEAKER_THRESHOLD = "VALIDATION_SPEAKER_THRESHOLD"
VALIDATION_MIC_RECORD_TIMEOUT = "VALIDATION_MIC_RECORD_TIMEOUT"
VALIDATION_MIC_PHRASE_TIMEOUT = "VALIDATION_MIC_PHRASE_TIMEOUT"
VALIDATION_MIC_MAX_PHRASES = "VALIDATION_MIC_MAX_PHRASES"
VALIDATION_SPEAKER_RECORD_TIMEOUT = "VALIDATION_SPEAKER_RECORD_TIMEOUT"
VALIDATION_SPEAKER_PHRASE_TIMEOUT = "VALIDATION_SPEAKER_PHRASE_TIMEOUT"
VALIDATION_SPEAKER_MAX_PHRASES = "VALIDATION_SPEAKER_MAX_PHRASES"
VALIDATION_INVALID_IP = "VALIDATION_INVALID_IP"
VALIDATION_CANNOT_SET_IP = "VALIDATION_CANNOT_SET_IP"
# ============================================================================
# 認証エラー (AUTH_*)
# ============================================================================
AUTH_DEEPL_LENGTH = "AUTH_DEEPL_LENGTH"
AUTH_DEEPL_FAILED = "AUTH_DEEPL_FAILED"
AUTH_PLAMO_LENGTH = "AUTH_PLAMO_LENGTH"
AUTH_PLAMO_FAILED = "AUTH_PLAMO_FAILED"
AUTH_GEMINI_LENGTH = "AUTH_GEMINI_LENGTH"
AUTH_GEMINI_FAILED = "AUTH_GEMINI_FAILED"
AUTH_OPENAI_INVALID = "AUTH_OPENAI_INVALID"
AUTH_OPENAI_FAILED = "AUTH_OPENAI_FAILED"
AUTH_GROQ_INVALID = "AUTH_GROQ_INVALID"
AUTH_GROQ_FAILED = "AUTH_GROQ_FAILED"
AUTH_OPENROUTER_INVALID = "AUTH_OPENROUTER_INVALID"
AUTH_OPENROUTER_FAILED = "AUTH_OPENROUTER_FAILED"
# ============================================================================
# モデル選択エラー (MODEL_*)
# ============================================================================
MODEL_PLAMO_INVALID = "MODEL_PLAMO_INVALID"
MODEL_GEMINI_INVALID = "MODEL_GEMINI_INVALID"
MODEL_OPENAI_INVALID = "MODEL_OPENAI_INVALID"
MODEL_GROQ_INVALID = "MODEL_GROQ_INVALID"
MODEL_OPENROUTER_INVALID = "MODEL_OPENROUTER_INVALID"
MODEL_LMSTUDIO_INVALID = "MODEL_LMSTUDIO_INVALID"
MODEL_OLLAMA_INVALID = "MODEL_OLLAMA_INVALID"
# ============================================================================
# 接続エラー (CONNECTION_*)
# ============================================================================
CONNECTION_LMSTUDIO_FAILED = "CONNECTION_LMSTUDIO_FAILED"
CONNECTION_OLLAMA_FAILED = "CONNECTION_OLLAMA_FAILED"
CONNECTION_LMSTUDIO_URL_INVALID = "CONNECTION_LMSTUDIO_URL_INVALID"
# ============================================================================
# WebSocketエラー (WEBSOCKET_*)
# ============================================================================
WEBSOCKET_HOST_INVALID = "WEBSOCKET_HOST_INVALID"
WEBSOCKET_PORT_UNAVAILABLE = "WEBSOCKET_PORT_UNAVAILABLE"
WEBSOCKET_SERVER_UNAVAILABLE = "WEBSOCKET_SERVER_UNAVAILABLE"
# ============================================================================
# VRC連携エラー (VRC_*)
# ============================================================================
VRC_MIC_MUTE_SYNC_OSC_DISABLED = "VRC_MIC_MUTE_SYNC_OSC_DISABLED"
# ============================================================================
# 汎用エラー (GENERAL_*)
# ============================================================================
GENERAL_EXCEPTION = "GENERAL_EXCEPTION"
GENERAL_UNKNOWN = "GENERAL_UNKNOWN"
class ErrorCategory(str, Enum):
"""エラーカテゴリ"""
DEVICE = "device"
TRANSLATION = "translation"
TRANSCRIPTION = "transcription"
WEIGHT = "weight"
VALIDATION = "validation"
AUTH = "auth"
MODEL = "model"
CONNECTION = "connection"
WEBSOCKET = "websocket"
VRC = "vrc"
GENERAL = "general"
# エラーコードのメタデータ定義
ERROR_METADATA: Dict[ErrorCode, Dict[str, Any]] = {
# デバイスエラー
ErrorCode.DEVICE_NO_MIC: {
"category": ErrorCategory.DEVICE,
"message": "No mic device detected",
"severity": "error",
"user_action_required": True,
},
ErrorCode.DEVICE_NO_SPEAKER: {
"category": ErrorCategory.DEVICE,
"message": "No speaker device detected",
"severity": "error",
"user_action_required": True,
},
# 翻訳エラー
ErrorCode.TRANSLATION_ENGINE_LIMIT: {
"category": ErrorCategory.TRANSLATION,
"message": "Translation engine limit error",
"severity": "warning",
"user_action_required": False,
"auto_fallback": True,
},
ErrorCode.TRANSLATION_VRAM_CHAT: {
"category": ErrorCategory.TRANSLATION,
"message": "VRAM out of memory during translation of chat",
"severity": "critical",
"user_action_required": True,
},
ErrorCode.TRANSLATION_VRAM_MIC: {
"category": ErrorCategory.TRANSLATION,
"message": "VRAM out of memory during translation of mic",
"severity": "critical",
"user_action_required": True,
},
ErrorCode.TRANSLATION_VRAM_SPEAKER: {
"category": ErrorCategory.TRANSLATION,
"message": "VRAM out of memory during translation of speaker",
"severity": "critical",
"user_action_required": True,
},
ErrorCode.TRANSLATION_VRAM_ENABLE: {
"category": ErrorCategory.TRANSLATION,
"message": "VRAM out of memory enabling translation",
"severity": "critical",
"user_action_required": True,
},
ErrorCode.TRANSLATION_DISABLED_VRAM: {
"category": ErrorCategory.TRANSLATION,
"message": "Translation disabled due to VRAM overflow",
"severity": "critical",
"user_action_required": True,
},
# 音声認識エラー
ErrorCode.TRANSCRIPTION_VRAM_MIC: {
"category": ErrorCategory.TRANSCRIPTION,
"message": "VRAM out of memory during mic transcription",
"severity": "critical",
"user_action_required": True,
},
ErrorCode.TRANSCRIPTION_VRAM_SPEAKER: {
"category": ErrorCategory.TRANSCRIPTION,
"message": "VRAM out of memory during speaker transcription",
"severity": "critical",
"user_action_required": True,
},
ErrorCode.TRANSCRIPTION_SEND_DISABLED_VRAM: {
"category": ErrorCategory.TRANSCRIPTION,
"message": "Transcription send disabled due to VRAM overflow",
"severity": "critical",
"user_action_required": True,
},
ErrorCode.TRANSCRIPTION_RECEIVE_DISABLED_VRAM: {
"category": ErrorCategory.TRANSCRIPTION,
"message": "Transcription receive disabled due to VRAM overflow",
"severity": "critical",
"user_action_required": True,
},
# ウェイトダウンロードエラー
ErrorCode.WEIGHT_CTRANSLATE2_DOWNLOAD: {
"category": ErrorCategory.WEIGHT,
"message": "CTranslate2 weight download error",
"severity": "error",
"user_action_required": True,
},
ErrorCode.WEIGHT_WHISPER_DOWNLOAD: {
"category": ErrorCategory.WEIGHT,
"message": "Whisper weight download error",
"severity": "error",
"user_action_required": True,
},
# バリデーションエラー
ErrorCode.VALIDATION_MIC_THRESHOLD: {
"category": ErrorCategory.VALIDATION,
"message": "Mic energy threshold value is out of range",
"severity": "warning",
"user_action_required": True,
},
ErrorCode.VALIDATION_SPEAKER_THRESHOLD: {
"category": ErrorCategory.VALIDATION,
"message": "Speaker energy threshold value is out of range",
"severity": "warning",
"user_action_required": True,
},
ErrorCode.VALIDATION_MIC_RECORD_TIMEOUT: {
"category": ErrorCategory.VALIDATION,
"message": "Mic record timeout value is out of range",
"severity": "warning",
"user_action_required": True,
},
ErrorCode.VALIDATION_MIC_PHRASE_TIMEOUT: {
"category": ErrorCategory.VALIDATION,
"message": "Mic phrase timeout value is out of range",
"severity": "warning",
"user_action_required": True,
},
ErrorCode.VALIDATION_MIC_MAX_PHRASES: {
"category": ErrorCategory.VALIDATION,
"message": "Mic max phrases value is out of range",
"severity": "warning",
"user_action_required": True,
},
ErrorCode.VALIDATION_SPEAKER_RECORD_TIMEOUT: {
"category": ErrorCategory.VALIDATION,
"message": "Speaker record timeout value is out of range",
"severity": "warning",
"user_action_required": True,
},
ErrorCode.VALIDATION_SPEAKER_PHRASE_TIMEOUT: {
"category": ErrorCategory.VALIDATION,
"message": "Speaker phrase timeout value is out of range",
"severity": "warning",
"user_action_required": True,
},
ErrorCode.VALIDATION_SPEAKER_MAX_PHRASES: {
"category": ErrorCategory.VALIDATION,
"message": "Speaker max phrases value is out of range",
"severity": "warning",
"user_action_required": True,
},
ErrorCode.VALIDATION_INVALID_IP: {
"category": ErrorCategory.VALIDATION,
"message": "Invalid IP address",
"severity": "warning",
"user_action_required": True,
},
ErrorCode.VALIDATION_CANNOT_SET_IP: {
"category": ErrorCategory.VALIDATION,
"message": "Cannot set IP address",
"severity": "error",
"user_action_required": True,
},
# 認証エラー
ErrorCode.AUTH_DEEPL_LENGTH: {
"category": ErrorCategory.AUTH,
"message": "DeepL auth key length is not correct",
"severity": "warning",
"user_action_required": True,
},
ErrorCode.AUTH_DEEPL_FAILED: {
"category": ErrorCategory.AUTH,
"message": "Authentication failure of deepL auth key",
"severity": "error",
"user_action_required": True,
},
ErrorCode.AUTH_PLAMO_LENGTH: {
"category": ErrorCategory.AUTH,
"message": "Plamo auth key length is not correct",
"severity": "warning",
"user_action_required": True,
},
ErrorCode.AUTH_PLAMO_FAILED: {
"category": ErrorCategory.AUTH,
"message": "Authentication failure of plamo auth key",
"severity": "error",
"user_action_required": True,
},
ErrorCode.AUTH_GEMINI_LENGTH: {
"category": ErrorCategory.AUTH,
"message": "Gemini auth key length is not correct",
"severity": "warning",
"user_action_required": True,
},
ErrorCode.AUTH_GEMINI_FAILED: {
"category": ErrorCategory.AUTH,
"message": "Authentication failure of gemini auth key",
"severity": "error",
"user_action_required": True,
},
ErrorCode.AUTH_OPENAI_INVALID: {
"category": ErrorCategory.AUTH,
"message": "OpenAI auth key is not valid",
"severity": "warning",
"user_action_required": True,
},
ErrorCode.AUTH_OPENAI_FAILED: {
"category": ErrorCategory.AUTH,
"message": "Authentication failure of OpenAI auth key",
"severity": "error",
"user_action_required": True,
},
ErrorCode.AUTH_GROQ_INVALID: {
"category": ErrorCategory.AUTH,
"message": "Groq auth key is not valid",
"severity": "warning",
"user_action_required": True,
},
ErrorCode.AUTH_GROQ_FAILED: {
"category": ErrorCategory.AUTH,
"message": "Authentication failure of Groq auth key",
"severity": "error",
"user_action_required": True,
},
ErrorCode.AUTH_OPENROUTER_INVALID: {
"category": ErrorCategory.AUTH,
"message": "OpenRouter auth key is not valid",
"severity": "warning",
"user_action_required": True,
},
ErrorCode.AUTH_OPENROUTER_FAILED: {
"category": ErrorCategory.AUTH,
"message": "Authentication failure of OpenRouter auth key",
"severity": "error",
"user_action_required": True,
},
# モデル選択エラー
ErrorCode.MODEL_PLAMO_INVALID: {
"category": ErrorCategory.MODEL,
"message": "Plamo model is not valid",
"severity": "warning",
"user_action_required": True,
},
ErrorCode.MODEL_GEMINI_INVALID: {
"category": ErrorCategory.MODEL,
"message": "Gemini model is not valid",
"severity": "warning",
"user_action_required": True,
},
ErrorCode.MODEL_OPENAI_INVALID: {
"category": ErrorCategory.MODEL,
"message": "OpenAI model is not valid",
"severity": "warning",
"user_action_required": True,
},
ErrorCode.MODEL_GROQ_INVALID: {
"category": ErrorCategory.MODEL,
"message": "Groq model is not valid",
"severity": "warning",
"user_action_required": True,
},
ErrorCode.MODEL_OPENROUTER_INVALID: {
"category": ErrorCategory.MODEL,
"message": "OpenRouter model is not valid",
"severity": "warning",
"user_action_required": True,
},
ErrorCode.MODEL_LMSTUDIO_INVALID: {
"category": ErrorCategory.MODEL,
"message": "LMStudio model is not valid",
"severity": "warning",
"user_action_required": True,
},
ErrorCode.MODEL_OLLAMA_INVALID: {
"category": ErrorCategory.MODEL,
"message": "ollama model is not valid",
"severity": "warning",
"user_action_required": True,
},
# 接続エラー
ErrorCode.CONNECTION_LMSTUDIO_FAILED: {
"category": ErrorCategory.CONNECTION,
"message": "Cannot connect to LMStudio server",
"severity": "error",
"user_action_required": True,
},
ErrorCode.CONNECTION_OLLAMA_FAILED: {
"category": ErrorCategory.CONNECTION,
"message": "Cannot connect to ollama server",
"severity": "error",
"user_action_required": True,
},
ErrorCode.CONNECTION_LMSTUDIO_URL_INVALID: {
"category": ErrorCategory.CONNECTION,
"message": "LMStudio URL is not valid",
"severity": "warning",
"user_action_required": True,
},
# WebSocketエラー
ErrorCode.WEBSOCKET_HOST_INVALID: {
"category": ErrorCategory.WEBSOCKET,
"message": "WebSocket server host is not available",
"severity": "error",
"user_action_required": True,
},
ErrorCode.WEBSOCKET_PORT_UNAVAILABLE: {
"category": ErrorCategory.WEBSOCKET,
"message": "WebSocket server port is not available",
"severity": "error",
"user_action_required": True,
},
ErrorCode.WEBSOCKET_SERVER_UNAVAILABLE: {
"category": ErrorCategory.WEBSOCKET,
"message": "WebSocket server host or port is not available",
"severity": "error",
"user_action_required": True,
},
# VRC連携エラー
ErrorCode.VRC_MIC_MUTE_SYNC_OSC_DISABLED: {
"category": ErrorCategory.VRC,
"message": "Cannot enable VRC mic mute sync while OSC query is disabled",
"severity": "warning",
"user_action_required": True,
},
# 汎用エラー
ErrorCode.GENERAL_EXCEPTION: {
"category": ErrorCategory.GENERAL,
"message": "An error occurred",
"severity": "error",
"user_action_required": False,
},
ErrorCode.GENERAL_UNKNOWN: {
"category": ErrorCategory.GENERAL,
"message": "Unknown error",
"severity": "error",
"user_action_required": False,
},
}
class VRCTError:
"""VRCTエラーハンドリングクラス"""
@staticmethod
def create_error_response(
error_code: ErrorCode,
data: Any = None,
details: Optional[Dict[str, Any]] = None,
custom_message: Optional[str] = None
) -> Dict[str, Any]:
"""統一されたエラーレスポンスを生成
Args:
error_code: エラーコード
data: エラー時に戻す値(通常は元の値)
details: 追加の詳細情報
custom_message: カスタムメッセージ(指定しない場合はデフォルトメッセージ)
Returns:
エラーレスポンス辞書
"""
metadata = ERROR_METADATA.get(error_code, ERROR_METADATA[ErrorCode.GENERAL_UNKNOWN])
return {
"status": 400,
"result": {
"error_code": error_code.value,
"message": custom_message or metadata["message"],
"data": data,
"details": details or {},
"category": metadata["category"].value,
"severity": metadata["severity"],
}
}
@staticmethod
def create_exception_error_response(
exception: Exception,
data: Any = None,
error_code: ErrorCode = ErrorCode.GENERAL_EXCEPTION
) -> Dict[str, Any]:
"""例外からエラーレスポンスを生成
Args:
exception: 発生した例外
data: エラー時に戻す値
error_code: エラーコード
Returns:
エラーレスポンス辞書
"""
return VRCTError.create_error_response(
error_code=error_code,
data=data,
custom_message=f"Error: {str(exception)}",
details={"exception_type": type(exception).__name__}
)
# エンドポイントとエラーコードのマッピング
# UIがエラーハンドリングする際の参照として使用
ENDPOINT_ERROR_MAPPING: Dict[str, Dict[str, ErrorCode]] = {
# run_mapping経由のエラー通知
"/run/error_device": {
"NO_MIC": ErrorCode.DEVICE_NO_MIC,
"NO_SPEAKER": ErrorCode.DEVICE_NO_SPEAKER,
},
"/run/error_translation_engine": {
"LIMIT": ErrorCode.TRANSLATION_ENGINE_LIMIT,
},
"/run/error_translation_chat_vram_overflow": {
"VRAM": ErrorCode.TRANSLATION_VRAM_CHAT,
},
"/run/error_translation_mic_vram_overflow": {
"VRAM": ErrorCode.TRANSLATION_VRAM_MIC,
},
"/run/error_translation_speaker_vram_overflow": {
"VRAM": ErrorCode.TRANSLATION_VRAM_SPEAKER,
},
"/run/error_transcription_mic_vram_overflow": {
"VRAM": ErrorCode.TRANSCRIPTION_VRAM_MIC,
},
"/run/error_transcription_speaker_vram_overflow": {
"VRAM": ErrorCode.TRANSCRIPTION_VRAM_SPEAKER,
},
"/run/error_ctranslate2_weight": {
"DOWNLOAD": ErrorCode.WEIGHT_CTRANSLATE2_DOWNLOAD,
},
"/run/error_whisper_weight": {
"DOWNLOAD": ErrorCode.WEIGHT_WHISPER_DOWNLOAD,
},
# エンドポイント直接のエラーレスポンス
"/set/data/mic_threshold": {
"OUT_OF_RANGE": ErrorCode.VALIDATION_MIC_THRESHOLD,
},
"/set/data/speaker_threshold": {
"OUT_OF_RANGE": ErrorCode.VALIDATION_SPEAKER_THRESHOLD,
},
"/set/data/mic_record_timeout": {
"OUT_OF_RANGE": ErrorCode.VALIDATION_MIC_RECORD_TIMEOUT,
},
"/set/data/mic_phrase_timeout": {
"OUT_OF_RANGE": ErrorCode.VALIDATION_MIC_PHRASE_TIMEOUT,
},
"/set/data/mic_max_phrases": {
"OUT_OF_RANGE": ErrorCode.VALIDATION_MIC_MAX_PHRASES,
},
"/set/data/speaker_record_timeout": {
"OUT_OF_RANGE": ErrorCode.VALIDATION_SPEAKER_RECORD_TIMEOUT,
},
"/set/data/speaker_phrase_timeout": {
"OUT_OF_RANGE": ErrorCode.VALIDATION_SPEAKER_PHRASE_TIMEOUT,
},
"/set/data/speaker_max_phrases": {
"OUT_OF_RANGE": ErrorCode.VALIDATION_SPEAKER_MAX_PHRASES,
},
"/set/data/osc_ip_address": {
"INVALID": ErrorCode.VALIDATION_INVALID_IP,
"CANNOT_SET": ErrorCode.VALIDATION_CANNOT_SET_IP,
},
"/set/data/deepl_auth_key": {
"LENGTH": ErrorCode.AUTH_DEEPL_LENGTH,
"FAILED": ErrorCode.AUTH_DEEPL_FAILED,
},
"/set/data/plamo_auth_key": {
"LENGTH": ErrorCode.AUTH_PLAMO_LENGTH,
"FAILED": ErrorCode.AUTH_PLAMO_FAILED,
},
"/set/data/selected_plamo_model": {
"INVALID": ErrorCode.MODEL_PLAMO_INVALID,
},
"/set/data/gemini_auth_key": {
"LENGTH": ErrorCode.AUTH_GEMINI_LENGTH,
"FAILED": ErrorCode.AUTH_GEMINI_FAILED,
},
"/set/data/selected_gemini_model": {
"INVALID": ErrorCode.MODEL_GEMINI_INVALID,
},
"/set/data/openai_auth_key": {
"INVALID": ErrorCode.AUTH_OPENAI_INVALID,
"FAILED": ErrorCode.AUTH_OPENAI_FAILED,
},
"/set/data/selected_openai_model": {
"INVALID": ErrorCode.MODEL_OPENAI_INVALID,
},
"/set/data/groq_auth_key": {
"INVALID": ErrorCode.AUTH_GROQ_INVALID,
"FAILED": ErrorCode.AUTH_GROQ_FAILED,
},
"/set/data/selected_groq_model": {
"INVALID": ErrorCode.MODEL_GROQ_INVALID,
},
"/set/data/openrouter_auth_key": {
"INVALID": ErrorCode.AUTH_OPENROUTER_INVALID,
"FAILED": ErrorCode.AUTH_OPENROUTER_FAILED,
},
"/set/data/selected_openrouter_model": {
"INVALID": ErrorCode.MODEL_OPENROUTER_INVALID,
},
"/run/lmstudio_connection": {
"FAILED": ErrorCode.CONNECTION_LMSTUDIO_FAILED,
},
"/set/data/lmstudio_url": {
"INVALID": ErrorCode.CONNECTION_LMSTUDIO_URL_INVALID,
},
"/set/data/selected_lmstudio_model": {
"INVALID": ErrorCode.MODEL_LMSTUDIO_INVALID,
},
"/run/ollama_connection": {
"FAILED": ErrorCode.CONNECTION_OLLAMA_FAILED,
},
"/set/data/selected_ollama_model": {
"INVALID": ErrorCode.MODEL_OLLAMA_INVALID,
},
"/set/data/websocket_host": {
"INVALID_IP": ErrorCode.VALIDATION_INVALID_IP,
"UNAVAILABLE": ErrorCode.WEBSOCKET_HOST_INVALID,
},
"/set/data/websocket_port": {
"UNAVAILABLE": ErrorCode.WEBSOCKET_PORT_UNAVAILABLE,
},
"/set/enable/websocket_server": {
"UNAVAILABLE": ErrorCode.WEBSOCKET_SERVER_UNAVAILABLE,
},
"/set/enable/vrc_mic_mute_sync": {
"OSC_DISABLED": ErrorCode.VRC_MIC_MUTE_SYNC_OSC_DISABLED,
},
}
def get_error_metadata(error_code: ErrorCode) -> Dict[str, Any]:
"""エラーコードのメタデータを取得
Args:
error_code: エラーコード
Returns:
メタデータ辞書
"""
return ERROR_METADATA.get(error_code, ERROR_METADATA[ErrorCode.GENERAL_UNKNOWN])
def is_critical_error(error_code: ErrorCode) -> bool:
"""クリティカルエラーかどうかを判定
Args:
error_code: エラーコード
Returns:
クリティカルエラーの場合True
"""
metadata = get_error_metadata(error_code)
return metadata.get("severity") == "critical"
def requires_user_action(error_code: ErrorCode) -> bool:
"""ユーザーアクションが必要なエラーかどうかを判定
Args:
error_code: エラーコード
Returns:
ユーザーアクションが必要な場合True
"""
metadata = get_error_metadata(error_code)
return metadata.get("user_action_required", False)