Merge branch 'refs/heads/develop' into fix/memory_bug_fix

This commit is contained in:
lixinyue
2026-01-14 14:54:33 +08:00
10 changed files with 114 additions and 111 deletions

View File

@@ -181,10 +181,15 @@ const Conversation: FC = () => {
currentConversationId = newId
break
case 'message':
const { content } = item.data as { content: string }
updateAssistantMessage(content)
const { content, chunk, conversation_id: curId } = item.data as { content: string; chunk: string; conversation_id: string; }
updateAssistantMessage(content ?? chunk)
if (curId) {
currentConversationId = curId;
}
break
case 'end':
case 'workflow_end':
setLoading(false)
if (currentConversationId && currentConversationId !== conversation_id) {
setConversationId(currentConversationId)

View File

@@ -103,7 +103,11 @@ const ChatVariableModal = forwardRef<ChatVariableModalRef, ChatVariableModalProp
label={t('workflow.config.parameter-extractor.default')}
>
{type === 'number'
? <InputNumber placeholder={t('common.enter')} style={{ width: '100%' }} />
? <InputNumber
placeholder={t('common.enter')}
style={{ width: '100%' }}
onChange={(value) => form.setFieldValue('defaultValue', value)}
/>
: type === 'boolean'
? <Select
placeholder={t('common.pleaseSelect')}

View File

@@ -54,6 +54,7 @@ const Chat = forwardRef<ChatRef, { appId: string; graphRef: GraphRef }>(({ appId
const handleClose = () => {
setOpen(false)
setChatList([])
setVariables([])
}
const handleEditVariables = () => {
variableConfigModalRef.current?.handleOpen(variables)

View File

@@ -80,7 +80,7 @@ const VariableConfigModal = forwardRef<VariableConfigModalRef, VariableEditModal
field.type === 'string' && <Input placeholder={t('common.pleaseEnter')} />
}
{
field.type === 'number' && <InputNumber placeholder={t('common.pleaseEnter')} style={{ width: '100%' }} />
field.type === 'number' && <InputNumber placeholder={t('common.pleaseEnter')} style={{ width: '100%' }} onChange={(value) => form.setFieldValue(['variables', name, 'value'], value)} />
}
{
field.type === 'boolean' && <Checkbox>{`${field.name}·${field.description}`}</Checkbox>

View File

@@ -46,7 +46,8 @@ const AddNode: ReactShapeConfig['component'] = ({ node, graph }) => {
graph.addEdge({
source: { cell: edge.getSourceCellId(), port: edge.getSourcePortId() },
target: { cell: newNode.id, port: newNode.getPorts().find((port: any) => port.group === 'left')?.id || 'left' },
attrs: edge.getAttrs()
attrs: edge.getAttrs(),
zIndex: 3
});
});

View File

@@ -85,6 +85,7 @@ const PortClickHandler: React.FC<PortClickHandlerProps> = ({ graph }) => {
},
},
},
zIndex: 0
});
// 循环节点内子节点通过连接桩添加时,调整循环节点大小

View File

@@ -60,7 +60,7 @@ const AssignmentList: FC<AssignmentListProps> = ({
>
<VariableSelect
placeholder={t('common.pleaseSelect')}
options={options.filter(vo => vo.nodeData.type === 'loop' || vo.value.includes('conv.'))}
options={options.filter(vo => vo.nodeData.type === 'loop' || vo.value.includes('conv.') || (vo.nodeData.type === 'iteration' && (vo.label === 'item' || vo.label === 'index')))}
popupMatchSelectWidth={false}
onChange={() => {
form.setFieldValue([parentName, name, 'operation'], undefined);

View File

@@ -40,8 +40,6 @@ const operatorsObj: { [key: string]: SelectProps['options'] } = {
boolean: [
{ value: 'eq', label: 'workflow.config.if-else.boolean.eq' },
{ value: 'ne', label: 'workflow.config.if-else.boolean.ne' },
{ value: 'empty', label: 'workflow.config.if-else.empty' },
{ value: 'not_empty', label: 'workflow.config.if-else.not_empty' },
]
}
@@ -85,11 +83,14 @@ const CaseList: FC<CaseListProps> = ({
selectedNode.prop('size', { width: 240, height: newHeight })
// 计算端口间距
const dy = totalPorts;
// 添加 IF 端口
selectedNode.addPort({
id: 'CASE1',
group: 'right',
args: { dy: 24 },
// args: { dy },
attrs: { text: { text: 'IF', fontSize: 12, fill: '#5B6167' }}
});
@@ -98,6 +99,7 @@ const CaseList: FC<CaseListProps> = ({
selectedNode.addPort({
id: `CASE${i + 1}`,
group: 'right',
// args: { dy },
attrs: { text: { text: 'ELIF', fontSize: 12, fill: '#5B6167' }}
});
}
@@ -106,11 +108,22 @@ const CaseList: FC<CaseListProps> = ({
selectedNode.addPort({
id: `CASE${caseCount + 1}`,
group: 'right',
// args: { dy },
attrs: { text: { text: 'ELSE', fontSize: 12, fill: '#5B6167' }}
});
// 恢复仍然存在的端口连线
setTimeout(() => {
// 计算删除前的总端口数来确定原ELSE端口编号
const originalCaseCount = removedCaseIndex !== undefined ? caseCount + 1 : caseCount;
const originalElsePortNumber = originalCaseCount + 1;
// 检查ELSE端口是否有连线
const elseHasConnection = edgeConnections.some(({ sourcePortId, isIncoming }: any) => {
const caseNumber = parseInt(sourcePortId.match(/CASE(\d+)/)?.[1] || '0');
return !isIncoming && caseNumber === originalElsePortNumber;
});
edgeConnections.forEach(({ edge, sourcePortId, targetCellId, targetPortId, sourceCellId, isIncoming }: any) => {
// 如果是进入连线(左侧端口),直接恢复
if (isIncoming) {
@@ -138,7 +151,7 @@ const CaseList: FC<CaseListProps> = ({
// 处理右侧端口连线
const originalCaseNumber = parseInt(sourcePortId.match(/CASE(\d+)/)?.[1] || '0');
// 如果是被删除的端口,不重新创建连线
// 如果是被删除的端口,只删除该端口的连线
if (removedCaseIndex !== undefined && originalCaseNumber === removedCaseIndex + 1) {
graphRef.current?.removeCell(edge);
return;
@@ -146,15 +159,8 @@ const CaseList: FC<CaseListProps> = ({
let newPortId = sourcePortId;
// 如果是原来的ELSE端口重新映射到新的ELSE端口
const maxOriginalCaseNumber = Math.max(...edgeConnections
.filter(({ isIncoming }: any) => !isIncoming)
.map(({ sourcePortId }: any) => {
const match = sourcePortId.match(/CASE(\d+)/);
return match ? parseInt(match[1]) : 0;
}));
if (originalCaseNumber === maxOriginalCaseNumber) {
// 如果是原来的ELSE端口且有连线重新映射到新的ELSE端口
if (originalCaseNumber === originalElsePortNumber && elseHasConnection) {
newPortId = `CASE${caseCount + 1}`; // 新的ELSE端口
} else if (removedCaseIndex !== undefined && originalCaseNumber > removedCaseIndex + 1) {
// 如果是被删除端口之后的端口,编号向前移动

View File

@@ -53,6 +53,7 @@ const operatorsObj: { [key: string]: SelectProps['options'] } = {
const ConditionList: FC<CaseListProps> = ({
options,
parentName,
selectedNode,
}) => {
const { t } = useTranslation();
const form = Form.useFormInstance();
@@ -114,7 +115,12 @@ const ConditionList: FC<CaseListProps> = ({
<Col span={14}>
<Form.Item name={[field.name, 'left']} noStyle>
<VariableSelect
options={options.filter(vo => vo.value.includes('sys.') || vo.value.includes('conv.') || vo.nodeData.type === 'loop')}
options={options.filter(vo =>
vo.value.includes('sys.') ||
vo.value.includes('conv.') ||
vo.nodeData.type === 'loop' ||
(vo.nodeData.cycle && vo.nodeData.cycle === selectedNode?.id)
)}
size="small"
allowClear={false}
popupMatchSelectWidth={false}

View File

@@ -566,42 +566,6 @@ const Properties: FC<PropertiesProps> = ({
nodeData: nodeData,
});
}
// Check if connected via ERROR connection point
const errorEdges = edges.filter(edge =>
edge.getTargetCellId() === selectedNode.id &&
edge.getSourceCellId() === nodeId &&
edge.getSourcePortId() === 'ERROR'
);
if (errorEdges.length > 0) {
const errorMessageKey = `${dataNodeId}_error_message`;
const errorTypeKey = `${dataNodeId}_error_type`;
if (!addedKeys.has(errorMessageKey)) {
addedKeys.add(errorMessageKey);
variableList.push({
key: errorMessageKey,
label: 'error_message',
type: 'variable',
dataType: 'string',
value: `${dataNodeId}.error_message`,
nodeData: nodeData,
});
}
if (!addedKeys.has(errorTypeKey)) {
addedKeys.add(errorTypeKey);
variableList.push({
key: errorTypeKey,
label: 'error_type',
type: 'variable',
dataType: 'string',
value: `${dataNodeId}.error_type`,
nodeData: nodeData,
});
}
}
break
case 'jinja-render':
const jinjaOutputKey = `${dataNodeId}_output`;
@@ -793,54 +757,6 @@ const Properties: FC<PropertiesProps> = ({
}
}
}
// Check if parent loop/iteration is connected to http-request via ERROR connection
if (parentData.type === 'loop' || parentData.type === 'iteration') {
const parentPreviousNodeIds = getAllPreviousNodes(parentLoopNode.id);
parentPreviousNodeIds.forEach(prevNodeId => {
const prevNode = nodes.find(n => n.id === prevNodeId);
if (!prevNode) return;
const prevNodeData = prevNode.getData();
if (prevNodeData.type === 'http-request') {
// Check if connected via ERROR connection point
const errorEdges = edges.filter(edge => {
return edge.getTargetCellId() === parentLoopNode.id &&
edge.getSourceCellId() === prevNodeId &&
edge.getSourcePortId() === 'ERROR'
});
if (errorEdges.length > 0) {
const errorMessageKey = `${prevNodeData.id}_error_message`;
const errorTypeKey = `${prevNodeData.id}_error_type`;
if (!addedKeys.has(errorMessageKey)) {
addedKeys.add(errorMessageKey);
variableList.push({
key: errorMessageKey,
label: 'error_message',
type: 'variable',
dataType: 'string',
value: `${prevNodeData.id}.error_message`,
nodeData: prevNodeData,
});
}
if (!addedKeys.has(errorTypeKey)) {
addedKeys.add(errorTypeKey);
variableList.push({
key: errorTypeKey,
label: 'error_type',
type: 'variable',
dataType: 'string',
value: `${prevNodeData.id}.error_type`,
nodeData: prevNodeData,
});
}
}
}
});
}
}
return variableList;
@@ -999,12 +915,12 @@ const Properties: FC<PropertiesProps> = ({
return filteredList;
}
if (nodeType === 'knowledge-retrieval' || nodeType === 'parameter-extractor' && key !== 'prompt' || nodeType === 'memory-read' || nodeType === 'memory-write' || nodeType === 'question-classifier') {
let filteredList = variableList.filter(variable => variable.dataType === 'string');
return addParentIterationVars(filteredList);
let filteredList = addParentIterationVars(variableList).filter(variable => variable.dataType === 'string');
return filteredList;
}
if (nodeType === 'parameter-extractor' && key === 'prompt') {
let filteredList = variableList.filter(variable => variable.dataType === 'string' || variable.dataType === 'number');
return addParentIterationVars(filteredList);
let filteredList = addParentIterationVars(variableList).filter(variable => variable.dataType === 'string' || variable.dataType === 'number');
return filteredList;
}
if (nodeType === 'iteration' && key === 'output') {
return variableList.filter(variable => variable.value.includes('sys.'));
@@ -1013,8 +929,71 @@ const Properties: FC<PropertiesProps> = ({
return variableList.filter(variable => variable.dataType.includes('array'));
}
if (nodeType === 'loop' && key === 'condition') {
let filteredList = variableList.filter(variable => variable.nodeData.type !== 'loop');
return addParentIterationVars(filteredList);
let filteredList = addParentIterationVars(variableList).filter(variable => variable.nodeData.type !== 'loop');
// Add child node output variables for loop nodes
if (selectedNode) {
const graph = graphRef.current;
if (graph) {
const nodes = graph.getNodes();
const childNodes = nodes.filter(node => {
const nodeData = node.getData();
return nodeData?.cycle === selectedNode.id;
});
// Add output variables from child nodes
childNodes.forEach(childNode => {
const childData = childNode.getData();
const childNodeId = childData.id;
// Add child node output variables based on their type
switch(childData.type) {
case 'llm':
case 'jinja-render':
case 'tool':
const outputKey = `${childNodeId}_output`;
const existingOutput = filteredList.find(v => v.key === outputKey);
if (!existingOutput) {
filteredList.push({
key: outputKey,
label: 'output',
type: 'variable',
dataType: 'string',
value: `${childNodeId}.output`,
nodeData: childData,
});
}
break;
case 'http-request':
const bodyKey = `${childNodeId}_body`;
const statusKey = `${childNodeId}_status_code`;
if (!filteredList.find(v => v.key === bodyKey)) {
filteredList.push({
key: bodyKey,
label: 'body',
type: 'variable',
dataType: 'string',
value: `${childNodeId}.body`,
nodeData: childData,
});
}
if (!filteredList.find(v => v.key === statusKey)) {
filteredList.push({
key: statusKey,
label: 'status_code',
type: 'variable',
dataType: 'number',
value: `${childNodeId}.status_code`,
nodeData: childData,
});
}
break;
}
});
}
}
return filteredList;
}
// For all other node types, add parent iteration variables if applicable
@@ -1025,7 +1004,7 @@ const Properties: FC<PropertiesProps> = ({
// const defaultVariableList = calculateVariableList(selectedNode as Node, graphRef, workflowConfig )
console.log('values', values)
// console.log('variableList', variableList, defaultVariableList)
console.log('variableList', variableList)
return (
<div className="rb:w-75 rb:fixed rb:right-0 rb:top-16 rb:bottom-0 rb:p-3">