| """ |
| 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: str = Field( |
| default="sqlite:///./saap_production.db", |
| env="DATABASE_URL", |
| description="Database connection URL" |
| ) |
| |
| |
| 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_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" |
| } |
| |
| @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" |
| } |
|
|
| class OpenRouterSettings(BaseSettings): |
| """OpenRouter API configuration for cost-efficient models""" |
| |
| |
| 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") |
| |
| |
| 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") |
| |
| |
| 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") |
| |
| |
| 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") |
| |
| |
| 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 |
| }, |
| "john_alesi": { |
| "model": self.john_model, |
| "max_tokens": self.john_max_tokens, |
| "temperature": self.john_temperature, |
| "cost_per_1m": 0.25 |
| }, |
| "lara_alesi": { |
| "model": self.lara_model, |
| "max_tokens": self.lara_max_tokens, |
| "temperature": self.lara_temperature, |
| "cost_per_1m": 0.15 |
| } |
| } |
| |
| return configs.get(agent_name.lower(), { |
| "model": self.fallback_model, |
| "max_tokens": 600, |
| "temperature": 0.7, |
| "cost_per_1m": 0.0 |
| }) |
| |
| 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" |
| } |
|
|
| 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_algorithm: str = Field(default="HS256", env="JWT_ALGORITHM") |
| jwt_expire_minutes: int = Field(default=1440, env="JWT_EXPIRE_MINUTES") |
| |
| |
| rate_limit_requests: int = Field(default=1000, env="RATE_LIMIT_REQUESTS") |
| rate_limit_window: int = Field(default=3600, env="RATE_LIMIT_WINDOW") |
| |
| |
| 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" |
| } |
| |
| @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" |
| ) |
| |
| |
| 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") |
| log_file_backup_count: int = Field(default=5, env="LOG_FILE_BACKUP_COUNT") |
| |
| |
| 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" |
| } |
|
|
| 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") |
| |
| |
| max_message_history: int = Field(default=1000, env="MAX_MESSAGE_HISTORY") |
| cleanup_old_messages_days: int = Field(default=30, env="CLEANUP_OLD_MESSAGES_DAYS") |
| |
| |
| primary_provider: str = Field(default="colossus", env="PRIMARY_PROVIDER") |
| 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") |
| |
| |
| target_response_time: float = Field(default=2.0, env="TARGET_RESPONSE_TIME") |
| target_cost_per_request: float = Field(default=0.002, env="TARGET_COST_PER_REQUEST") |
| cost_vs_speed_priority: str = Field(default="balanced", env="COST_VS_SPEED_PRIORITY") |
| |
| |
| daily_cost_budget: float = Field(default=10.0, env="DAILY_COST_BUDGET") |
| agent_cost_budget: float = Field(default=2.0, env="AGENT_COST_BUDGET") |
| warning_cost_threshold: float = Field(default=0.80, env="WARNING_COST_THRESHOLD") |
| |
| |
| 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" |
| } |
| |
| @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""" |
| |
| |
| 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") |
| |
| |
| host: str = Field(default="100.64.0.45", env="HOST") |
| port: int = Field(default=8000, env="PORT") |
| reload: bool = Field(default=False, env="RELOAD") |
| |
| |
| database: DatabaseSettings = DatabaseSettings() |
| colossus: ColossusSettings = ColossusSettings() |
| openrouter: OpenRouterSettings = OpenRouterSettings() |
| 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" |
| } |
| |
| @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() |
|
|
| |
| settings = get_settings() |
|
|
| |
| 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) |
|
|
| |
| 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}") |
| logger.info(f"π CORS Origins: {settings.get_cors_origins()}") |
| |
| |
| 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__": |
| |
| 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()}") |
| |
| |
| 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)") |