refactor(agent): add turn hook factories

This commit is contained in:
chengyongru
2026-07-08 21:02:12 +08:00
committed by Xubin Ren
parent 04dbf17426
commit 8559458258
7 changed files with 248 additions and 32 deletions
+36 -20
View File
@@ -21,7 +21,7 @@ from nanobot.agent.autocompact import AutoCompact
from nanobot.agent.automation_turns import publish_next_deferred_turn
from nanobot.agent.context import ContextBuilder
from nanobot.agent.cron_turns import CronTurnCoordinator
from nanobot.agent.hook import AgentHook
from nanobot.agent.hook import AgentHook, AgentTurnHookFactory
from nanobot.agent.memory import Consolidator
from nanobot.agent.runner import _MAX_INJECTIONS_PER_TURN, AgentRunner, AgentRunSpec
from nanobot.agent.subagent import SubagentManager
@@ -141,6 +141,7 @@ class TurnContext:
ephemeral: bool = False
run_extra_hooks_for_ephemeral: bool = False
hooks: list[AgentHook] = field(default_factory=list)
hook_factories: list[AgentTurnHookFactory] = field(default_factory=list)
tools: ToolRegistry | None = None
turn_wall_started_at: float = field(default_factory=time.time)
@@ -214,6 +215,7 @@ class AgentLoop:
session_ttl_minutes: int = 0,
consolidation_ratio: float = 0.5,
hooks: list[AgentHook] | None = None,
hook_factories: list[AgentTurnHookFactory] | None = None,
unified_session: bool = False,
disabled_skills: list[str] | None = None,
tools_config: ToolsConfig | None = None,
@@ -284,6 +286,7 @@ class AgentLoop:
self._start_time = time.time()
self._last_usage: dict[str, int] = {}
self._extra_hooks: list[AgentHook] = hooks or []
self._hook_factories: list[AgentTurnHookFactory] = hook_factories or []
self.context = ContextBuilder(workspace, timezone=timezone, disabled_skills=disabled_skills)
self.sessions = session_manager or SessionManager(workspace)
@@ -740,6 +743,7 @@ class AgentLoop:
ephemeral: bool = False,
run_extra_hooks_for_ephemeral: bool = False,
hooks: list[AgentHook] | None = None,
hook_factories: list[AgentTurnHookFactory] | None = None,
tools: ToolRegistry | None = None,
) -> tuple[str | None, list[str], list[dict], str, bool]:
"""Run the agent iteration loop.
@@ -753,24 +757,6 @@ class AgentLoop:
"""
self._sync_subagent_runtime_limits()
hook = build_agent_turn_hook(AgentTurnHookSpec(
on_progress=on_progress,
on_stream=on_stream,
on_stream_end=on_stream_end,
channel=channel,
chat_id=chat_id,
message_id=message_id,
metadata=metadata,
session_key=session_key,
tool_hint_max_length=self.tool_hint_max_length,
set_tool_context=self._set_tool_context,
on_iteration=lambda iteration: setattr(self, "_current_iteration", iteration),
registered_hooks=self._extra_hooks,
turn_hooks=list(hooks or []),
ephemeral=ephemeral,
run_extra_hooks_for_ephemeral=run_extra_hooks_for_ephemeral,
))
async def _checkpoint(payload: dict[str, Any]) -> None:
if session is None:
return
@@ -846,6 +832,27 @@ class AgentLoop:
message_metadata=metadata,
session_metadata=session.metadata if session is not None else None,
)
effective_tools = tools or self.tools
hook = build_agent_turn_hook(AgentTurnHookSpec(
on_progress=on_progress,
on_stream=on_stream,
on_stream_end=on_stream_end,
channel=channel,
chat_id=chat_id,
message_id=message_id,
metadata=metadata,
session_key=active_session_key,
workspace=effective_scope.project_path,
tool_hint_max_length=self.tool_hint_max_length,
set_tool_context=self._set_tool_context,
on_iteration=lambda iteration: setattr(self, "_current_iteration", iteration),
registered_hook_factories=self._hook_factories,
turn_hook_factories=list(hook_factories or []),
registered_hooks=self._extra_hooks,
turn_hooks=list(hooks or []),
ephemeral=ephemeral,
run_extra_hooks_for_ephemeral=run_extra_hooks_for_ephemeral,
))
request_ctx = RequestContext(
channel=channel,
chat_id=chat_id,
@@ -872,7 +879,7 @@ class AgentLoop:
try:
result = await self.runner.run(AgentRunSpec(
initial_messages=initial_messages,
tools=tools or self.tools,
tools=effective_tools,
model=self.model,
max_iterations=self.max_iterations,
max_tool_result_chars=self.max_tool_result_chars,
@@ -1215,6 +1222,7 @@ class AgentLoop:
on_stream: Callable[[str], Awaitable[None]] | None = None,
on_stream_end: Callable[..., Awaitable[None]] | None = None,
pending_queue: asyncio.Queue | None = None,
hook_factories: list[AgentTurnHookFactory] | None = None,
) -> OutboundMessage | None:
"""Process a system inbound message (e.g. subagent announce)."""
channel, chat_id = (
@@ -1276,6 +1284,7 @@ class AgentLoop:
metadata=msg.metadata,
session_key=key,
pending_queue=pending_queue,
hook_factories=hook_factories,
)
wall_done = time.time()
latency_ms = max(0, int((wall_done - t_wall) * 1000))
@@ -1316,6 +1325,7 @@ class AgentLoop:
ephemeral: bool = False,
run_extra_hooks_for_ephemeral: bool = False,
hooks: list[AgentHook] | None = None,
hook_factories: list[AgentTurnHookFactory] | None = None,
tools: ToolRegistry | None = None,
) -> OutboundMessage | None:
"""Process a single inbound message and return the response."""
@@ -1329,6 +1339,7 @@ class AgentLoop:
on_stream=on_stream,
on_stream_end=on_stream_end,
pending_queue=pending_queue,
hook_factories=hook_factories,
)
key = session_key or msg.session_key
@@ -1350,6 +1361,7 @@ class AgentLoop:
ephemeral=ephemeral,
run_extra_hooks_for_ephemeral=run_extra_hooks_for_ephemeral,
hooks=list(hooks or []),
hook_factories=list(hook_factories or []),
tools=tools,
)
@@ -1579,6 +1591,7 @@ class AgentLoop:
ephemeral=ctx.ephemeral,
run_extra_hooks_for_ephemeral=ctx.run_extra_hooks_for_ephemeral,
hooks=ctx.hooks,
hook_factories=ctx.hook_factories,
tools=ctx.tools,
)
final_content, tools_used, all_msgs, stop_reason, had_injections = result
@@ -1896,6 +1909,7 @@ class AgentLoop:
ephemeral: bool = False,
_run_extra_hooks_for_ephemeral: bool = False,
hooks: list[AgentHook] | None = None,
hook_factories: list[AgentTurnHookFactory] | None = None,
tools: ToolRegistry | None = None,
persist_user_message: bool = True,
) -> OutboundMessage | None:
@@ -1923,6 +1937,8 @@ class AgentLoop:
kwargs["run_extra_hooks_for_ephemeral"] = True
if hooks is not None:
kwargs["hooks"] = hooks
if hook_factories is not None:
kwargs["hook_factories"] = hook_factories
if tools is not None:
kwargs["tools"] = tools
return await self._process_message(