Files
ragflow/web/src/utils/canvas-util.tsx
Yongteng Lei dd839f30e8 Fix: code supports matplotlib (#13724)
### What problem does this PR solve?

Code as "final" node: 

![img_v3_02vs_aece4caf-8403-4939-9e68-9845a22c2cfg](https://github.com/user-attachments/assets/9d87b8df-da6b-401c-bf6d-8b807fe92c22)

Code as "mid" node:

![img_v3_02vv_f74f331f-d755-44ab-a18c-96fff8cbd34g](https://github.com/user-attachments/assets/c94ef3f9-2a6c-47cb-9d2b-19703d2752e4)


### Type of change

- [x] New Feature (non-breaking change which adds functionality)
2026-03-20 20:32:00 +08:00

149 lines
3.5 KiB
TypeScript

import {
AgentStructuredOutputField,
JsonSchemaDataType,
Operator,
} from '@/constants/agent';
import { BaseNode } from '@/interfaces/database/agent';
import OperatorIcon from '@/pages/agent/operator-icon';
import { Edge } from '@xyflow/react';
import { get, isEmpty } from 'lodash';
import { ReactNode } from 'react';
export function filterAllUpstreamNodeIds(edges: Edge[], nodeIds: string[]) {
return nodeIds.reduce<string[]>((pre, nodeId) => {
const currentEdges = edges.filter((x) => x.target === nodeId);
const upstreamNodeIds: string[] = currentEdges.map((x) => x.source);
const ids = upstreamNodeIds.concat(
filterAllUpstreamNodeIds(edges, upstreamNodeIds),
);
ids.forEach((x) => {
if (pre.every((y) => y !== x)) {
pre.push(x);
}
});
return pre;
}, []);
}
export function filterChildNodeIds(nodes: BaseNode[], nodeId?: string) {
return nodes.filter((x) => x.parentId === nodeId).map((x) => x.id);
}
export function isAgentStructured(id?: string, label?: string) {
return (
label === AgentStructuredOutputField && id?.startsWith(`${Operator.Agent}:`)
);
}
export function buildVariableValue(value: string, nodeId?: string) {
return `${nodeId}@${value}`;
}
export function buildSecondaryOutputOptions(
outputs: Record<string, any> = {},
nodeId?: string,
parentLabel?: string | ReactNode,
icon?: ReactNode,
) {
return Object.keys(outputs).map((x) => ({
label: x,
value: buildVariableValue(x, nodeId),
parentLabel,
icon,
type: isAgentStructured(nodeId, x)
? JsonSchemaDataType.Object
: outputs[x]?.type,
}));
}
function getNodeOutputs(x: BaseNode) {
const outputs = x.data.form?.outputs ?? {};
if (x.data.label !== Operator.Code) {
return outputs;
}
return {
...outputs,
content: outputs.content ?? {
type: JsonSchemaDataType.String,
value: '',
},
};
}
export function buildOutputOptions(x: BaseNode) {
return {
label: x.data.name,
value: x.id,
title: x.data.name,
options: buildSecondaryOutputOptions(
getNodeOutputs(x),
x.id,
x.data.name,
<OperatorIcon name={x.data.label as Operator} />,
),
};
}
export function buildNodeOutputOptions({
nodes, // all nodes
nodeIds, // Need to obtain the output node IDs
}: {
nodes: BaseNode[];
nodeIds: string[];
}) {
const nodeWithOutputList = nodes.filter(
(x) => nodeIds.some((y) => y === x.id) && !isEmpty(getNodeOutputs(x)),
);
return nodeWithOutputList.map((x) => buildOutputOptions(x));
}
export function buildUpstreamNodeOutputOptions({
nodes,
edges,
nodeId,
}: {
nodes: BaseNode[];
edges: Edge[];
nodeId?: string;
}) {
if (!nodeId) {
return [];
}
const upstreamIds = filterAllUpstreamNodeIds(edges, [nodeId]);
return buildNodeOutputOptions({ nodes, nodeIds: upstreamIds });
}
export function buildChildOutputOptions({
nodes,
nodeId,
}: {
nodes: BaseNode[];
nodeId?: string;
}) {
const nodeWithOutputList = nodes.filter(
(x) => x.parentId === nodeId && !isEmpty(getNodeOutputs(x)),
);
return nodeWithOutputList.map((x) => buildOutputOptions(x));
}
export function getStructuredDatatype(value: Record<string, any> | unknown) {
const dataType = get(value, 'type');
const arrayItemsType = get(value, 'items.type', JsonSchemaDataType.String);
const compositeDataType =
dataType === JsonSchemaDataType.Array
? `${dataType}<${arrayItemsType}>`
: dataType;
return { dataType, compositeDataType };
}