"""Application configuration via environment variables.""" from __future__ import annotations from pydantic_settings import BaseSettings from pydantic import Field class Settings(BaseSettings): # --- Service --- service_name: str = "agentic-microservice" service_version: str = "1.0.0" host: str = "0.0.0.0" port: int = 8000 debug: bool = False # --- Redis --- redis_host: str = "localhost" redis_port: int = 6379 redis_db: int = 0 redis_password: str = "" redis_key_prefix: str = "agentic" session_ttl_seconds: int = 86400 # 24h @property def redis_url(self) -> str: auth = f":{self.redis_password}@" if self.redis_password else "" return f"redis://{auth}{self.redis_host}:{self.redis_port}/{self.redis_db}" # --- Model providers --- anthropic_api_key: str = "" anthropic_base_url: str = "" # Custom base URL (for MiniMax Anthropic-compatible, etc.) openai_api_key: str = "" openai_base_url: str = "" # Custom base URL (for MiniMax, DeepInfra, etc.) default_model_provider: str = "claude" default_model_id: str = "claude-sonnet-4-20250514" max_tokens: int = 4096 temperature: float = 0.3 # --- Context engine --- model_context_window: int = 0 # 0 = use legacy fixed budget / explicit override model_max_output_tokens: int = 4096 context_max_tokens: int = 0 # 0 = auto-budget from model window, fallback legacy 120k compaction_threshold_tokens: int = 0 # 0 = derive from ratio compaction_threshold_ratio: float = 0.80 context_reserve_ratio: float = 0.10 artifact_summary_max_chars: int = 2000 knowledge_base_max_tokens: int = 30_000 working_context_max_items: int = 20 tool_raw_output_max_chars: int = 2000 conversation_recent_raw_limit: int = 2 task_history_max_entries: int = 20 task_history_max_tokens: int = 1500 # --- MCP --- mcp_config_path: str = "" # Path to mcp.json; empty = legacy single-server mode mcp_server_command: str = "" # Legacy: single server command mcp_server_args: list[str] = Field(default_factory=list) mcp_timeout_seconds: float = 30.0 mcp_startup_timeout_seconds: float = 10.0 # --- Pricing (per 1M tokens) --- cost_per_1m_input: float = 2.50 cost_per_1m_output: float = 15.00 # --- Orchestrator --- max_execution_steps: int = 25 subagent_max_steps: int = 30 max_execution_timeout_seconds: float = 300.0 # 5 min global timeout # --- SSE --- sse_keepalive_seconds: float = 15.0 model_config = {"env_prefix": "AGENTIC_", "env_file": ".env", "extra": "ignore"} @property def reserve_tokens(self) -> int: if self.model_context_window <= 0: return 0 return max(0, int(self.model_context_window * self.context_reserve_ratio)) @property def effective_context_budget(self) -> int: if self.context_max_tokens > 0: return self.context_max_tokens if self.model_context_window > 0: budget = ( self.model_context_window - max(0, self.model_max_output_tokens) - self.reserve_tokens ) return max(1, budget) return 120_000 @property def effective_compaction_threshold(self) -> int: if self.compaction_threshold_tokens > 0: return min(self.compaction_threshold_tokens, self.effective_context_budget) return max(1, int(self.effective_context_budget * self.compaction_threshold_ratio)) settings = Settings()