Merge pull request #701 from SuanmoSuanyangTechnology/feature/ui_upgrade_zy

Feature/UI upgrade zy
This commit is contained in:
yingzhao
2026-03-26 18:49:52 +08:00
committed by GitHub
8 changed files with 48 additions and 135 deletions

View File

@@ -2163,6 +2163,7 @@ Memory Bear: After the rebellion, regional warlordism intensified for several re
'memory-write': 'Memory Storage', 'memory-write': 'Memory Storage',
unknown: 'Unknown Node', unknown: 'Unknown Node',
notes: 'Sticky Note', notes: 'Sticky Note',
'document-extractor': 'Document Extractor',
clickToConfigure: 'Click to configure node parameters', clickToConfigure: 'Click to configure node parameters',
nodeProperties: 'Node Properties', nodeProperties: 'Node Properties',
@@ -2361,6 +2362,9 @@ Memory Bear: After the rebellion, regional warlordism intensified for several re
placeholder: 'Enter note...', placeholder: 'Enter note...',
removeLink: 'Remove Link', removeLink: 'Remove Link',
}, },
'document-extractor': {
file_selector: 'Input Variable',
},
name: 'Key', name: 'Key',
type: 'Type', type: 'Type',
value: 'Value', value: 'Value',

View File

@@ -2160,6 +2160,7 @@ export const zh = {
'memory-write': '记忆储存', 'memory-write': '记忆储存',
unknown: '未知节点', unknown: '未知节点',
notes: '便签', notes: '便签',
'document-extractor': '文档提取器',
clickToConfigure: '点击配置节点参数', clickToConfigure: '点击配置节点参数',
nodeProperties: '节点属性', nodeProperties: '节点属性',
@@ -2361,6 +2362,9 @@ export const zh = {
placeholder: '输入注释...', placeholder: '输入注释...',
removeLink: '取消链接', removeLink: '取消链接',
}, },
'document-extractor': {
file_selector: '输入变量',
},
name: '键', name: '键',
type: '类型', type: '类型',
value: '值', value: '值',

View File

@@ -161,10 +161,12 @@ const Neo4j: FC = () => {
<RelationshipNetwork /> <RelationshipNetwork />
</Flex> </Flex>
</Flex> </Flex>
<EndUserProfile ref={ref} onDataLoaded={handleNameUpdate} className={selectedKey === 'userProfile' ? 'rb:block!' : 'rb:hidden!'} /> <div onClick={(e) => e.stopPropagation()}>
<AboutMe ref={aboutMeRef} className={selectedKey === 'aboutMe' ? 'rb:block!' : 'rb:hidden!'} /> <EndUserProfile ref={ref} onDataLoaded={handleNameUpdate} className={selectedKey === 'userProfile' ? 'rb:block!' : 'rb:hidden!'} />
<InterestDistribution className={selectedKey === 'interestDistribution' ? 'rb:block!' : 'rb:hidden!'} /> <AboutMe ref={aboutMeRef} className={selectedKey === 'aboutMe' ? 'rb:block!' : 'rb:hidden!'} />
<MemoryInsight ref={memoryInsightRef} className={selectedKey === 'memoryInsight' ? 'rb:block!' : 'rb:hidden!'} /> <InterestDistribution className={selectedKey === 'interestDistribution' ? 'rb:block!' : 'rb:hidden!'} />
<MemoryInsight ref={memoryInsightRef} className={selectedKey === 'memoryInsight' ? 'rb:block!' : 'rb:hidden!'} />
</div>
</div> </div>
) )
} }

View File

