您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. import {
  2. DSLComponents,
  3. ICategorizeItem,
  4. ICategorizeItemResult,
  5. RAGFlowNodeType,
  6. } from '@/interfaces/database/flow';
  7. import { removeUselessFieldsFromValues } from '@/utils/form';
  8. import { Edge, Node, Position, XYPosition } from '@xyflow/react';
  9. import { FormInstance, FormListFieldData } from 'antd';
  10. import { humanId } from 'human-id';
  11. import { curry, get, intersectionWith, isEqual, omit, sample } from 'lodash';
  12. import pipe from 'lodash/fp/pipe';
  13. import isObject from 'lodash/isObject';
  14. import { v4 as uuidv4 } from 'uuid';
  15. import {
  16. CategorizeAnchorPointPositions,
  17. NoDebugOperatorsList,
  18. NodeMap,
  19. Operator,
  20. } from './constant';
  21. import { IPosition } from './interface';
  22. const buildEdges = (
  23. operatorIds: string[],
  24. currentId: string,
  25. allEdges: Edge[],
  26. isUpstream = false,
  27. componentName: string,
  28. nodeParams: Record<string, unknown>,
  29. ) => {
  30. operatorIds.forEach((cur) => {
  31. const source = isUpstream ? cur : currentId;
  32. const target = isUpstream ? currentId : cur;
  33. if (!allEdges.some((e) => e.source === source && e.target === target)) {
  34. const edge: Edge = {
  35. id: uuidv4(),
  36. label: '',
  37. // type: 'step',
  38. source: source,
  39. target: target,
  40. // markerEnd: {
  41. // type: MarkerType.ArrowClosed,
  42. // color: 'rgb(157 149 225)',
  43. // width: 20,
  44. // height: 20,
  45. // },
  46. };
  47. if (componentName === Operator.Categorize && !isUpstream) {
  48. const categoryDescription =
  49. nodeParams.category_description as ICategorizeItemResult;
  50. const name = Object.keys(categoryDescription).find(
  51. (x) => categoryDescription[x].to === target,
  52. );
  53. if (name) {
  54. edge.sourceHandle = name;
  55. }
  56. }
  57. allEdges.push(edge);
  58. }
  59. });
  60. };
  61. export const buildNodesAndEdgesFromDSLComponents = (data: DSLComponents) => {
  62. const nodes: Node[] = [];
  63. let edges: Edge[] = [];
  64. Object.entries(data).forEach(([key, value]) => {
  65. const downstream = [...value.downstream];
  66. const upstream = [...value.upstream];
  67. const { component_name: componentName, params } = value.obj;
  68. nodes.push({
  69. id: key,
  70. type: NodeMap[value.obj.component_name as Operator] || 'ragNode',
  71. position: { x: 0, y: 0 },
  72. data: {
  73. label: componentName,
  74. name: humanId(),
  75. form: params,
  76. },
  77. sourcePosition: Position.Left,
  78. targetPosition: Position.Right,
  79. });
  80. buildEdges(upstream, key, edges, true, componentName, params);
  81. buildEdges(downstream, key, edges, false, componentName, params);
  82. });
  83. return { nodes, edges };
  84. };
  85. const buildComponentDownstreamOrUpstream = (
  86. edges: Edge[],
  87. nodeId: string,
  88. isBuildDownstream = true,
  89. ) => {
  90. return edges
  91. .filter((y) => y[isBuildDownstream ? 'source' : 'target'] === nodeId)
  92. .map((y) => y[isBuildDownstream ? 'target' : 'source']);
  93. };
  94. const removeUselessDataInTheOperator = curry(
  95. (operatorName: string, params: Record<string, unknown>) => {
  96. if (
  97. operatorName === Operator.Generate ||
  98. operatorName === Operator.Categorize
  99. ) {
  100. return removeUselessFieldsFromValues(params, '');
  101. }
  102. return params;
  103. },
  104. );
  105. // initialize data for operators without parameters
  106. // const initializeOperatorParams = curry((operatorName: string, values: any) => {
  107. // if (isEmpty(values)) {
  108. // return initialFormValuesMap[operatorName as Operator];
  109. // }
  110. // return values;
  111. // });
  112. const buildOperatorParams = (operatorName: string) =>
  113. pipe(
  114. removeUselessDataInTheOperator(operatorName),
  115. // initializeOperatorParams(operatorName), // Final processing, for guarantee
  116. );
  117. // construct a dsl based on the node information of the graph
  118. export const buildDslComponentsByGraph = (
  119. nodes: RAGFlowNodeType[],
  120. edges: Edge[],
  121. oldDslComponents: DSLComponents,
  122. ): DSLComponents => {
  123. const components: DSLComponents = {};
  124. nodes
  125. ?.filter((x) => x.data.label !== Operator.Note)
  126. .forEach((x) => {
  127. const id = x.id;
  128. const operatorName = x.data.label;
  129. components[id] = {
  130. obj: {
  131. ...(oldDslComponents[id]?.obj ?? {}),
  132. component_name: operatorName,
  133. params:
  134. buildOperatorParams(operatorName)(
  135. x.data.form as Record<string, unknown>,
  136. ) ?? {},
  137. },
  138. downstream: buildComponentDownstreamOrUpstream(edges, id, true),
  139. upstream: buildComponentDownstreamOrUpstream(edges, id, false),
  140. parent_id: x?.parentId,
  141. };
  142. });
  143. return components;
  144. };
  145. export const receiveMessageError = (res: any) =>
  146. res && (res?.response.status !== 200 || res?.data?.code !== 0);
  147. // Replace the id in the object with text
  148. export const replaceIdWithText = (
  149. obj: Record<string, unknown> | unknown[] | unknown,
  150. getNameById: (id?: string) => string | undefined,
  151. ) => {
  152. if (isObject(obj)) {
  153. const ret: Record<string, unknown> | unknown[] = Array.isArray(obj)
  154. ? []
  155. : {};
  156. Object.keys(obj).forEach((key) => {
  157. const val = (obj as Record<string, unknown>)[key];
  158. const text = typeof val === 'string' ? getNameById(val) : undefined;
  159. (ret as Record<string, unknown>)[key] = text
  160. ? text
  161. : replaceIdWithText(val, getNameById);
  162. });
  163. return ret;
  164. }
  165. return obj;
  166. };
  167. export const isEdgeEqual = (previous: Edge, current: Edge) =>
  168. previous.source === current.source &&
  169. previous.target === current.target &&
  170. previous.sourceHandle === current.sourceHandle;
  171. export const buildNewPositionMap = (
  172. currentKeys: string[],
  173. previousPositionMap: Record<string, IPosition>,
  174. ) => {
  175. // index in use
  176. const indexesInUse = Object.values(previousPositionMap).map((x) => x.idx);
  177. const previousKeys = Object.keys(previousPositionMap);
  178. const intersectionKeys = intersectionWith(
  179. previousKeys,
  180. currentKeys,
  181. (categoryDataKey: string, positionMapKey: string) =>
  182. categoryDataKey === positionMapKey,
  183. );
  184. // difference set
  185. const currentDifferenceKeys = currentKeys.filter(
  186. (x) => !intersectionKeys.some((y: string) => y === x),
  187. );
  188. const newPositionMap = currentDifferenceKeys.reduce<
  189. Record<string, IPosition>
  190. >((pre, cur) => {
  191. // take a coordinate
  192. const effectiveIdxes = CategorizeAnchorPointPositions.map(
  193. (x, idx) => idx,
  194. ).filter((x) => !indexesInUse.some((y) => y === x));
  195. const idx = sample(effectiveIdxes);
  196. if (idx !== undefined) {
  197. indexesInUse.push(idx);
  198. pre[cur] = { ...CategorizeAnchorPointPositions[idx], idx };
  199. }
  200. return pre;
  201. }, {});
  202. return { intersectionKeys, newPositionMap };
  203. };
  204. export const isKeysEqual = (currentKeys: string[], previousKeys: string[]) => {
  205. return isEqual(currentKeys.sort(), previousKeys.sort());
  206. };
  207. export const getOperatorIndex = (handleTitle: string) => {
  208. return handleTitle.split(' ').at(-1);
  209. };
  210. // Get the value of other forms except itself
  211. export const getOtherFieldValues = (
  212. form: FormInstance,
  213. formListName: string = 'items',
  214. field: FormListFieldData,
  215. latestField: string,
  216. ) =>
  217. (form.getFieldValue([formListName]) ?? [])
  218. .map((x: any) => {
  219. return get(x, latestField);
  220. })
  221. .filter(
  222. (x: string) =>
  223. x !== form.getFieldValue([formListName, field.name, latestField]),
  224. );
  225. export const generateSwitchHandleText = (idx: number) => {
  226. return `Case ${idx + 1}`;
  227. };
  228. export const getNodeDragHandle = (nodeType?: string) => {
  229. return nodeType === Operator.Note ? '.note-drag-handle' : undefined;
  230. };
  231. const splitName = (name: string) => {
  232. const names = name.split('_');
  233. const type = names.at(0);
  234. const index = Number(names.at(-1));
  235. return { type, index };
  236. };
  237. export const generateNodeNamesWithIncreasingIndex = (
  238. name: string,
  239. nodes: RAGFlowNodeType[],
  240. ) => {
  241. const templateNameList = nodes
  242. .filter((x) => {
  243. const temporaryName = x.data.name;
  244. const { type, index } = splitName(temporaryName);
  245. return (
  246. temporaryName.match(/_/g)?.length === 1 &&
  247. type === name &&
  248. !isNaN(index)
  249. );
  250. })
  251. .map((x) => {
  252. const temporaryName = x.data.name;
  253. const { index } = splitName(temporaryName);
  254. return {
  255. idx: index,
  256. name: temporaryName,
  257. };
  258. })
  259. .sort((a, b) => a.idx - b.idx);
  260. let index: number = 0;
  261. for (let i = 0; i < templateNameList.length; i++) {
  262. const idx = templateNameList[i]?.idx;
  263. const nextIdx = templateNameList[i + 1]?.idx;
  264. if (idx + 1 !== nextIdx) {
  265. index = idx + 1;
  266. break;
  267. }
  268. }
  269. return `${name}_${index}`;
  270. };
  271. export const duplicateNodeForm = (nodeData?: RAGFlowNodeType['data']) => {
  272. const form: Record<string, any> = { ...(nodeData?.form ?? {}) };
  273. // Delete the downstream node corresponding to the to field of the Categorize operator
  274. if (nodeData?.label === Operator.Categorize) {
  275. form.category_description = Object.keys(form.category_description).reduce<
  276. Record<string, Record<string, any>>
  277. >((pre, cur) => {
  278. pre[cur] = {
  279. ...form.category_description[cur],
  280. to: undefined,
  281. };
  282. return pre;
  283. }, {});
  284. }
  285. // Delete the downstream nodes corresponding to the yes and no fields of the Relevant operator
  286. if (nodeData?.label === Operator.Relevant) {
  287. form.yes = undefined;
  288. form.no = undefined;
  289. }
  290. return {
  291. ...(nodeData ?? { label: '' }),
  292. form,
  293. };
  294. };
  295. export const getDrawerWidth = () => {
  296. return window.innerWidth > 1278 ? '40%' : 470;
  297. };
  298. export const needsSingleStepDebugging = (label: string) => {
  299. return !NoDebugOperatorsList.some((x) => (label as Operator) === x);
  300. };
  301. // Get the coordinates of the node relative to the Iteration node
  302. export function getRelativePositionToIterationNode(
  303. nodes: RAGFlowNodeType[],
  304. position?: XYPosition, // relative position
  305. ) {
  306. if (!position) {
  307. return;
  308. }
  309. const iterationNodes = nodes.filter(
  310. (node) => node.data.label === Operator.Iteration,
  311. );
  312. for (const iterationNode of iterationNodes) {
  313. const {
  314. position: { x, y },
  315. width,
  316. height,
  317. } = iterationNode;
  318. const halfWidth = (width || 0) / 2;
  319. if (
  320. position.x >= x - halfWidth &&
  321. position.x <= x + halfWidth &&
  322. position.y >= y &&
  323. position.y <= y + (height || 0)
  324. ) {
  325. return {
  326. parentId: iterationNode.id,
  327. position: { x: position.x - x + halfWidth, y: position.y - y },
  328. };
  329. }
  330. }
  331. }
  332. export const generateDuplicateNode = (
  333. position?: XYPosition,
  334. label?: string,
  335. ) => {
  336. const nextPosition = {
  337. x: (position?.x || 0) + 50,
  338. y: (position?.y || 0) + 50,
  339. };
  340. return {
  341. selected: false,
  342. dragging: false,
  343. id: `${label}:${humanId()}`,
  344. position: nextPosition,
  345. dragHandle: getNodeDragHandle(label),
  346. };
  347. };
  348. /**
  349. * convert the following object into a list
  350. *
  351. * {
  352. "product_related": {
  353. "description": "The question is about product usage, appearance and how it works.",
  354. "examples": "Why it always beaming?\nHow to install it onto the wall?\nIt leaks, what to do?",
  355. "to": "generate:0"
  356. }
  357. }
  358. */
  359. export const buildCategorizeListFromObject = (
  360. categorizeItem: ICategorizeItemResult,
  361. ) => {
  362. // Categorize's to field has two data sources, with edges as the data source.
  363. // Changes in the edge or to field need to be synchronized to the form field.
  364. return Object.keys(categorizeItem)
  365. .reduce<Array<ICategorizeItem>>((pre, cur) => {
  366. // synchronize edge data to the to field
  367. pre.push({ name: cur, ...categorizeItem[cur] });
  368. return pre;
  369. }, [])
  370. .sort((a, b) => a.index - b.index);
  371. };
  372. /**
  373. * Convert the list in the following form into an object
  374. * {
  375. "items": [
  376. {
  377. "name": "Categorize 1",
  378. "description": "111",
  379. "examples": "ddd",
  380. "to": "Retrieval:LazyEelsStick"
  381. }
  382. ]
  383. }
  384. */
  385. export const buildCategorizeObjectFromList = (list: Array<ICategorizeItem>) => {
  386. return list.reduce<ICategorizeItemResult>((pre, cur) => {
  387. if (cur?.name) {
  388. pre[cur.name] = omit(cur, 'name');
  389. }
  390. return pre;
  391. }, {});
  392. };
  393. export function convertToStringArray(
  394. list: Array<{ value: string | number | boolean }>,
  395. ) {
  396. if (!Array.isArray(list)) {
  397. return [];
  398. }
  399. return list.map((x) => x.value);
  400. }
  401. export function convertToObjectArray(list: Array<string | number | boolean>) {
  402. if (!Array.isArray(list)) {
  403. return [];
  404. }
  405. return list.map((x) => ({ value: x }));
  406. }