ambivo-agents 1.0.1__py3-none-any.whl → 1.0.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ambivo_agents/agents/knowledge_base.py +2 -2
- ambivo_agents/agents/moderator.py +2 -1
- ambivo_agents/config/loader.py +625 -98
- ambivo_agents-1.0.3.dist-info/METADATA +921 -0
- {ambivo_agents-1.0.1.dist-info → ambivo_agents-1.0.3.dist-info}/RECORD +9 -9
- ambivo_agents-1.0.1.dist-info/METADATA +0 -1090
- {ambivo_agents-1.0.1.dist-info → ambivo_agents-1.0.3.dist-info}/WHEEL +0 -0
- {ambivo_agents-1.0.1.dist-info → ambivo_agents-1.0.3.dist-info}/entry_points.txt +0 -0
- {ambivo_agents-1.0.1.dist-info → ambivo_agents-1.0.3.dist-info}/licenses/LICENSE +0 -0
- {ambivo_agents-1.0.1.dist-info → ambivo_agents-1.0.3.dist-info}/top_level.txt +0 -0
ambivo_agents/config/loader.py
CHANGED
@@ -1,13 +1,22 @@
|
|
1
1
|
# ambivo_agents/config/loader.py
|
2
2
|
"""
|
3
|
-
|
4
|
-
|
3
|
+
Enhanced configuration loader for ambivo_agents.
|
4
|
+
Supports both YAML file and environment variables for configuration.
|
5
|
+
YAML file is now OPTIONAL when environment variables are provided.
|
5
6
|
"""
|
6
7
|
|
7
8
|
import os
|
8
|
-
import
|
9
|
+
import logging
|
9
10
|
from pathlib import Path
|
10
|
-
from typing import Dict, Any
|
11
|
+
from typing import Dict, Any, Optional, Union, List
|
12
|
+
|
13
|
+
# Try to import yaml, but make it optional
|
14
|
+
try:
|
15
|
+
import yaml
|
16
|
+
|
17
|
+
YAML_AVAILABLE = True
|
18
|
+
except ImportError:
|
19
|
+
YAML_AVAILABLE = False
|
11
20
|
|
12
21
|
|
13
22
|
class ConfigurationError(Exception):
|
@@ -15,30 +24,281 @@ class ConfigurationError(Exception):
|
|
15
24
|
pass
|
16
25
|
|
17
26
|
|
18
|
-
|
27
|
+
# Environment variable prefix for all ambivo_agents settings
|
28
|
+
ENV_PREFIX = "AMBIVO_AGENTS_"
|
29
|
+
|
30
|
+
# Environment variable mapping for configuration sections
|
31
|
+
ENV_VARIABLE_MAPPING = {
|
32
|
+
# Redis Configuration
|
33
|
+
f"{ENV_PREFIX}REDIS_HOST": ("redis", "host"),
|
34
|
+
f"{ENV_PREFIX}REDIS_PORT": ("redis", "port"),
|
35
|
+
f"{ENV_PREFIX}REDIS_PASSWORD": ("redis", "password"),
|
36
|
+
f"{ENV_PREFIX}REDIS_DB": ("redis", "db"),
|
37
|
+
|
38
|
+
# LLM Configuration
|
39
|
+
f"{ENV_PREFIX}LLM_PREFERRED_PROVIDER": ("llm", "preferred_provider"),
|
40
|
+
f"{ENV_PREFIX}LLM_TEMPERATURE": ("llm", "temperature"),
|
41
|
+
f"{ENV_PREFIX}LLM_MAX_TOKENS": ("llm", "max_tokens"),
|
42
|
+
f"{ENV_PREFIX}LLM_OPENAI_API_KEY": ("llm", "openai_api_key"),
|
43
|
+
f"{ENV_PREFIX}OPENAI_API_KEY": ("llm", "openai_api_key"), # Alternative
|
44
|
+
f"{ENV_PREFIX}LLM_ANTHROPIC_API_KEY": ("llm", "anthropic_api_key"),
|
45
|
+
f"{ENV_PREFIX}ANTHROPIC_API_KEY": ("llm", "anthropic_api_key"), # Alternative
|
46
|
+
f"{ENV_PREFIX}LLM_VOYAGE_API_KEY": ("llm", "voyage_api_key"),
|
47
|
+
f"{ENV_PREFIX}VOYAGE_API_KEY": ("llm", "voyage_api_key"), # Alternative
|
48
|
+
f"{ENV_PREFIX}LLM_AWS_ACCESS_KEY_ID": ("llm", "aws_access_key_id"),
|
49
|
+
f"{ENV_PREFIX}AWS_ACCESS_KEY_ID": ("llm", "aws_access_key_id"), # Alternative
|
50
|
+
f"{ENV_PREFIX}LLM_AWS_SECRET_ACCESS_KEY": ("llm", "aws_secret_access_key"),
|
51
|
+
f"{ENV_PREFIX}AWS_SECRET_ACCESS_KEY": ("llm", "aws_secret_access_key"), # Alternative
|
52
|
+
f"{ENV_PREFIX}LLM_AWS_REGION": ("llm", "aws_region"),
|
53
|
+
f"{ENV_PREFIX}AWS_REGION": ("llm", "aws_region"), # Alternative
|
54
|
+
|
55
|
+
# Agent Capabilities
|
56
|
+
f"{ENV_PREFIX}AGENT_CAPABILITIES_ENABLE_KNOWLEDGE_BASE": ("agent_capabilities", "enable_knowledge_base"),
|
57
|
+
f"{ENV_PREFIX}ENABLE_KNOWLEDGE_BASE": ("agent_capabilities", "enable_knowledge_base"), # Alternative
|
58
|
+
f"{ENV_PREFIX}AGENT_CAPABILITIES_ENABLE_WEB_SEARCH": ("agent_capabilities", "enable_web_search"),
|
59
|
+
f"{ENV_PREFIX}ENABLE_WEB_SEARCH": ("agent_capabilities", "enable_web_search"), # Alternative
|
60
|
+
f"{ENV_PREFIX}AGENT_CAPABILITIES_ENABLE_CODE_EXECUTION": ("agent_capabilities", "enable_code_execution"),
|
61
|
+
f"{ENV_PREFIX}ENABLE_CODE_EXECUTION": ("agent_capabilities", "enable_code_execution"), # Alternative
|
62
|
+
f"{ENV_PREFIX}AGENT_CAPABILITIES_ENABLE_WEB_SCRAPING": ("agent_capabilities", "enable_web_scraping"),
|
63
|
+
f"{ENV_PREFIX}ENABLE_WEB_SCRAPING": ("agent_capabilities", "enable_web_scraping"), # Alternative
|
64
|
+
f"{ENV_PREFIX}AGENT_CAPABILITIES_ENABLE_MEDIA_EDITOR": ("agent_capabilities", "enable_media_editor"),
|
65
|
+
f"{ENV_PREFIX}ENABLE_MEDIA_EDITOR": ("agent_capabilities", "enable_media_editor"), # Alternative
|
66
|
+
f"{ENV_PREFIX}AGENT_CAPABILITIES_ENABLE_YOUTUBE_DOWNLOAD": ("agent_capabilities", "enable_youtube_download"),
|
67
|
+
f"{ENV_PREFIX}ENABLE_YOUTUBE_DOWNLOAD": ("agent_capabilities", "enable_youtube_download"), # Alternative
|
68
|
+
f"{ENV_PREFIX}AGENT_CAPABILITIES_ENABLE_PROXY_MODE": ("agent_capabilities", "enable_proxy_mode"),
|
69
|
+
f"{ENV_PREFIX}ENABLE_PROXY_MODE": ("agent_capabilities", "enable_proxy_mode"), # Alternative
|
70
|
+
f"{ENV_PREFIX}AGENT_CAPABILITIES_ENABLE_FILE_PROCESSING": ("agent_capabilities", "enable_file_processing"),
|
71
|
+
f"{ENV_PREFIX}AGENT_CAPABILITIES_ENABLE_WEB_INGESTION": ("agent_capabilities", "enable_web_ingestion"),
|
72
|
+
f"{ENV_PREFIX}AGENT_CAPABILITIES_ENABLE_API_CALLS": ("agent_capabilities", "enable_api_calls"),
|
73
|
+
|
74
|
+
# Web Search Configuration
|
75
|
+
f"{ENV_PREFIX}WEB_SEARCH_BRAVE_API_KEY": ("web_search", "brave_api_key"),
|
76
|
+
f"{ENV_PREFIX}BRAVE_API_KEY": ("web_search", "brave_api_key"), # Alternative
|
77
|
+
f"{ENV_PREFIX}WEB_SEARCH_AVESAPI_API_KEY": ("web_search", "avesapi_api_key"),
|
78
|
+
f"{ENV_PREFIX}AVES_API_KEY": ("web_search", "avesapi_api_key"), # Alternative
|
79
|
+
f"{ENV_PREFIX}WEB_SEARCH_DEFAULT_MAX_RESULTS": ("web_search", "default_max_results"),
|
80
|
+
f"{ENV_PREFIX}WEB_SEARCH_MAX_RESULTS": ("web_search", "default_max_results"), # Alternative
|
81
|
+
|
82
|
+
# Knowledge Base Configuration
|
83
|
+
f"{ENV_PREFIX}KNOWLEDGE_BASE_QDRANT_URL": ("knowledge_base", "qdrant_url"),
|
84
|
+
f"{ENV_PREFIX}QDRANT_URL": ("knowledge_base", "qdrant_url"), # Alternative
|
85
|
+
f"{ENV_PREFIX}KNOWLEDGE_BASE_QDRANT_API_KEY": ("knowledge_base", "qdrant_api_key"),
|
86
|
+
f"{ENV_PREFIX}QDRANT_API_KEY": ("knowledge_base", "qdrant_api_key"), # Alternative
|
87
|
+
f"{ENV_PREFIX}KNOWLEDGE_BASE_CHUNK_SIZE": ("knowledge_base", "chunk_size"),
|
88
|
+
f"{ENV_PREFIX}KB_CHUNK_SIZE": ("knowledge_base", "chunk_size"), # Alternative
|
89
|
+
f"{ENV_PREFIX}KNOWLEDGE_BASE_SIMILARITY_TOP_K": ("knowledge_base", "similarity_top_k"),
|
90
|
+
f"{ENV_PREFIX}KB_SIMILARITY_TOP_K": ("knowledge_base", "similarity_top_k"), # Alternative
|
91
|
+
f"{ENV_PREFIX}DEFAULT_COLLECTION_PREFIX": ("knowledge_base", "default_collection_prefix"),
|
92
|
+
|
93
|
+
# Web Scraping Configuration
|
94
|
+
f"{ENV_PREFIX}WEB_SCRAPING_PROXY_CONFIG_HTTP_PROXY": ("web_scraping", "proxy_config", "http_proxy"),
|
95
|
+
f"{ENV_PREFIX}SCRAPER_PROXY": ("web_scraping", "proxy_config", "http_proxy"), # Alternative
|
96
|
+
f"{ENV_PREFIX}WEB_SCRAPING_PROXY_ENABLED": ("web_scraping", "proxy_enabled"),
|
97
|
+
f"{ENV_PREFIX}SCRAPER_PROXY_ENABLED": ("web_scraping", "proxy_enabled"), # Alternative
|
98
|
+
f"{ENV_PREFIX}WEB_SCRAPING_TIMEOUT": ("web_scraping", "timeout"),
|
99
|
+
f"{ENV_PREFIX}SCRAPER_TIMEOUT": ("web_scraping", "timeout"), # Alternative
|
100
|
+
f"{ENV_PREFIX}WEB_SCRAPING_DOCKER_IMAGE": ("web_scraping", "docker_image"),
|
101
|
+
|
102
|
+
# YouTube Download Configuration
|
103
|
+
f"{ENV_PREFIX}YOUTUBE_DOWNLOAD_DOWNLOAD_DIR": ("youtube_download", "download_dir"),
|
104
|
+
f"{ENV_PREFIX}YOUTUBE_DOWNLOAD_DIR": ("youtube_download", "download_dir"), # Alternative
|
105
|
+
f"{ENV_PREFIX}YOUTUBE_DOWNLOAD_DEFAULT_AUDIO_ONLY": ("youtube_download", "default_audio_only"),
|
106
|
+
f"{ENV_PREFIX}YOUTUBE_DEFAULT_AUDIO_ONLY": ("youtube_download", "default_audio_only"), # Alternative
|
107
|
+
f"{ENV_PREFIX}YOUTUBE_DOWNLOAD_TIMEOUT": ("youtube_download", "timeout"),
|
108
|
+
f"{ENV_PREFIX}YOUTUBE_TIMEOUT": ("youtube_download", "timeout"), # Alternative
|
109
|
+
f"{ENV_PREFIX}YOUTUBE_DOWNLOAD_DOCKER_IMAGE": ("youtube_download", "docker_image"),
|
110
|
+
|
111
|
+
# Media Editor Configuration
|
112
|
+
f"{ENV_PREFIX}MEDIA_EDITOR_INPUT_DIR": ("media_editor", "input_dir"),
|
113
|
+
f"{ENV_PREFIX}MEDIA_INPUT_DIR": ("media_editor", "input_dir"), # Alternative
|
114
|
+
f"{ENV_PREFIX}MEDIA_EDITOR_OUTPUT_DIR": ("media_editor", "output_dir"),
|
115
|
+
f"{ENV_PREFIX}MEDIA_OUTPUT_DIR": ("media_editor", "output_dir"), # Alternative
|
116
|
+
f"{ENV_PREFIX}MEDIA_EDITOR_TIMEOUT": ("media_editor", "timeout"),
|
117
|
+
f"{ENV_PREFIX}MEDIA_TIMEOUT": ("media_editor", "timeout"), # Alternative
|
118
|
+
f"{ENV_PREFIX}MEDIA_EDITOR_DOCKER_IMAGE": ("media_editor", "docker_image"),
|
119
|
+
|
120
|
+
# Docker Configuration
|
121
|
+
f"{ENV_PREFIX}DOCKER_MEMORY_LIMIT": ("docker", "memory_limit"),
|
122
|
+
f"{ENV_PREFIX}DOCKER_TIMEOUT": ("docker", "timeout"),
|
123
|
+
f"{ENV_PREFIX}DOCKER_IMAGES": ("docker", "images"),
|
124
|
+
f"{ENV_PREFIX}DOCKER_IMAGE": ("docker", "images"), # Alternative - will be converted to list
|
125
|
+
f"{ENV_PREFIX}DOCKER_WORK_DIR": ("docker", "work_dir"),
|
126
|
+
|
127
|
+
# Service Configuration
|
128
|
+
f"{ENV_PREFIX}SERVICE_MAX_SESSIONS": ("service", "max_sessions"),
|
129
|
+
f"{ENV_PREFIX}SERVICE_LOG_LEVEL": ("service", "log_level"),
|
130
|
+
f"{ENV_PREFIX}SERVICE_SESSION_TIMEOUT": ("service", "session_timeout"),
|
131
|
+
f"{ENV_PREFIX}SERVICE_ENABLE_METRICS": ("service", "enable_metrics"),
|
132
|
+
f"{ENV_PREFIX}SERVICE_LOG_TO_FILE": ("service", "log_to_file"),
|
133
|
+
|
134
|
+
# Moderator Configuration
|
135
|
+
f"{ENV_PREFIX}MODERATOR_DEFAULT_ENABLED_AGENTS": ("moderator", "default_enabled_agents"),
|
136
|
+
f"{ENV_PREFIX}MODERATOR_ENABLED_AGENTS": ("moderator", "default_enabled_agents"), # Alternative
|
137
|
+
f"{ENV_PREFIX}MODERATOR_ROUTING_CONFIDENCE_THRESHOLD": ("moderator", "routing", "confidence_threshold"),
|
138
|
+
f"{ENV_PREFIX}MODERATOR_CONFIDENCE_THRESHOLD": ("moderator", "routing", "confidence_threshold"), # Alternative
|
139
|
+
}
|
140
|
+
|
141
|
+
# Required environment variables for minimal configuration
|
142
|
+
REQUIRED_ENV_VARS = [
|
143
|
+
f"{ENV_PREFIX}REDIS_HOST",
|
144
|
+
f"{ENV_PREFIX}REDIS_PORT",
|
145
|
+
]
|
146
|
+
|
147
|
+
# At least one LLM provider is required
|
148
|
+
LLM_PROVIDER_ENV_VARS = [
|
149
|
+
f"{ENV_PREFIX}LLM_OPENAI_API_KEY",
|
150
|
+
f"{ENV_PREFIX}OPENAI_API_KEY",
|
151
|
+
f"{ENV_PREFIX}LLM_ANTHROPIC_API_KEY",
|
152
|
+
f"{ENV_PREFIX}ANTHROPIC_API_KEY",
|
153
|
+
f"{ENV_PREFIX}LLM_AWS_ACCESS_KEY_ID",
|
154
|
+
f"{ENV_PREFIX}AWS_ACCESS_KEY_ID"
|
155
|
+
]
|
156
|
+
|
157
|
+
|
158
|
+
def load_config(config_path: str = None, use_env_vars: bool = None) -> Dict[str, Any]:
|
19
159
|
"""
|
20
|
-
Load configuration
|
160
|
+
Load configuration with OPTIONAL YAML file support.
|
161
|
+
|
162
|
+
Priority order:
|
163
|
+
1. Environment variables (if detected or use_env_vars=True)
|
164
|
+
2. YAML file (if available and no env vars)
|
165
|
+
3. Minimal defaults (if nothing else available)
|
21
166
|
|
22
167
|
Args:
|
23
|
-
config_path: Optional path to config file
|
24
|
-
|
168
|
+
config_path: Optional path to config file
|
169
|
+
use_env_vars: Force use of environment variables. If None, auto-detects.
|
25
170
|
|
26
171
|
Returns:
|
27
172
|
Configuration dictionary
|
28
173
|
|
29
174
|
Raises:
|
30
|
-
ConfigurationError: If
|
175
|
+
ConfigurationError: If no valid configuration found
|
31
176
|
"""
|
177
|
+
|
178
|
+
config = {}
|
179
|
+
config_source = ""
|
180
|
+
|
181
|
+
# Auto-detect if we should use environment variables
|
182
|
+
if use_env_vars is None:
|
183
|
+
use_env_vars = _has_env_vars()
|
184
|
+
|
185
|
+
if use_env_vars:
|
186
|
+
# PRIMARY: Try environment variables first
|
187
|
+
try:
|
188
|
+
config = _load_config_from_env()
|
189
|
+
config_source = "environment variables"
|
190
|
+
logging.info("✅ Configuration loaded from environment variables")
|
191
|
+
|
192
|
+
# Validate env config
|
193
|
+
_validate_config(config)
|
194
|
+
|
195
|
+
# Add config source metadata
|
196
|
+
config['_config_source'] = config_source
|
197
|
+
return config
|
198
|
+
|
199
|
+
except ConfigurationError as e:
|
200
|
+
if _has_minimal_env_vars():
|
201
|
+
# If we have some env vars but they're incomplete, raise error
|
202
|
+
raise ConfigurationError(f"Incomplete environment variable configuration: {e}")
|
203
|
+
else:
|
204
|
+
# Fall back to YAML file
|
205
|
+
logging.warning(f"Environment variable config incomplete: {e}")
|
206
|
+
use_env_vars = False
|
207
|
+
|
208
|
+
if not use_env_vars:
|
209
|
+
# FALLBACK: Try YAML file
|
210
|
+
try:
|
211
|
+
yaml_config = _load_config_from_yaml(config_path)
|
212
|
+
if config:
|
213
|
+
# Merge env vars with YAML (env vars take precedence)
|
214
|
+
config = _merge_configs(yaml_config, config)
|
215
|
+
config_source = "YAML file + environment variables"
|
216
|
+
else:
|
217
|
+
config = yaml_config
|
218
|
+
config_source = "YAML file"
|
219
|
+
|
220
|
+
logging.info(f"✅ Configuration loaded from {config_source}")
|
221
|
+
|
222
|
+
except ConfigurationError as e:
|
223
|
+
if config:
|
224
|
+
# We have partial env config, use it even if YAML failed
|
225
|
+
logging.warning(f"YAML config failed, using environment variables: {e}")
|
226
|
+
config_source = "environment variables (partial)"
|
227
|
+
else:
|
228
|
+
# No config at all - use minimal defaults
|
229
|
+
logging.warning(f"Both environment variables and YAML failed: {e}")
|
230
|
+
config = _get_minimal_defaults()
|
231
|
+
config_source = "minimal defaults"
|
232
|
+
|
233
|
+
if not config:
|
234
|
+
raise ConfigurationError(
|
235
|
+
"No configuration found. Please either:\n"
|
236
|
+
"1. Set environment variables with AMBIVO_AGENTS_ prefix, OR\n"
|
237
|
+
"2. Create agent_config.yaml in your project directory\n\n"
|
238
|
+
f"Required environment variables: {REQUIRED_ENV_VARS + ['At least one of: ' + str(LLM_PROVIDER_ENV_VARS)]}"
|
239
|
+
)
|
240
|
+
|
241
|
+
# Add metadata about config source
|
242
|
+
config['_config_source'] = config_source
|
243
|
+
|
244
|
+
return config
|
245
|
+
|
246
|
+
|
247
|
+
def _has_env_vars() -> bool:
|
248
|
+
"""Check if ANY ambivo agents environment variables are set."""
|
249
|
+
return any(os.getenv(env_var) for env_var in ENV_VARIABLE_MAPPING.keys())
|
250
|
+
|
251
|
+
|
252
|
+
def _has_minimal_env_vars() -> bool:
|
253
|
+
"""Check if minimal required environment variables are set."""
|
254
|
+
# Check if we have Redis config
|
255
|
+
has_redis = any(os.getenv(var) for var in REQUIRED_ENV_VARS)
|
256
|
+
|
257
|
+
# Check if we have at least one LLM provider
|
258
|
+
has_llm = any(os.getenv(var) for var in LLM_PROVIDER_ENV_VARS)
|
259
|
+
|
260
|
+
return has_redis and has_llm
|
261
|
+
|
262
|
+
|
263
|
+
def _load_config_from_env() -> Dict[str, Any]:
|
264
|
+
"""Load configuration from environment variables."""
|
265
|
+
config = {}
|
266
|
+
|
267
|
+
# Process all mapped environment variables
|
268
|
+
for env_var, config_path in ENV_VARIABLE_MAPPING.items():
|
269
|
+
value = os.getenv(env_var)
|
270
|
+
if value is not None:
|
271
|
+
_set_nested_value(config, config_path, _convert_env_value(value))
|
272
|
+
|
273
|
+
# Set defaults for sections that exist
|
274
|
+
_set_env_config_defaults(config)
|
275
|
+
|
276
|
+
# Validate that we have minimum required configuration
|
277
|
+
if not config.get('redis') or not config.get('llm'):
|
278
|
+
missing = []
|
279
|
+
if not config.get('redis'):
|
280
|
+
missing.append('redis')
|
281
|
+
if not config.get('llm'):
|
282
|
+
missing.append('llm')
|
283
|
+
raise ConfigurationError(f"Missing required sections from environment variables: {missing}")
|
284
|
+
|
285
|
+
return config
|
286
|
+
|
287
|
+
|
288
|
+
def _load_config_from_yaml(config_path: str = None) -> Dict[str, Any]:
|
289
|
+
"""Load configuration from YAML file."""
|
290
|
+
if not YAML_AVAILABLE:
|
291
|
+
raise ConfigurationError("PyYAML is required to load YAML configuration files")
|
292
|
+
|
32
293
|
if config_path:
|
33
294
|
config_file = Path(config_path)
|
34
295
|
else:
|
35
|
-
# Search for agent_config.yaml starting from current directory
|
36
296
|
config_file = _find_config_file()
|
37
297
|
|
38
298
|
if not config_file or not config_file.exists():
|
39
299
|
raise ConfigurationError(
|
40
|
-
"agent_config.yaml not found
|
41
|
-
"
|
300
|
+
f"agent_config.yaml not found{' at ' + str(config_path) if config_path else ' in current or parent directories'}. "
|
301
|
+
"Either create this file or use environment variables."
|
42
302
|
)
|
43
303
|
|
44
304
|
try:
|
@@ -48,9 +308,7 @@ def load_config(config_path: str = None) -> Dict[str, Any]:
|
|
48
308
|
if not config:
|
49
309
|
raise ConfigurationError("agent_config.yaml is empty or contains invalid YAML")
|
50
310
|
|
51
|
-
# Validate required sections
|
52
311
|
_validate_config(config)
|
53
|
-
|
54
312
|
return config
|
55
313
|
|
56
314
|
except yaml.YAMLError as e:
|
@@ -59,7 +317,239 @@ def load_config(config_path: str = None) -> Dict[str, Any]:
|
|
59
317
|
raise ConfigurationError(f"Failed to load agent_config.yaml: {e}")
|
60
318
|
|
61
319
|
|
62
|
-
def
|
320
|
+
def _get_minimal_defaults() -> Dict[str, Any]:
|
321
|
+
"""Get minimal default configuration when nothing else is available."""
|
322
|
+
return {
|
323
|
+
'redis': {
|
324
|
+
'host': 'localhost',
|
325
|
+
'port': 6379,
|
326
|
+
'db': 0,
|
327
|
+
'password': None
|
328
|
+
},
|
329
|
+
'llm': {
|
330
|
+
'preferred_provider': 'openai',
|
331
|
+
'temperature': 0.7,
|
332
|
+
'max_tokens': 4000
|
333
|
+
},
|
334
|
+
'agent_capabilities': {
|
335
|
+
'enable_knowledge_base': False,
|
336
|
+
'enable_web_search': False,
|
337
|
+
'enable_code_execution': True,
|
338
|
+
'enable_file_processing': False,
|
339
|
+
'enable_web_ingestion': False,
|
340
|
+
'enable_api_calls': False,
|
341
|
+
'enable_web_scraping': False,
|
342
|
+
'enable_proxy_mode': True,
|
343
|
+
'enable_media_editor': False,
|
344
|
+
'enable_youtube_download': False
|
345
|
+
},
|
346
|
+
'service': {
|
347
|
+
'enable_metrics': True,
|
348
|
+
'log_level': 'INFO',
|
349
|
+
'max_sessions': 100,
|
350
|
+
'session_timeout': 3600
|
351
|
+
},
|
352
|
+
'moderator': {
|
353
|
+
'default_enabled_agents': ['assistant']
|
354
|
+
},
|
355
|
+
'docker': {
|
356
|
+
'images': ['sgosain/amb-ubuntu-python-public-pod'],
|
357
|
+
'memory_limit': '512m',
|
358
|
+
'timeout': 60,
|
359
|
+
'work_dir': '/opt/ambivo/work_dir'
|
360
|
+
}
|
361
|
+
}
|
362
|
+
|
363
|
+
|
364
|
+
def _set_nested_value(config: Dict[str, Any], path: tuple, value: Any) -> None:
|
365
|
+
"""Set a nested value in configuration dictionary."""
|
366
|
+
current = config
|
367
|
+
|
368
|
+
# Navigate to the parent of the target key
|
369
|
+
for key in path[:-1]:
|
370
|
+
if key not in current:
|
371
|
+
current[key] = {}
|
372
|
+
current = current[key]
|
373
|
+
|
374
|
+
# Handle special cases
|
375
|
+
final_key = path[-1]
|
376
|
+
|
377
|
+
if final_key == "images" and isinstance(value, str):
|
378
|
+
# Docker images should be a list
|
379
|
+
current[final_key] = [value]
|
380
|
+
elif final_key == "default_enabled_agents" and isinstance(value, str):
|
381
|
+
# Moderator enabled agents should be a list
|
382
|
+
current[final_key] = [agent.strip() for agent in value.split(",")]
|
383
|
+
else:
|
384
|
+
current[final_key] = value
|
385
|
+
|
386
|
+
|
387
|
+
def _convert_env_value(value: str) -> Union[str, int, float, bool]:
|
388
|
+
"""Convert environment variable string to appropriate type."""
|
389
|
+
if not value:
|
390
|
+
return None
|
391
|
+
|
392
|
+
# Boolean conversion
|
393
|
+
if value.lower() in ('true', 'yes', '1', 'on'):
|
394
|
+
return True
|
395
|
+
elif value.lower() in ('false', 'no', '0', 'off'):
|
396
|
+
return False
|
397
|
+
|
398
|
+
# Integer conversion
|
399
|
+
try:
|
400
|
+
if '.' not in value and value.lstrip('-').isdigit():
|
401
|
+
return int(value)
|
402
|
+
except ValueError:
|
403
|
+
pass
|
404
|
+
|
405
|
+
# Float conversion
|
406
|
+
try:
|
407
|
+
return float(value)
|
408
|
+
except ValueError:
|
409
|
+
pass
|
410
|
+
|
411
|
+
# String (default)
|
412
|
+
return value
|
413
|
+
|
414
|
+
|
415
|
+
def _set_env_config_defaults(config: Dict[str, Any]) -> None:
|
416
|
+
"""Set default values for configuration sections when using environment variables."""
|
417
|
+
|
418
|
+
# Set Redis defaults
|
419
|
+
if 'redis' in config:
|
420
|
+
config['redis'].setdefault('db', 0)
|
421
|
+
|
422
|
+
# Set LLM defaults
|
423
|
+
if 'llm' in config:
|
424
|
+
config['llm'].setdefault('temperature', 0.5)
|
425
|
+
config['llm'].setdefault('max_tokens', 4000)
|
426
|
+
config['llm'].setdefault('preferred_provider', 'openai')
|
427
|
+
|
428
|
+
# Set agent capabilities defaults
|
429
|
+
if 'agent_capabilities' in config:
|
430
|
+
caps = config['agent_capabilities']
|
431
|
+
caps.setdefault('enable_file_processing', True)
|
432
|
+
caps.setdefault('enable_web_ingestion', True)
|
433
|
+
caps.setdefault('enable_api_calls', True)
|
434
|
+
caps.setdefault('enable_agent_collaboration', True)
|
435
|
+
caps.setdefault('enable_result_synthesis', True)
|
436
|
+
caps.setdefault('enable_multi_source_validation', True)
|
437
|
+
caps.setdefault('max_concurrent_operations', 5)
|
438
|
+
caps.setdefault('operation_timeout_seconds', 30)
|
439
|
+
caps.setdefault('max_memory_usage_mb', 500)
|
440
|
+
|
441
|
+
# Set web search defaults
|
442
|
+
if 'web_search' in config:
|
443
|
+
ws = config['web_search']
|
444
|
+
ws.setdefault('default_max_results', 10)
|
445
|
+
ws.setdefault('search_timeout_seconds', 10)
|
446
|
+
ws.setdefault('enable_caching', True)
|
447
|
+
ws.setdefault('cache_ttl_minutes', 30)
|
448
|
+
|
449
|
+
# Set knowledge base defaults
|
450
|
+
if 'knowledge_base' in config:
|
451
|
+
kb = config['knowledge_base']
|
452
|
+
kb.setdefault('chunk_size', 1024)
|
453
|
+
kb.setdefault('chunk_overlap', 20)
|
454
|
+
kb.setdefault('similarity_top_k', 5)
|
455
|
+
kb.setdefault('vector_size', 1536)
|
456
|
+
kb.setdefault('distance_metric', 'cosine')
|
457
|
+
kb.setdefault('default_collection_prefix', '')
|
458
|
+
kb.setdefault('max_file_size_mb', 50)
|
459
|
+
|
460
|
+
# Set web scraping defaults
|
461
|
+
if 'web_scraping' in config:
|
462
|
+
ws = config['web_scraping']
|
463
|
+
ws.setdefault('timeout', 120)
|
464
|
+
ws.setdefault('proxy_enabled', False)
|
465
|
+
ws.setdefault('docker_image', 'sgosain/amb-ubuntu-python-public-pod')
|
466
|
+
|
467
|
+
# Set YouTube download defaults
|
468
|
+
if 'youtube_download' in config:
|
469
|
+
yt = config['youtube_download']
|
470
|
+
yt.setdefault('download_dir', './youtube_downloads')
|
471
|
+
yt.setdefault('timeout', 600)
|
472
|
+
yt.setdefault('memory_limit', '1g')
|
473
|
+
yt.setdefault('default_audio_only', True)
|
474
|
+
yt.setdefault('docker_image', 'sgosain/amb-ubuntu-python-public-pod')
|
475
|
+
|
476
|
+
# Set media editor defaults
|
477
|
+
if 'media_editor' in config:
|
478
|
+
me = config['media_editor']
|
479
|
+
me.setdefault('input_dir', './examples/media_input')
|
480
|
+
me.setdefault('output_dir', './examples/media_output')
|
481
|
+
me.setdefault('timeout', 300)
|
482
|
+
me.setdefault('docker_image', 'sgosain/amb-ubuntu-python-public-pod')
|
483
|
+
me.setdefault('work_dir', '/opt/ambivo/work_dir')
|
484
|
+
|
485
|
+
# Set Docker defaults
|
486
|
+
if 'docker' in config:
|
487
|
+
docker = config['docker']
|
488
|
+
docker.setdefault('memory_limit', '512m')
|
489
|
+
docker.setdefault('timeout', 60)
|
490
|
+
docker.setdefault('work_dir', '/opt/ambivo/work_dir')
|
491
|
+
if 'images' not in docker:
|
492
|
+
docker['images'] = ['sgosain/amb-ubuntu-python-public-pod']
|
493
|
+
|
494
|
+
# Set service defaults
|
495
|
+
if 'service' in config:
|
496
|
+
service = config['service']
|
497
|
+
service.setdefault('max_sessions', 100)
|
498
|
+
service.setdefault('session_timeout', 3600)
|
499
|
+
service.setdefault('log_level', 'INFO')
|
500
|
+
service.setdefault('log_to_file', False)
|
501
|
+
service.setdefault('enable_metrics', True)
|
502
|
+
|
503
|
+
# Set moderator defaults
|
504
|
+
if 'moderator' in config:
|
505
|
+
mod = config['moderator']
|
506
|
+
if 'default_enabled_agents' not in mod:
|
507
|
+
# Set default based on what's enabled
|
508
|
+
enabled_agents = ['assistant']
|
509
|
+
if config.get('agent_capabilities', {}).get('enable_knowledge_base'):
|
510
|
+
enabled_agents.append('knowledge_base')
|
511
|
+
if config.get('agent_capabilities', {}).get('enable_web_search'):
|
512
|
+
enabled_agents.append('web_search')
|
513
|
+
if config.get('agent_capabilities', {}).get('enable_youtube_download'):
|
514
|
+
enabled_agents.append('youtube_download')
|
515
|
+
if config.get('agent_capabilities', {}).get('enable_media_editor'):
|
516
|
+
enabled_agents.append('media_editor')
|
517
|
+
if config.get('agent_capabilities', {}).get('enable_web_scraping'):
|
518
|
+
enabled_agents.append('web_scraper')
|
519
|
+
mod['default_enabled_agents'] = enabled_agents
|
520
|
+
|
521
|
+
# Set routing defaults
|
522
|
+
if 'routing' not in mod:
|
523
|
+
mod['routing'] = {}
|
524
|
+
mod['routing'].setdefault('confidence_threshold', 0.6)
|
525
|
+
mod['routing'].setdefault('enable_multi_agent', True)
|
526
|
+
mod['routing'].setdefault('fallback_agent', 'assistant')
|
527
|
+
mod['routing'].setdefault('max_routing_attempts', 3)
|
528
|
+
|
529
|
+
# Set memory management defaults
|
530
|
+
config.setdefault('memory_management', {
|
531
|
+
'compression': {'enabled': True, 'algorithm': 'lz4', 'compression_level': 1},
|
532
|
+
'cache': {'enabled': True, 'max_size': 1000, 'ttl_seconds': 300},
|
533
|
+
'backup': {'enabled': True, 'interval_minutes': 60, 'backup_directory': './backups'}
|
534
|
+
})
|
535
|
+
|
536
|
+
|
537
|
+
def _merge_configs(yaml_config: Dict[str, Any], env_config: Dict[str, Any]) -> Dict[str, Any]:
|
538
|
+
"""Merge YAML and environment configurations (env takes precedence)."""
|
539
|
+
|
540
|
+
def deep_merge(base: dict, override: dict) -> dict:
|
541
|
+
result = base.copy()
|
542
|
+
for key, value in override.items():
|
543
|
+
if key in result and isinstance(result[key], dict) and isinstance(value, dict):
|
544
|
+
result[key] = deep_merge(result[key], value)
|
545
|
+
else:
|
546
|
+
result[key] = value
|
547
|
+
return result
|
548
|
+
|
549
|
+
return deep_merge(yaml_config, env_config)
|
550
|
+
|
551
|
+
|
552
|
+
def _find_config_file() -> Optional[Path]:
|
63
553
|
"""Find agent_config.yaml in current directory or parent directories."""
|
64
554
|
current_dir = Path.cwd()
|
65
555
|
|
@@ -78,15 +568,7 @@ def _find_config_file() -> Path:
|
|
78
568
|
|
79
569
|
|
80
570
|
def _validate_config(config: Dict[str, Any]) -> None:
|
81
|
-
"""
|
82
|
-
Validate that required configuration sections exist.
|
83
|
-
|
84
|
-
Args:
|
85
|
-
config: Configuration dictionary
|
86
|
-
|
87
|
-
Raises:
|
88
|
-
ConfigurationError: If required sections are missing
|
89
|
-
"""
|
571
|
+
"""Validate that required configuration sections exist."""
|
90
572
|
required_sections = ['redis', 'llm']
|
91
573
|
missing_sections = []
|
92
574
|
|
@@ -97,7 +579,7 @@ def _validate_config(config: Dict[str, Any]) -> None:
|
|
97
579
|
if missing_sections:
|
98
580
|
raise ConfigurationError(
|
99
581
|
f"Required configuration sections missing: {missing_sections}. "
|
100
|
-
"Please check your
|
582
|
+
"Please check your configuration."
|
101
583
|
)
|
102
584
|
|
103
585
|
# Validate Redis config
|
@@ -124,29 +606,50 @@ def _validate_config(config: Dict[str, Any]) -> None:
|
|
124
606
|
|
125
607
|
|
126
608
|
def get_config_section(section: str, config: Dict[str, Any] = None) -> Dict[str, Any]:
|
127
|
-
"""
|
128
|
-
Get a specific configuration section.
|
129
|
-
|
130
|
-
Args:
|
131
|
-
section: Section name (e.g., 'redis', 'llm', 'web_scraping')
|
132
|
-
config: Optional config dict. If None, loads from file.
|
133
|
-
|
134
|
-
Returns:
|
135
|
-
Configuration section dictionary
|
136
|
-
|
137
|
-
Raises:
|
138
|
-
ConfigurationError: If section is not found
|
139
|
-
"""
|
609
|
+
"""Get a specific configuration section."""
|
140
610
|
if config is None:
|
141
611
|
config = load_config()
|
142
612
|
|
143
613
|
if section not in config:
|
144
|
-
|
614
|
+
# Return empty dict instead of raising error to allow graceful fallback
|
615
|
+
logging.warning(f"Configuration section '{section}' not found")
|
616
|
+
return {}
|
145
617
|
|
146
618
|
return config[section]
|
147
619
|
|
148
620
|
|
149
|
-
#
|
621
|
+
# Environment variable convenience functions
|
622
|
+
def print_env_var_template():
|
623
|
+
"""Print a template of all available environment variables."""
|
624
|
+
print("# Ambivo Agents Environment Variables Template")
|
625
|
+
print("# Copy and customize these environment variables as needed")
|
626
|
+
print("# All variables use the AMBIVO_AGENTS_ prefix")
|
627
|
+
print()
|
628
|
+
|
629
|
+
sections = {}
|
630
|
+
for env_var, path in ENV_VARIABLE_MAPPING.items():
|
631
|
+
section = path[0]
|
632
|
+
if section not in sections:
|
633
|
+
sections[section] = []
|
634
|
+
sections[section].append(env_var)
|
635
|
+
|
636
|
+
for section, vars in sections.items():
|
637
|
+
print(f"# {section.upper()} Configuration")
|
638
|
+
for var in sorted(vars):
|
639
|
+
print(f"# export {var}=your_value_here")
|
640
|
+
print()
|
641
|
+
|
642
|
+
|
643
|
+
def get_current_config_source() -> str:
|
644
|
+
"""Get the source of the current configuration."""
|
645
|
+
try:
|
646
|
+
config = load_config()
|
647
|
+
return config.get('_config_source', 'unknown')
|
648
|
+
except:
|
649
|
+
return 'none'
|
650
|
+
|
651
|
+
|
652
|
+
# Backward compatibility - keep existing functions
|
150
653
|
CAPABILITY_TO_AGENT_TYPE = {
|
151
654
|
'assistant': 'assistant',
|
152
655
|
'code_execution': 'code_executor',
|
@@ -155,7 +658,7 @@ CAPABILITY_TO_AGENT_TYPE = {
|
|
155
658
|
'knowledge_base': 'knowledge_base',
|
156
659
|
'web_search': 'web_search',
|
157
660
|
'media_editor': 'media_editor',
|
158
|
-
'youtube_download': 'youtube_download'
|
661
|
+
'youtube_download': 'youtube_download'
|
159
662
|
}
|
160
663
|
|
161
664
|
CONFIG_FLAG_TO_CAPABILITY = {
|
@@ -163,38 +666,26 @@ CONFIG_FLAG_TO_CAPABILITY = {
|
|
163
666
|
'enable_knowledge_base': 'knowledge_base',
|
164
667
|
'enable_web_search': 'web_search',
|
165
668
|
'enable_media_editor': 'media_editor',
|
166
|
-
'enable_youtube_download': 'youtube_download',
|
669
|
+
'enable_youtube_download': 'youtube_download',
|
167
670
|
'enable_code_execution': 'code_execution',
|
168
671
|
'enable_proxy_mode': 'proxy'
|
169
672
|
}
|
170
673
|
|
171
674
|
|
172
675
|
def validate_agent_capabilities(config: Dict[str, Any] = None) -> Dict[str, bool]:
|
173
|
-
"""
|
174
|
-
Validate and return available agent capabilities based on configuration.
|
175
|
-
|
176
|
-
This is the SINGLE SOURCE OF TRUTH for capability checking.
|
177
|
-
|
178
|
-
Args:
|
179
|
-
config: Optional config dict. If None, loads from file.
|
180
|
-
|
181
|
-
Returns:
|
182
|
-
Dictionary of capability_name -> enabled status
|
183
|
-
"""
|
676
|
+
"""Validate and return available agent capabilities based on configuration."""
|
184
677
|
if config is None:
|
185
678
|
config = load_config()
|
186
679
|
|
187
|
-
# Always available capabilities
|
188
680
|
capabilities = {
|
189
|
-
'assistant': True,
|
190
|
-
'code_execution': True,
|
191
|
-
'
|
681
|
+
'assistant': True,
|
682
|
+
'code_execution': True,
|
683
|
+
'moderator': True,
|
684
|
+
'proxy': True,
|
192
685
|
}
|
193
686
|
|
194
|
-
# Get agent_capabilities section
|
195
687
|
agent_caps = config.get('agent_capabilities', {})
|
196
688
|
|
197
|
-
# Check optional capabilities based on both flag AND config section existence
|
198
689
|
capabilities['web_scraping'] = (
|
199
690
|
agent_caps.get('enable_web_scraping', False) and
|
200
691
|
'web_scraping' in config
|
@@ -215,7 +706,6 @@ def validate_agent_capabilities(config: Dict[str, Any] = None) -> Dict[str, bool
|
|
215
706
|
'media_editor' in config
|
216
707
|
)
|
217
708
|
|
218
|
-
# YouTube download capability
|
219
709
|
capabilities['youtube_download'] = (
|
220
710
|
agent_caps.get('enable_youtube_download', False) and
|
221
711
|
'youtube_download' in config
|
@@ -225,31 +715,15 @@ def validate_agent_capabilities(config: Dict[str, Any] = None) -> Dict[str, bool
|
|
225
715
|
|
226
716
|
|
227
717
|
def get_available_agent_types(config: Dict[str, Any] = None) -> Dict[str, bool]:
|
228
|
-
"""
|
229
|
-
Get available agent types based on capabilities.
|
230
|
-
|
231
|
-
This maps capabilities to agent type names consistently.
|
232
|
-
|
233
|
-
Args:
|
234
|
-
config: Optional config dict. If None, loads from file.
|
235
|
-
|
236
|
-
Returns:
|
237
|
-
Dictionary of agent_type_name -> available status
|
238
|
-
"""
|
718
|
+
"""Get available agent types based on capabilities."""
|
239
719
|
try:
|
240
720
|
capabilities = validate_agent_capabilities(config)
|
241
|
-
|
242
|
-
# Map capabilities to agent types using centralized mapping
|
243
721
|
agent_types = {}
|
244
722
|
for capability, agent_type in CAPABILITY_TO_AGENT_TYPE.items():
|
245
723
|
agent_types[agent_type] = capabilities.get(capability, False)
|
246
|
-
|
247
724
|
return agent_types
|
248
|
-
|
249
725
|
except Exception as e:
|
250
|
-
import logging
|
251
726
|
logging.error(f"Error getting available agent types: {e}")
|
252
|
-
# Safe fallback
|
253
727
|
return {
|
254
728
|
'assistant': True,
|
255
729
|
'code_executor': True,
|
@@ -262,30 +736,14 @@ def get_available_agent_types(config: Dict[str, Any] = None) -> Dict[str, bool]:
|
|
262
736
|
}
|
263
737
|
|
264
738
|
|
265
|
-
def get_enabled_capabilities(config: Dict[str, Any] = None) ->
|
266
|
-
"""
|
267
|
-
Get list of enabled capability names.
|
268
|
-
|
269
|
-
Args:
|
270
|
-
config: Optional config dict. If None, loads from file.
|
271
|
-
|
272
|
-
Returns:
|
273
|
-
List of enabled capability names
|
274
|
-
"""
|
739
|
+
def get_enabled_capabilities(config: Dict[str, Any] = None) -> List[str]:
|
740
|
+
"""Get list of enabled capability names."""
|
275
741
|
capabilities = validate_agent_capabilities(config)
|
276
742
|
return [cap for cap, enabled in capabilities.items() if enabled]
|
277
743
|
|
278
744
|
|
279
|
-
def get_available_agent_type_names(config: Dict[str, Any] = None) ->
|
280
|
-
"""
|
281
|
-
Get list of available agent type names.
|
282
|
-
|
283
|
-
Args:
|
284
|
-
config: Optional config dict. If None, loads from file.
|
285
|
-
|
286
|
-
Returns:
|
287
|
-
List of available agent type names
|
288
|
-
"""
|
745
|
+
def get_available_agent_type_names(config: Dict[str, Any] = None) -> List[str]:
|
746
|
+
"""Get list of available agent type names."""
|
289
747
|
agent_types = get_available_agent_types(config)
|
290
748
|
return [agent_type for agent_type, available in agent_types.items() if available]
|
291
749
|
|
@@ -298,4 +756,73 @@ def capability_to_agent_type(capability: str) -> str:
|
|
298
756
|
def agent_type_to_capability(agent_type: str) -> str:
|
299
757
|
"""Convert agent type name to capability name."""
|
300
758
|
reverse_mapping = {v: k for k, v in CAPABILITY_TO_AGENT_TYPE.items()}
|
301
|
-
return reverse_mapping.get(agent_type, agent_type)
|
759
|
+
return reverse_mapping.get(agent_type, agent_type)
|
760
|
+
|
761
|
+
|
762
|
+
def debug_env_vars():
|
763
|
+
"""Debug function to print all AMBIVO_AGENTS_ environment variables."""
|
764
|
+
print("🔍 AMBIVO_AGENTS Environment Variables Debug")
|
765
|
+
print("=" * 50)
|
766
|
+
|
767
|
+
env_vars = {k: v for k, v in os.environ.items() if k.startswith('AMBIVO_AGENTS_')}
|
768
|
+
|
769
|
+
if not env_vars:
|
770
|
+
print("❌ No AMBIVO_AGENTS_ environment variables found")
|
771
|
+
return
|
772
|
+
|
773
|
+
print(f"✅ Found {len(env_vars)} environment variables:")
|
774
|
+
for key, value in sorted(env_vars.items()):
|
775
|
+
# Mask sensitive values
|
776
|
+
if any(sensitive in key.lower() for sensitive in ['key', 'password', 'secret']):
|
777
|
+
masked_value = value[:8] + "..." if len(value) > 8 else "***"
|
778
|
+
print(f" {key} = {masked_value}")
|
779
|
+
else:
|
780
|
+
print(f" {key} = {value}")
|
781
|
+
|
782
|
+
print("\n🔧 Configuration loading test:")
|
783
|
+
try:
|
784
|
+
config = load_config()
|
785
|
+
print(f"✅ Config loaded successfully from: {config.get('_config_source', 'unknown')}")
|
786
|
+
print(f"📊 Sections: {list(config.keys())}")
|
787
|
+
except Exception as e:
|
788
|
+
print(f"❌ Config loading failed: {e}")
|
789
|
+
|
790
|
+
|
791
|
+
def check_config_health() -> Dict[str, Any]:
|
792
|
+
"""Check the health of the current configuration."""
|
793
|
+
health = {
|
794
|
+
'config_loaded': False,
|
795
|
+
'config_source': 'none',
|
796
|
+
'redis_configured': False,
|
797
|
+
'llm_configured': False,
|
798
|
+
'agents_enabled': [],
|
799
|
+
'errors': []
|
800
|
+
}
|
801
|
+
|
802
|
+
try:
|
803
|
+
config = load_config()
|
804
|
+
health['config_loaded'] = True
|
805
|
+
health['config_source'] = config.get('_config_source', 'unknown')
|
806
|
+
|
807
|
+
# Check Redis
|
808
|
+
redis_config = config.get('redis', {})
|
809
|
+
if redis_config.get('host') and redis_config.get('port'):
|
810
|
+
health['redis_configured'] = True
|
811
|
+
else:
|
812
|
+
health['errors'].append('Redis not properly configured')
|
813
|
+
|
814
|
+
# Check LLM
|
815
|
+
llm_config = config.get('llm', {})
|
816
|
+
if any(key in llm_config for key in ['openai_api_key', 'anthropic_api_key', 'aws_access_key_id']):
|
817
|
+
health['llm_configured'] = True
|
818
|
+
else:
|
819
|
+
health['errors'].append('No LLM provider configured')
|
820
|
+
|
821
|
+
# Check enabled agents
|
822
|
+
capabilities = validate_agent_capabilities(config)
|
823
|
+
health['agents_enabled'] = [cap for cap, enabled in capabilities.items() if enabled]
|
824
|
+
|
825
|
+
except Exception as e:
|
826
|
+
health['errors'].append(f"Configuration error: {e}")
|
827
|
+
|
828
|
+
return health
|