diff --git a/app/application/messaging/agent.py b/app/application/messaging/agent.py index a637f7b3d..74490263f 100644 --- a/app/application/messaging/agent.py +++ b/app/application/messaging/agent.py @@ -396,6 +396,60 @@ def build_legacy_web_agent_segments(content: str, tools: list[dict[str, Any]]) - return segments +def _apply_web_agent_tool_event(event: dict[str, Any], assistant_message: dict[str, Any]) -> None: + """将工具生命周期事件写入 WebAgent 展示消息及其有序片段。""" + tool_id = str(event.get("tool_id") or event.get("tool_call_id") or "") + tool_status = str(event.get("status") or "running") + if tool_id and tool_status in {"running", "done", "error"}: + matching_tool = next( + (tool for tool in assistant_message["tools"] if str(tool.get("id") or "") == tool_id), + None, + ) + if tool_status == "running": + if matching_tool is None: + tool_index = len(assistant_message["tools"]) + assistant_message["tools"].append( + { + "id": tool_id, + "tool_name": str(event.get("tool_name") or ""), + "message": str(event.get("message") or "").strip(), + "status": "running", + } + ) + assistant_message.setdefault("segments", []).append({"type": "tool", "toolIndex": tool_index}) + return + if matching_tool is not None: + matching_tool["status"] = tool_status + return + + # 兼容尚未升级的事件生产者:没有调用 ID 时只能把前一批提示视为已结束。 + for tool in assistant_message["tools"]: + if tool.get("status") == "running": + tool["status"] = "done" + tool_index = len(assistant_message["tools"]) + assistant_message["tools"].append( + { + "id": f"tool-{uuid.uuid4().hex}", + "message": str(event.get("message") or "").strip(), + "status": "running", + } + ) + assistant_message.setdefault("segments", []).append({"type": "tool", "toolIndex": tool_index}) + + +def _apply_web_agent_thinking_event(event: dict[str, Any], assistant_message: dict[str, Any]) -> None: + """将模型思考状态写入 WebAgent 展示消息,供前端显示耗时。""" + is_thinking = str(event.get("status") or "") == "running" + assistant_message["thinking"] = is_thinking + if is_thinking: + assistant_message["thinking_started_at"] = event.get("started_at") + assistant_message["thinking_elapsed_ms"] = 0 + else: + assistant_message["thinking_elapsed_ms"] = ( + event.get("elapsed_ms") or assistant_message.get("thinking_elapsed_ms") or 0 + ) + + def apply_web_agent_display_event(event: dict[str, Any], assistant_message: dict[str, Any]) -> None: """ 将 WebAgent SSE 事件同步应用到服务端展示消息快照。 @@ -404,45 +458,7 @@ def apply_web_agent_display_event(event: dict[str, Any], assistant_message: dict if event_type == "delta": append_web_agent_text_segment(assistant_message, event.get("content") or "") elif event_type == "tool": - tool_id = str(event.get("tool_id") or event.get("tool_call_id") or "") - tool_status = str(event.get("status") or "running") - if tool_id and tool_status in {"running", "done", "error"}: - matching_tool = next( - (tool for tool in assistant_message["tools"] if str(tool.get("id") or "") == tool_id), - None, - ) - if tool_status == "running": - if matching_tool is None: - tool_index = len(assistant_message["tools"]) - assistant_message["tools"].append( - { - "id": tool_id, - "tool_name": str(event.get("tool_name") or ""), - "message": str(event.get("message") or "").strip(), - "status": "running", - } - ) - assistant_message.setdefault("segments", []).append( - {"type": "tool", "toolIndex": tool_index} - ) - return - if matching_tool is not None: - matching_tool["status"] = tool_status - return - - # 兼容尚未升级的事件生产者:没有调用 ID 时只能把前一批提示视为已结束。 - for tool in assistant_message["tools"]: - if tool.get("status") == "running": - tool["status"] = "done" - tool_index = len(assistant_message["tools"]) - assistant_message["tools"].append( - { - "id": f"tool-{uuid.uuid4().hex}", - "message": str(event.get("message") or "").strip(), - "status": "running", - } - ) - assistant_message.setdefault("segments", []).append({"type": "tool", "toolIndex": tool_index}) + _apply_web_agent_tool_event(event, assistant_message) elif event_type == "attachment" and event.get("attachment"): assistant_message["attachments"].append(event["attachment"]) elif event_type == "choice" and event.get("choice"): @@ -462,13 +478,7 @@ def apply_web_agent_display_event(event: dict[str, Any], assistant_message: dict ) assistant_message["status"] = target_message.get("status") or "done" elif event_type == "thinking": - is_thinking = str(event.get("status") or "") == "running" - assistant_message["thinking"] = is_thinking - if is_thinking: - assistant_message["thinking_started_at"] = event.get("started_at") - assistant_message["thinking_elapsed_ms"] = 0 - else: - assistant_message["thinking_elapsed_ms"] = event.get("elapsed_ms") or assistant_message.get("thinking_elapsed_ms") or 0 + _apply_web_agent_thinking_event(event, assistant_message) elif event_type == "error": assistant_message["status"] = "error" if not assistant_message["content"]: diff --git a/tests/fixtures/architecture/complexity-baseline.json b/tests/fixtures/architecture/complexity-baseline.json index 9382a3d47..4cdbc989b 100644 --- a/tests/fixtures/architecture/complexity-baseline.json +++ b/tests/fixtures/architecture/complexity-baseline.json @@ -28,7 +28,6 @@ "app/application/classification/projection.py:_project_condition": 16, "app/application/download/organization.py:organize_existing_source": 22, "app/application/formatting.py:EpisodeFormatRuleHelper._auto_recommend": 25, - "app/application/messaging/agent.py:apply_web_agent_display_event": 20, "app/application/messaging/skill.py:SkillInteractionHandler._handle_callback_interaction": 23, "app/application/messaging/skill.py:SkillInteractionHandler._handle_text_interaction": 27, "app/application/messaging/webagent/stream.py:_build_web_agent_event_generator": 16,