fix: honor strict workflow agent entry mode

This commit is contained in:
Xin Wang
2026-08-03 16:34:26 +08:00
parent 06719e4801
commit 67389fc5a1
5 changed files with 103 additions and 19 deletions

View File

@@ -867,6 +867,66 @@ class WorkflowBrainTests(unittest.IsolatedAsyncioTestCase):
self.assertEqual(brain._flow_tool(timeout_tool, "start").timeout_secs, 7.0)
self.assertIsNone(brain._flow_tool(session_tool, "start").timeout_secs)
async def test_flow_transition_honors_strict_agent_entry_mode(self):
brain = WorkflowBrain(
{
"specVersion": 3,
"settings": {},
"nodes": [
{"id": "start", "type": "start", "data": {}},
{
"id": "waiting",
"type": "agent",
"data": {"entryMode": "wait_user"},
},
{
"id": "immediate",
"type": "agent",
"data": {"entryMode": "generate"},
},
],
"edges": [],
}
)
async def queue_frame(_frame):
pass
brain._runtime = BrainRuntime(
context=LLMContext(messages=[]),
llm=FakeLLM(),
queue_frame=queue_frame,
set_system_prompt=lambda _prompt: None,
set_tools=lambda _tools: None,
call_end=FakeCallEnd(),
)
brain._state.begin_user_turn("完成上一阶段")
waiting_config = brain._flow_managed_transition_config(
{"name": "waiting"},
triggering_user_text="完成上一阶段",
)
waiting_action = waiting_config["pre_actions"][0]
self.assertFalse(waiting_config["respond_immediately"])
self.assertTrue(waiting_action["consume_triggering_turn"])
await waiting_action["handler"](waiting_action, SimpleNamespace())
self.assertEqual(brain._state.status, WorkflowStatus.WAITING_USER)
self.assertIsNone(brain._state.pending_user_turn)
brain._state.begin_user_turn("请立即处理")
immediate_config = brain._flow_managed_transition_config(
{"name": "immediate"},
triggering_user_text="请立即处理",
)
immediate_action = immediate_config["pre_actions"][0]
self.assertTrue(immediate_config["respond_immediately"])
self.assertFalse(immediate_action["consume_triggering_turn"])
await immediate_action["handler"](immediate_action, SimpleNamespace())
self.assertEqual(brain._state.status, WorkflowStatus.RUNNING_AGENT)
self.assertIsNotNone(brain._state.pending_user_turn)
async def test_session_update_refreshes_current_agent_without_routing(self):
cfg = prepare_dynamic_config(
AssistantConfig(
@@ -1614,6 +1674,7 @@ class WorkflowBrainTests(unittest.IsolatedAsyncioTestCase):
"data": {
"prompt": "处理用户输入",
"contextPolicy": "fresh",
"entryMode": "generate",
},
},
],
@@ -1874,6 +1935,9 @@ class WorkflowBrainTests(unittest.IsolatedAsyncioTestCase):
for message in manager.configs[-1]["task_messages"]
)
)
runs_before_second_transition = sum(
isinstance(frame, LLMRunFrame) for frame in queued
)
# The user-turn processor must return while the second Message is
# still waiting for its transport playback boundary.
@@ -1892,7 +1956,12 @@ class WorkflowBrainTests(unittest.IsolatedAsyncioTestCase):
if manager.current_node == "agent2":
break
self.assertEqual(manager.current_node, "agent2")
self.assertTrue(any(isinstance(frame, LLMRunFrame) for frame in queued))
self.assertEqual(
sum(isinstance(frame, LLMRunFrame) for frame in queued),
runs_before_second_transition,
)
self.assertEqual(brain._state.status, WorkflowStatus.WAITING_USER)
self.assertIsNone(brain._state.pending_user_turn)
self.assertEqual(
manager.configs[-1]["task_messages"],
[
@@ -1906,7 +1975,6 @@ class WorkflowBrainTests(unittest.IsolatedAsyncioTestCase):
],
)
await brain.on_assistant_text_end("agent2-turn", "信息确认完成", False)
await brain.on_user_turn_end("结束通话")
self.assertEqual(manager.current_node, "end")
self.assertTrue(call_end.finished)
@@ -2031,7 +2099,7 @@ class WorkflowBrainTests(unittest.IsolatedAsyncioTestCase):
)
)
async def test_start_llm_conditions_wait_for_and_route_first_user_turn(self):
async def test_waiting_agent_does_not_reply_to_transitioning_user_turn(self):
brain = WorkflowBrain(
{
"specVersion": 3,
@@ -2161,9 +2229,16 @@ class WorkflowBrainTests(unittest.IsolatedAsyncioTestCase):
image_message,
manager.config["task_messages"],
)
self.assertTrue(any(isinstance(frame, LLMRunFrame) for frame in queued))
self.assertFalse(any(isinstance(frame, LLMRunFrame) for frame in queued))
self.assertEqual(brain._state.status, WorkflowStatus.WAITING_USER)
self.assertIsNone(brain._state.pending_user_turn)
self.assertIn("我想吃饭", brain._store.values["system__conversation_history"])
await brain.on_user_turn_end("我要一份米饭")
self.assertTrue(any(isinstance(frame, LLMRunFrame) for frame in queued))
self.assertEqual(brain._state.status, WorkflowStatus.RUNNING_AGENT)
async def test_start_expression_condition_also_waits_for_user_turn(self):
brain = WorkflowBrain(
{