"""把 predictions.steps_json 转成 RunLog.get_traces 的行形。 infer_adhoc 的 traces 表为空,轨迹存于 steps_json({thought, tool_call, tool_output})。 诊断管线经 get_traces 消费轨迹,故需此确定性转换适配。 """ from __future__ import annotations import json from typing import Any def steps_json_to_trace_rows( video_id: str, question_id: str, steps_json: str ) -> list[dict[str, Any]]: """将单题 steps_json 解析为 trace 行列表(step 从 0 递增)。 参数: video_id: 视频 ID。 question_id: 题 ID。 steps_json: predictions.steps_json 原文(JSON 数组字符串)。 返回: 行字典列表,字段对齐 traces 表 schema;空/空数组返回 []。 关键实现细节: - 工具名读 tool_call.tool(infer_adhoc 真实字段),对极少数历史 数据的 name 做 back-compat 回退。 - steps_json 非 JSON 数组时直接报错,不做兜底掩盖。 """ if not steps_json or not steps_json.strip(): return [] steps = json.loads(steps_json) if not isinstance(steps, list): raise ValueError(f"steps_json 非数组: {question_id}") rows: list[dict[str, Any]] = [] for i, s in enumerate(steps): call = s.get("tool_call") or {} rows.append( { "video_id": video_id, "question_id": question_id, "step": i, # infer_adhoc 用 "tool";back-compat 兼容极少数 "name" "tool_name": call.get("tool", call.get("name")), "tool_args": call.get("args", {}), "tool_output": s.get("tool_output"), "thought": s.get("thought"), } ) return rows