Merge branch 'refactoring_backend' into develop
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -4,6 +4,15 @@
|
||||
|
||||
`controller.py` は VRCT アプリケーションのビジネスロジック層であり、フロントエンド(UI)とバックエンド(Model)の間の制御フローを担当する。音声認識、翻訳、OSC通信、オーバーレイ表示など、VRCT の全機能の調整役として動作し、各種設定の取得・更新、デバイス管理、エラーハンドリングを提供する。
|
||||
|
||||
## 最近の更新 (2026-01-03)
|
||||
|
||||
- 起動高速化: 初期化時間を約12.6s→8.9sに短縮
|
||||
- AI Models Check 並列化: CTranslate2/Whisperの重みチェックを2並列で実行
|
||||
- 翻訳エンジン判定の非同期化: LMStudio/Ollamaをバックグラウンド判定、他APIは4並列
|
||||
- 重みチェック結果のキャッシュ: `_ctranslate2_available_cache` / `_whisper_available_cache` を導入し後続処理で再利用
|
||||
- 音声認識エンジン判定の高速化: Whisperはキャッシュ結果を利用し0.56s→0.00s
|
||||
- ソフトウェア更新チェックの非同期化: GitHub APIチェックをバックグラウンド化
|
||||
|
||||
## アーキテクチャ上の位置づけ
|
||||
|
||||
```
|
||||
|
||||
@@ -4,6 +4,26 @@
|
||||
|
||||
VRCTアプリケーションのビジネスロジックを制御するコントローラークラスです。UI層とモデル層の間に位置し、ユーザーの入力を適切な処理に変換し、結果を UI に返す役割を担います。全ての機能制御、設定管理、状態管理を一元的に行います。
|
||||
|
||||
## 最近の更新 (2026-01-03)
|
||||
|
||||
### 起動高速化・非同期化
|
||||
|
||||
- 初期化時間を約12.6s→8.9sに短縮(環境計測値)
|
||||
- AI Models Check を2並列化(CTranslate2/Whisper)し、結果を `_ctranslate2_available_cache` / `_whisper_available_cache` に保存
|
||||
- 翻訳エンジン判定を並列化(ThreadPoolExecutor, max_workers=4)し、LMStudio/Ollamaはバックグラウンド判定に変更
|
||||
- ソフトウェア更新チェックをバックグラウンド化
|
||||
- OSC受信初期化をバックグラウンド化し、OSCQueryサービス生成は接続成功まで継続リトライ
|
||||
- 翻訳/音声認識エンジンのセット処理で重みチェックキャッシュを再利用し再計測を排除(0.98s/0.52s→0.00s)
|
||||
|
||||
### 影響
|
||||
|
||||
| 項目 | 内容 |
|
||||
|------|------|
|
||||
| 起動時間 | 約3.7s短縮(12.6s→8.9s) |
|
||||
| 並列・非同期化 | 翻訳・音声認識エンジン判定を並列/バックグラウンド化 |
|
||||
| 安定性 | OSCQuery起動のリトライ上限でブロッキングを抑制 |
|
||||
| 再利用性 | 重みチェック結果をキャッシュし重複I/Oを削減 |
|
||||
|
||||
## 最近の更新 (2025-10-20)
|
||||
|
||||
### 新規ローカルLLM翻訳エンジン統合
|
||||
@@ -399,25 +419,29 @@ speakerMessage(result: dict) -> None
|
||||
|
||||
## エラーハンドリング
|
||||
|
||||
### VRAM不足エラー
|
||||
### エラー構造
|
||||
- すべて `VRCTError` で生成し、ステータス・コード・メッセージ・data を統一
|
||||
- `create_error_response()` / `create_exception_error_response()` を使用し、`self.run()` へそのまま渡す
|
||||
- 代表コード: デバイス系 (`DEVICE_NO_MIC` / `DEVICE_NO_SPEAKER`)、VRAM系 (`TRANSLATION_VRAM_*`)、認証系 (`AUTH_*`)、モデル不正 (`MODEL_*`)、バリデーション系 (`VALIDATION_*`)、接続系 (`CONNECTION_LMSTUDIO_FAILED` など)
|
||||
|
||||
- 自動的にCTranslate2への切り替え
|
||||
- ユーザーへの適切な通知
|
||||
### VRAM不足エラー
|
||||
- 翻訳処理中に VRAM 例外を検出し `/run/error_translation_*_vram_overflow` で通知
|
||||
- 翻訳機能を自動で無効化し、`TRANSLATION_DISABLED_VRAM` を通知
|
||||
- マイク/スピーカー/チャット/有効化時の各パスで専用コードを返却
|
||||
|
||||
### デバイスエラー
|
||||
- マイク・スピーカー未検出時に `DEVICE_NO_MIC` / `DEVICE_NO_SPEAKER`
|
||||
- エネルギーしきい値/タイムアウト等のバリデーションに `VALIDATION_*` を使用
|
||||
|
||||
- デバイス接続状態の監視
|
||||
- 自動復旧機能
|
||||
### 認証・モデルエラー
|
||||
- DeepL/Plamo/Gemini/OpenAI/Groq/OpenRouter の認証失敗やキー長不正を `AUTH_*` で通知
|
||||
- モデル未選択/不正時は `MODEL_*` で通知し、選択リストを再送
|
||||
|
||||
### ネットワークエラー
|
||||
|
||||
- 接続状態の定期確認
|
||||
- オフライン機能への切り替え
|
||||
### 接続エラー
|
||||
- LMStudio/Ollama 接続失敗を `CONNECTION_*` で通知し、翻訳エンジンリストを更新
|
||||
|
||||
### 設定エラー
|
||||
|
||||
- 設定値の妥当性チェック
|
||||
- デフォルト値への復帰
|
||||
- IP アドレスやしきい値などの不正値を `VALIDATION_*` で統一し、リクエスト値を data に格納
|
||||
|
||||
## パフォーマンス考慮事項
|
||||
|
||||
|
||||
212
src-python/docs/error_handling_migration_guide.md
Normal file
212
src-python/docs/error_handling_migration_guide.md
Normal file
@@ -0,0 +1,212 @@
|
||||
# エラーハンドリング統一システム移行ガイド
|
||||
|
||||
## 概要
|
||||
|
||||
`errors.py`で定義された統一エラーシステムを使用して、すべてのエラーハンドリングを標準化しました。
|
||||
|
||||
## 変更パターン
|
||||
|
||||
### 1. 基本的なエラーレスポンス
|
||||
|
||||
#### 修正前:
|
||||
```python
|
||||
response = {
|
||||
"status": 400,
|
||||
"result": {
|
||||
"message": "Error message",
|
||||
"data": some_value
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 修正後:
|
||||
```python
|
||||
from errors import ErrorCode, VRCTError
|
||||
|
||||
response = VRCTError.create_error_response(
|
||||
ErrorCode.APPROPRIATE_ERROR_CODE,
|
||||
data=some_value
|
||||
)
|
||||
```
|
||||
|
||||
### 2. run_mapping経由のエラー通知
|
||||
|
||||
#### 修正前:
|
||||
```python
|
||||
self.run(
|
||||
400,
|
||||
self.run_mapping["error_device"],
|
||||
{
|
||||
"message": "No mic device detected",
|
||||
"data": None
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
#### 修正後:
|
||||
```python
|
||||
error_response = VRCTError.create_error_response(
|
||||
ErrorCode.DEVICE_NO_MIC,
|
||||
data=None
|
||||
)
|
||||
self.run(
|
||||
error_response["status"],
|
||||
self.run_mapping["error_device"],
|
||||
error_response["result"],
|
||||
)
|
||||
```
|
||||
|
||||
### 3. 例外からのエラー生成
|
||||
|
||||
#### 修正前:
|
||||
```python
|
||||
except Exception as e:
|
||||
errorLogging()
|
||||
response = {
|
||||
"status": 400,
|
||||
"result": {
|
||||
"message": f"Error {e}",
|
||||
"data": original_value
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 修正後:
|
||||
```python
|
||||
except Exception as e:
|
||||
errorLogging()
|
||||
response = VRCTError.create_exception_error_response(
|
||||
e,
|
||||
data=original_value
|
||||
)
|
||||
```
|
||||
|
||||
## 既に移行済みの箇所
|
||||
|
||||
### デバイスエラー
|
||||
- ✅ `progressBarMicEnergy` - `ErrorCode.DEVICE_NO_MIC`
|
||||
- ✅ `progressBarSpeakerEnergy` - `ErrorCode.DEVICE_NO_SPEAKER`
|
||||
|
||||
### ウェイトダウンロードエラー
|
||||
- ✅ `DownloadCTranslate2.downloaded` - `ErrorCode.WEIGHT_CTRANSLATE2_DOWNLOAD`
|
||||
- ✅ `DownloadWhisper.downloaded` - `ErrorCode.WEIGHT_WHISPER_DOWNLOAD`
|
||||
|
||||
### 翻訳エラー
|
||||
- ✅ `micMessage` - `ErrorCode.TRANSLATION_ENGINE_LIMIT`, `ErrorCode.TRANSLATION_VRAM_MIC`, `ErrorCode.TRANSLATION_DISABLED_VRAM`
|
||||
- ✅ `speakerMessage` - `ErrorCode.TRANSLATION_ENGINE_LIMIT`, `ErrorCode.TRANSLATION_VRAM_SPEAKER`, `ErrorCode.TRANSLATION_DISABLED_VRAM`
|
||||
- ✅ `chatMessage` - `ErrorCode.TRANSLATION_ENGINE_LIMIT`, `ErrorCode.TRANSLATION_VRAM_CHAT`, `ErrorCode.TRANSLATION_DISABLED_VRAM`
|
||||
- ✅ `setEnableTranslation` - `ErrorCode.TRANSLATION_VRAM_ENABLE`, `ErrorCode.TRANSLATION_DISABLED_VRAM`
|
||||
|
||||
### バリデーションエラー
|
||||
- ✅ `setMicThreshold` - `ErrorCode.VALIDATION_MIC_THRESHOLD`
|
||||
- ✅ `setSpeakerThreshold` - `ErrorCode.VALIDATION_SPEAKER_THRESHOLD`
|
||||
- ✅ `setMicRecordTimeout` - `ErrorCode.VALIDATION_MIC_RECORD_TIMEOUT`
|
||||
- ✅ `setMicPhraseTimeout` - `ErrorCode.VALIDATION_MIC_PHRASE_TIMEOUT`
|
||||
- ✅ `setMicMaxPhrases` - `ErrorCode.VALIDATION_MIC_MAX_PHRASES`
|
||||
- ✅ `setSpeakerRecordTimeout` - `ErrorCode.VALIDATION_SPEAKER_RECORD_TIMEOUT`
|
||||
- ✅ `setSpeakerPhraseTimeout` - `ErrorCode.VALIDATION_SPEAKER_PHRASE_TIMEOUT`
|
||||
- ✅ `setSpeakerMaxPhrases` - `ErrorCode.VALIDATION_SPEAKER_MAX_PHRASES`
|
||||
- ✅ `setOscIpAddress` - `ErrorCode.VALIDATION_INVALID_IP`, `ErrorCode.VALIDATION_CANNOT_SET_IP`
|
||||
|
||||
### VRC連携エラー
|
||||
- ✅ `setEnableVrcMicMuteSync` - `ErrorCode.VRC_MIC_MUTE_SYNC_OSC_DISABLED`
|
||||
|
||||
### 認証エラー
|
||||
- ✅ `setDeeplAuthKey` - `ErrorCode.AUTH_DEEPL_LENGTH`, `ErrorCode.AUTH_DEEPL_FAILED`
|
||||
|
||||
## 未移行の箇所(要対応)
|
||||
|
||||
以下の箇所は同様のパターンで移行が必要です:
|
||||
|
||||
### 認証関連
|
||||
- ⬜ `setPlamoAuthKey` - `ErrorCode.AUTH_PLAMO_LENGTH`, `ErrorCode.AUTH_PLAMO_FAILED`
|
||||
- ⬜ `setPlamoModel` - `ErrorCode.MODEL_PLAMO_INVALID`
|
||||
- ⬜ `setGeminiAuthKey` - `ErrorCode.AUTH_GEMINI_LENGTH`, `ErrorCode.AUTH_GEMINI_FAILED`
|
||||
- ⬜ `setGeminiModel` - `ErrorCode.MODEL_GEMINI_INVALID`
|
||||
- ⬜ `setOpenAIAuthKey` - `ErrorCode.AUTH_OPENAI_INVALID`, `ErrorCode.AUTH_OPENAI_FAILED`
|
||||
- ⬜ `setOpenAIModel` - `ErrorCode.MODEL_OPENAI_INVALID`
|
||||
- ⬜ `setGroqAuthKey` - `ErrorCode.AUTH_GROQ_INVALID`, `ErrorCode.AUTH_GROQ_FAILED`
|
||||
- ⬜ `setGroqModel` - `ErrorCode.MODEL_GROQ_INVALID`
|
||||
- ⬜ `setOpenRouterAuthKey` - `ErrorCode.AUTH_OPENROUTER_INVALID`, `ErrorCode.AUTH_OPENROUTER_FAILED`
|
||||
- ⬜ `setOpenRouterModel` - `ErrorCode.MODEL_OPENROUTER_INVALID`
|
||||
|
||||
### 接続関連
|
||||
- ⬜ `checkTranslatorLMStudioConnection` - `ErrorCode.CONNECTION_LMSTUDIO_FAILED`
|
||||
- ⬜ `setTranslatorLMStudioURL` - `ErrorCode.CONNECTION_LMSTUDIO_URL_INVALID`
|
||||
- ⬜ `setTranslatorLMStudioModel` - `ErrorCode.MODEL_LMSTUDIO_INVALID`
|
||||
- ⬜ `checkTranslatorOllamaConnection` - `ErrorCode.CONNECTION_OLLAMA_FAILED`
|
||||
- ⬜ `setTranslatorOllamaModel` - `ErrorCode.MODEL_OLLAMA_INVALID`
|
||||
|
||||
### WebSocket関連
|
||||
- ⬜ `setWebSocketHost` - `ErrorCode.VALIDATION_INVALID_IP`, `ErrorCode.WEBSOCKET_HOST_INVALID`
|
||||
- ⬜ `setWebSocketPort` - `ErrorCode.WEBSOCKET_PORT_UNAVAILABLE`
|
||||
- ⬜ `setEnableWebSocketServer` - `ErrorCode.WEBSOCKET_SERVER_UNAVAILABLE`
|
||||
|
||||
### 音声認識VRAM関連
|
||||
- ⬜ `startTranscriptionSendMessage` - `ErrorCode.TRANSCRIPTION_VRAM_MIC`, `ErrorCode.TRANSCRIPTION_SEND_DISABLED_VRAM`
|
||||
- ⬜ `startTranscriptionReceiveMessage` - `ErrorCode.TRANSCRIPTION_VRAM_SPEAKER`, `ErrorCode.TRANSCRIPTION_RECEIVE_DISABLED_VRAM`
|
||||
|
||||
## エラーコードとエンドポイントの対応
|
||||
|
||||
`errors.py`の`ENDPOINT_ERROR_MAPPING`に、すべてのエンドポイントとエラーコードの対応が定義されています。
|
||||
UI開発者はこのマッピングを参照して、各エンドポイントがどのようなエラーを返すか確認できます。
|
||||
|
||||
## エラーレスポンスの構造
|
||||
|
||||
統一されたエラーレスポンスは以下の構造を持ちます:
|
||||
|
||||
```python
|
||||
{
|
||||
"status": 400, # HTTPステータスコード
|
||||
"result": {
|
||||
"error_code": "ERROR_CODE_CONSTANT", # エラーコード定数
|
||||
"message": "Human readable message", # 人間が読めるメッセージ
|
||||
"data": None or original_value, # エラー時に戻す値(通常は元の値)
|
||||
"details": {}, # 追加情報(オプション)
|
||||
"category": "category_name", # エラーカテゴリ
|
||||
"severity": "warning|error|critical", # 重要度
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## UI側での活用
|
||||
|
||||
UI側では`error_code`を使用して、エラーの種類を判定し、適切な処理を行うことができます:
|
||||
|
||||
```javascript
|
||||
if (response.status === 400) {
|
||||
const { error_code, message, data, severity } = response.result;
|
||||
|
||||
switch (error_code) {
|
||||
case "DEVICE_NO_MIC":
|
||||
// マイクデバイスエラーの処理
|
||||
break;
|
||||
case "VALIDATION_MIC_THRESHOLD":
|
||||
// バリデーションエラーの処理(元の値に戻す)
|
||||
setValue(data);
|
||||
break;
|
||||
// ...
|
||||
}
|
||||
|
||||
// 重要度に応じた表示
|
||||
if (severity === "critical") {
|
||||
showCriticalError(message);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 移行作業の進め方
|
||||
|
||||
1. **パターンの確認**: 上記の変更パターンを参照
|
||||
2. **エラーコードの特定**: `errors.py`から適切な`ErrorCode`を選択
|
||||
3. **コードの置き換え**: 古いエラーハンドリングを新しいシステムに置き換え
|
||||
4. **テスト**: エラーが正しく返されることを確認
|
||||
5. **チェックリストの更新**: このドキュメントの✅を更新
|
||||
|
||||
## 注意事項
|
||||
|
||||
- すべてのエラーは`errors.py`に定義されたエラーコードを使用すること
|
||||
- 新しいエラーが必要な場合は、まず`errors.py`に追加すること
|
||||
- エラーメッセージは`ERROR_METADATA`で定義されたデフォルトメッセージを使用すること
|
||||
- カスタムメッセージが必要な場合は`custom_message`パラメータを使用
|
||||
- `data`パラメータには、エラー時にUIが元の値に戻せるように、元の値を渡すこと
|
||||
694
src-python/errors.py
Normal file
694
src-python/errors.py
Normal file
@@ -0,0 +1,694 @@
|
||||
# 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)
|
||||
Reference in New Issue
Block a user