mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-08-06 17:38:35 +00:00
fix(agent): on_progress tool_events only when callback accepts; align progress tests with main
Made-with: Cursor
This commit is contained in:
parent
c23d719780
commit
469fc90fe6
@ -4,6 +4,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import dataclasses
|
import dataclasses
|
||||||
|
import inspect
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
@ -104,17 +105,32 @@ class _LoopHook(AgentHook):
|
|||||||
await self._on_progress(thought)
|
await self._on_progress(thought)
|
||||||
tool_hint = self._loop._strip_think(self._loop._tool_hint(context.tool_calls))
|
tool_hint = self._loop._strip_think(self._loop._tool_hint(context.tool_calls))
|
||||||
tool_events = [self._loop._tool_event_start_payload(tc) for tc in context.tool_calls]
|
tool_events = [self._loop._tool_event_start_payload(tc) for tc in context.tool_calls]
|
||||||
await self._on_progress(tool_hint, tool_hint=True, tool_events=tool_events)
|
await self._loop._invoke_on_progress(
|
||||||
|
self._on_progress,
|
||||||
|
tool_hint,
|
||||||
|
tool_hint=True,
|
||||||
|
tool_events=tool_events,
|
||||||
|
)
|
||||||
for tc in context.tool_calls:
|
for tc in context.tool_calls:
|
||||||
args_str = json.dumps(tc.arguments, ensure_ascii=False)
|
args_str = json.dumps(tc.arguments, ensure_ascii=False)
|
||||||
logger.info("Tool call: {}({})", tc.name, args_str[:200])
|
logger.info("Tool call: {}({})", tc.name, args_str[:200])
|
||||||
self._loop._set_tool_context(self._channel, self._chat_id, self._message_id)
|
self._loop._set_tool_context(self._channel, self._chat_id, self._message_id)
|
||||||
|
|
||||||
async def after_iteration(self, context: AgentHookContext) -> None:
|
async def after_iteration(self, context: AgentHookContext) -> None:
|
||||||
if self._on_progress and context.tool_calls and context.tool_events:
|
if (
|
||||||
|
self._on_progress
|
||||||
|
and context.tool_calls
|
||||||
|
and context.tool_events
|
||||||
|
and self._loop._on_progress_accepts_tool_events(self._on_progress)
|
||||||
|
):
|
||||||
tool_events = self._loop._tool_event_finish_payloads(context)
|
tool_events = self._loop._tool_event_finish_payloads(context)
|
||||||
if tool_events:
|
if tool_events:
|
||||||
await self._on_progress("", tool_events=tool_events)
|
await self._loop._invoke_on_progress(
|
||||||
|
self._on_progress,
|
||||||
|
"",
|
||||||
|
tool_hint=False,
|
||||||
|
tool_events=tool_events,
|
||||||
|
)
|
||||||
u = context.usage or {}
|
u = context.usage or {}
|
||||||
logger.debug(
|
logger.debug(
|
||||||
"LLM usage: prompt={} completion={} cached={}",
|
"LLM usage: prompt={} completion={} cached={}",
|
||||||
@ -380,6 +396,29 @@ class AgentLoop:
|
|||||||
sub_cancelled = await self.subagents.cancel_by_session(key)
|
sub_cancelled = await self.subagents.cancel_by_session(key)
|
||||||
return cancelled + sub_cancelled
|
return cancelled + sub_cancelled
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _on_progress_accepts_tool_events(cb: Callable[..., Any]) -> bool:
|
||||||
|
try:
|
||||||
|
sig = inspect.signature(cb)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
return False
|
||||||
|
if any(p.kind == inspect.Parameter.VAR_KEYWORD for p in sig.parameters.values()):
|
||||||
|
return True
|
||||||
|
return "tool_events" in sig.parameters
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def _invoke_on_progress(
|
||||||
|
on_progress: Callable[..., Awaitable[None]],
|
||||||
|
content: str,
|
||||||
|
*,
|
||||||
|
tool_hint: bool = False,
|
||||||
|
tool_events: list[dict[str, Any]] | None = None,
|
||||||
|
) -> None:
|
||||||
|
if tool_events and AgentLoop._on_progress_accepts_tool_events(on_progress):
|
||||||
|
await on_progress(content, tool_hint=tool_hint, tool_events=tool_events)
|
||||||
|
else:
|
||||||
|
await on_progress(content, tool_hint=tool_hint)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _tool_event_start_payload(tool_call: Any) -> dict[str, Any]:
|
def _tool_event_start_payload(tool_call: Any) -> dict[str, Any]:
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -99,12 +99,17 @@ class TestToolEventProgress:
|
|||||||
loop.tools.prepare_call = MagicMock(return_value=(None, {"command": "ls"}, None))
|
loop.tools.prepare_call = MagicMock(return_value=(None, {"command": "ls"}, None))
|
||||||
loop.tools.execute = AsyncMock(return_value="file.txt")
|
loop.tools.execute = AsyncMock(return_value="file.txt")
|
||||||
|
|
||||||
msg = InboundMessage(channel="telegram", chat_id="chat1", content="run ls")
|
msg = InboundMessage(
|
||||||
await loop.run(msg)
|
channel="telegram",
|
||||||
|
sender_id="u1",
|
||||||
|
chat_id="chat1",
|
||||||
|
content="run ls",
|
||||||
|
)
|
||||||
|
await loop._dispatch(msg)
|
||||||
|
|
||||||
# Drain all outbound messages and find the one carrying _tool_events
|
# Drain all outbound messages and find the one carrying _tool_events
|
||||||
outbound = []
|
outbound = []
|
||||||
while bus.outbound_size() > 0:
|
while bus.outbound_size > 0:
|
||||||
outbound.append(await bus.consume_outbound())
|
outbound.append(await bus.consume_outbound())
|
||||||
|
|
||||||
tool_event_msgs = [m for m in outbound if m.metadata and m.metadata.get("_tool_events")]
|
tool_event_msgs = [m for m in outbound if m.metadata and m.metadata.get("_tool_events")]
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user