from __future__ import annotations from functools import lru_cache from pydantic import Field, field_validator from pydantic_settings import BaseSettings, SettingsConfigDict class Settings(BaseSettings): model_config = SettingsConfigDict( extra="ignore", ) app_name: str = "Brand Chatbot API" api_prefix: str = "/api" cors_origins: list[str] = Field(default_factory=list) blog_posts_csv_path: str = "data/blog/blog_posts_rows.csv" blog_chunks_csv_path: str = "data/blog/blog_chunks_rows.csv" blog_vector_cache_path: str = "data/blog/blog_vector_cache.json" agent_sessions_db_path: str = "agent_conversations.db" database_url: str = "sqlite:///./brand_chatbot.db" openai_api_key: str | None = None openai_base_url: str | None = None openai_chat_model: str = "gpt-5.4-mini" openai_guardrail_model: str = "gpt-4o-mini" openai_embedding_model: str = "text-embedding-3-small" agents_disable_tracing: bool = True request_timeout_seconds: int = 45 retrieval_top_k: int = 4 retrieval_faiss_candidate_limit: int = 24 retrieval_vector_threshold: float = 0.14 conversation_history_window: int = 6 embedding_batch_size: int = 32 lead_notification_provider: str = "gmail_smtp" lead_notification_to_email: str | None = None lead_notification_from_email: str | None = None lead_notification_from_name: str = "Brand Chatbot" gmail_smtp_host: str = "smtp.gmail.com" gmail_smtp_port: int = 587 gmail_smtp_username: str | None = None gmail_smtp_password: str | None = None gmail_smtp_starttls: bool = True ceo_contact_email: str = "antti@blinkhelsinki.fi" @field_validator("cors_origins", mode="before") @classmethod def split_origins(cls, value: str | list[str]) -> list[str]: if isinstance(value, str): return [item.strip() for item in value.split(",") if item.strip()] return value @property def openai_key(self) -> str: api_key = self.openai_api_key if not api_key: raise RuntimeError("Missing OpenAI API key. Set OPENAI_API_KEY in .env.") return api_key @property def normalized_openai_base_url(self) -> str | None: api_base = self.openai_base_url if not api_base: return None return api_base.strip() or None @lru_cache def get_settings() -> Settings: return Settings()