DecisionLab / app /validation.py
RealFalconsAI's picture
Upload 39 files
66ee87e verified
Raw History Blame Contribute Delete
2.91 kB
"""Validation of /api/decide question sets.
Pure Python, no web framework: raises ValueError with the message shown to the client.
app/main.py turns it into HTTP 422. Moved from app/main.py `_validate` in 1.2.0; checks and messages unchanged.
"""
from __future__ import annotations
LIMITS = {"questions": 20, "name": 64, "instructions": 2000, "option": 500}
def _text_len(x) -> int:
return len(x) if isinstance(x, str) else len(str(x))
def validate_questions(questions: dict[str, dict], max_options: int) -> None:
if not questions:
raise ValueError("Add at least one question.")
if len(questions) > LIMITS["questions"]:
raise ValueError(f"At most {LIMITS['questions']} questions per request (got {len(questions)}).")
for name, q in questions.items():
if len(name) > LIMITS["name"]:
raise ValueError(f"Question names are limited to {LIMITS['name']} characters.")
qtype = q.get("type", "choice")
if qtype not in ("choice", "score", "noul"):
raise ValueError(f"Question '{name}': type must be choice, score or noul (got '{qtype}').")
if not (q.get("instructions") or q.get("question")):
raise ValueError(f"Question '{name}': add 'instructions' with the question text.")
if qtype in ("choice", "score"):
crit = q.get("criteria", q.get("options"))
n = len(crit) if isinstance(crit, (dict, list)) else 0
if n < 2:
raise ValueError(f"Question '{name}': give 'criteria' with at least 2 options.")
if n > max_options:
raise ValueError(f"Question '{name}': {n} options is over the lab's limit of {max_options}.")
pairs = crit.items() if isinstance(crit, dict) else ((c, "") for c in crit)
for k, v in pairs:
if isinstance(k, (dict, list)) or isinstance(v, (dict, list)):
raise ValueError(f"Question '{name}': options must be text.")
if _text_len(k) > LIMITS["option"] or _text_len(v) > LIMITS["option"]:
raise ValueError(f"Question '{name}': each option (key and text) is limited to {LIMITS['option']} characters.")
text = q.get("instructions") or q.get("question") or ""
if _text_len(text) > LIMITS["instructions"]:
raise ValueError(f"Question '{name}': instructions are limited to {LIMITS['instructions']} characters.")
def validate_models(requested: list[str] | None, known: list[str]) -> list[str]:
"""The models to run: every known model when None, else the requested keys de-duplicated in order.
An unknown key is an error, not a silent skip."""
if requested is None:
return list(known)
for k in requested:
if k not in known:
raise ValueError(f"Unknown model '{k}'. Known models: {', '.join(known)}.")
return list(dict.fromkeys(requested))