"""Configuration management for News Agent""" import yaml from pathlib import Path from typing import Any from pydantic import BaseModel, Field, HttpUrl from pydantic_settings import BaseSettings, SettingsConfigDict class RSSSource(BaseModel): """RSS feed source configuration""" name: str url: HttpUrl category: str class AIFilteringConfig(BaseModel): """AI filtering configuration""" enabled: bool = True min_score: float = Field(ge=0, le=10, default=6.5) max_articles: int = Field(ge=1, default=15) class AIConfig(BaseModel): """AI processing configuration""" provider: str = "openrouter" base_url: str = "https://openrouter.ai/api/v1" model: str = "google/gemini-flash-1.5" filtering: AIFilteringConfig interests: list[str] class SMTPConfig(BaseModel): """SMTP server configuration""" host: str = "localhost" port: int = 25 use_tls: bool = False use_ssl: bool = False username: str | None = None password: str | None = None class EmailConfig(BaseModel): """Email configuration""" to: str from_: str = Field(alias="from") from_name: str = "Daily Tech News Agent" subject_template: str = "Tech News Digest - {date}" smtp: SMTPConfig class ScheduleConfig(BaseModel): """Schedule configuration""" time: str = "07:00" timezone: str = "Europe/Oslo" class DatabaseConfig(BaseModel): """Database configuration""" path: str = "data/articles.db" retention_days: int = 30 class LoggingConfig(BaseModel): """Logging configuration""" level: str = "INFO" file: str = "data/logs/news-agent.log" max_bytes: int = 10485760 backup_count: int = 5 class EnvSettings(BaseSettings): """Environment variable settings""" model_config = SettingsConfigDict(env_file=".env", case_sensitive=False, extra="ignore") openrouter_api_key: str openrouter_site_url: str | None = None openrouter_site_name: str | None = None smtp_username: str | None = None smtp_password: str | None = None error_notification_email: str | None = None class Config: """Main configuration manager""" def __init__(self, config_path: str | Path = "config.yaml"): """Load configuration from YAML file and environment variables""" self.config_path = Path(config_path) # Load YAML configuration with open(self.config_path) as f: self._config: dict[str, Any] = yaml.safe_load(f) # Load environment variables self.env = EnvSettings() @property def rss_sources(self) -> list[RSSSource]: """Get all RSS sources""" return [RSSSource(**src) for src in self._config["sources"]["rss"]] @property def ai(self) -> AIConfig: """Get AI configuration""" return AIConfig(**self._config["ai"]) @property def email(self) -> EmailConfig: """Get email configuration""" email_config = self._config["email"].copy() # Merge SMTP credentials from environment variables if self.env.smtp_username: email_config["smtp"]["username"] = self.env.smtp_username if self.env.smtp_password: email_config["smtp"]["password"] = self.env.smtp_password return EmailConfig(**email_config) @property def schedule(self) -> ScheduleConfig: """Get schedule configuration""" return ScheduleConfig(**self._config["schedule"]) @property def database(self) -> DatabaseConfig: """Get database configuration""" return DatabaseConfig(**self._config["database"]) @property def logging(self) -> LoggingConfig: """Get logging configuration""" return LoggingConfig(**self._config["logging"]) # Global config instance (lazy loaded) _config: Config | None = None def get_config() -> Config: """Get or create global config instance""" global _config if _config is None: _config = Config() return _config