|
6 | 6 | import os |
7 | 7 | import shutil |
8 | 8 | import time |
| 9 | +import warnings |
9 | 10 | from datetime import UTC, datetime |
10 | 11 | from pathlib import Path |
11 | 12 |
|
|
14 | 15 |
|
15 | 16 | from advanced_omi_backend.config import ( |
16 | 17 | get_diarization_settings as load_diarization_settings, |
| 18 | +) |
| 19 | +from advanced_omi_backend.config import ( |
17 | 20 | save_diarization_settings, |
18 | 21 | ) |
| 22 | +from advanced_omi_backend.config_loader import get_plugins_yml_path |
19 | 23 | from advanced_omi_backend.model_registry import _find_config_path, load_models_config |
20 | 24 | from advanced_omi_backend.models.user import User |
21 | 25 |
|
22 | 26 | logger = logging.getLogger(__name__) |
23 | 27 | audio_logger = logging.getLogger("audio_processing") |
24 | 28 |
|
25 | 29 |
|
| 30 | +async def get_config_diagnostics(): |
| 31 | + """ |
| 32 | + Get comprehensive configuration diagnostics. |
| 33 | + |
| 34 | + Returns warnings, errors, and status for all configuration components. |
| 35 | + """ |
| 36 | + diagnostics = { |
| 37 | + "timestamp": datetime.now(UTC).isoformat(), |
| 38 | + "overall_status": "healthy", |
| 39 | + "issues": [], |
| 40 | + "warnings": [], |
| 41 | + "info": [], |
| 42 | + "components": {} |
| 43 | + } |
| 44 | + |
| 45 | + # Test OmegaConf configuration loading |
| 46 | + try: |
| 47 | + from advanced_omi_backend.config_loader import load_config |
| 48 | + |
| 49 | + # Capture warnings during config load |
| 50 | + with warnings.catch_warnings(record=True) as w: |
| 51 | + warnings.simplefilter("always") |
| 52 | + config = load_config(force_reload=True) |
| 53 | + |
| 54 | + # Check for OmegaConf warnings |
| 55 | + for warning in w: |
| 56 | + warning_msg = str(warning.message) |
| 57 | + if "some elements are missing" in warning_msg.lower(): |
| 58 | + # Extract the variable name from warning |
| 59 | + if "variable '" in warning_msg.lower(): |
| 60 | + var_name = warning_msg.split("'")[1] |
| 61 | + diagnostics["warnings"].append({ |
| 62 | + "component": "OmegaConf", |
| 63 | + "severity": "warning", |
| 64 | + "message": f"Environment variable '{var_name}' not set (using empty default)", |
| 65 | + "resolution": f"Set {var_name} in .env file if needed" |
| 66 | + }) |
| 67 | + |
| 68 | + diagnostics["components"]["omegaconf"] = { |
| 69 | + "status": "healthy", |
| 70 | + "message": "Configuration loaded successfully" |
| 71 | + } |
| 72 | + except Exception as e: |
| 73 | + diagnostics["overall_status"] = "unhealthy" |
| 74 | + diagnostics["issues"].append({ |
| 75 | + "component": "OmegaConf", |
| 76 | + "severity": "error", |
| 77 | + "message": f"Failed to load configuration: {str(e)}", |
| 78 | + "resolution": "Check config/defaults.yml and config/config.yml syntax" |
| 79 | + }) |
| 80 | + diagnostics["components"]["omegaconf"] = { |
| 81 | + "status": "unhealthy", |
| 82 | + "message": str(e) |
| 83 | + } |
| 84 | + |
| 85 | + # Test model registry |
| 86 | + try: |
| 87 | + from advanced_omi_backend.model_registry import get_models_registry |
| 88 | + |
| 89 | + with warnings.catch_warnings(record=True) as w: |
| 90 | + warnings.simplefilter("always") |
| 91 | + registry = get_models_registry() |
| 92 | + |
| 93 | + # Capture model loading warnings |
| 94 | + for warning in w: |
| 95 | + warning_msg = str(warning.message) |
| 96 | + diagnostics["warnings"].append({ |
| 97 | + "component": "Model Registry", |
| 98 | + "severity": "warning", |
| 99 | + "message": warning_msg, |
| 100 | + "resolution": "Check model definitions in config/defaults.yml" |
| 101 | + }) |
| 102 | + |
| 103 | + if registry: |
| 104 | + diagnostics["components"]["model_registry"] = { |
| 105 | + "status": "healthy", |
| 106 | + "message": f"Loaded {len(registry.models)} models", |
| 107 | + "details": { |
| 108 | + "total_models": len(registry.models), |
| 109 | + "defaults": dict(registry.defaults) if registry.defaults else {} |
| 110 | + } |
| 111 | + } |
| 112 | + |
| 113 | + # Check critical models |
| 114 | + stt = registry.get_default("stt") |
| 115 | + stt_stream = registry.get_default("stt_stream") |
| 116 | + llm = registry.get_default("llm") |
| 117 | + |
| 118 | + # STT check |
| 119 | + if stt: |
| 120 | + if stt.api_key: |
| 121 | + diagnostics["info"].append({ |
| 122 | + "component": "STT (Batch)", |
| 123 | + "message": f"Configured: {stt.name} ({stt.model_provider}) - API key present" |
| 124 | + }) |
| 125 | + else: |
| 126 | + diagnostics["warnings"].append({ |
| 127 | + "component": "STT (Batch)", |
| 128 | + "severity": "warning", |
| 129 | + "message": f"{stt.name} ({stt.model_provider}) - No API key configured", |
| 130 | + "resolution": "Transcription will fail without API key" |
| 131 | + }) |
| 132 | + else: |
| 133 | + diagnostics["issues"].append({ |
| 134 | + "component": "STT (Batch)", |
| 135 | + "severity": "error", |
| 136 | + "message": "No batch STT model configured", |
| 137 | + "resolution": "Set defaults.stt in config.yml" |
| 138 | + }) |
| 139 | + diagnostics["overall_status"] = "partial" |
| 140 | + |
| 141 | + # Streaming STT check |
| 142 | + if stt_stream: |
| 143 | + if stt_stream.api_key: |
| 144 | + diagnostics["info"].append({ |
| 145 | + "component": "STT (Streaming)", |
| 146 | + "message": f"Configured: {stt_stream.name} ({stt_stream.model_provider}) - API key present" |
| 147 | + }) |
| 148 | + else: |
| 149 | + diagnostics["warnings"].append({ |
| 150 | + "component": "STT (Streaming)", |
| 151 | + "severity": "warning", |
| 152 | + "message": f"{stt_stream.name} ({stt_stream.model_provider}) - No API key configured", |
| 153 | + "resolution": "Real-time transcription will fail without API key" |
| 154 | + }) |
| 155 | + else: |
| 156 | + diagnostics["warnings"].append({ |
| 157 | + "component": "STT (Streaming)", |
| 158 | + "severity": "warning", |
| 159 | + "message": "No streaming STT model configured - streaming worker disabled", |
| 160 | + "resolution": "Set defaults.stt_stream in config.yml for WebSocket transcription" |
| 161 | + }) |
| 162 | + |
| 163 | + # LLM check |
| 164 | + if llm: |
| 165 | + if llm.api_key: |
| 166 | + diagnostics["info"].append({ |
| 167 | + "component": "LLM", |
| 168 | + "message": f"Configured: {llm.name} ({llm.model_provider}) - API key present" |
| 169 | + }) |
| 170 | + else: |
| 171 | + diagnostics["warnings"].append({ |
| 172 | + "component": "LLM", |
| 173 | + "severity": "warning", |
| 174 | + "message": f"{llm.name} ({llm.model_provider}) - No API key configured", |
| 175 | + "resolution": "Memory extraction will fail without API key" |
| 176 | + }) |
| 177 | + |
| 178 | + else: |
| 179 | + diagnostics["overall_status"] = "unhealthy" |
| 180 | + diagnostics["issues"].append({ |
| 181 | + "component": "Model Registry", |
| 182 | + "severity": "error", |
| 183 | + "message": "Failed to load model registry", |
| 184 | + "resolution": "Check config/defaults.yml for syntax errors" |
| 185 | + }) |
| 186 | + diagnostics["components"]["model_registry"] = { |
| 187 | + "status": "unhealthy", |
| 188 | + "message": "Registry failed to load" |
| 189 | + } |
| 190 | + except Exception as e: |
| 191 | + diagnostics["overall_status"] = "partial" |
| 192 | + diagnostics["issues"].append({ |
| 193 | + "component": "Model Registry", |
| 194 | + "severity": "error", |
| 195 | + "message": f"Error loading registry: {str(e)}", |
| 196 | + "resolution": "Check logs for detailed error information" |
| 197 | + }) |
| 198 | + diagnostics["components"]["model_registry"] = { |
| 199 | + "status": "unhealthy", |
| 200 | + "message": str(e) |
| 201 | + } |
| 202 | + |
| 203 | + # Check environment variables |
| 204 | + env_checks = [ |
| 205 | + ("DEEPGRAM_API_KEY", "Required for Deepgram transcription"), |
| 206 | + ("OPENAI_API_KEY", "Required for OpenAI LLM and embeddings"), |
| 207 | + ("AUTH_SECRET_KEY", "Required for authentication"), |
| 208 | + ("ADMIN_EMAIL", "Required for admin user login"), |
| 209 | + ("ADMIN_PASSWORD", "Required for admin user login"), |
| 210 | + ] |
| 211 | + |
| 212 | + for env_var, description in env_checks: |
| 213 | + value = os.getenv(env_var) |
| 214 | + if not value or value == "": |
| 215 | + diagnostics["warnings"].append({ |
| 216 | + "component": "Environment Variables", |
| 217 | + "severity": "warning", |
| 218 | + "message": f"{env_var} not set - {description}", |
| 219 | + "resolution": f"Set {env_var} in .env file" |
| 220 | + }) |
| 221 | + |
| 222 | + return diagnostics |
| 223 | + |
| 224 | + |
26 | 225 | async def get_current_metrics(): |
27 | 226 | """Get current system metrics.""" |
28 | 227 | try: |
@@ -626,7 +825,7 @@ async def validate_chat_config_yaml(prompt_text: str) -> dict: |
626 | 825 | async def get_plugins_config_yaml() -> str: |
627 | 826 | """Get plugins configuration as YAML text.""" |
628 | 827 | try: |
629 | | - plugins_yml_path = Path("/app/plugins.yml") |
| 828 | + plugins_yml_path = get_plugins_yml_path() |
630 | 829 |
|
631 | 830 | # Default empty plugins config |
632 | 831 | default_config = """plugins: |
@@ -658,7 +857,7 @@ async def get_plugins_config_yaml() -> str: |
658 | 857 | async def save_plugins_config_yaml(yaml_content: str) -> dict: |
659 | 858 | """Save plugins configuration from YAML text.""" |
660 | 859 | try: |
661 | | - plugins_yml_path = Path("/app/plugins.yml") |
| 860 | + plugins_yml_path = get_plugins_yml_path() |
662 | 861 |
|
663 | 862 | # Validate YAML can be parsed |
664 | 863 | try: |
|
0 commit comments