from functools import lru_cache from pathlib import Path from typing import Literal from pydantic_settings import BaseSettings, SettingsConfigDict # Repo-root .env for native dev; inside Docker the file is absent and # configuration comes from real environment variables (which take precedence). _REPO_ROOT = Path(__file__).resolve().parents[2] _ENV_FILE = _REPO_ROOT / ".env" class Settings(BaseSettings): model_config = SettingsConfigDict( env_prefix="PABLAN_", env_file=_ENV_FILE, extra="ignore" ) env: Literal["development", "production"] = "development" database_url: str = "postgresql+asyncpg://pablan:change-me@localhost:5432/pablan" # Secure=false is needed for dev over plain http on non-localhost # addresses (WireGuard IPs) — see .env.example. cookie_secure: bool = True auth_session_ttl_days: int = 14 query_retention_days: int = 90 # The shipped template catalog (repo templates/ in dev; the customer # stack mounts the directory and overrides this path). templates_dir: str = str(_REPO_ROOT / "templates") help_dir: str = str(_REPO_ROOT / "help") # The instance's own language: which blueprint variant the first-install # starter set uses, and the fallback when a visitor states no preference. # Per-user choice lives on users.locale and wins over this. default_locale: Literal["de", "en"] = "de" log_level: str = "INFO" # Content debug logging (prompts/responses) — NEVER in production. debug_log_prompts: bool = False llm_timeout_seconds: float = 120.0 # How many requests Pablan lets one endpoint see at once, and how long a # request waits for a free slot before it is answered with "busy". Match # llm_max_parallel to the server's parallel slots (llama.cpp: --parallel). # See app/llm/gate.py. llm_max_parallel: int = 4 llm_queue_wait_seconds: float = 20.0 llm_max_queued: int = 24 job_poll_seconds: float = 1.0 chat_base_url: str = "http://localhost:8001/v1" chat_api_key: str = "none" chat_model: str = "" utility_base_url: str = "http://localhost:8001/v1" utility_api_key: str = "none" utility_model: str = "" embedding_base_url: str = "http://localhost:8002/v1" embedding_api_key: str = "none" embedding_model: str = "" @lru_cache def get_settings() -> Settings: return Settings()