feat(agent): add interrupt-aware tool dispatcher

This commit is contained in:
qzl
2026-03-03 15:44:41 +08:00
parent dedd23fdf9
commit 3a64410641
2 changed files with 108 additions and 0 deletions
+54
View File
@@ -0,0 +1,54 @@
from __future__ import annotations
from typing import Any
from pydantic import BaseModel
class InterruptResult(BaseModel):
interrupt_type: str
tool_name: str
tool_args: dict[str, Any]
class BackendExecutionResult(BaseModel):
tool_name: str
tool_args: dict[str, Any]
result: Any | None = None
class ToolDispatcher:
def dispatch(
self, tool: dict[str, Any]
) -> InterruptResult | BackendExecutionResult:
return dispatch_tool_call(tool)
def dispatch_tool_call(
tool: dict[str, Any],
) -> InterruptResult | BackendExecutionResult:
name = tool["name"]
target = tool["execution_target"]
args = tool.get("args", {})
if target == "frontend":
return InterruptResult(
interrupt_type="tool_execution",
tool_name=name,
tool_args=args,
)
if target == "backend":
requires_approval = tool.get("requires_approval", False)
if requires_approval:
return InterruptResult(
interrupt_type="approval_required",
tool_name=name,
tool_args=args,
)
return BackendExecutionResult(
tool_name=name,
tool_args=args,
)
raise ValueError(f"Unknown execution_target: {target}")
@@ -0,0 +1,54 @@
from __future__ import annotations
from v1.agent.tool_dispatcher import (
BackendExecutionResult,
InterruptResult,
ToolDispatcher,
dispatch_tool_call,
)
class TestToolDispatcher:
def test_frontend_tool_returns_interrupt(self):
tool = {
"name": "ui.navigate_to",
"execution_target": "frontend",
"args": {"path": "/home"},
}
result = dispatch_tool_call(tool)
assert isinstance(result, InterruptResult)
assert result.interrupt_type == "tool_execution"
assert result.tool_name == "ui.navigate_to"
def test_backend_tool_executes_directly(self):
tool = {
"name": "srv.get_user_info",
"execution_target": "backend",
"args": {"user_id": "u1"},
"requires_approval": False,
}
result = dispatch_tool_call(tool)
assert isinstance(result, BackendExecutionResult)
assert result.tool_name == "srv.get_user_info"
def test_backend_tool_with_approval_returns_interrupt(self):
tool = {
"name": "srv.transfer_funds",
"execution_target": "backend",
"args": {"to": "u2", "amount": 100},
"requires_approval": True,
}
result = dispatch_tool_call(tool)
assert isinstance(result, InterruptResult)
assert result.interrupt_type == "approval_required"
assert result.tool_name == "srv.transfer_funds"
def test_dispatcher_class_can_dispatch(self):
dispatcher = ToolDispatcher()
tool = {
"name": "ui.show_dialog",
"execution_target": "frontend",
"args": {"message": "Hello"},
}
result = dispatcher.dispatch(tool)
assert isinstance(result, InterruptResult)