""" SAAP Configuration Settings - Production Ready with OpenRouter Integration Environment-based configuration management for On-Premise deployment """ import os from pathlib import Path from typing import Optional, List from pydantic import Field, field_validator from pydantic_settings import BaseSettings from functools import lru_cache import logging class DatabaseSettings(BaseSettings): """Database configuration settings""" # Database URL - supports SQLite, PostgreSQL, MySQL database_url: str = Field( default="sqlite:///./saap_production.db", env="DATABASE_URL", description="Database connection URL" ) # Connection pool settings pool_size: int = Field(default=10, env="DB_POOL_SIZE") max_overflow: int = Field(default=20, env="DB_MAX_OVERFLOW") pool_timeout: int = Field(default=30, env="DB_POOL_TIMEOUT") pool_recycle: int = Field(default=3600, env="DB_POOL_RECYCLE") # SQLite specific sqlite_check_same_thread: bool = Field(default=False, env="SQLITE_CHECK_SAME_THREAD") model_config = { "env_file": ".env", "env_file_encoding": "utf-8", "case_sensitive": False, "extra": "allow" # Allow extra fields } @field_validator('database_url') def validate_database_url(cls, v): """Ensure database URL is properly formatted""" if not v.startswith(('sqlite:///', 'postgresql://', 'mysql://')): raise ValueError('Unsupported database type. Use sqlite, postgresql, or mysql.') return v class ColossusSettings(BaseSettings): """colossus Server configuration""" api_base: str = Field( default="https://ai.adrian-schupp.de", env="COLOSSUS_API_BASE", description="colossus server base URL" ) api_key: str = Field( default="sk-dBoxml3krytIRLdjr35Lnw", env="COLOSSUS_API_KEY", description="colossus API key" ) default_model: str = Field( default="mistral-small3.2:24b-instruct-2506", env="COLOSSUS_DEFAULT_MODEL" ) timeout: int = Field(default=60, env="COLOSSUS_TIMEOUT") max_retries: int = Field(default=3, env="COLOSSUS_MAX_RETRIES") model_config = { "env_file": ".env", "env_file_encoding": "utf-8", "case_sensitive": False, "extra": "allow" # Allow extra fields } class OpenRouterSettings(BaseSettings): """OpenRouter API configuration for cost-efficient models""" # API Configuration api_key: str = Field( default="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", env="OPENROUTER_API_KEY", description="OpenRouter API key for cost-efficient LLM access" ) base_url: str = Field( default="https://openrouter.ai/api/v1", env="OPENROUTER_BASE_URL" ) enabled: bool = Field(default=True, env="OPENROUTER_ENABLED") # Cost Optimization Settings use_cost_optimization: bool = Field(default=True, env="OPENROUTER_USE_COST_OPTIMIZATION") max_cost_per_request: float = Field(default=0.01, env="OPENROUTER_MAX_COST_PER_REQUEST") fallback_to_free: bool = Field(default=True, env="OPENROUTER_FALLBACK_TO_FREE") # Agent-Specific Model Configuration jane_model: str = Field(default="openai/gpt-4o-mini", env="JANE_ALESI_MODEL") jane_max_tokens: int = Field(default=800, env="JANE_ALESI_MAX_TOKENS") jane_temperature: float = Field(default=0.7, env="JANE_ALESI_TEMPERATURE") john_model: str = Field(default="anthropic/claude-3-haiku", env="JOHN_ALESI_MODEL") john_max_tokens: int = Field(default=1200, env="JOHN_ALESI_MAX_TOKENS") john_temperature: float = Field(default=0.5, env="JOHN_ALESI_TEMPERATURE") lara_model: str = Field(default="openai/gpt-4o-mini", env="LARA_ALESI_MODEL") lara_max_tokens: int = Field(default=1000, env="LARA_ALESI_MAX_TOKENS") lara_temperature: float = Field(default=0.3, env="LARA_ALESI_TEMPERATURE") # Free Model Fallbacks fallback_model: str = Field(default="meta-llama/llama-3.2-3b-instruct:free", env="FALLBACK_MODEL") analyst_model: str = Field(default="meta-llama/llama-3.2-3b-instruct:free", env="ANALYST_MODEL") # Cost Tracking enable_cost_tracking: bool = Field(default=True, env="ENABLE_COST_TRACKING") cost_alert_threshold: float = Field(default=5.0, env="COST_ALERT_THRESHOLD") log_performance_metrics: bool = Field(default=True, env="LOG_PERFORMANCE_METRICS") save_cost_analytics: bool = Field(default=True, env="SAVE_COST_ANALYTICS") model_config = { "env_file": ".env", "env_file_encoding": "utf-8", "case_sensitive": False, "extra": "allow" } def get_agent_model_config(self, agent_name: str) -> dict: """Get model configuration for specific agent""" configs = { "jane_alesi": { "model": self.jane_model, "max_tokens": self.jane_max_tokens, "temperature": self.jane_temperature, "cost_per_1m": 0.15 # GPT-4o-mini }, "john_alesi": { "model": self.john_model, "max_tokens": self.john_max_tokens, "temperature": self.john_temperature, "cost_per_1m": 0.25 # Claude-3-Haiku }, "lara_alesi": { "model": self.lara_model, "max_tokens": self.lara_max_tokens, "temperature": self.lara_temperature, "cost_per_1m": 0.15 # GPT-4o-mini } } return configs.get(agent_name.lower(), { "model": self.fallback_model, "max_tokens": 600, "temperature": 0.7, "cost_per_1m": 0.0 # Free model }) class RedisSettings(BaseSettings): """Redis configuration for message queuing""" host: str = Field(default="localhost", env="REDIS_HOST") port: int = Field(default=6379, env="REDIS_PORT") password: Optional[str] = Field(default=None, env="REDIS_PASSWORD") database: int = Field(default=0, env="REDIS_DB") max_connections: int = Field(default=50, env="REDIS_MAX_CONNECTIONS") model_config = { "env_file": ".env", "env_file_encoding": "utf-8", "case_sensitive": False, "extra": "allow" # Allow extra fields } class SecuritySettings(BaseSettings): """Security and authentication settings""" secret_key: str = Field( default="saap-development-secret-change-in-production", env="SECRET_KEY", min_length=32 ) # JWT Settings jwt_algorithm: str = Field(default="HS256", env="JWT_ALGORITHM") jwt_expire_minutes: int = Field(default=1440, env="JWT_EXPIRE_MINUTES") # 24 hours # API Rate limiting rate_limit_requests: int = Field(default=1000, env="RATE_LIMIT_REQUESTS") rate_limit_window: int = Field(default=3600, env="RATE_LIMIT_WINDOW") # 1 hour # CORS settings - Updated for network access allowed_origins: str = Field( default="http://localhost:5173,http://localhost:8080,http://localhost:3000,http://100.64.0.45:5173,http://100.64.0.45:8080,http://100.64.0.45:3000,http://100.64.0.45:8000", env="ALLOWED_ORIGINS" ) model_config = { "env_file": ".env", "env_file_encoding": "utf-8", "case_sensitive": False, "extra": "allow" # Allow extra fields } @field_validator('allowed_origins') def parse_allowed_origins(cls, v): """Parse comma-separated origins string into list""" if isinstance(v, str): return [origin.strip() for origin in v.split(',') if origin.strip()] return v def get_allowed_origins_list(self) -> List[str]: """Get allowed origins as a list""" if isinstance(self.allowed_origins, str): return [origin.strip() for origin in self.allowed_origins.split(',') if origin.strip()] return self.allowed_origins class LoggingSettings(BaseSettings): """Enhanced logging configuration with cost tracking""" log_level: str = Field(default="INFO", env="LOG_LEVEL") log_format: str = Field( default="%(asctime)s - %(name)s - %(levelname)s - %(message)s", env="LOG_FORMAT" ) # File logging log_to_file: bool = Field(default=True, env="LOG_TO_FILE") log_file_path: str = Field(default="logs/saap.log", env="LOG_FILE_PATH") log_file_max_size: int = Field(default=10485760, env="LOG_FILE_MAX_SIZE") # 10MB log_file_backup_count: int = Field(default=5, env="LOG_FILE_BACKUP_COUNT") # Cost & Performance Logging log_cost_metrics: bool = Field(default=True, env="LOG_COST_METRICS") cost_log_path: str = Field(default="logs/saap_costs.log", env="COST_LOG_PATH") performance_log_path: str = Field(default="logs/saap_performance.log", env="PERFORMANCE_LOG_PATH") model_config = { "env_file": ".env", "env_file_encoding": "utf-8", "case_sensitive": False, "extra": "allow" # Allow extra fields } class AgentSettings(BaseSettings): """Enhanced agent-specific configuration with multi-provider support""" default_agent_timeout: int = Field(default=60, env="DEFAULT_AGENT_TIMEOUT") max_concurrent_agents: int = Field(default=10, env="MAX_CONCURRENT_AGENTS") agent_health_check_interval: int = Field(default=300, env="AGENT_HEALTH_CHECK_INTERVAL") # 5 minutes # Performance settings max_message_history: int = Field(default=1000, env="MAX_MESSAGE_HISTORY") cleanup_old_messages_days: int = Field(default=30, env="CLEANUP_OLD_MESSAGES_DAYS") # Multi-Provider Strategy primary_provider: str = Field(default="colossus", env="PRIMARY_PROVIDER") # colossus, openrouter fallback_provider: str = Field(default="openrouter", env="FALLBACK_PROVIDER") auto_fallback_on_error: bool = Field(default=True, env="AUTO_FALLBACK_ON_ERROR") fallback_timeout_threshold: int = Field(default=30, env="FALLBACK_TIMEOUT_THRESHOLD") # seconds # Cost Efficiency Targets target_response_time: float = Field(default=2.0, env="TARGET_RESPONSE_TIME") # seconds target_cost_per_request: float = Field(default=0.002, env="TARGET_COST_PER_REQUEST") # $0.002 cost_vs_speed_priority: str = Field(default="balanced", env="COST_VS_SPEED_PRIORITY") # cost, speed, balanced # Daily Cost Budgets daily_cost_budget: float = Field(default=10.0, env="DAILY_COST_BUDGET") # $10/day agent_cost_budget: float = Field(default=2.0, env="AGENT_COST_BUDGET") # $2/agent/day warning_cost_threshold: float = Field(default=0.80, env="WARNING_COST_THRESHOLD") # 80% # Model Selection Strategy use_free_models_first: bool = Field(default=False, env="USE_FREE_MODELS_FIRST") smart_model_selection: bool = Field(default=True, env="SMART_MODEL_SELECTION") cost_learning_enabled: bool = Field(default=True, env="COST_LEARNING_ENABLED") model_config = { "env_file": ".env", "env_file_encoding": "utf-8", "case_sensitive": False, "extra": "allow" # Allow extra fields } @field_validator('primary_provider', 'fallback_provider') def validate_provider(cls, v): """Validate provider names""" allowed = ['colossus', 'openrouter'] if v not in allowed: raise ValueError(f'Provider must be one of: {allowed}') return v @field_validator('cost_vs_speed_priority') def validate_priority(cls, v): """Validate priority setting""" allowed = ['cost', 'speed', 'balanced'] if v not in allowed: raise ValueError(f'Priority must be one of: {allowed}') return v class SaapSettings(BaseSettings): """Main SAAP Application Settings with OpenRouter Integration""" # Application Info app_name: str = Field(default="SAAP - satware AI Autonomous Agent Platform", env="APP_NAME") app_version: str = Field(default="1.0.0", env="APP_VERSION") environment: str = Field(default="production", env="ENVIRONMENT") debug: bool = Field(default=False, env="DEBUG") # Server settings - Updated for network access host: str = Field(default="100.64.0.45", env="HOST") # ๐ŸŒ Changed from 0.0.0.0 port: int = Field(default=8000, env="PORT") reload: bool = Field(default=False, env="RELOAD") # Component Settings database: DatabaseSettings = DatabaseSettings() colossus: ColossusSettings = ColossusSettings() openrouter: OpenRouterSettings = OpenRouterSettings() # NEW! redis: RedisSettings = RedisSettings() security: SecuritySettings = SecuritySettings() logging: LoggingSettings = LoggingSettings() agents: AgentSettings = AgentSettings() model_config = { "env_file": ".env", "env_file_encoding": "utf-8", "case_sensitive": False, "extra": "allow" # Allow extra fields from .env that aren't explicitly defined } @field_validator('environment') def validate_environment(cls, v): """Validate environment setting""" allowed_envs = ['development', 'staging', 'production', 'testing'] if v.lower() not in allowed_envs: raise ValueError(f'Environment must be one of: {allowed_envs}') return v.lower() def get_database_url(self) -> str: """Get database URL with environment-specific adjustments""" if self.environment == 'testing': return "sqlite:///./test_saap.db" elif self.environment == 'development': return "sqlite:///./saap_dev.db" else: return self.database.database_url def is_production(self) -> bool: """Check if running in production""" return self.environment == 'production' def get_cors_origins(self) -> List[str]: """Get CORS allowed origins as list""" return self.security.get_allowed_origins_list() def get_log_config(self) -> dict: """Get logging configuration for uvicorn/fastapi""" return { "version": 1, "disable_existing_loggers": False, "formatters": { "default": { "format": self.logging.log_format, }, "cost": { "format": "%(asctime)s - COST - %(message)s", }, "performance": { "format": "%(asctime)s - PERF - %(message)s", } }, "handlers": { "default": { "formatter": "default", "class": "logging.StreamHandler", "stream": "ext://sys.stdout", }, "file": { "formatter": "default", "class": "logging.handlers.RotatingFileHandler", "filename": self.logging.log_file_path, "maxBytes": self.logging.log_file_max_size, "backupCount": self.logging.log_file_backup_count, } if self.logging.log_to_file else None, "cost_file": { "formatter": "cost", "class": "logging.handlers.RotatingFileHandler", "filename": self.logging.cost_log_path, "maxBytes": self.logging.log_file_max_size, "backupCount": self.logging.log_file_backup_count, } if self.logging.log_cost_metrics else None, "performance_file": { "formatter": "performance", "class": "logging.handlers.RotatingFileHandler", "filename": self.logging.performance_log_path, "maxBytes": self.logging.log_file_max_size, "backupCount": self.logging.log_file_backup_count, } if self.logging.log_cost_metrics else None }, "loggers": { "": { "handlers": [h for h in ["default", "file"] if h], "level": self.logging.log_level, }, "saap.cost": { "handlers": [h for h in ["cost_file"] if h], "level": "INFO", "propagate": False, }, "saap.performance": { "handlers": [h for h in ["performance_file"] if h], "level": "INFO", "propagate": False, } }, } def get_openrouter_config_for_agent(self, agent_name: str) -> dict: """Get OpenRouter configuration for specific agent""" return self.openrouter.get_agent_model_config(agent_name) @lru_cache() def get_settings() -> SaapSettings: """Get cached settings instance""" return SaapSettings() # Global settings instance settings = get_settings() # Create logs directory if needed if settings.logging.log_to_file: Path(settings.logging.log_file_path).parent.mkdir(parents=True, exist_ok=True) if settings.logging.log_cost_metrics: Path(settings.logging.cost_log_path).parent.mkdir(parents=True, exist_ok=True) Path(settings.logging.performance_log_path).parent.mkdir(parents=True, exist_ok=True) # Environment-specific logging setup def setup_logging(): """Setup logging configuration""" import logging.config log_config = settings.get_log_config() logging.config.dictConfig(log_config) logger = logging.getLogger(__name__) logger.info(f"๐Ÿš€ SAAP Configuration loaded:") logger.info(f" Environment: {settings.environment}") logger.info(f" Database: {settings.get_database_url()}") logger.info(f" colossus: {settings.colossus.api_base}") logger.info(f" OpenRouter: {settings.openrouter.enabled}") logger.info(f" Redis: {settings.redis.host}:{settings.redis.port}") logger.info(f" Debug Mode: {settings.debug}") logger.info(f"๐ŸŒ Server Host: {settings.host}:{settings.port}") # Log the network settings logger.info(f"๐Ÿ”’ CORS Origins: {settings.get_cors_origins()}") # Cost tracking logger if settings.logging.log_cost_metrics: cost_logger = logging.getLogger("saap.cost") cost_logger.info("๐Ÿ’ฐ Cost tracking initialized") performance_logger = logging.getLogger("saap.performance") performance_logger.info("๐Ÿ“Š Performance monitoring initialized") if __name__ == "__main__": # Test configuration setup_logging() logger = logging.getLogger(__name__) logger.info("๐Ÿงช Configuration Test:") logger.info(f" App Name: {settings.app_name}") logger.info(f" Database URL: {settings.get_database_url()}") logger.info(f" Production Mode: {settings.is_production()}") logger.info(f" OpenRouter Enabled: {settings.openrouter.enabled}") logger.info(f"๐ŸŒ Network Settings:") logger.info(f" Host: {settings.host}") logger.info(f" Port: {settings.port}") logger.info(f" CORS Origins: {settings.get_cors_origins()}") # Test agent model configs for agent in ['jane_alesi', 'john_alesi', 'lara_alesi']: config = settings.get_openrouter_config_for_agent(agent) logger.info(f" {agent}: {config['model']} (${config['cost_per_1m']}/1M tokens)")