| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456 |
- import {
- DSLComponents,
- ICategorizeItem,
- ICategorizeItemResult,
- RAGFlowNodeType,
- } from '@/interfaces/database/flow';
- import { removeUselessFieldsFromValues } from '@/utils/form';
- import { Edge, Node, Position, XYPosition } from '@xyflow/react';
- import { FormInstance, FormListFieldData } from 'antd';
- import { humanId } from 'human-id';
- import { curry, get, intersectionWith, isEqual, omit, sample } from 'lodash';
- import pipe from 'lodash/fp/pipe';
- import isObject from 'lodash/isObject';
- import { v4 as uuidv4 } from 'uuid';
- import {
- CategorizeAnchorPointPositions,
- NoDebugOperatorsList,
- NodeMap,
- Operator,
- } from './constant';
- import { IPosition } from './interface';
-
- const buildEdges = (
- operatorIds: string[],
- currentId: string,
- allEdges: Edge[],
- isUpstream = false,
- componentName: string,
- nodeParams: Record<string, unknown>,
- ) => {
- operatorIds.forEach((cur) => {
- const source = isUpstream ? cur : currentId;
- const target = isUpstream ? currentId : cur;
- if (!allEdges.some((e) => e.source === source && e.target === target)) {
- const edge: Edge = {
- id: uuidv4(),
- label: '',
- // type: 'step',
- source: source,
- target: target,
- // markerEnd: {
- // type: MarkerType.ArrowClosed,
- // color: 'rgb(157 149 225)',
- // width: 20,
- // height: 20,
- // },
- };
- if (componentName === Operator.Categorize && !isUpstream) {
- const categoryDescription =
- nodeParams.category_description as ICategorizeItemResult;
-
- const name = Object.keys(categoryDescription).find(
- (x) => categoryDescription[x].to === target,
- );
-
- if (name) {
- edge.sourceHandle = name;
- }
- }
- allEdges.push(edge);
- }
- });
- };
-
- export const buildNodesAndEdgesFromDSLComponents = (data: DSLComponents) => {
- const nodes: Node[] = [];
- let edges: Edge[] = [];
-
- Object.entries(data).forEach(([key, value]) => {
- const downstream = [...value.downstream];
- const upstream = [...value.upstream];
- const { component_name: componentName, params } = value.obj;
- nodes.push({
- id: key,
- type: NodeMap[value.obj.component_name as Operator] || 'ragNode',
- position: { x: 0, y: 0 },
- data: {
- label: componentName,
- name: humanId(),
- form: params,
- },
- sourcePosition: Position.Left,
- targetPosition: Position.Right,
- });
-
- buildEdges(upstream, key, edges, true, componentName, params);
- buildEdges(downstream, key, edges, false, componentName, params);
- });
-
- return { nodes, edges };
- };
-
- const buildComponentDownstreamOrUpstream = (
- edges: Edge[],
- nodeId: string,
- isBuildDownstream = true,
- ) => {
- return edges
- .filter((y) => y[isBuildDownstream ? 'source' : 'target'] === nodeId)
- .map((y) => y[isBuildDownstream ? 'target' : 'source']);
- };
-
- const removeUselessDataInTheOperator = curry(
- (operatorName: string, params: Record<string, unknown>) => {
- if (
- operatorName === Operator.Generate ||
- operatorName === Operator.Categorize
- ) {
- return removeUselessFieldsFromValues(params, '');
- }
- return params;
- },
- );
- // initialize data for operators without parameters
- // const initializeOperatorParams = curry((operatorName: string, values: any) => {
- // if (isEmpty(values)) {
- // return initialFormValuesMap[operatorName as Operator];
- // }
- // return values;
- // });
-
- const buildOperatorParams = (operatorName: string) =>
- pipe(
- removeUselessDataInTheOperator(operatorName),
- // initializeOperatorParams(operatorName), // Final processing, for guarantee
- );
-
- // construct a dsl based on the node information of the graph
- export const buildDslComponentsByGraph = (
- nodes: RAGFlowNodeType[],
- edges: Edge[],
- oldDslComponents: DSLComponents,
- ): DSLComponents => {
- const components: DSLComponents = {};
-
- nodes
- ?.filter((x) => x.data.label !== Operator.Note)
- .forEach((x) => {
- const id = x.id;
- const operatorName = x.data.label;
- components[id] = {
- obj: {
- ...(oldDslComponents[id]?.obj ?? {}),
- component_name: operatorName,
- params:
- buildOperatorParams(operatorName)(
- x.data.form as Record<string, unknown>,
- ) ?? {},
- },
- downstream: buildComponentDownstreamOrUpstream(edges, id, true),
- upstream: buildComponentDownstreamOrUpstream(edges, id, false),
- parent_id: x?.parentId,
- };
- });
-
- return components;
- };
-
- export const receiveMessageError = (res: any) =>
- res && (res?.response.status !== 200 || res?.data?.code !== 0);
-
- // Replace the id in the object with text
- export const replaceIdWithText = (
- obj: Record<string, unknown> | unknown[] | unknown,
- getNameById: (id?: string) => string | undefined,
- ) => {
- if (isObject(obj)) {
- const ret: Record<string, unknown> | unknown[] = Array.isArray(obj)
- ? []
- : {};
- Object.keys(obj).forEach((key) => {
- const val = (obj as Record<string, unknown>)[key];
- const text = typeof val === 'string' ? getNameById(val) : undefined;
- (ret as Record<string, unknown>)[key] = text
- ? text
- : replaceIdWithText(val, getNameById);
- });
-
- return ret;
- }
-
- return obj;
- };
-
- export const isEdgeEqual = (previous: Edge, current: Edge) =>
- previous.source === current.source &&
- previous.target === current.target &&
- previous.sourceHandle === current.sourceHandle;
-
- export const buildNewPositionMap = (
- currentKeys: string[],
- previousPositionMap: Record<string, IPosition>,
- ) => {
- // index in use
- const indexesInUse = Object.values(previousPositionMap).map((x) => x.idx);
- const previousKeys = Object.keys(previousPositionMap);
- const intersectionKeys = intersectionWith(
- previousKeys,
- currentKeys,
- (categoryDataKey: string, positionMapKey: string) =>
- categoryDataKey === positionMapKey,
- );
- // difference set
- const currentDifferenceKeys = currentKeys.filter(
- (x) => !intersectionKeys.some((y: string) => y === x),
- );
- const newPositionMap = currentDifferenceKeys.reduce<
- Record<string, IPosition>
- >((pre, cur) => {
- // take a coordinate
- const effectiveIdxes = CategorizeAnchorPointPositions.map(
- (x, idx) => idx,
- ).filter((x) => !indexesInUse.some((y) => y === x));
- const idx = sample(effectiveIdxes);
- if (idx !== undefined) {
- indexesInUse.push(idx);
- pre[cur] = { ...CategorizeAnchorPointPositions[idx], idx };
- }
-
- return pre;
- }, {});
-
- return { intersectionKeys, newPositionMap };
- };
-
- export const isKeysEqual = (currentKeys: string[], previousKeys: string[]) => {
- return isEqual(currentKeys.sort(), previousKeys.sort());
- };
-
- export const getOperatorIndex = (handleTitle: string) => {
- return handleTitle.split(' ').at(-1);
- };
-
- // Get the value of other forms except itself
- export const getOtherFieldValues = (
- form: FormInstance,
- formListName: string = 'items',
- field: FormListFieldData,
- latestField: string,
- ) =>
- (form.getFieldValue([formListName]) ?? [])
- .map((x: any) => {
- return get(x, latestField);
- })
- .filter(
- (x: string) =>
- x !== form.getFieldValue([formListName, field.name, latestField]),
- );
-
- export const generateSwitchHandleText = (idx: number) => {
- return `Case ${idx + 1}`;
- };
-
- export const getNodeDragHandle = (nodeType?: string) => {
- return nodeType === Operator.Note ? '.note-drag-handle' : undefined;
- };
-
- const splitName = (name: string) => {
- const names = name.split('_');
- const type = names.at(0);
- const index = Number(names.at(-1));
-
- return { type, index };
- };
-
- export const generateNodeNamesWithIncreasingIndex = (
- name: string,
- nodes: RAGFlowNodeType[],
- ) => {
- const templateNameList = nodes
- .filter((x) => {
- const temporaryName = x.data.name;
-
- const { type, index } = splitName(temporaryName);
-
- return (
- temporaryName.match(/_/g)?.length === 1 &&
- type === name &&
- !isNaN(index)
- );
- })
- .map((x) => {
- const temporaryName = x.data.name;
- const { index } = splitName(temporaryName);
-
- return {
- idx: index,
- name: temporaryName,
- };
- })
- .sort((a, b) => a.idx - b.idx);
-
- let index: number = 0;
- for (let i = 0; i < templateNameList.length; i++) {
- const idx = templateNameList[i]?.idx;
- const nextIdx = templateNameList[i + 1]?.idx;
- if (idx + 1 !== nextIdx) {
- index = idx + 1;
- break;
- }
- }
-
- return `${name}_${index}`;
- };
-
- export const duplicateNodeForm = (nodeData?: RAGFlowNodeType['data']) => {
- const form: Record<string, any> = { ...(nodeData?.form ?? {}) };
-
- // Delete the downstream node corresponding to the to field of the Categorize operator
- if (nodeData?.label === Operator.Categorize) {
- form.category_description = Object.keys(form.category_description).reduce<
- Record<string, Record<string, any>>
- >((pre, cur) => {
- pre[cur] = {
- ...form.category_description[cur],
- to: undefined,
- };
- return pre;
- }, {});
- }
-
- // Delete the downstream nodes corresponding to the yes and no fields of the Relevant operator
- if (nodeData?.label === Operator.Relevant) {
- form.yes = undefined;
- form.no = undefined;
- }
-
- return {
- ...(nodeData ?? { label: '' }),
- form,
- };
- };
-
- export const getDrawerWidth = () => {
- return window.innerWidth > 1278 ? '40%' : 470;
- };
-
- export const needsSingleStepDebugging = (label: string) => {
- return !NoDebugOperatorsList.some((x) => (label as Operator) === x);
- };
-
- // Get the coordinates of the node relative to the Iteration node
- export function getRelativePositionToIterationNode(
- nodes: RAGFlowNodeType[],
- position?: XYPosition, // relative position
- ) {
- if (!position) {
- return;
- }
-
- const iterationNodes = nodes.filter(
- (node) => node.data.label === Operator.Iteration,
- );
-
- for (const iterationNode of iterationNodes) {
- const {
- position: { x, y },
- width,
- height,
- } = iterationNode;
- const halfWidth = (width || 0) / 2;
- if (
- position.x >= x - halfWidth &&
- position.x <= x + halfWidth &&
- position.y >= y &&
- position.y <= y + (height || 0)
- ) {
- return {
- parentId: iterationNode.id,
- position: { x: position.x - x + halfWidth, y: position.y - y },
- };
- }
- }
- }
-
- export const generateDuplicateNode = (
- position?: XYPosition,
- label?: string,
- ) => {
- const nextPosition = {
- x: (position?.x || 0) + 50,
- y: (position?.y || 0) + 50,
- };
-
- return {
- selected: false,
- dragging: false,
- id: `${label}:${humanId()}`,
- position: nextPosition,
- dragHandle: getNodeDragHandle(label),
- };
- };
-
- /**
- * convert the following object into a list
- *
- * {
- "product_related": {
- "description": "The question is about product usage, appearance and how it works.",
- "examples": "Why it always beaming?\nHow to install it onto the wall?\nIt leaks, what to do?",
- "to": "generate:0"
- }
- }
- */
- export const buildCategorizeListFromObject = (
- categorizeItem: ICategorizeItemResult,
- ) => {
- // Categorize's to field has two data sources, with edges as the data source.
- // Changes in the edge or to field need to be synchronized to the form field.
- return Object.keys(categorizeItem)
- .reduce<Array<ICategorizeItem>>((pre, cur) => {
- // synchronize edge data to the to field
-
- pre.push({ name: cur, ...categorizeItem[cur] });
- return pre;
- }, [])
- .sort((a, b) => a.index - b.index);
- };
-
- /**
- * Convert the list in the following form into an object
- * {
- "items": [
- {
- "name": "Categorize 1",
- "description": "111",
- "examples": "ddd",
- "to": "Retrieval:LazyEelsStick"
- }
- ]
- }
- */
- export const buildCategorizeObjectFromList = (list: Array<ICategorizeItem>) => {
- return list.reduce<ICategorizeItemResult>((pre, cur) => {
- if (cur?.name) {
- pre[cur.name] = omit(cur, 'name');
- }
- return pre;
- }, {});
- };
-
- export function convertToStringArray(
- list: Array<{ value: string | number | boolean }>,
- ) {
- if (!Array.isArray(list)) {
- return [];
- }
- return list.map((x) => x.value);
- }
-
- export function convertToObjectArray(list: Array<string | number | boolean>) {
- if (!Array.isArray(list)) {
- return [];
- }
- return list.map((x) => ({ value: x }));
- }
|