# Author: Simon-Pierre Boucher — simon-pierre.boucher@uqo.ca """Paths, configuration loading and seeds. All scripts import from here.""" from __future__ import annotations import os from functools import lru_cache from pathlib import Path from typing import Any import yaml from dotenv import load_dotenv ROOT = Path(__file__).resolve().parents[2] DATA = ROOT / "data" RAW = DATA / "raw" INTERIM = DATA / "interim" PROCESSED = DATA / "processed" SYNTHETIC = DATA / "synthetic" RESULTS = ROOT / "results" FIGURES = ROOT / "figures" CONFIGS = ROOT / "configs" REPORTS = ROOT / "reports" LOGS = ROOT / "logs" PAPER = ROOT / "paper" load_dotenv(ROOT / ".env", override=False) SEED = 20261027 def ensure_dirs() -> None: for d in (RAW, INTERIM, PROCESSED, SYNTHETIC, RESULTS, FIGURES, REPORTS, LOGS, RESULTS / "ledger", RESULTS / "runs"): d.mkdir(parents=True, exist_ok=True) @lru_cache(maxsize=None) def load_config(name: str) -> dict[str, Any]: """Load configs/.yaml.""" with open(CONFIGS / f"{name}.yaml", encoding="utf-8") as fh: return yaml.safe_load(fh) def provider_mode() -> str: """'live' or 'mock'. Mock is forced when AV_PROVIDER_MODE=mock or when no key is present.""" mode = os.environ.get("AV_PROVIDER_MODE", "").strip().lower() if mode in {"mock", "live"}: return mode if os.environ.get("ANTHROPIC_API_KEY") or os.environ.get("OPENAI_API_KEY"): return "live" return "mock" def max_spend_usd() -> float: try: return float(os.environ.get("AV_MAX_SPEND_USD", "50")) except ValueError: return 50.0 def model_info(model_id: str) -> dict[str, Any]: models = load_config("models")["models"] if model_id not in models: raise KeyError(f"model {model_id!r} is not registered in configs/models.yaml") return models[model_id]