"""Configuration schema using Pydantic.""" from pathlib import Path from pydantic import BaseModel, Field from pydantic_settings import BaseSettings class WhatsAppConfig(BaseModel): """WhatsApp channel configuration.""" enabled: bool = False bridge_url: str = "ws://localhost:3001" allow_from: list[str] = Field(default_factory=list) # Allowed phone numbers class TelegramConfig(BaseModel): """Telegram channel configuration.""" enabled: bool = False token: str = "" # Bot token from @BotFather allow_from: list[str] = Field(default_factory=list) # Allowed user IDs or usernames class ChannelsConfig(BaseModel): """Configuration for chat channels.""" whatsapp: WhatsAppConfig = Field(default_factory=WhatsAppConfig) telegram: TelegramConfig = Field(default_factory=TelegramConfig) class AgentDefaults(BaseModel): """Default agent configuration.""" workspace: str = "~/.nanobot/workspace" model: str = "anthropic/claude-opus-4-5" max_tokens: int = 8192 temperature: float = 0.7 max_tool_iterations: int = 20 class AgentsConfig(BaseModel): """Agent configuration.""" defaults: AgentDefaults = Field(default_factory=AgentDefaults) class ProviderConfig(BaseModel): """LLM provider configuration.""" api_key: str = "" api_base: str | None = None class ProvidersConfig(BaseModel): """Configuration for LLM providers.""" anthropic: ProviderConfig = Field(default_factory=ProviderConfig) openai: ProviderConfig = Field(default_factory=ProviderConfig) openrouter: ProviderConfig = Field(default_factory=ProviderConfig) vllm: ProviderConfig = Field(default_factory=ProviderConfig) class GatewayConfig(BaseModel): """Gateway/server configuration.""" host: str = "0.0.0.0" port: int = 18789 class WebSearchConfig(BaseModel): """Web search tool configuration.""" api_key: str = "" # Brave Search API key max_results: int = 5 class WebToolsConfig(BaseModel): """Web tools configuration.""" search: WebSearchConfig = Field(default_factory=WebSearchConfig) class ToolsConfig(BaseModel): """Tools configuration.""" web: WebToolsConfig = Field(default_factory=WebToolsConfig) class Config(BaseSettings): """Root configuration for nanobot.""" agents: AgentsConfig = Field(default_factory=AgentsConfig) channels: ChannelsConfig = Field(default_factory=ChannelsConfig) providers: ProvidersConfig = Field(default_factory=ProvidersConfig) gateway: GatewayConfig = Field(default_factory=GatewayConfig) tools: ToolsConfig = Field(default_factory=ToolsConfig) @property def workspace_path(self) -> Path: """Get expanded workspace path.""" return Path(self.agents.defaults.workspace).expanduser() def get_api_key(self) -> str | None: """Get API key in priority order: OpenRouter > Anthropic > OpenAI > vLLM.""" return ( self.providers.openrouter.api_key or self.providers.anthropic.api_key or self.providers.openai.api_key or self.providers.vllm.api_key or None ) def get_api_base(self) -> str | None: """Get API base URL if using OpenRouter or vLLM.""" if self.providers.openrouter.api_key: return self.providers.openrouter.api_base or "https://openrouter.ai/api/v1" if self.providers.vllm.api_base: return self.providers.vllm.api_base return None class Config: env_prefix = "NANOBOT_" env_nested_delimiter = "__"