Awesome_Embodied_MM / src /wam /config.py
HardToFindAGoodUserName's picture
Deploy Awesome-WAM long-context Q&A app
1a5ba1e verified
Raw
History Blame Contribute Delete
3.05 kB
"""Configuration loading.
Loads ``config/config.yaml`` plus the human-readable ``config/profile.md`` and resolves
the project root. Environment variables (e.g. API keys) are loaded from a local ``.env``
when present so dev runs work without exporting anything.
"""
from __future__ import annotations
import os
from functools import lru_cache
from pathlib import Path
from typing import Any
import yaml
try: # optional: only needed for local dev convenience
from dotenv import load_dotenv
except ImportError: # pragma: no cover
load_dotenv = None
def project_root() -> Path:
"""Repo root = two levels up from this file (src/wam/config.py)."""
return Path(__file__).resolve().parents[2]
class Config:
"""Thin wrapper over the parsed config dict with dotted-path access."""
def __init__(self, data: dict[str, Any], root: Path):
self._data = data
self.root = root
def get(self, path: str, default: Any = None) -> Any:
"""Fetch a nested value by dotted path, e.g. ``config.get("models.tiers.cheap")``."""
node: Any = self._data
for part in path.split("."):
if not isinstance(node, dict) or part not in node:
return default
node = node[part]
return node
def __getitem__(self, key: str) -> Any:
return self._data[key]
@property
def data(self) -> dict[str, Any]:
return self._data
# --- convenience resolvers -------------------------------------------------
def path(self, key: str) -> Path:
"""Resolve a configured relative path (under ``paths.*``) against the repo root."""
rel = self.get(f"paths.{key}")
if rel is None:
raise KeyError(f"paths.{key} not configured")
return (self.root / rel).resolve()
def require_env(self, env_var: str) -> str:
val = os.environ.get(env_var)
if not val:
raise RuntimeError(f"Required environment variable {env_var} is not set")
return val
def profile_text(self) -> str:
"""The research-interest profile fed to relevance/scoring prompts."""
return (self.root / "config" / "profile.md").read_text(encoding="utf-8")
def model_for_stage(self, stage: str, fallback_tier: str = "mid") -> str:
"""Resolve the OpenRouter model id for a pipeline stage via stage_tiers -> tiers."""
tier = self.get(f"models.stage_tiers.{stage}", fallback_tier)
model = self.get(f"models.tiers.{tier}")
if not model:
raise KeyError(f"No model configured for tier '{tier}'")
return model
@lru_cache(maxsize=1)
def load_config(config_path: str | None = None) -> Config:
"""Load and cache the project config. Also loads ``.env`` if available."""
root = project_root()
if load_dotenv is not None:
load_dotenv(root / ".env")
path = Path(config_path) if config_path else root / "config" / "config.yaml"
data = yaml.safe_load(path.read_text(encoding="utf-8"))
return Config(data, root)