feat: add workflow action runtime policies
This commit is contained in:
@@ -92,6 +92,10 @@ class FakeFunctionParams:
|
||||
self.properties = properties
|
||||
|
||||
|
||||
async def noop_queue_frame(_frame):
|
||||
return None
|
||||
|
||||
|
||||
class BrainRegistryTests(unittest.TestCase):
|
||||
def test_capability_matrix(self):
|
||||
self.assertEqual(
|
||||
@@ -806,6 +810,187 @@ class WorkflowBrainTests(unittest.IsolatedAsyncioTestCase):
|
||||
self.assertEqual(variable_events[-1]["changed"], ["order_status"])
|
||||
self.assertEqual(variable_events[-1]["variables"], {"order_status": "paid"})
|
||||
|
||||
async def test_action_result_assignment_modes_reach_tool_executor(self):
|
||||
tool = RuntimeTool(
|
||||
id="client_action",
|
||||
name="客户端操作",
|
||||
function_name="show_message",
|
||||
type="client",
|
||||
)
|
||||
brain = WorkflowBrain(
|
||||
AssistantConfig(
|
||||
type="workflow",
|
||||
graph={
|
||||
"specVersion": 3,
|
||||
"settings": {},
|
||||
"nodes": [
|
||||
{"id": "start", "type": "start", "data": {}},
|
||||
{
|
||||
"id": "inherit_action",
|
||||
"type": "action",
|
||||
"data": {
|
||||
"toolId": "client_action",
|
||||
"resultAssignmentMode": "inherit",
|
||||
},
|
||||
},
|
||||
{
|
||||
"id": "override_action",
|
||||
"type": "action",
|
||||
"data": {
|
||||
"toolId": "client_action",
|
||||
"resultAssignmentMode": "override",
|
||||
"resultAssignments": {"choice": "action"},
|
||||
},
|
||||
},
|
||||
{
|
||||
"id": "none_action",
|
||||
"type": "action",
|
||||
"data": {
|
||||
"toolId": "client_action",
|
||||
"resultAssignmentMode": "none",
|
||||
},
|
||||
},
|
||||
],
|
||||
"edges": [],
|
||||
},
|
||||
tools=[tool],
|
||||
)
|
||||
)
|
||||
captured = []
|
||||
|
||||
async def execute(_tool, _arguments, *, result_assignments=None):
|
||||
captured.append(result_assignments)
|
||||
return {"status": "ok", "updated_variables": []}
|
||||
|
||||
brain._runtime = BrainRuntime(
|
||||
context=LLMContext(messages=[]),
|
||||
llm=FakeLLM(),
|
||||
queue_frame=noop_queue_frame,
|
||||
set_system_prompt=lambda _prompt: None,
|
||||
set_tools=lambda _tools: None,
|
||||
call_end=FakeCallEnd(),
|
||||
)
|
||||
brain._tools.execute = execute
|
||||
|
||||
await brain._enter_action("inherit_action")
|
||||
await brain._enter_action("override_action")
|
||||
await brain._enter_action("none_action")
|
||||
|
||||
self.assertEqual(captured, [None, {"choice": "action"}, {}])
|
||||
|
||||
async def test_action_client_error_sets_error_status(self):
|
||||
tool = RuntimeTool(
|
||||
id="client_action",
|
||||
name="客户端操作",
|
||||
function_name="show_message",
|
||||
type="client",
|
||||
)
|
||||
brain = WorkflowBrain(
|
||||
AssistantConfig(
|
||||
type="workflow",
|
||||
graph={
|
||||
"specVersion": 3,
|
||||
"settings": {},
|
||||
"nodes": [
|
||||
{"id": "start", "type": "start", "data": {}},
|
||||
{
|
||||
"id": "action",
|
||||
"type": "action",
|
||||
"data": {"toolId": "client_action"},
|
||||
},
|
||||
],
|
||||
"edges": [],
|
||||
},
|
||||
tools=[tool],
|
||||
)
|
||||
)
|
||||
|
||||
async def execute(_tool, _arguments, *, result_assignments=None):
|
||||
return {
|
||||
"status": "error",
|
||||
"message": "用户关闭了确认弹窗",
|
||||
"updated_variables": [],
|
||||
}
|
||||
|
||||
brain._runtime = BrainRuntime(
|
||||
context=LLMContext(messages=[]),
|
||||
llm=FakeLLM(),
|
||||
queue_frame=noop_queue_frame,
|
||||
set_system_prompt=lambda _prompt: None,
|
||||
set_tools=lambda _tools: None,
|
||||
call_end=FakeCallEnd(),
|
||||
)
|
||||
brain._tools.execute = execute
|
||||
|
||||
await brain._enter_action("action")
|
||||
|
||||
self.assertEqual(brain._store.values["system__last_action_status"], "error")
|
||||
self.assertEqual(
|
||||
brain._store.values["system__last_action_error"],
|
||||
"用户关闭了确认弹窗",
|
||||
)
|
||||
|
||||
async def test_action_block_policy_only_suppresses_input_while_running(self):
|
||||
tool = RuntimeTool(
|
||||
id="client_action",
|
||||
name="客户端操作",
|
||||
function_name="show_message",
|
||||
type="client",
|
||||
)
|
||||
brain = WorkflowBrain(
|
||||
AssistantConfig(
|
||||
type="workflow",
|
||||
graph={
|
||||
"specVersion": 3,
|
||||
"settings": {},
|
||||
"nodes": [
|
||||
{"id": "start", "type": "start", "data": {}},
|
||||
{
|
||||
"id": "block_action",
|
||||
"type": "action",
|
||||
"data": {
|
||||
"toolId": "client_action",
|
||||
"userInputPolicy": "block",
|
||||
},
|
||||
},
|
||||
{
|
||||
"id": "queue_action",
|
||||
"type": "action",
|
||||
"data": {
|
||||
"toolId": "client_action",
|
||||
"userInputPolicy": "queue",
|
||||
},
|
||||
},
|
||||
],
|
||||
"edges": [],
|
||||
},
|
||||
tools=[tool],
|
||||
)
|
||||
)
|
||||
input_states = []
|
||||
|
||||
async def execute(_tool, _arguments, *, result_assignments=None):
|
||||
input_states.append("executing")
|
||||
return {"status": "ok", "updated_variables": []}
|
||||
|
||||
brain._runtime = BrainRuntime(
|
||||
context=LLMContext(messages=[]),
|
||||
llm=FakeLLM(),
|
||||
queue_frame=noop_queue_frame,
|
||||
set_system_prompt=lambda _prompt: None,
|
||||
set_tools=lambda _tools: None,
|
||||
call_end=FakeCallEnd(),
|
||||
set_input_enabled=input_states.append,
|
||||
)
|
||||
brain._tools.execute = execute
|
||||
|
||||
await brain._enter_action("block_action")
|
||||
self.assertEqual(input_states, [False, "executing", True])
|
||||
|
||||
input_states.clear()
|
||||
await brain._enter_action("queue_action")
|
||||
self.assertEqual(input_states, ["executing"])
|
||||
|
||||
async def test_nodes_without_outgoing_edges_remain_active(self):
|
||||
queued = []
|
||||
|
||||
|
||||
Reference in New Issue
Block a user