166 lines
6.3 KiB
Python
Raw Permalink Normal View History

"""
AgentCore Configuration Manager
Unified configuration management for all AgentCore consumers
"""
import os
import yaml
import logging
from typing import Dict, Any, List, Optional
from pathlib import Path
logger = logging.getLogger(__name__)
class AgentCoreConfigManager:
"""Unified configuration management for all AgentCore consumers"""
def __init__(self, environment: str = "debug"):
"""
Initialize configuration manager
Args:
environment: Environment type ("debug" or "performance")
"""
self.environment = environment
self.project_root = self._find_project_root()
self._validator = None # Will be imported when needed to avoid circular imports
def _find_project_root(self) -> Path:
"""Find the project root directory containing .agentcore.yaml"""
current = Path(__file__).parent
while current != current.parent:
if (current / '.agentcore.yaml').exists():
return current
current = current.parent
# Fallback to parent of shared directory
return Path(__file__).parent.parent
def _load_yaml(self, relative_path: str) -> Dict[str, Any]:
"""Load YAML file relative to project root"""
file_path = self.project_root / relative_path
if not file_path.exists():
logger.warning(f"Configuration file not found: {file_path}")
return {}
try:
with open(file_path, 'r') as f:
content = yaml.safe_load(f) or {}
logger.debug(f"Loaded configuration from {file_path}")
return content
except Exception as e:
logger.error(f"Failed to load configuration from {file_path}: {e}")
return {}
def _save_yaml(self, relative_path: str, data: Dict[str, Any]) -> None:
"""Save YAML file relative to project root"""
file_path = self.project_root / relative_path
# Create directory if it doesn't exist
file_path.parent.mkdir(parents=True, exist_ok=True)
try:
with open(file_path, 'w') as f:
yaml.dump(data, f, default_flow_style=False, indent=2)
logger.debug(f"Saved configuration to {file_path}")
except Exception as e:
logger.error(f"Failed to save configuration to {file_path}: {e}")
raise
def _deep_merge(self, base: Dict[str, Any], override: Dict[str, Any]) -> Dict[str, Any]:
"""Deep merge two dictionaries, with override taking precedence"""
result = base.copy()
for key, value in override.items():
if key in result and isinstance(result[key], dict) and isinstance(value, dict):
result[key] = self._deep_merge(result[key], value)
else:
result[key] = value
return result
# Static Configuration Methods
def get_static_config(self) -> Dict[str, Any]:
"""Get static configuration (version controlled)"""
# Load consolidated static config file
return self._load_yaml("config/static-config.yaml")
def get_base_settings(self) -> Dict[str, Any]:
"""Get base settings only (backward compatibility)"""
return self.get_static_config()
# Dynamic Configuration Methods
def get_dynamic_config(self) -> Dict[str, Any]:
"""Get dynamic configuration (deployment generated)"""
# Load consolidated dynamic config file
return self._load_yaml("config/dynamic-config.yaml")
def update_dynamic_config(self, updates: Dict[str, Any]) -> None:
"""Update dynamic configuration file"""
file_path = "config/dynamic-config.yaml"
current = self._load_yaml(file_path)
updated = self._deep_merge(current, updates)
self._save_yaml(file_path, updated)
# Merged Configuration Methods
def get_merged_config(self) -> Dict[str, Any]:
"""Get complete configuration (static + dynamic merged)"""
static = self.get_static_config()
dynamic = self.get_dynamic_config()
return self._deep_merge(static, dynamic)
# Convenience Methods for Backward Compatibility
def get_model_settings(self) -> Dict[str, Any]:
"""Get model settings (backward compatibility)"""
config = self.get_merged_config()
aws_config = config.get("aws", {})
agents_config = config.get("agents", {})
return {
"region_name": aws_config.get("region", "us-east-1"),
"model_id": agents_config.get("modelid", "us.anthropic.claude-3-7-sonnet-20250219-v1:0"),
"temperature": 0.7, # Default from current usage
"max_tokens": 4096 # Default from current usage
}
def get_gateway_url(self) -> str:
"""Get gateway URL (backward compatibility)"""
config = self.get_merged_config()
return config.get("gateway", {}).get("url", "")
def get_oauth_settings(self) -> Dict[str, Any]:
"""Get OAuth settings (backward compatibility)"""
config = self.get_merged_config()
return config.get("okta", {})
def get_tools_schema(self) -> List[Dict[str, Any]]:
"""Get Bedrock agent tools schema (for gateway target creation)"""
config = self.get_static_config()
return config.get("tools_schema", [])
def get_mcp_lambda_config(self) -> Dict[str, Any]:
"""Get MCP lambda configuration (for deployment and gateway operations)"""
config = self.get_merged_config()
return config.get("mcp_lambda", {})
def validate(self) -> bool:
"""Validate current configuration"""
try:
# Import validator here to avoid circular imports
if self._validator is None:
from .config_validator import ConfigValidator
self._validator = ConfigValidator()
static = self.get_static_config()
dynamic = self.get_dynamic_config()
merged = self.get_merged_config()
self._validator.validate_static(static)
self._validator.validate_dynamic(dynamic)
return True
except Exception as e:
logger.error(f"Configuration validation failed: {e}")
return False