|
2 | 2 | actually work with -- messages, tool calls, token counts, file activity, etc.""" |
3 | 3 |
|
4 | 4 | import json |
| 5 | +import logging |
5 | 6 | import math |
6 | 7 | import os |
7 | 8 | from datetime import datetime |
8 | | -from typing import Any |
| 9 | +from typing import Any, cast, get_args |
9 | 10 |
|
10 | 11 | from models.record_data import RecordDataUnion |
11 | | -from models.session import MessageDict, SessionDict, ToolUseDict |
| 12 | +from models.session import MessageDict, RoleLiteral, SessionDict, ToolUseDict |
12 | 13 | from models.tool_results import ToolResultUnion, is_tool_result_dict |
13 | 14 | from utils.jsonl_helpers import ( |
14 | 15 | entry_message as _entry_message, |
|
23 | 24 |
|
24 | 25 | __all__ = ["parse_session", "quick_session_info"] |
25 | 26 |
|
| 27 | +_HANDLED_ENTRY_TYPES = frozenset({"user", "assistant", "system", "progress"}) |
| 28 | +_VALID_ROLES = frozenset(get_args(RoleLiteral)) |
| 29 | + |
| 30 | + |
| 31 | +def _coerce_role(raw: str) -> RoleLiteral: |
| 32 | + if raw in _VALID_ROLES: |
| 33 | + return cast(RoleLiteral, raw) |
| 34 | + logging.warning("Unknown message role %r; mapping to 'system'", raw) |
| 35 | + return "system" |
| 36 | + |
| 37 | + |
| 38 | +def _fallback_message(entry: dict[str, Any], role: RoleLiteral) -> MessageDict: |
| 39 | + """Minimal message for JSONL entry types without a dedicated processor.""" |
| 40 | + content = entry.get("content", "") |
| 41 | + text = content if isinstance(content, str) else (str(content) if content is not None else "") |
| 42 | + return { |
| 43 | + "role": role, |
| 44 | + "uuid": entry.get("uuid"), |
| 45 | + "parent_uuid": entry.get("parentUuid"), |
| 46 | + "timestamp": entry.get("timestamp"), |
| 47 | + "content": text, |
| 48 | + "is_sidechain": entry.get("isSidechain", False), |
| 49 | + } |
| 50 | + |
26 | 51 |
|
27 | 52 | def _safe_int(val: Any) -> int: |
28 | 53 | """Coerce a value to a non-negative int for token accounting; non-numeric, |
@@ -127,6 +152,10 @@ def parse_session(filepath: str) -> SessionDict: |
127 | 152 | _process_system(entry, messages, metadata) |
128 | 153 | elif entry_type == "progress": |
129 | 154 | _process_progress(entry, messages) |
| 155 | + elif entry_type: |
| 156 | + type_str = entry_type if isinstance(entry_type, str) else str(entry_type) |
| 157 | + if type_str not in _HANDLED_ENTRY_TYPES: |
| 158 | + messages.append(_fallback_message(entry, _coerce_role(type_str))) |
130 | 159 |
|
131 | 160 | metadata["models_used"] = sorted(metadata["models_used"]) |
132 | 161 | metadata["service_tiers"] = sorted(metadata["service_tiers"]) |
|
0 commit comments