v0.12: stabilize search routing and profile-driven phase4 pipeline
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
"""Model profile loading and resolution utilities."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
import yaml
|
||||
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parents[2]
|
||||
DEFAULT_MODEL_CONFIG = REPO_ROOT / "configs" / "models.yaml"
|
||||
LEGACY_MODEL_CONFIG = REPO_ROOT / "configs" / "model_profiles.yaml"
|
||||
|
||||
|
||||
class ModelConfigError(RuntimeError):
|
||||
pass
|
||||
|
||||
|
||||
def load_model_config(path: Path | None = None) -> dict[str, Any]:
|
||||
cfg_path = path or DEFAULT_MODEL_CONFIG
|
||||
if not cfg_path.exists() and LEGACY_MODEL_CONFIG.exists():
|
||||
cfg_path = LEGACY_MODEL_CONFIG
|
||||
if not cfg_path.exists():
|
||||
raise ModelConfigError(f"model config not found: {cfg_path}")
|
||||
try:
|
||||
data = yaml.safe_load(cfg_path.read_text(encoding="utf-8")) or {}
|
||||
except Exception as exc:
|
||||
raise ModelConfigError(f"invalid YAML in {cfg_path}: {exc}") from exc
|
||||
if not isinstance(data, dict):
|
||||
raise ModelConfigError(f"invalid model config shape in {cfg_path}")
|
||||
return data
|
||||
|
||||
|
||||
def resolve_model_profile(
|
||||
*,
|
||||
profile: str | None = None,
|
||||
overrides: dict[str, str] | None = None,
|
||||
path: Path | None = None,
|
||||
) -> dict[str, Any]:
|
||||
cfg = load_model_config(path)
|
||||
profiles = cfg.get("profiles") or {}
|
||||
defaults = cfg.get("defaults") or {}
|
||||
selected = profile or defaults.get("profile")
|
||||
if not selected:
|
||||
raise ModelConfigError("no model profile provided and no defaults.profile set")
|
||||
if selected not in profiles:
|
||||
raise ModelConfigError(f"unknown model profile: {selected}")
|
||||
|
||||
roles = dict((profiles[selected] or {}).get("roles") or {})
|
||||
if defaults.get("script_models"):
|
||||
for role, model in (defaults.get("script_models") or {}).items():
|
||||
roles.setdefault(role, model)
|
||||
for role, model in (overrides or {}).items():
|
||||
roles[role] = model
|
||||
|
||||
return {
|
||||
"profile": selected,
|
||||
"description": (profiles[selected] or {}).get("description", ""),
|
||||
"roles": roles,
|
||||
}
|
||||
|
||||
|
||||
def list_model_profiles(path: Path | None = None) -> list[str]:
|
||||
cfg = load_model_config(path)
|
||||
profiles = cfg.get("profiles") or {}
|
||||
return sorted(profiles.keys())
|
||||
|
||||
|
||||
def parse_model_overrides(items: list[str] | None) -> dict[str, str]:
|
||||
out: dict[str, str] = {}
|
||||
for item in items or []:
|
||||
if "=" not in item:
|
||||
raise ModelConfigError(f"invalid override '{item}', expected role=model")
|
||||
role, model = item.split("=", 1)
|
||||
role = role.strip()
|
||||
model = model.strip()
|
||||
if not role or not model:
|
||||
raise ModelConfigError(f"invalid override '{item}', expected role=model")
|
||||
out[role] = model
|
||||
return out
|
||||
Reference in New Issue
Block a user