obe-matrix / scripts /verify_provider.py
Syed-Hassan's picture
Upload folder using huggingface_hub
378fe8c verified
Raw History Blame Contribute Delete
5.34 kB
"""Check that the configured provider actually produces valid output.
An HTTP 200 proves nothing — llama2 7b/13b returned 200s full of wrong field
names, inline comments, and fractional weights. Only a real generation that
validates against the Pydantic schema counts as working.
Run before any deploy, and again after changing OBE_LLM_MODEL:
python scripts/verify_provider.py
Exits non-zero if any generation fails.
"""
import logging
import os
import sys
import time
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
from obe.course_plo_prompt import build_course_plo_messages
from obe.llm_client import (
generate_course_plo_mapping,
generate_obe_matrix,
generate_question_set,
get_provider,
)
from obe.prompt import build_messages
from obe.question_prompt import build_question_messages
from obe.schema import BLOOM_LABELS
logging.basicConfig(level=logging.INFO, format="%(levelname)s %(name)s: %(message)s")
CHAPTER = """
Memory is the process by which information is encoded, stored, and retrieved.
Encoding converts sensory input into a form the brain can process. Storage
maintains that information over time, across sensory, short-term, and long-term
stores. Retrieval brings stored information back into awareness. The multi-store
model distinguishes these stages, while the levels-of-processing framework
argues that depth of processing at encoding predicts later recall better than
time spent in any one store. Interference and decay both explain forgetting.
"""
PLOS = [
"PLO-01: Apply core psychological theories to explain human behaviour.",
"PLO-02: Evaluate research evidence critically.",
]
def _check(label: str, fn) -> tuple[bool, float]:
print(f"\n-- {label} " + "-" * (56 - len(label)))
start = time.perf_counter()
try:
result = fn()
except Exception as exc:
elapsed = time.perf_counter() - start
print(f" FAIL ({elapsed:.1f}s): {type(exc).__name__}: {exc}")
return False, elapsed
elapsed = time.perf_counter() - start
print(f" OK ({elapsed:.1f}s)")
return True, elapsed
def _obe():
matrix = generate_obe_matrix(build_messages("Cognitive Psychology", PLOS, CHAPTER))
assert matrix.clos, "no CLOs generated"
first = matrix.clos[0]
weights = first.assessment_weights
total = weights.quiz + weights.assignment + weights.midterm + weights.final
print(f" {len(matrix.clos)} CLOs; first at Bloom {first.bloom_level} "
f"({first.bloom_label}); weights sum to {total:g}")
print(f" CLO: {first.clo_text[:80]}")
return matrix
def _questions():
qs = generate_question_set(
build_question_messages(
clo_text="Explain how encoding depth affects long-term recall.",
content_text=CHAPTER,
bloom_level=2,
bloom_label=BLOOM_LABELS[2],
difficulty="Medium",
question_type="MCQ (single correct)",
question_count=3,
option_count=4,
)
)
assert qs.questions, "no questions generated"
first = qs.questions[0]
assert len(first.options) == 4, f"expected 4 options, got {len(first.options)}"
print(f" {len(qs.questions)} questions; {len(first.options)} options on the first")
print(f" Q: {first.question_text[:80]}")
return qs
def _mapping():
result = generate_course_plo_mapping(
build_course_plo_messages(
program="BS Psychology",
course_batch=[
{
"Code": "PSY-101",
"Course Title": "Introduction to Psychology",
"Objectives": "Survey of major areas of psychology.",
"Cr. Hrs.": "3",
},
{
"Code": "PSY-201",
"Course Title": "Research Methods I",
"Objectives": "Design and evaluation of psychological research.",
"Cr. Hrs.": "3",
},
],
plo_list=[
{"PLO Code": "PLO-01", "PLO Text": PLOS[0]},
{"PLO Code": "PLO-02", "PLO Text": PLOS[1]},
],
)
)
assert result.mappings, "no mappings generated"
codes = [m.course_code for m in result.mappings]
print(f" {len(result.mappings)} courses mapped: {', '.join(codes)}")
return result
def main() -> int:
provider = get_provider()
print(f"Provider : {provider.name}")
print(f"Model : {provider.model}")
print(f"Schema : {provider.schema_mode.value}")
print(f"Timeout : {provider.timeout}s")
if provider.schema_mode.value != "native":
print("\n NOTE: no constrained decoding for this model — output is not")
print(" schema-guaranteed. The runner will fall back and repair, but")
print(" a strict-capable model is safer for production.")
results = [
_check("OBEMatrix", _obe),
_check("QuestionSet", _questions),
_check("CoursePLOMappingSet", _mapping),
]
passed = sum(1 for ok, _ in results if ok)
total_time = sum(t for _, t in results)
print("\n" + "=" * 60)
print(f"{passed}/{len(results)} passed in {total_time:.1f}s")
return 0 if passed == len(results) else 1
if __name__ == "__main__":
sys.exit(main())