返回 JoyAI-Echo
progress_events.py
根目录 / echo_longvideo / Director_Agent / nanobot / utils / progress_events.py
1 """Structured progress-event helpers shared by agent runtimes."""
2
3 from __future__ import annotations
4
5 import inspect
6 from collections.abc import Awaitable, Callable
7 from typing import Any
8
9 from nanobot.agent.hook import AgentHookContext
10
11
12 def on_progress_accepts_tool_events(cb: Callable[..., Any]) -> bool:
13 try:
14 sig = inspect.signature(cb)
15 except (TypeError, ValueError):
16 return False
17 if any(p.kind == inspect.Parameter.VAR_KEYWORD for p in sig.parameters.values()):
18 return True
19 return "tool_events" in sig.parameters
20
21
22 async def invoke_on_progress(
23 on_progress: Callable[..., Awaitable[None]],
24 content: str,
25 *,
26 tool_hint: bool = False,
27 tool_events: list[dict[str, Any]] | None = None,
28 ) -> None:
29 if tool_events and on_progress_accepts_tool_events(on_progress):
30 await on_progress(content, tool_hint=tool_hint, tool_events=tool_events)
31 return
32 await on_progress(content, tool_hint=tool_hint)
33
34
35 def build_tool_event_start_payload(tool_call: Any) -> dict[str, Any]:
36 return {
37 "version": 1,
38 "phase": "start",
39 "call_id": str(getattr(tool_call, "id", "") or ""),
40 "name": getattr(tool_call, "name", ""),
41 "arguments": getattr(tool_call, "arguments", {}) or {},
42 "result": None,
43 "error": None,
44 "files": [],
45 "embeds": [],
46 }
47
48
49 def tool_event_result_extras(result: Any) -> tuple[list[Any], list[Any]]:
50 if not isinstance(result, dict):
51 return [], []
52 files = result.get("files") if isinstance(result.get("files"), list) else []
53 embeds = result.get("embeds") if isinstance(result.get("embeds"), list) else []
54 return files, embeds
55
56
57 def build_tool_event_finish_payloads(context: AgentHookContext) -> list[dict[str, Any]]:
58 payloads: list[dict[str, Any]] = []
59 count = min(len(context.tool_calls), len(context.tool_results), len(context.tool_events))
60 for idx in range(count):
61 tool_call = context.tool_calls[idx]
62 result = context.tool_results[idx]
63 event = context.tool_events[idx] if isinstance(context.tool_events[idx], dict) else {}
64 status = event.get("status")
65 phase = "end" if status == "ok" else "error"
66 files, embeds = tool_event_result_extras(result)
67 payload = {
68 "version": 1,
69 "phase": phase,
70 "call_id": str(getattr(tool_call, "id", "") or ""),
71 "name": getattr(tool_call, "name", ""),
72 "arguments": getattr(tool_call, "arguments", {}) or {},
73 "result": result if phase == "end" else None,
74 "error": None,
75 "files": files,
76 "embeds": embeds,
77 }
78 if phase == "error":
79 if isinstance(result, str) and result.strip():
80 payload["error"] = result.strip()
81 else:
82 payload["error"] = str(event.get("detail") or "Tool execution failed")
83 payloads.append(payload)
84 return payloads
85
85 lines PYTHON