from __future__ import annotations import os from dataclasses import dataclass from pathlib import Path from dotenv import load_dotenv PROJECT_ROOT = Path(__file__).resolve().parents[1] DATA_DIR = PROJECT_ROOT / "data" IMAGE_DIR = DATA_DIR / "images" FAVORITES_DIR = DATA_DIR / "favorites" METADATA_CSV = DATA_DIR / "metadata.csv" DEFAULT_NAMESPACE = "image-retrieval-lab3" DEFAULT_MODEL_NAME = "sentence-transformers/clip-ViT-B-32" ALL_CATEGORIES_LABEL = "All" FAVORITES_ZIP_PATH = FAVORITES_DIR / "favorites.zip" class ConfigurationError(RuntimeError): """Raised when required runtime configuration is missing or invalid.""" @dataclass(frozen=True) class Settings: project_root: Path image_dir: Path favorites_dir: Path metadata_csv: Path clip_model_name: str upstash_url: str | None upstash_token: str | None upstash_namespace: str def require_upstash(self) -> None: missing = [] if not self.upstash_url: missing.append("UPSTASH_VECTOR_REST_URL") if not self.upstash_token: missing.append("UPSTASH_VECTOR_REST_TOKEN") if missing: joined = ", ".join(missing) raise ConfigurationError( f"Missing Upstash Vector configuration: {joined}. " "Copy .env.example to .env and fill in your Upstash REST URL and token." ) def load_settings() -> Settings: load_dotenv(PROJECT_ROOT / ".env") return Settings( project_root=PROJECT_ROOT, image_dir=IMAGE_DIR, favorites_dir=FAVORITES_DIR, metadata_csv=METADATA_CSV, clip_model_name=os.getenv("CLIP_MODEL_NAME", DEFAULT_MODEL_NAME), upstash_url=os.getenv("UPSTASH_VECTOR_REST_URL"), upstash_token=os.getenv("UPSTASH_VECTOR_REST_TOKEN"), upstash_namespace=os.getenv("UPSTASH_VECTOR_NAMESPACE", DEFAULT_NAMESPACE), )