@@ -53,7 +53,7 @@ const InitialValuePlugin: React.FC<InitialValuePluginProps> = ({ value, options
const root = $getRoot(); const root = $getRoot();
root.clear(); root.clear();
const parts = value.split(/(\{\{[^}]+\}\})/); const parts = value.split(/(\{\{[^}]+\}\}|\n)/);
if (enableLineNumbers) { if (enableLineNumbers) {
const lines = value.split('\n'); const lines = value.split('\n');
@@ -63,8 +63,14 @@ const InitialValuePlugin: React.FC<InitialValuePluginProps> = ({ value, options
root.append(paragraph); root.append(paragraph);
}); });
} else { } else {
const paragraph = $createParagraphNode(); let paragraph = $createParagraphNode();
parts.forEach(part => { parts.forEach(part => {
if (part === '\n') {
root.append(paragraph);
paragraph = $createParagraphNode();
return;
}
const match = part.match(/^\{\{([^.]+)\.([^}]+)\}\}$/); const match = part.match(/^\{\{([^.]+)\.([^}]+)\}\}$/);
const contextMatch = part.match(/^\{\{context\}\}$/); const contextMatch = part.match(/^\{\{context\}\}$/);
const conversationMatch = part.match(/^\{\{conv\.([^}]+)\}\}$/); const conversationMatch = part.match(/^\{\{conv\.([^}]+)\}\}$/);
@@ -78,10 +84,10 @@ const InitialValuePlugin: React.FC<InitialValuePluginProps> = ({ value, options
} }
return return
} }
if (conversationMatch) { if (conversationMatch) {
const [_, variableName] = conversationMatch; const [_, variableName] = conversationMatch;
const conversationSuggestion = optionsRef.current.find(s => const conversationSuggestion = optionsRef.current.find(s =>
s.group === 'CONVERSATION' && s.label === variableName s.group === 'CONVERSATION' && s.label === variableName
); );
if (conversationSuggestion) { if (conversationSuggestion) {
@@ -91,7 +97,7 @@ const InitialValuePlugin: React.FC<InitialValuePluginProps> = ({ value, options
} }
return return
} }
if (match) { if (match) {
const [_, nodeId, label] = match; const [_, nodeId, label] = match;

View File

@@ -1,10 +1,11 @@
import { type FC } from 'react' import { type FC, useMemo } from 'react'
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Form, Select, Input, Button, InputNumber, Flex } from 'antd' import { Form, Select, Input, Button, InputNumber, Flex } from 'antd'
import VariableSelect from '../VariableSelect' import VariableSelect from '../VariableSelect'
import type { Suggestion } from '../../Editor/plugin/AutocompletePlugin' import type { Suggestion } from '../../Editor/plugin/AutocompletePlugin'
import RadioGroupBtn from '../RadioGroupBtn' import RadioGroupBtn from '../RadioGroupBtn'
import { getChildNodeVariables } from '../hooks/useVariableList'
interface CycleVar { interface CycleVar {
name: string; name: string;
@@ -44,40 +45,14 @@ const CycleVarsList: FC<CycleVarsListProps> = ({
const { t } = useTranslation(); const { t } = useTranslation();
const form = Form.useFormInstance(); const form = Form.useFormInstance();
// 获取循环节点的子节点变量 const availableOptions = useMemo(() => {
const getChildNodeVariables = () => {
if (!selectedNode || !graphRef?.current || selectedNode.getData()?.type !== 'loop') { if (!selectedNode || !graphRef?.current || selectedNode.getData()?.type !== 'loop') {
return options; return options;
} }
const childVars = getChildNodeVariables(selectedNode, graphRef);
const loopNodeId = selectedNode.getData()?.id; return options.filter(option => !childVars.some(item => item.value === option.value))
const childNodes = graphRef.current.getNodes().filter((node: any) => }, [options, selectedNode, graphRef]);
node.getData()?.cycle === loopNodeId
);
const childVariables: Suggestion[] = [];
childNodes.forEach((childNode: any) => {
const childData = childNode.getData();
if (childData?.config) {
Object.keys(childData.config).forEach(key => {
if (childData.config[key]?.defaultValue) {
childVariables.push({
key: `${childData.id}.${key}`,
label: `${childData.name || childData.type}.${key}`,
type: 'output',
dataType: 'string',
value: `${childData.id}.${key}`,
nodeData: childData
});
}
});
}
});
return [...options, ...childVariables];
};
const availableOptions = getChildNodeVariables();
return ( return (
<Form.List name={parentName}> <Form.List name={parentName}>

View File

@@ -42,6 +42,9 @@ const NODE_VARIABLES = {
'memory-read': [ 'memory-read': [
{ label: 'answer', dataType: 'string', field: 'answer' }, { label: 'answer', dataType: 'string', field: 'answer' },
{ label: 'intermediate_outputs', dataType: 'array[object]', field: 'intermediate_outputs' } { label: 'intermediate_outputs', dataType: 'array[object]', field: 'intermediate_outputs' }
],
'document-extractor': [
{ label: 'text', dataType: 'string', field: 'text' },
] ]
} as const; } as const;
@@ -177,7 +180,8 @@ const hasOutputNodeTypes = [
'var-aggregator', 'var-aggregator',
'http-request', 'http-request',
'tool', 'tool',
'jinja-render' 'jinja-render',
'document-extractor'
]; ];
/** /**

View File

@@ -636,6 +636,8 @@ const Properties: FC<PropertiesProps> = ({
size="small" size="small"
parentName={key} parentName={key}
options={getFilteredVariableList(selectedNode?.data?.type, key)} options={getFilteredVariableList(selectedNode?.data?.type, key)}
selectedNode={selectedNode}
graphRef={graphRef}
/> />
</Form.Item> </Form.Item>
) )

View File

@@ -473,6 +473,16 @@ export const nodeLibrary: NodeLibrary[] = [
}, },
} }
}, },
{
type: "document-extractor", icon: codeExecutionIcon,
config: {
file_selector: {
type: 'variableList',
placeholder: 'common.pleaseSelect',
onFilterVariableNames: ['sys.files']
}
}
},
] ]
}, },
]; ];
@@ -858,100 +868,6 @@ export interface OutputVariable {
}>; }>;
} }
/**
* Output variable definitions for each node type
* Specifies what variables each node produces
*/
export const outputVariable: { [key: string]: OutputVariable } = {
start: {
sys: [
{ name: "message", type: "string" },
{ name: "conversation_id", type: "string" },
{ name: "execution_id", type: "string", },
{ name: "workspace_id", type: "string" },
{ name: "user_id", type: "string" },
],
define: ['variables']
},
end: {
},
llm: {
default: [
{ name: "output", type: "string" },
]
},
'knowledge-retrieval': {
default: [
{ name: "output", type: "array[object]" },
]
},
'parameter-extractor': {
default: [
{ name: "__is_success", type: "number" },
{ name: "__reason", type: "string" },
],
define: ['params']
},
'memory-read': {
default: [
{ name: "answer", type: "string" },
{ name: "intermediate_outputs", type: "array[object]" },
],
},
'memory-write': {
},
'if-else': {
},
'question-classifier': {
default: [
{ name: "class_name", type: "string" },
// { name: "output", type: "string" },
],
},
'iteration': {
default: [
// { name: "item", type: "string" }, // 仅内部使用
{ name: "output", type: "array[string]" },
],
},
'loop': {
define: ['cycle_vars']
},
'cycle-start': {
},
'break': {
},
'var-aggregator': {
// default: [
// { name: "output", type: "string" },
// ],
define: ['group_variables']
},
'assigner': {
},
'http-request': {
default: [
{ name: "body", type: "string" },
{ name: "status_code", type: "number" },
],
},
'tool': {
default: [
{ name: "data", type: "string" },
],
},
'jinja-render': {
default: [
{ name: "output", type: "string" },
],
},
}
/** /**
* Default edge attributes configuration * Default edge attributes configuration
* Defines visual styling for edges/connections * Defines visual styling for edges/connections