Add Dify integration and enhance workflow node specifications
- Introduce new fields `dify_api_url` and `dify_api_key` in `AssistantConfig` for Dify API integration. - Update `requirements.txt` to include `dify-client-python` for Dify SDK support. - Modify `config_resolver` to handle Dify connection information. - Add a new `globalNode` type in workflow specifications to provide unified settings across workflows. - Enhance node specifications with additional constraints and default values for better configuration management. - Update frontend components to support the new `globalNode` type and its properties, improving workflow editor functionality.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
"""工作流节点规格 + 图校验(对齐 dograh 的 node-spec / GraphConstraints 思路)。
|
||||
|
||||
当前实现 3 个核心节点:开始(startCall)/智能体(agentNode)/结束(endCall)。
|
||||
当前实现 4 个核心节点:开始(startCall)/智能体(agentNode)/结束(endCall)/全局(globalNode)。
|
||||
本模块是「节点类型」的唯一事实源:
|
||||
- /api/node-types 接口直接吐这里的规格;
|
||||
- 助手保存时用这里的约束校验 workflow 图。
|
||||
@@ -13,7 +13,7 @@ from __future__ import annotations
|
||||
from typing import Any
|
||||
|
||||
# 规格版本号:节点定义有破坏性变更时 +1,前端可据此判断是否需要刷新缓存。
|
||||
SPEC_VERSION = "1"
|
||||
SPEC_VERSION = "2"
|
||||
|
||||
# 每个节点的图约束。None 表示不限制。
|
||||
# min_incoming / max_incoming:入边数量
|
||||
@@ -27,11 +27,28 @@ NODE_SPECS: list[dict[str, Any]] = [
|
||||
"icon": "Play",
|
||||
"accent": "mint",
|
||||
"addable": False,
|
||||
"constraints": {"minIncoming": 0, "maxIncoming": 0},
|
||||
"constraints": {
|
||||
"minIncoming": 0,
|
||||
"maxIncoming": 0,
|
||||
"minInstances": 1,
|
||||
"maxInstances": 1,
|
||||
},
|
||||
"fields": [
|
||||
{"key": "name", "label": "节点名称", "type": "text"},
|
||||
{"key": "greeting", "label": "开场白", "type": "textarea"},
|
||||
{"key": "prompt", "label": "节点提示词", "type": "textarea"},
|
||||
{"key": "name", "label": "节点名称", "type": "text", "default": "开始"},
|
||||
{"key": "greeting", "label": "开场白", "type": "textarea", "default": ""},
|
||||
{"key": "prompt", "label": "节点提示词", "type": "textarea", "default": ""},
|
||||
{
|
||||
"key": "allowInterrupt",
|
||||
"label": "允许用户打断",
|
||||
"type": "switch",
|
||||
"default": True,
|
||||
},
|
||||
{
|
||||
"key": "addGlobalPrompt",
|
||||
"label": "应用全局提示词",
|
||||
"type": "switch",
|
||||
"default": True,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -44,9 +61,26 @@ NODE_SPECS: list[dict[str, Any]] = [
|
||||
"addable": True,
|
||||
"constraints": {"minIncoming": 1},
|
||||
"fields": [
|
||||
{"key": "name", "label": "节点名称", "type": "text"},
|
||||
{"key": "prompt", "label": "节点提示词", "type": "textarea", "required": True},
|
||||
{"key": "allowInterrupt", "label": "允许用户打断", "type": "switch"},
|
||||
{"key": "name", "label": "节点名称", "type": "text", "default": "智能体节点"},
|
||||
{
|
||||
"key": "prompt",
|
||||
"label": "节点提示词",
|
||||
"type": "textarea",
|
||||
"required": True,
|
||||
"default": "",
|
||||
},
|
||||
{
|
||||
"key": "allowInterrupt",
|
||||
"label": "允许用户打断",
|
||||
"type": "switch",
|
||||
"default": True,
|
||||
},
|
||||
{
|
||||
"key": "addGlobalPrompt",
|
||||
"label": "应用全局提示词",
|
||||
"type": "switch",
|
||||
"default": True,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -59,8 +93,40 @@ NODE_SPECS: list[dict[str, Any]] = [
|
||||
"addable": True,
|
||||
"constraints": {"minIncoming": 1, "minOutgoing": 0, "maxOutgoing": 0},
|
||||
"fields": [
|
||||
{"key": "name", "label": "节点名称", "type": "text"},
|
||||
{"key": "prompt", "label": "结束语提示词", "type": "textarea"},
|
||||
{"key": "name", "label": "节点名称", "type": "text", "default": "结束"},
|
||||
{"key": "prompt", "label": "结束语提示词", "type": "textarea", "default": ""},
|
||||
{
|
||||
"key": "addGlobalPrompt",
|
||||
"label": "应用全局提示词",
|
||||
"type": "switch",
|
||||
"default": False,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "globalNode",
|
||||
"displayName": "全局节点",
|
||||
"category": "global_node",
|
||||
"description": "为整个工作流提供统一的人设、语气和公共规则。无需连线,每个流程最多一个。",
|
||||
"icon": "Globe2",
|
||||
"accent": "lavender",
|
||||
"addable": True,
|
||||
"constraints": {
|
||||
"minIncoming": 0,
|
||||
"maxIncoming": 0,
|
||||
"minOutgoing": 0,
|
||||
"maxOutgoing": 0,
|
||||
"maxInstances": 1,
|
||||
},
|
||||
"fields": [
|
||||
{"key": "name", "label": "节点名称", "type": "text", "default": "全局设定"},
|
||||
{
|
||||
"key": "prompt",
|
||||
"label": "全局提示词",
|
||||
"type": "textarea",
|
||||
"required": True,
|
||||
"default": "你是一个友好、专业的语音助手。请使用简短、自然、适合口语表达的句子。",
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
@@ -79,7 +145,7 @@ def validate_graph(graph: dict[str, Any]) -> list[str]:
|
||||
基础规则(对齐 dograh 的核心不变量):
|
||||
1. 节点类型必须是已注册类型;
|
||||
2. 有且仅有一个 startCall;
|
||||
3. 至少有一个 endCall;
|
||||
3. 至少有一个 endCall,全局节点最多一个;
|
||||
4. 边的 source/target 必须指向存在的节点;
|
||||
5. 入边/出边数量满足各节点类型的约束。
|
||||
|
||||
@@ -120,6 +186,21 @@ def validate_graph(graph: dict[str, Any]) -> list[str]:
|
||||
if type_counts.get("endCall", 0) == 0:
|
||||
errors.append("工作流至少需要一个「结束」节点")
|
||||
|
||||
for node_type, spec in _SPEC_BY_NAME.items():
|
||||
# 开始节点上方已有更明确的中文错误提示,避免重复报错。
|
||||
if node_type == "startCall":
|
||||
continue
|
||||
constraints = spec["constraints"]
|
||||
count = type_counts.get(node_type, 0)
|
||||
_check_count(
|
||||
errors,
|
||||
count,
|
||||
constraints,
|
||||
"Instances",
|
||||
node_type,
|
||||
"实例",
|
||||
)
|
||||
|
||||
# 统计入边/出边
|
||||
incoming: dict[str, int] = {nid: 0 for nid in node_ids}
|
||||
outgoing: dict[str, int] = {nid: 0 for nid in node_ids}
|
||||
|
||||
Reference in New Issue
Block a user