|
5 | 5 | from models.errors import SessionValidationError |
6 | 6 | from models.session import SessionDict |
7 | 7 |
|
8 | | -_REQUIRED_SESSION_KEYS = ("session_id", "title", "messages", "metadata") |
| 8 | +_ROOT_PATH = "(root)" |
| 9 | + |
| 10 | + |
| 11 | +def _require_field( |
| 12 | + container: dict[str, Any], |
| 13 | + key: str, |
| 14 | + expected_type: type[Any], |
| 15 | + type_label: str, |
| 16 | + *, |
| 17 | + path: str | None = None, |
| 18 | +) -> Any: |
| 19 | + field_path = path or key |
| 20 | + if key not in container: |
| 21 | + raise SessionValidationError(field_path, "missing required field") |
| 22 | + return _require_value(field_path, container[key], expected_type, type_label) |
| 23 | + |
| 24 | + |
| 25 | +def _require_value( |
| 26 | + path: str, |
| 27 | + val: Any, |
| 28 | + expected_type: type[Any], |
| 29 | + type_label: str, |
| 30 | +) -> Any: |
| 31 | + if val is None: |
| 32 | + raise SessionValidationError(path, "must not be null") |
| 33 | + if not isinstance(val, expected_type): |
| 34 | + raise SessionValidationError( |
| 35 | + path, f"expected {type_label}, got {type(val).__name__}" |
| 36 | + ) |
| 37 | + return val |
9 | 38 |
|
10 | 39 |
|
11 | 40 | def validate_session_dict(data: dict[str, Any]) -> SessionDict: |
12 | 41 | """Validate a plain dict matches SessionDict before returning it.""" |
13 | 42 | # Runtime guard for dynamic callers; mypy already types the parameter as dict. |
14 | 43 | if not isinstance(data, dict): |
15 | | - raise SessionValidationError("$", "expected dict") |
16 | | - |
17 | | - for key in _REQUIRED_SESSION_KEYS: |
18 | | - if key not in data: |
19 | | - raise SessionValidationError(key, "missing required field") |
20 | | - |
21 | | - session_id = data["session_id"] |
22 | | - # Explicit null check before isinstance so errors say "must not be null". |
23 | | - if session_id is None: |
24 | | - raise SessionValidationError("session_id", "must not be null") |
25 | | - if not isinstance(session_id, str): |
26 | | - raise SessionValidationError( |
27 | | - "session_id", f"expected str, got {type(session_id).__name__}" |
28 | | - ) |
29 | | - |
30 | | - title = data["title"] |
31 | | - if title is None: |
32 | | - raise SessionValidationError("title", "must not be null") |
33 | | - if not isinstance(title, str): |
34 | | - raise SessionValidationError( |
35 | | - "title", f"expected str, got {type(title).__name__}" |
36 | | - ) |
| 44 | + raise SessionValidationError(_ROOT_PATH, "expected dict") |
37 | 45 |
|
38 | | - messages = data["messages"] |
39 | | - if messages is None: |
40 | | - raise SessionValidationError("messages", "must not be null") |
41 | | - if not isinstance(messages, list): |
42 | | - raise SessionValidationError( |
43 | | - "messages", f"expected list, got {type(messages).__name__}" |
44 | | - ) |
| 46 | + _require_field(data, "session_id", str, "str") |
| 47 | + _require_field(data, "title", str, "str") |
| 48 | + messages = _require_field(data, "messages", list, "list") |
| 49 | + _require_field(data, "metadata", dict, "dict") |
45 | 50 |
|
46 | 51 | for index, message in enumerate(messages): |
47 | 52 | path = f"messages[{index}]" |
48 | | - if message is None: |
49 | | - raise SessionValidationError(path, "must not be null") |
50 | | - if not isinstance(message, dict): |
51 | | - raise SessionValidationError( |
52 | | - path, f"expected dict, got {type(message).__name__}" |
53 | | - ) |
54 | | - if "role" not in message: |
55 | | - raise SessionValidationError(f"{path}.role", "missing required field") |
56 | | - role = message["role"] |
57 | | - if role is None: |
58 | | - raise SessionValidationError(f"{path}.role", "must not be null") |
59 | | - if not isinstance(role, str): |
60 | | - raise SessionValidationError( |
61 | | - f"{path}.role", f"expected str, got {type(role).__name__}" |
62 | | - ) |
63 | | - |
64 | | - metadata = data["metadata"] |
65 | | - if metadata is None: |
66 | | - raise SessionValidationError("metadata", "must not be null") |
67 | | - if not isinstance(metadata, dict): |
68 | | - raise SessionValidationError( |
69 | | - "metadata", f"expected dict, got {type(metadata).__name__}" |
70 | | - ) |
| 53 | + msg_dict = _require_value(path, message, dict, "dict") |
| 54 | + _require_field(msg_dict, "role", str, "str", path=f"{path}.role") |
71 | 55 |
|
72 | 56 | return cast(SessionDict, data) |
0 commit comments