from __future__ import annotations from dataclasses import dataclass import os from typing import Tuple DEFAULT_METRICS: Tuple[str, ...] = ( "F1", "Precision", "Recall", "Mean_IoU", "Mean_TP_IoU", ) def _parse_bool(value: str | None, default: bool) -> bool: if value is None: return default normalized = value.strip().lower() if normalized in {"1", "true", "yes", "y", "on"}: return True if normalized in {"0", "false", "no", "n", "off"}: return False return default def _parse_csv(value: str | None) -> tuple[str, ...]: if not value: return tuple() items = [item.strip() for item in value.split(",")] return tuple(item for item in items if item) @dataclass(frozen=True) class AppConfig: hf_token: str | None hf_owner: str | None repo_allowlist: tuple[str, ...] repo_prefixes: tuple[str, ...] repo_id_contains: str discovery_enabled: bool max_repos: int include_history: bool max_commits_per_repo: int metrics_section_heading: str cache_ttl_seconds: int auto_refresh_seconds: int max_load_seconds: int metrics: tuple[str, ...] def load_config() -> AppConfig: return AppConfig( hf_token=os.getenv("HF_TOKEN") or None, hf_owner=os.getenv("HF_OWNER") or None, repo_allowlist=_parse_csv(os.getenv("EVAL_REPO_ALLOWLIST")), repo_prefixes=_parse_csv(os.getenv("EVAL_REPO_PREFIXES")), repo_id_contains=os.getenv("EVAL_REPO_ID_CONTAINS", "eval").strip(), discovery_enabled=_parse_bool(os.getenv("EVAL_DISCOVERY_ENABLED"), True), max_repos=int(os.getenv("EVAL_MAX_REPOS", "200")), include_history=_parse_bool(os.getenv("EVAL_INCLUDE_HISTORY"), True), max_commits_per_repo=int(os.getenv("EVAL_MAX_COMMITS_PER_REPO", "20")), metrics_section_heading=os.getenv("EVAL_METRICS_SECTION_HEADING", "Performance Metrics").strip(), cache_ttl_seconds=int(os.getenv("EVAL_CACHE_TTL_SECONDS", "600")), auto_refresh_seconds=int(os.getenv("EVAL_AUTO_REFRESH_SECONDS", "300")), max_load_seconds=int(os.getenv("EVAL_MAX_LOAD_SECONDS", "90")), metrics=DEFAULT_METRICS, )