""" Hold Slayer Gateway — Configuration All settings loaded from environment variables / .env file. """ from pydantic import Field from pydantic_settings import BaseSettings, SettingsConfigDict class SIPTrunkSettings(BaseSettings): """SIP trunk provider configuration.""" model_config = SettingsConfigDict(env_prefix="SIP_TRUNK_") host: str = "sip.provider.com" port: int = 5060 username: str = "" password: str = "" transport: str = "udp" # udp, tcp, tls did: str = "" # Your phone number (E.164) class GatewaySIPSettings(BaseSettings): """Gateway SIP listener for device registration.""" model_config = SettingsConfigDict(env_prefix="GATEWAY_SIP_") host: str = "0.0.0.0" port: int = 5060 domain: str = "gateway.local" class SpeachesSettings(BaseSettings): """Speaches STT service configuration.""" model_config = SettingsConfigDict(env_prefix="SPEACHES_") url: str = "http://localhost:22070" prod_url: str = "http://localhost:22070" model: str = "whisper-large-v3" class ClassifierSettings(BaseSettings): """Audio classifier thresholds.""" model_config = SettingsConfigDict(env_prefix="CLASSIFIER_") music_threshold: float = 0.7 speech_threshold: float = 0.6 silence_threshold: float = 0.85 window_seconds: float = 3.0 class LLMSettings(BaseSettings): """LLM service configuration (OpenAI-compatible API).""" model_config = SettingsConfigDict(env_prefix="LLM_") base_url: str = "http://localhost:11434/v1" model: str = "llama3" api_key: str = "not-needed" timeout: float = 30.0 max_tokens: int = 1024 temperature: float = 0.3 class HoldSlayerSettings(BaseSettings): """Hold Slayer behavior settings.""" model_config = SettingsConfigDict(env_prefix="HOLD_SLAYER_", env_prefix_allow_empty=True) default_transfer_device: str = Field( default="sip_phone", validation_alias="DEFAULT_TRANSFER_DEVICE" ) max_hold_time: int = Field(default=7200, validation_alias="MAX_HOLD_TIME") hold_check_interval: float = Field(default=2.0, validation_alias="HOLD_CHECK_INTERVAL") class Settings(BaseSettings): """Root application settings.""" model_config = SettingsConfigDict( env_file=".env", env_file_encoding="utf-8", extra="ignore", ) # Database database_url: str = "postgresql+asyncpg://holdslayer:changeme@localhost:5432/holdslayer" # Server host: str = "0.0.0.0" port: int = 8000 debug: bool = True log_level: str = "info" # Notifications notify_sms_number: str = "" # Sub-configs sip_trunk: SIPTrunkSettings = Field(default_factory=SIPTrunkSettings) gateway_sip: GatewaySIPSettings = Field(default_factory=GatewaySIPSettings) speaches: SpeachesSettings = Field(default_factory=SpeachesSettings) classifier: ClassifierSettings = Field(default_factory=ClassifierSettings) llm: LLMSettings = Field(default_factory=LLMSettings) hold_slayer: HoldSlayerSettings = Field(default_factory=HoldSlayerSettings) # Singleton _settings: Settings | None = None def get_settings() -> Settings: """Get cached application settings.""" global _settings if _settings is None: _settings = Settings() return _settings