feat: add workflow action runtime policies
This commit is contained in:
@@ -724,19 +724,29 @@ class WorkflowBrain(BaseBrain):
|
||||
self._state.enter(node_id, WorkflowStatus.RUNNING_ACTION)
|
||||
await self._emit_node_active(node_id)
|
||||
data = self._engine.data(node_id)
|
||||
runtime = self._require_runtime()
|
||||
block_user_input = data.get("userInputPolicy") == "block"
|
||||
if block_user_input and runtime.set_input_enabled:
|
||||
# Blocking only suppresses new audio/text input while the Action
|
||||
# runs. It deliberately does not cancel the tool. The default
|
||||
# queue policy leaves input enabled; the turn lock serializes any
|
||||
# completed user turn until this automatic path has finished.
|
||||
runtime.set_input_enabled(False)
|
||||
tool_id = str(data.get("toolId") or "")
|
||||
tool = self._tool_by_id.get(tool_id)
|
||||
if not tool:
|
||||
self._store.values["system__last_action_status"] = "error"
|
||||
self._store.values["system__last_action_error"] = f"工具不存在:{tool_id}"
|
||||
return
|
||||
try:
|
||||
if not tool:
|
||||
raise ToolExecutionError(f"工具不存在:{tool_id}")
|
||||
arguments = self._store.render_data(data.get("arguments") or {})
|
||||
result = await self._tools.execute(
|
||||
tool,
|
||||
arguments,
|
||||
result_assignments=data.get("resultAssignments") or {},
|
||||
result_assignments=self._action_result_assignments(data),
|
||||
)
|
||||
if result.get("status") != "ok":
|
||||
raise ToolExecutionError(
|
||||
str(result.get("message") or "工具返回执行失败状态")
|
||||
)
|
||||
updated_variables = list(result.get("updated_variables") or [])
|
||||
if updated_variables:
|
||||
await self._emit_variables(
|
||||
@@ -749,6 +759,26 @@ class WorkflowBrain(BaseBrain):
|
||||
except (ToolExecutionError, ValueError) as exc:
|
||||
self._store.values["system__last_action_status"] = "error"
|
||||
self._store.values["system__last_action_error"] = str(exc)[:2048]
|
||||
finally:
|
||||
if block_user_input and runtime.set_input_enabled:
|
||||
runtime.set_input_enabled(True)
|
||||
|
||||
@staticmethod
|
||||
def _action_result_assignments(
|
||||
data: dict[str, Any],
|
||||
) -> dict[str, str] | None:
|
||||
"""Resolve node mapping semantics for ToolExecutor.
|
||||
|
||||
``None`` means inherit the reusable tool's mapping, while an empty
|
||||
dictionary explicitly disables all result assignments.
|
||||
"""
|
||||
mode = str(data.get("resultAssignmentMode") or "none")
|
||||
if mode == "inherit":
|
||||
return None
|
||||
if mode == "override":
|
||||
assignments = data.get("resultAssignments")
|
||||
return dict(assignments) if isinstance(assignments, dict) else {}
|
||||
return {}
|
||||
|
||||
async def _enter_handoff(self, node_id: str) -> None:
|
||||
self._state.enter(node_id, WorkflowStatus.HANDOFF)
|
||||
|
||||
Reference in New Issue
Block a user