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