"""Configuration loaded from env vars and .env file.""" from __future__ import annotations import os from pathlib import Path from typing import Literal from pydantic import Field from pydantic_settings import BaseSettings, SettingsConfigDict class Settings(BaseSettings): model_config = SettingsConfigDict( env_file=".env", env_file_encoding="utf-8", case_sensitive=False, extra="ignore", ) # Server (HF Spaces uses PORT env var, default 7860) host: str = "0.0.0.0" port: int = Field(default_factory=lambda: int(os.environ.get("PORT", "8765"))) log_level: str = "info" workers: int = 1 # Cache cache_enabled: bool = True cache_ttl_seconds: int = 3600 cache_max_entries: int = 10_000 # Timeouts solver_timeout: int = 30 whisper_timeout: int = 15 vision_timeout: int = 20 ollama_timeout: int = 30 # Whisper whisper_model: Literal["tiny", "base", "small", "medium", "large-v3"] = "tiny" whisper_device: Literal["cpu", "cuda"] = "cpu" whisper_compute_type: Literal["int8", "int8_float16", "int16", "float16", "float32"] = "int8" # Florence-2 florence_model: str = "microsoft/Florence-2-base" florence_device: Literal["cpu", "cuda"] = "cpu" florence_torch_dtype: Literal["float32", "float16", "bfloat16"] = "float32" # Moondream2 moondream_model: str = "vikhyatk/moondream2" moondream_device: Literal["cpu", "cuda"] = "cpu" # Qwen2.5 (text LLM) qwen_model: str = "Qwen/Qwen2.5-1.5B-Instruct" qwen_device: Literal["cpu", "cuda"] = "cpu" qwen_torch_dtype: Literal["float32", "float16", "bfloat16"] = "float32" # Ollama (optional) ollama_enabled: bool = False ollama_host: str = "http://localhost:11434" ollama_text_model: str = "qwen2.5:1.5b" ollama_vision_model: str = "qwen2.5vl:3b" # Directories cache_dir: Path = Field(default_factory=lambda: Path("models_cache")) def ensure_dirs(self) -> None: self.cache_dir.mkdir(parents=True, exist_ok=True) _settings: Settings | None = None def get_settings() -> Settings: global _settings if _settings is None: _settings = Settings() _settings.ensure_dirs() return _settings