Files
praxis/server/mastery/rubric_loader.py
T
Praxis CI 813bd586d6 docs(milestone): merge v0.3-mastery-scoring → main
v0.3 milestone merged to main. Mastery scoring + competency rubrics +
verifiable credentials (formative-tier) shipped. 13/13 REQ-IDs covered.
Next milestone: v0.4 (operator tier — cohort dashboard + auth + Postgres).

---ci---
project: praxis
phase: 2
milestone: v0.3
status: complete
milestone_complete: true
milestone_merged_to_main: true
---/ci---
2026-08-04 00:14:59 +00:00

65 lines
1.8 KiB
Python

"""Rubric loader — YAML → Pydantic Rubric (SLICE-01, D-039).
Loads a competency rubric by skill name from the `rubrics/` directory, validates
it against the Pydantic schema, and caches the parsed result in-memory for the
lifetime of the process. Used by the scoring engine (SLICE-03) and the path
engine (SLICE-05).
"""
from __future__ import annotations
from pathlib import Path
from threading import Lock
from typing import Dict
import yaml
from server.mastery.rubric_schema import Rubric, ValidationError
_DEFAULT_RUBRICS_DIR = Path(__file__).resolve().parent.parent.parent / "rubrics"
_cache: Dict[str, Rubric] = {}
_cache_lock = Lock()
def load_rubric(skill: str, rubrics_dir: Path | None = None) -> Rubric:
"""Load and validate a rubric by skill name.
Args:
skill: e.g. 'customer_service' (the YAML filename stem under rubrics/).
rubrics_dir: override the rubrics directory (default: repo /rubrics).
Returns:
A validated Rubric object. Cached in-memory per skill.
Raises:
FileNotFoundError: if the YAML file doesn't exist.
ValidationError: if the YAML fails schema validation (typed Pydantic error).
"""
with _cache_lock:
cached = _cache.get(skill)
if cached is not None:
return cached
base = rubrics_dir or _DEFAULT_RUBRICS_DIR
path = base / f"{skill}.yaml"
if not path.exists():
raise FileNotFoundError(f"Rubric YAML not found: {skill} in {base}")
with path.open("r", encoding="utf-8") as f:
raw = yaml.safe_load(f)
rubric = Rubric.model_validate(raw)
with _cache_lock:
_cache[skill] = rubric
return rubric
def clear_cache() -> None:
"""Clear the in-memory rubric cache (test helper)."""
with _cache_lock:
_cache.clear()
__all__ = ["load_rubric", "clear_cache", "ValidationError"]