File size: 2,905 Bytes
66ee87e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
"""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))