fix(sdk): populate RunResult.tools_used and RunResult.messages
``Nanobot.run()`` has always documented ``RunResult.tools_used`` and ``RunResult.messages`` but actually returned ``[]`` for both, so SDK consumers could never inspect which tools fired or what the final message list looked like — the only useful field was ``content``. This threads the data out via a tiny ``_SDKCaptureHook`` that installs alongside any user-supplied hooks. The capture hook accumulates tool names across iterations and snapshots the message list on each ``after_iteration`` call; the last snapshot reflects end-of-turn state. Only the SDK facade is touched: ``AgentLoop.process_direct`` and ``AgentRunner`` signatures are unchanged, so channels / CLI / API paths are unaffected.
This commit is contained in:
committed by
Xubin Ren
parent
4db50f2e32
commit
ca7877f272
+28
-4
@@ -6,7 +6,7 @@ from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from nanobot.agent.hook import AgentHook
|
||||
from nanobot.agent.hook import AgentHook, AgentHookContext
|
||||
from nanobot.agent.loop import AgentLoop
|
||||
from nanobot.bus.queue import MessageBus
|
||||
|
||||
@@ -104,9 +104,10 @@ class Nanobot:
|
||||
Different keys get independent history.
|
||||
hooks: Optional lifecycle hooks for this run.
|
||||
"""
|
||||
capture = _SDKCaptureHook()
|
||||
prev = self._loop._extra_hooks
|
||||
if hooks is not None:
|
||||
self._loop._extra_hooks = list(hooks)
|
||||
base_hooks = list(hooks) if hooks is not None else list(prev or [])
|
||||
self._loop._extra_hooks = [capture, *base_hooks]
|
||||
try:
|
||||
response = await self._loop.process_direct(
|
||||
message, session_key=session_key,
|
||||
@@ -115,7 +116,30 @@ class Nanobot:
|
||||
self._loop._extra_hooks = prev
|
||||
|
||||
content = (response.content if response else None) or ""
|
||||
return RunResult(content=content, tools_used=[], messages=[])
|
||||
return RunResult(
|
||||
content=content,
|
||||
tools_used=capture.tools_used,
|
||||
messages=capture.messages,
|
||||
)
|
||||
|
||||
|
||||
class _SDKCaptureHook(AgentHook):
|
||||
"""Record tool names and the final message list for ``RunResult``.
|
||||
|
||||
The runner mutates ``context.messages`` in place across iterations, so the
|
||||
snapshot is refreshed on every ``after_iteration`` call; the last call
|
||||
reflects the end-of-turn state the SDK caller cares about.
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self.tools_used: list[str] = []
|
||||
self.messages: list[dict[str, Any]] = []
|
||||
|
||||
async def after_iteration(self, context: AgentHookContext) -> None:
|
||||
for call in context.tool_calls:
|
||||
self.tools_used.append(call.name)
|
||||
self.messages = list(context.messages)
|
||||
|
||||
|
||||
def _make_provider(config: Any) -> Any:
|
||||
|
||||
Reference in New Issue
Block a user