Merge pull request #517 from SuanmoSuanyangTechnology/release/v0.2.6
Release/v0.2.6
This commit is contained in:
@@ -185,6 +185,9 @@ class DifyConverter(BaseConverter):
|
|||||||
"not empty": ComparisonOperator.NOT_EMPTY,
|
"not empty": ComparisonOperator.NOT_EMPTY,
|
||||||
"start with": ComparisonOperator.START_WITH,
|
"start with": ComparisonOperator.START_WITH,
|
||||||
"end with": ComparisonOperator.END_WITH,
|
"end with": ComparisonOperator.END_WITH,
|
||||||
|
"not contains": ComparisonOperator.NOT_CONTAINS,
|
||||||
|
"exists": ComparisonOperator.NOT_EMPTY,
|
||||||
|
"not exists": ComparisonOperator.EMPTY
|
||||||
}
|
}
|
||||||
return operator_map.get(operator, operator)
|
return operator_map.get(operator, operator)
|
||||||
|
|
||||||
@@ -364,7 +367,7 @@ class DifyConverter(BaseConverter):
|
|||||||
node_data = node["data"]
|
node_data = node["data"]
|
||||||
cases = []
|
cases = []
|
||||||
for case in node_data["cases"]:
|
for case in node_data["cases"]:
|
||||||
case_id = case["id"]
|
case_id = case.get("id") or case.get("case_id")
|
||||||
logical_operator = case["logical_operator"]
|
logical_operator = case["logical_operator"]
|
||||||
conditions = []
|
conditions = []
|
||||||
for condition in case["conditions"]:
|
for condition in case["conditions"]:
|
||||||
@@ -540,7 +543,8 @@ class DifyConverter(BaseConverter):
|
|||||||
] = self.trans_variable_format(content["value"])
|
] = self.trans_variable_format(content["value"])
|
||||||
else:
|
else:
|
||||||
if node_data["body"]["data"]:
|
if node_data["body"]["data"]:
|
||||||
body_content = node_data["body"]["data"][0]["value"]
|
body_content = (node_data["body"]["data"][0].get("value") or
|
||||||
|
self._process_list_variable_litearl(node_data["body"]["data"][0].get("file")))
|
||||||
else:
|
else:
|
||||||
body_content = ""
|
body_content = ""
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,8 @@ class DifyAdapter(BasePlatformAdapter, DifyConverter):
|
|||||||
"parameter-extractor": NodeType.PARAMETER_EXTRACTOR,
|
"parameter-extractor": NodeType.PARAMETER_EXTRACTOR,
|
||||||
"question-classifier": NodeType.QUESTION_CLASSIFIER,
|
"question-classifier": NodeType.QUESTION_CLASSIFIER,
|
||||||
"variable-aggregator": NodeType.VAR_AGGREGATOR,
|
"variable-aggregator": NodeType.VAR_AGGREGATOR,
|
||||||
"tool": NodeType.TOOL
|
"tool": NodeType.TOOL,
|
||||||
|
"": NodeType.NOTES
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, config: dict[str, Any]):
|
def __init__(self, config: dict[str, Any]):
|
||||||
@@ -165,7 +166,7 @@ class DifyAdapter(BasePlatformAdapter, DifyConverter):
|
|||||||
return NodeDefinition(
|
return NodeDefinition(
|
||||||
id=node["id"],
|
id=node["id"],
|
||||||
type=self.map_node_type(node_data["type"]),
|
type=self.map_node_type(node_data["type"]),
|
||||||
name=node_data.get("title"),
|
name=node_data.get("title") or "notes",
|
||||||
cycle=node.get("parentId"),
|
cycle=node.get("parentId"),
|
||||||
description=None,
|
description=None,
|
||||||
config=self._convert_node_config(node),
|
config=self._convert_node_config(node),
|
||||||
@@ -209,16 +210,15 @@ class DifyAdapter(BasePlatformAdapter, DifyConverter):
|
|||||||
|
|
||||||
source = edge["source"]
|
source = edge["source"]
|
||||||
target = edge["target"]
|
target = edge["target"]
|
||||||
edge_id = edge["id"]
|
|
||||||
label = None
|
label = None
|
||||||
if source in self.branch_node_cache:
|
if source in self.branch_node_cache:
|
||||||
case_id = "-".join(edge_id.split("-")[1:-2])
|
case_id = edge["sourceHandle"]
|
||||||
if case_id == "false":
|
if case_id == "false":
|
||||||
label = f'CASE{len(self.branch_node_cache[source])+1}'
|
label = f'CASE{len(self.branch_node_cache[source])+1}'
|
||||||
else:
|
else:
|
||||||
label = f'CASE{self.branch_node_cache[source].index(case_id) + 1}'
|
label = f'CASE{self.branch_node_cache[source].index(case_id) + 1}'
|
||||||
if source in self.error_branch_node_cache:
|
if source in self.error_branch_node_cache:
|
||||||
case_id = "-".join(edge_id.split("-")[1:-2])
|
case_id = edge["sourceHandle"]
|
||||||
if case_id == "source":
|
if case_id == "source":
|
||||||
label = "SUCCESS"
|
label = "SUCCESS"
|
||||||
else:
|
else:
|
||||||
@@ -243,6 +243,7 @@ class DifyAdapter(BasePlatformAdapter, DifyConverter):
|
|||||||
name=variable["name"],
|
name=variable["name"],
|
||||||
default=variable["value"],
|
default=variable["value"],
|
||||||
type=self.variable_type_map(variable["value_type"]),
|
type=self.variable_type_map(variable["value_type"]),
|
||||||
|
description=variable.get("description")
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.errors.append(ExceptionDefineition(
|
self.errors.append(ExceptionDefineition(
|
||||||
|
|||||||
@@ -292,6 +292,8 @@ class GraphBuilder:
|
|||||||
"""
|
"""
|
||||||
for node in self.nodes:
|
for node in self.nodes:
|
||||||
node_type = node.get("type")
|
node_type = node.get("type")
|
||||||
|
if node_type == NodeType.NOTES:
|
||||||
|
continue
|
||||||
node_id = node.get("id")
|
node_id = node.get("id")
|
||||||
cycle_node = node.get("cycle")
|
cycle_node = node.get("cycle")
|
||||||
if cycle_node:
|
if cycle_node:
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ class NodeType(StrEnum):
|
|||||||
MEMORY_WRITE = "memory-write"
|
MEMORY_WRITE = "memory-write"
|
||||||
|
|
||||||
UNKNOWN = "unknown"
|
UNKNOWN = "unknown"
|
||||||
|
NOTES = "notes"
|
||||||
|
|
||||||
|
|
||||||
BRANCH_NODES = [NodeType.IF_ELSE, NodeType.HTTP_REQUEST, NodeType.QUESTION_CLASSIFIER]
|
BRANCH_NODES = [NodeType.IF_ELSE, NodeType.HTTP_REQUEST, NodeType.QUESTION_CLASSIFIER]
|
||||||
|
|||||||
@@ -180,6 +180,8 @@ class KnowledgeRetrievalNode(BaseNode):
|
|||||||
RuntimeError: If no valid knowledge base is found or access is denied.
|
RuntimeError: If no valid knowledge base is found or access is denied.
|
||||||
"""
|
"""
|
||||||
self.typed_config = KnowledgeRetrievalNodeConfig(**self.config)
|
self.typed_config = KnowledgeRetrievalNodeConfig(**self.config)
|
||||||
|
if not self.typed_config.knowledge_bases:
|
||||||
|
return []
|
||||||
query = self._render_template(self.typed_config.query, variable_pool)
|
query = self._render_template(self.typed_config.query, variable_pool)
|
||||||
with get_db_read() as db:
|
with get_db_read() as db:
|
||||||
knowledge_bases = self.typed_config.knowledge_bases
|
knowledge_bases = self.typed_config.knowledge_bases
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ class WorkflowValidator:
|
|||||||
errors.append("工作流必须至少有一个 end 节点")
|
errors.append("工作流必须至少有一个 end 节点")
|
||||||
|
|
||||||
# 3. 验证节点 ID 唯一性
|
# 3. 验证节点 ID 唯一性
|
||||||
node_ids = [n.get("id") for n in nodes]
|
node_ids = [n.get("id") for n in nodes if n.get("type") != NodeType.NOTES]
|
||||||
if len(node_ids) != len(set(node_ids)):
|
if len(node_ids) != len(set(node_ids)):
|
||||||
duplicates = [nid for nid in node_ids if node_ids.count(nid) > 1]
|
duplicates = [nid for nid in node_ids if node_ids.count(nid) > 1]
|
||||||
errors.append(f"节点 ID 必须唯一,重复的 ID: {set(duplicates)}")
|
errors.append(f"节点 ID 必须唯一,重复的 ID: {set(duplicates)}")
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ const InitialValuePlugin: React.FC<InitialValuePluginProps> = ({ value, options
|
|||||||
const textContent = root.getTextContent();
|
const textContent = root.getTextContent();
|
||||||
if (textContent !== prevValueRef.current) {
|
if (textContent !== prevValueRef.current) {
|
||||||
isUserInputRef.current = true;
|
isUserInputRef.current = true;
|
||||||
|
prevValueRef.current = textContent;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -33,7 +34,13 @@ const InitialValuePlugin: React.FC<InitialValuePluginProps> = ({ value, options
|
|||||||
}, [editor]);
|
}, [editor]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if ((value !== prevValueRef.current || enableLineNumbers !== prevEnableLineNumbersRef.current) && !isUserInputRef.current) {
|
if (value !== prevValueRef.current || enableLineNumbers !== prevEnableLineNumbersRef.current) {
|
||||||
|
// Skip reset if the change was triggered by user input (avoid cursor jump)
|
||||||
|
if (isUserInputRef.current && enableLineNumbers === prevEnableLineNumbersRef.current) {
|
||||||
|
prevValueRef.current = value;
|
||||||
|
isUserInputRef.current = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
queueMicrotask(() => {
|
queueMicrotask(() => {
|
||||||
editor.update(() => {
|
editor.update(() => {
|
||||||
const root = $getRoot();
|
const root = $getRoot();
|
||||||
|
|||||||
@@ -529,6 +529,10 @@ export const unknownNode = {
|
|||||||
type: 'unknown',
|
type: 'unknown',
|
||||||
icon: unknownIcon
|
icon: unknownIcon
|
||||||
}
|
}
|
||||||
|
export const noteNode = {
|
||||||
|
type: 'notes',
|
||||||
|
icon: unknownIcon
|
||||||
|
}
|
||||||
|
|
||||||
export const nodeWidth = 240;
|
export const nodeWidth = 240;
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* @Author: ZhaoYing
|
* @Author: ZhaoYing
|
||||||
* @Date: 2026-02-03 15:17:48
|
* @Date: 2026-02-03 15:17:48
|
||||||
* @Last Modified by: ZhaoYing
|
* @Last Modified by: ZhaoYing
|
||||||
* @Last Modified time: 2026-02-28 17:59:34
|
* @Last Modified time: 2026-03-07 15:23:39
|
||||||
*/
|
*/
|
||||||
import { useRef, useEffect, useState } from 'react';
|
import { useRef, useEffect, useState } from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
@@ -12,7 +12,7 @@ import { Graph, Node, MiniMap, Snapline, Clipboard, Keyboard, type Edge } from '
|
|||||||
import { register } from '@antv/x6-react-shape';
|
import { register } from '@antv/x6-react-shape';
|
||||||
import type { PortMetadata } from '@antv/x6/lib/model/port';
|
import type { PortMetadata } from '@antv/x6/lib/model/port';
|
||||||
|
|
||||||
import { nodeRegisterLibrary, graphNodeLibrary, nodeLibrary, portMarkup, portAttrs, edgeAttrs, edge_color, edge_selected_color, portTextAttrs, defaultAbsolutePortGroups, nodeWidth, unknownNode } from '../constant';
|
import { nodeRegisterLibrary, graphNodeLibrary, nodeLibrary, portMarkup, portAttrs, edgeAttrs, edge_color, edge_selected_color, portTextAttrs, defaultAbsolutePortGroups, nodeWidth, unknownNode, noteNode } from '../constant';
|
||||||
import type { WorkflowConfig, NodeProperties, ChatVariable } from '../types';
|
import type { WorkflowConfig, NodeProperties, ChatVariable } from '../types';
|
||||||
import { getWorkflowConfig, saveWorkflowConfig } from '@/api/application'
|
import { getWorkflowConfig, saveWorkflowConfig } from '@/api/application'
|
||||||
|
|
||||||
@@ -128,7 +128,7 @@ export const useWorkflowGraph = ({
|
|||||||
if (nodes.length) {
|
if (nodes.length) {
|
||||||
const nodeList = nodes.map(node => {
|
const nodeList = nodes.map(node => {
|
||||||
const { id, type, name, position, config = {} } = node
|
const { id, type, name, position, config = {} } = node
|
||||||
let nodeLibraryConfig = [...nodeLibrary, { nodes: [unknownNode] }]
|
let nodeLibraryConfig = [...nodeLibrary, { nodes: [unknownNode, noteNode] }]
|
||||||
.flatMap(category => category.nodes)
|
.flatMap(category => category.nodes)
|
||||||
.find(n => n.type === type)
|
.find(n => n.type === type)
|
||||||
nodeLibraryConfig = JSON.parse(JSON.stringify({ config: {}, ...nodeLibraryConfig })) as NodeProperties
|
nodeLibraryConfig = JSON.parse(JSON.stringify({ config: {}, ...nodeLibraryConfig })) as NodeProperties
|
||||||
@@ -715,6 +715,8 @@ export const useWorkflowGraph = ({
|
|||||||
panning: isHandMode,
|
panning: isHandMode,
|
||||||
mousewheel: {
|
mousewheel: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
factor: 0.1,
|
||||||
|
modifiers: null,
|
||||||
},
|
},
|
||||||
connecting: {
|
connecting: {
|
||||||
connector: {
|
connector: {
|
||||||
|
|||||||
Reference in New Issue
Block a user