feat(workflow): add edge-tool routing and realtime runtime
This commit is contained in:
@@ -24,6 +24,8 @@ NODE_TYPES = {
|
||||
"end",
|
||||
}
|
||||
EDGE_MODES = {"llm", "expression", "always"}
|
||||
WORKFLOW_RUNTIME_MODES = {"pipeline", "realtime"}
|
||||
WORKFLOW_LLM_ROUTING_MODES = {"llm_router", "edge_tool"}
|
||||
AGENT_ENTRY_MODES = {"wait_user", "generate"}
|
||||
ACTION_RESULT_ASSIGNMENT_MODES = {"inherit", "override", "none"}
|
||||
ACTION_USER_INPUT_POLICIES = {"queue", "block"}
|
||||
@@ -254,7 +256,12 @@ def _normalize_message_data(data: dict[str, Any]) -> None:
|
||||
|
||||
|
||||
def _normalize_settings(settings: dict[str, Any], *, global_prompt: str = "") -> None:
|
||||
# Missing values belong to graphs saved before workflow runtime/routing
|
||||
# became explicit. Preserve their dedicated-router behavior.
|
||||
settings.setdefault("runtimeMode", "pipeline")
|
||||
settings.setdefault("llmRoutingMode", "llm_router")
|
||||
settings.setdefault("globalPrompt", global_prompt)
|
||||
settings.setdefault("defaultRealtimeResourceId", "")
|
||||
settings.setdefault("defaultLlmResourceId", "")
|
||||
settings.setdefault("defaultAsrResourceId", "")
|
||||
settings.setdefault("defaultTtsResourceId", "")
|
||||
@@ -418,6 +425,27 @@ def validate_graph(graph: dict[str, Any]) -> list[str]:
|
||||
return []
|
||||
|
||||
errors: list[str] = []
|
||||
settings = graph.get("settings") or {}
|
||||
runtime_mode = str(settings.get("runtimeMode") or "pipeline")
|
||||
llm_routing_mode = str(settings.get("llmRoutingMode") or "llm_router")
|
||||
if runtime_mode not in WORKFLOW_RUNTIME_MODES:
|
||||
errors.append(f"工作流运行模式无效:{runtime_mode}")
|
||||
if llm_routing_mode not in WORKFLOW_LLM_ROUTING_MODES:
|
||||
errors.append(f"大模型判断路由模式无效:{llm_routing_mode}")
|
||||
if runtime_mode == "realtime" and llm_routing_mode != "edge_tool":
|
||||
errors.append("Realtime 工作流的大模型判断路由只能使用边工具模式")
|
||||
if runtime_mode == "realtime" and not settings.get(
|
||||
"defaultRealtimeResourceId"
|
||||
):
|
||||
errors.append("Realtime 工作流必须选择 Realtime 模型")
|
||||
if runtime_mode == "realtime" and settings.get("visionEnabled"):
|
||||
errors.append("Realtime 工作流暂不支持视觉能力")
|
||||
if (
|
||||
runtime_mode == "realtime"
|
||||
and settings.get("knowledgeBaseId")
|
||||
and settings.get("knowledgeMode") != "on_demand"
|
||||
):
|
||||
errors.append("Realtime 工作流的知识库只能使用按需模式")
|
||||
node_by_id: dict[str, dict] = {}
|
||||
counts: dict[str, int] = defaultdict(int)
|
||||
for node in nodes:
|
||||
@@ -443,6 +471,34 @@ def validate_graph(graph: dict[str, Any]) -> list[str]:
|
||||
not isinstance(name, str) for name in state_names
|
||||
):
|
||||
errors.append(f"Agent 节点 {node_id} 的状态变量授权必须是列表")
|
||||
if runtime_mode == "realtime":
|
||||
if data.get("contextPolicy", "inherit") != "inherit":
|
||||
errors.append(
|
||||
f"Realtime Agent 节点 {node_id} 只能继承会话上下文"
|
||||
)
|
||||
if any(
|
||||
data.get(key)
|
||||
for key in (
|
||||
"llmResourceId",
|
||||
"asrResourceId",
|
||||
"ttsResourceId",
|
||||
)
|
||||
) or any(key in data for key in ("enableInterrupt", "turnConfig")):
|
||||
errors.append(
|
||||
f"Realtime Agent 节点 {node_id} 不支持节点级模型、语音或交互策略覆盖"
|
||||
)
|
||||
source = settings if data.get("inheritGlobalConfig", True) else data
|
||||
if source.get("visionEnabled"):
|
||||
errors.append(
|
||||
f"Realtime Agent 节点 {node_id} 暂不支持视觉能力"
|
||||
)
|
||||
if (
|
||||
source.get("knowledgeBaseId")
|
||||
and source.get("knowledgeMode") != "on_demand"
|
||||
):
|
||||
errors.append(
|
||||
f"Realtime Agent 节点 {node_id} 的知识库只能使用按需模式"
|
||||
)
|
||||
elif node_type == "message":
|
||||
data = node.get("data") or {}
|
||||
speech = data.get("speech")
|
||||
@@ -500,6 +556,10 @@ def validate_graph(graph: dict[str, Any]) -> list[str]:
|
||||
errors.append(
|
||||
f"Action 节点 {node_id} 的用户输入策略无效:{input_policy}"
|
||||
)
|
||||
elif runtime_mode == "realtime" and input_policy != "block":
|
||||
errors.append(
|
||||
f"Realtime Action 节点 {node_id} 必须阻止执行期间的用户输入"
|
||||
)
|
||||
elif node_type == "update_state":
|
||||
data = node.get("data") or {}
|
||||
assignments = data.get("assignments")
|
||||
@@ -550,6 +610,13 @@ def validate_graph(graph: dict[str, Any]) -> list[str]:
|
||||
if priority in priorities[source_id]:
|
||||
errors.append(f"节点 {source_id} 的条件边优先级不能重复:{priority}")
|
||||
priorities[source_id].add(priority)
|
||||
source_type = node_by_id[source_id].get("type")
|
||||
if llm_routing_mode == "edge_tool" and mode == "llm" and source_type != "agent":
|
||||
errors.append(f"边工具模式的大模型判断边只能从 Agent 发出:{edge_id}")
|
||||
if llm_routing_mode == "edge_tool" and mode == "always" and source_type == "agent":
|
||||
errors.append(
|
||||
f"边工具模式的 Agent 不能使用默认路径:{source_id}"
|
||||
)
|
||||
incoming[target_id] += 1
|
||||
outgoing[source_id] += 1
|
||||
adj[source_id].append(target_id)
|
||||
@@ -633,6 +700,7 @@ def graph_references(graph: dict[str, Any]) -> dict[str, set[str]]:
|
||||
str(value)
|
||||
for value in (
|
||||
settings.get("defaultLlmResourceId"),
|
||||
settings.get("defaultRealtimeResourceId"),
|
||||
settings.get("defaultAsrResourceId"),
|
||||
settings.get("defaultTtsResourceId"),
|
||||
(
|
||||
|
||||
Reference in New Issue
Block a user