feat: add workflow action outcomes and tracing
This commit is contained in:
@@ -29,7 +29,12 @@ from services.brains.dify_llm import (
|
||||
)
|
||||
from services.brains.workflow_brain import WorkflowBrain
|
||||
from services.runtime_variables import prepare_dynamic_config
|
||||
from services.workflow.models import LLMRouteResult, RouteStatus, WorkflowStatus
|
||||
from services.workflow.models import (
|
||||
ActionStatus,
|
||||
LLMRouteResult,
|
||||
RouteStatus,
|
||||
WorkflowStatus,
|
||||
)
|
||||
|
||||
|
||||
class FakeLLM:
|
||||
@@ -831,11 +836,18 @@ class WorkflowBrainTests(unittest.IsolatedAsyncioTestCase):
|
||||
set_system_prompt=lambda _prompt: None,
|
||||
set_tools=lambda _tools: None,
|
||||
call_end=FakeCallEnd(),
|
||||
session_id="conv_action",
|
||||
)
|
||||
brain._tools.execute = execute
|
||||
|
||||
await brain._enter_action("lookup_action")
|
||||
outcome = await brain._enter_action("lookup_action")
|
||||
|
||||
self.assertEqual(outcome.status, ActionStatus.SUCCESS)
|
||||
self.assertEqual(outcome.updated_variables, ("order_status",))
|
||||
self.assertEqual(
|
||||
brain._store.values["system__last_action_invocation_id"],
|
||||
outcome.invocation_id,
|
||||
)
|
||||
variable_events = [
|
||||
frame.message
|
||||
for frame in queued
|
||||
@@ -845,6 +857,17 @@ class WorkflowBrainTests(unittest.IsolatedAsyncioTestCase):
|
||||
self.assertEqual(variable_events[-1]["reason"], "action")
|
||||
self.assertEqual(variable_events[-1]["changed"], ["order_status"])
|
||||
self.assertEqual(variable_events[-1]["variables"], {"order_status": "paid"})
|
||||
completed_event = next(
|
||||
frame.message
|
||||
for frame in queued
|
||||
if isinstance(frame, OutputTransportMessageUrgentFrame)
|
||||
and frame.message.get("event") == "action_completed"
|
||||
)
|
||||
self.assertEqual(completed_event["outcome"]["status"], "success")
|
||||
self.assertEqual(completed_event["outcome"]["updatedVariables"], ["order_status"])
|
||||
self.assertEqual(completed_event["sessionId"], "conv_action")
|
||||
self.assertEqual(completed_event["workflowRevision"], brain._engine.revision)
|
||||
self.assertNotIn("result", completed_event["outcome"])
|
||||
|
||||
async def test_action_result_assignment_modes_reach_tool_executor(self):
|
||||
tool = RuntimeTool(
|
||||
@@ -958,14 +981,87 @@ class WorkflowBrainTests(unittest.IsolatedAsyncioTestCase):
|
||||
)
|
||||
brain._tools.execute = execute
|
||||
|
||||
await brain._enter_action("action")
|
||||
outcome = await brain._enter_action("action")
|
||||
|
||||
self.assertEqual(outcome.status, ActionStatus.FAILURE)
|
||||
self.assertEqual(outcome.error.code, "tool_error")
|
||||
self.assertEqual(brain._store.values["system__last_action_status"], "error")
|
||||
self.assertEqual(
|
||||
brain._store.values["system__last_action_error"],
|
||||
"用户关闭了确认弹窗",
|
||||
)
|
||||
|
||||
async def test_cancelled_action_does_not_follow_failure_or_default_edge(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"},
|
||||
},
|
||||
{"id": "end", "type": "end", "data": {}},
|
||||
],
|
||||
"edges": [
|
||||
{
|
||||
"id": "after_action",
|
||||
"source": "action",
|
||||
"target": "end",
|
||||
"data": {"mode": "always"},
|
||||
}
|
||||
],
|
||||
},
|
||||
tools=[tool],
|
||||
)
|
||||
)
|
||||
queued = []
|
||||
|
||||
async def queue_frame(frame):
|
||||
queued.append(frame)
|
||||
|
||||
async def execute(_tool, _arguments, *, result_assignments=None):
|
||||
return {
|
||||
"status": "error",
|
||||
"message": "会话已结束",
|
||||
"updated_variables": [],
|
||||
}
|
||||
|
||||
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._tools.execute = execute
|
||||
|
||||
config = await brain._resolve_path("action")
|
||||
|
||||
self.assertEqual(config["name"], "action")
|
||||
self.assertEqual(
|
||||
brain._store.values["system__last_action_status"],
|
||||
"cancelled",
|
||||
)
|
||||
self.assertFalse(
|
||||
any(
|
||||
isinstance(frame, OutputTransportMessageUrgentFrame)
|
||||
and frame.message.get("event") == "edge_selected"
|
||||
for frame in queued
|
||||
)
|
||||
)
|
||||
|
||||
async def test_action_block_policy_only_suppresses_input_while_running(self):
|
||||
tool = RuntimeTool(
|
||||
id="client_action",
|
||||
|
||||
Reference in New Issue
Block a user