"""Lightweight read-only accessor for configs/llm_profiles.json. Kept in ``rag_eval`` (not ``webapp``) so the runner can look up per-model credentials without depending on the webapp layer. """ from __future__ import annotations import json import logging from pathlib import Path from typing import Any logger = logging.getLogger(__name__) _PROFILES_PATH = Path(__file__).resolve().parents[2] / "configs" / "llm_profiles.json" def find_by_model(model_name: str) -> dict[str, Any] | None: """Return the first profile whose ``model`` field matches *model_name*, or None. Returns None (without raising) when the profiles file does not exist or cannot be parsed — callers fall back to environment-variable defaults. """ if not _PROFILES_PATH.exists(): return None try: data = json.loads(_PROFILES_PATH.read_text(encoding="utf-8")) for profile in data.get("profiles", []): if profile.get("model") == model_name: return profile except Exception as exc: # noqa: BLE001 logger.warning("[profile_store] failed to read %s: %s", _PROFILES_PATH, exc) return None def profile_to_client_kwargs( profile: dict[str, Any], fallback_api_key: str | None, fallback_timeout: float, ) -> dict[str, Any]: """Convert a profile dict into keyword arguments for ``openai.AsyncOpenAI``. Fields present in the profile override the supplied fallback values. """ kwargs: dict[str, Any] = { "api_key": profile.get("api_key") or fallback_api_key or "", "timeout": float(profile.get("timeout_seconds") or fallback_timeout), } base_url = (profile.get("base_url") or "").strip() if base_url: kwargs["base_url"] = base_url return kwargs