balibabu commited on
Commit
c298fc4
·
1 Parent(s): 1043572

feat: get the operator type from id #918 (#1323)

Browse files

### What problem does this PR solve?

feat: get the operator type from id #918

### Type of change

- [x] New Feature (non-breaking change which adds functionality)

web/src/pages/flow/headhunter_zh.json ADDED
@@ -0,0 +1,483 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "edges": [
3
+ {
4
+ "id": "1a75afcd-05ea-4bf1-af5e-adff4f870100",
5
+ "label": "",
6
+ "source": "begin",
7
+ "target": "answer:0",
8
+ "markerEnd": {
9
+ "type": "arrow"
10
+ }
11
+ },
12
+ {
13
+ "id": "a88e567e-1c23-457f-a0db-a90f1ea14d21",
14
+ "label": "",
15
+ "source": "message:reject",
16
+ "target": "answer:0",
17
+ "markerEnd": {
18
+ "type": "arrow"
19
+ }
20
+ },
21
+ {
22
+ "id": "ad5fdb81-568e-46c5-82b1-1f50b625ae23",
23
+ "label": "",
24
+ "source": "answer:0",
25
+ "target": "categorize:0",
26
+ "markerEnd": {
27
+ "type": "arrow"
28
+ }
29
+ },
30
+ {
31
+ "id": "fbb4c147-0d4c-4dd2-9264-3088e02c40d1",
32
+ "label": "",
33
+ "source": "categorize:0",
34
+ "target": "message:introduction",
35
+ "markerEnd": {
36
+ "type": "arrow"
37
+ }
38
+ },
39
+ {
40
+ "id": "40da6d94-7d51-4d77-a5dd-1d583daba147",
41
+ "label": "",
42
+ "source": "categorize:0",
43
+ "target": "generate:casual",
44
+ "markerEnd": {
45
+ "type": "arrow"
46
+ }
47
+ },
48
+ {
49
+ "id": "e4c7f5dc-5ccb-45da-af0d-4fbcb9dbb6f9",
50
+ "label": "",
51
+ "source": "categorize:0",
52
+ "target": "message:reject",
53
+ "markerEnd": {
54
+ "type": "arrow"
55
+ }
56
+ },
57
+ {
58
+ "id": "a8c0b4e5-9597-4335-bded-5b1dd46f7507",
59
+ "label": "",
60
+ "source": "categorize:0",
61
+ "target": "retrieval:0",
62
+ "markerEnd": {
63
+ "type": "arrow"
64
+ }
65
+ },
66
+ {
67
+ "id": "223972c4-08f7-47ae-b9ce-9a52dba044a7",
68
+ "label": "",
69
+ "source": "message:introduction",
70
+ "target": "answer:1",
71
+ "markerEnd": {
72
+ "type": "arrow"
73
+ }
74
+ },
75
+ {
76
+ "id": "ece123ba-a84c-41d6-a843-63df331f945e",
77
+ "label": "",
78
+ "source": "generate:aboutJob",
79
+ "target": "answer:1",
80
+ "markerEnd": {
81
+ "type": "arrow"
82
+ }
83
+ },
84
+ {
85
+ "id": "fa07af7d-29de-4066-89b7-f1bee184e260",
86
+ "label": "",
87
+ "source": "generate:casual",
88
+ "target": "answer:1",
89
+ "markerEnd": {
90
+ "type": "arrow"
91
+ }
92
+ },
93
+ {
94
+ "id": "0d7565d8-673c-4a5e-90f4-f4296384eef0",
95
+ "label": "",
96
+ "source": "generate:get_wechat",
97
+ "target": "answer:1",
98
+ "markerEnd": {
99
+ "type": "arrow"
100
+ }
101
+ },
102
+ {
103
+ "id": "e615e3d2-8ba5-448f-b001-ab926078cca6",
104
+ "label": "",
105
+ "source": "generate:nowechat",
106
+ "target": "answer:1",
107
+ "markerEnd": {
108
+ "type": "arrow"
109
+ }
110
+ },
111
+ {
112
+ "id": "a9cf0f0a-77b5-42a2-a71f-e71086a525f0",
113
+ "label": "",
114
+ "source": "answer:1",
115
+ "target": "categorize:1",
116
+ "markerEnd": {
117
+ "type": "arrow"
118
+ }
119
+ },
120
+ {
121
+ "id": "4e4c5461-cdc6-46c1-afb1-5892cb54104a",
122
+ "label": "",
123
+ "source": "answer:0",
124
+ "target": "categorize:1",
125
+ "markerEnd": {
126
+ "type": "arrow"
127
+ }
128
+ },
129
+ {
130
+ "id": "44c8cb42-a2d9-43df-83bb-175d4870a027",
131
+ "label": "",
132
+ "source": "categorize:1",
133
+ "target": "retrieval:0",
134
+ "markerEnd": {
135
+ "type": "arrow"
136
+ }
137
+ },
138
+ {
139
+ "id": "86d2308d-c91a-4a21-a6d6-081c92fc517c",
140
+ "label": "",
141
+ "source": "categorize:1",
142
+ "target": "generate:casual",
143
+ "markerEnd": {
144
+ "type": "arrow"
145
+ }
146
+ },
147
+ {
148
+ "id": "e5de8fc4-b801-44cf-8240-02f2aac6ae23",
149
+ "label": "",
150
+ "source": "categorize:1",
151
+ "target": "generate:get_wechat",
152
+ "markerEnd": {
153
+ "type": "arrow"
154
+ }
155
+ },
156
+ {
157
+ "id": "a9511914-d696-46b4-90c2-a3a940205b5c",
158
+ "label": "",
159
+ "source": "categorize:1",
160
+ "target": "generate:nowechat",
161
+ "markerEnd": {
162
+ "type": "arrow"
163
+ }
164
+ },
165
+ {
166
+ "id": "07db19d7-e356-4c24-8e0d-353b1e6e6ee4",
167
+ "label": "",
168
+ "source": "retrieval:0",
169
+ "target": "generate:aboutJob",
170
+ "markerEnd": {
171
+ "type": "arrow"
172
+ }
173
+ },
174
+ {
175
+ "id": "e17eb04f-c773-44b2-8a7b-7f770185c4fa",
176
+ "label": "",
177
+ "source": "relevant:0",
178
+ "target": "generate:aboutJob",
179
+ "markerEnd": {
180
+ "type": "arrow"
181
+ }
182
+ }
183
+ ],
184
+ "nodes": [
185
+ {
186
+ "id": "begin",
187
+ "type": "beginNode",
188
+ "position": {
189
+ "x": 0,
190
+ "y": 0
191
+ },
192
+ "data": {
193
+ "label": "Begin",
194
+ "params": {
195
+ "prologue": "您好!我是AGI方向的猎头,了解到您是这方面的大佬,然后冒昧的就联系到您。这边有个机会想和您分享,RAGFlow正在招聘您这个岗位的资深的工程师不知道您那边是不是感兴趣?"
196
+ },
197
+ "downstream": ["answer:0"],
198
+ "upstream": []
199
+ },
200
+ "sourcePosition": "left",
201
+ "targetPosition": "right"
202
+ },
203
+ {
204
+ "id": "answer:0",
205
+ "type": "ragNode",
206
+ "position": {
207
+ "x": 0,
208
+ "y": 0
209
+ },
210
+ "data": {
211
+ "label": "Answer",
212
+ "params": {},
213
+ "downstream": ["categorize:0"],
214
+ "upstream": ["begin", "message:reject"]
215
+ },
216
+ "sourcePosition": "left",
217
+ "targetPosition": "right"
218
+ },
219
+ {
220
+ "id": "categorize:0",
221
+ "type": "categorizeNode",
222
+ "position": {
223
+ "x": 0,
224
+ "y": 0
225
+ },
226
+ "data": {
227
+ "label": "Categorize",
228
+ "params": {
229
+ "llm_id": "deepseek-chat",
230
+ "category_description": {
231
+ "about_job": {
232
+ "description": "该问题关于职位本身或公司的信息。",
233
+ "examples": "什么岗位?\n汇报对象是谁?\n公司多少人?\n公司有啥产品?\n具体工作内容是啥?\n地点哪里?\n双休吗?",
234
+ "to": "retrieval:0"
235
+ },
236
+ "casual": {
237
+ "description": "该问题不关于职位本身或公司的信息,属于闲聊。",
238
+ "examples": "你好\n好久不见\n你男的女的?\n你是猴子派来的救兵吗?\n上午开会了?\n你叫啥?\n最近市场如何?生意好做吗?",
239
+ "to": "generate:casual"
240
+ },
241
+ "interested": {
242
+ "description": "该回答表示他对于该职位感兴趣。",
243
+ "examples": "嗯\n说吧\n说说看\n还好吧\n是的\n哦\nyes\n具体说说",
244
+ "to": "message:introduction"
245
+ },
246
+ "answer": {
247
+ "description": "该回答表示他对于该职位不感兴趣,或感觉受到骚扰。",
248
+ "examples": "不需要\n不感兴趣\n暂时不看\n不要\nno\n我已经不干这个了\n我不是这个方向的",
249
+ "to": "message:reject"
250
+ }
251
+ }
252
+ },
253
+ "downstream": [
254
+ "message:introduction",
255
+ "generate:casual",
256
+ "message:reject",
257
+ "retrieval:0"
258
+ ],
259
+ "upstream": ["answer:0"]
260
+ },
261
+ "sourcePosition": "left",
262
+ "targetPosition": "right"
263
+ },
264
+ {
265
+ "id": "message:introduction",
266
+ "type": "ragNode",
267
+ "position": {
268
+ "x": 0,
269
+ "y": 0
270
+ },
271
+ "data": {
272
+ "label": "Message",
273
+ "params": {
274
+ "messages": [
275
+ "我简单介绍以下:\nRAGFlow 是一款基于深度文档理解构建的开源 RAG(Retrieval-Augmented Generation)引擎。RAGFlow 可以为各种规模的企业及个人提供一套精简的 RAG 工作流程,结合大语言模型(LLM)针对用户各类不同的复杂格式数据提供可靠的问答以及有理有据的引用。https://github.com/infiniflow/ragflow\n您那边还有什么要了解的?"
276
+ ]
277
+ },
278
+ "downstream": ["answer:1"],
279
+ "upstream": ["categorize:0"]
280
+ },
281
+ "sourcePosition": "left",
282
+ "targetPosition": "right"
283
+ },
284
+ {
285
+ "id": "answer:1",
286
+ "type": "ragNode",
287
+ "position": {
288
+ "x": 0,
289
+ "y": 0
290
+ },
291
+ "data": {
292
+ "label": "Answer",
293
+ "params": {},
294
+ "downstream": ["categorize:1"],
295
+ "upstream": [
296
+ "message:introduction",
297
+ "generate:aboutJob",
298
+ "generate:casual",
299
+ "generate:get_wechat",
300
+ "generate:nowechat"
301
+ ]
302
+ },
303
+ "sourcePosition": "left",
304
+ "targetPosition": "right"
305
+ },
306
+ {
307
+ "id": "categorize:1",
308
+ "type": "categorizeNode",
309
+ "position": {
310
+ "x": 0,
311
+ "y": 0
312
+ },
313
+ "data": {
314
+ "label": "Categorize",
315
+ "params": {
316
+ "llm_id": "deepseek-chat",
317
+ "category_description": {
318
+ "about_job": {
319
+ "description": "该问题关于职位本身或公司的信息。",
320
+ "examples": "什么岗位?\n汇报对象是谁?\n公司多少人?\n公司有啥产品?\n具体工作内容是啥?\n地点哪里?\n双休吗?",
321
+ "to": "retrieval:0"
322
+ },
323
+ "casual": {
324
+ "description": "该问题不关于职位本身或公司的信息,属于闲聊。",
325
+ "examples": "你好\n好久不见\n你男的女的?\n你是猴子派来的救兵吗?\n上午开会了?\n你叫啥?\n最近市场如何?生意好做吗?",
326
+ "to": "generate:casual"
327
+ },
328
+ "wechat": {
329
+ "description": "该回答表示他愿意加微信,或者已经报了微信号。",
330
+ "examples": "嗯\n可以\n是的\n哦\nyes\n15002333453\nwindblow_2231",
331
+ "to": "generate:get_wechat"
332
+ },
333
+ "giveup": {
334
+ "description": "该回答表示他不愿意加微信。",
335
+ "examples": "不需要\n不感兴趣\n暂时不看\n不要\nno\n不方便\n不知道还要加我微信",
336
+ "to": "generate:nowechat"
337
+ }
338
+ },
339
+ "message_history_window_size": 8
340
+ },
341
+ "downstream": [
342
+ "retrieval:0",
343
+ "generate:casual",
344
+ "generate:get_wechat",
345
+ "generate:nowechat"
346
+ ],
347
+ "upstream": ["answer:0"]
348
+ },
349
+ "sourcePosition": "left",
350
+ "targetPosition": "right"
351
+ },
352
+ {
353
+ "id": "generate:casual",
354
+ "type": "ragNode",
355
+ "position": {
356
+ "x": 0,
357
+ "y": 0
358
+ },
359
+ "data": {
360
+ "label": "Generate",
361
+ "params": {
362
+ "llm_id": "deepseek-chat",
363
+ "prompt": "你是AGI方向的猎头,现在候选人的聊了和职位无关的话题,请耐心的回应候选人,并将话题往该AGI的职位上带,最好能要到候选人微信号以便后面保持联系。",
364
+ "temperature": 0.9,
365
+ "message_history_window_size": 12,
366
+ "cite": false
367
+ },
368
+ "downstream": ["answer:1"],
369
+ "upstream": ["categorize:0", "categorize:1"]
370
+ },
371
+ "sourcePosition": "left",
372
+ "targetPosition": "right"
373
+ },
374
+ {
375
+ "id": "retrieval:0",
376
+ "type": "ragNode",
377
+ "position": {
378
+ "x": 0,
379
+ "y": 0
380
+ },
381
+ "data": {
382
+ "label": "Retrieval",
383
+ "params": {
384
+ "similarity_threshold": 0.2,
385
+ "keywords_similarity_weight": 0.3,
386
+ "top_n": 6,
387
+ "top_k": 1024,
388
+ "rerank_id": "BAAI/bge-reranker-v2-m3",
389
+ "kb_ids": ["869a236818b811ef91dffa163e197198"]
390
+ },
391
+ "downstream": ["generate:aboutJob"],
392
+ "upstream": ["categorize:0", "categorize:1"]
393
+ },
394
+ "sourcePosition": "left",
395
+ "targetPosition": "right"
396
+ },
397
+ {
398
+ "id": "generate:aboutJob",
399
+ "type": "ragNode",
400
+ "position": {
401
+ "x": 0,
402
+ "y": 0
403
+ },
404
+ "data": {
405
+ "label": "Generate",
406
+ "params": {
407
+ "llm_id": "deepseek-chat",
408
+ "prompt": "你是AGI方向的猎头,候选人问了有关职位或公司的问题,你根据以下职位信息回答。如果职位信息中不包含候选人的问题就回答不清楚、不知道、有待确认等。回答完后引导候选人加微信号,如:\n - 方便加一下微信吗,我把JD发您看看?\n - 微信号多少,我把详细职位JD发您?\n 职位信息如下:\n {input}\n 职位信息如上。",
409
+ "temperature": 0.02
410
+ },
411
+ "downstream": ["answer:1"],
412
+ "upstream": ["relevant:0"]
413
+ },
414
+ "sourcePosition": "left",
415
+ "targetPosition": "right"
416
+ },
417
+ {
418
+ "id": "generate:get_wechat",
419
+ "type": "ragNode",
420
+ "position": {
421
+ "x": 0,
422
+ "y": 0
423
+ },
424
+ "data": {
425
+ "label": "Generate",
426
+ "params": {
427
+ "llm_id": "deepseek-chat",
428
+ "prompt": "你是AGI方向的猎头,候选人表示不反感加微信,如果对方已经报了微信号,表示感谢和信任并表示马上会加上;如果没有,则问对方微信号多少。你的微信号是weixin_kevin,E-mail是[email protected]。说话不要重复。不要总是您好。",
429
+ "temperature": 0.1,
430
+ "message_history_window_size": 12,
431
+ "cite": false
432
+ },
433
+ "downstream": ["answer:1"],
434
+ "upstream": ["categorize:1"]
435
+ },
436
+ "sourcePosition": "left",
437
+ "targetPosition": "right"
438
+ },
439
+ {
440
+ "id": "generate:nowechat",
441
+ "type": "ragNode",
442
+ "position": {
443
+ "x": 0,
444
+ "y": 0
445
+ },
446
+ "data": {
447
+ "label": "Generate",
448
+ "params": {
449
+ "llm_id": "deepseek-chat",
450
+ "prompt": "你是AGI方向的猎头,当你提出加微信时对方表示拒绝。你需要耐心礼貌的回应候选人,表示对于保护隐私信息给予理解,也可以询问他对该职位的看法和顾虑。并在恰当的时机再次询问微信联系方式。也可以鼓励候选人主动与你取得联系。你的微信号是weixin_kevin,E-mail是[email protected]。说话不要重复。不要总是您好。",
451
+ "temperature": 0.1,
452
+ "message_history_window_size": 12,
453
+ "cite": false
454
+ },
455
+ "downstream": ["answer:1"],
456
+ "upstream": ["categorize:1"]
457
+ },
458
+ "sourcePosition": "left",
459
+ "targetPosition": "right"
460
+ },
461
+ {
462
+ "id": "message:reject",
463
+ "type": "ragNode",
464
+ "position": {
465
+ "x": 0,
466
+ "y": 0
467
+ },
468
+ "data": {
469
+ "label": "Message",
470
+ "params": {
471
+ "messages": [
472
+ "好的,祝您生活愉快,工作顺利。",
473
+ "哦,好的,感谢您宝贵的时间!"
474
+ ]
475
+ },
476
+ "downstream": ["answer:0"],
477
+ "upstream": ["categorize:0"]
478
+ },
479
+ "sourcePosition": "left",
480
+ "targetPosition": "right"
481
+ }
482
+ ]
483
+ }
web/src/pages/flow/hooks.ts CHANGED
@@ -27,7 +27,7 @@ import { humanId } from 'human-id';
27
  import { useParams } from 'umi';
28
  import { NodeMap, Operator, RestrictedUpstreamMap } from './constant';
29
  import useGraphStore, { RFState } from './store';
30
- import { buildDslComponentsByGraph, getOperatorTypeFromId } from './utils';
31
 
32
  const selector = (state: RFState) => ({
33
  nodes: state.nodes,
@@ -249,7 +249,7 @@ export const useSetLlmSetting = (form?: FormInstance) => {
249
  };
250
 
251
  export const useValidateConnection = () => {
252
- const edges = useGraphStore((state) => state.edges);
253
  // restricted lines cannot be connected successfully.
254
  const isValidConnection = useCallback(
255
  (connection: Connection) => {
@@ -265,7 +265,7 @@ export const useValidateConnection = () => {
265
  ]?.every((x) => x !== getOperatorTypeFromId(connection.target));
266
  return ret;
267
  },
268
- [edges],
269
  );
270
 
271
  return isValidConnection;
 
27
  import { useParams } from 'umi';
28
  import { NodeMap, Operator, RestrictedUpstreamMap } from './constant';
29
  import useGraphStore, { RFState } from './store';
30
+ import { buildDslComponentsByGraph } from './utils';
31
 
32
  const selector = (state: RFState) => ({
33
  nodes: state.nodes,
 
249
  };
250
 
251
  export const useValidateConnection = () => {
252
+ const { edges, getOperatorTypeFromId } = useGraphStore((state) => state);
253
  // restricted lines cannot be connected successfully.
254
  const isValidConnection = useCallback(
255
  (connection: Connection) => {
 
265
  ]?.every((x) => x !== getOperatorTypeFromId(connection.target));
266
  return ret;
267
  },
268
+ [edges, getOperatorTypeFromId],
269
  );
270
 
271
  return isValidConnection;
web/src/pages/flow/list/hooks.ts CHANGED
@@ -2,7 +2,9 @@ import { useSetModalState } from '@/hooks/commonHooks';
2
  import { useFetchFlowList, useSetFlow } from '@/hooks/flow-hooks';
3
  import { useCallback, useState } from 'react';
4
  import { useNavigate } from 'umi';
5
- import { dsl } from '../mock';
 
 
6
 
7
  export const useFetchDataOnMount = () => {
8
  const { data, loading } = useFetchFlowList();
@@ -22,7 +24,10 @@ export const useSaveFlow = () => {
22
 
23
  const onFlowOk = useCallback(
24
  async (title: string) => {
25
- const ret = await setFlow({ title, dsl });
 
 
 
26
 
27
  if (ret?.retcode === 0) {
28
  hideFlowSettingModal();
 
2
  import { useFetchFlowList, useSetFlow } from '@/hooks/flow-hooks';
3
  import { useCallback, useState } from 'react';
4
  import { useNavigate } from 'umi';
5
+ // import { dsl } from '../mock';
6
+ import headhunterZhComponents from '../../../../../graph/test/dsl_examples/headhunter_zh.json';
7
+ import headhunter_zh from '../headhunter_zh.json';
8
 
9
  export const useFetchDataOnMount = () => {
10
  const { data, loading } = useFetchFlowList();
 
24
 
25
  const onFlowOk = useCallback(
26
  async (title: string) => {
27
+ const ret = await setFlow({
28
+ title,
29
+ dsl: { ...headhunterZhComponents, graph: headhunter_zh },
30
+ });
31
 
32
  if (ret?.retcode === 0) {
33
  hideFlowSettingModal();
web/src/pages/flow/store.ts CHANGED
@@ -20,7 +20,6 @@ import { create } from 'zustand';
20
  import { devtools } from 'zustand/middleware';
21
  import { Operator } from './constant';
22
  import { NodeData } from './interface';
23
- import { getOperatorTypeFromId } from './utils';
24
 
25
  export type RFState = {
26
  nodes: Node<NodeData>[];
@@ -35,7 +34,7 @@ export type RFState = {
35
  updateNodeForm: (nodeId: string, values: any) => void;
36
  onSelectionChange: OnSelectionChangeFunc;
37
  addNode: (nodes: Node) => void;
38
- getNode: (id?: string) => Node | undefined;
39
  addEdge: (connection: Connection) => void;
40
  getEdge: (id: string) => Edge | undefined;
41
  deletePreviousEdgeOfClassificationNode: (connection: Connection) => void;
@@ -46,6 +45,7 @@ export type RFState = {
46
  deleteEdgeBySourceAndSourceHandle: (connection: Partial<Connection>) => void;
47
  findNodeByName: (operatorName: Operator) => Node | undefined;
48
  updateMutableNodeFormItem: (id: string, field: string, value: any) => void;
 
49
  };
50
 
51
  // this is our useStore hook that we can use in our components to get parts of the store and call actions
@@ -87,9 +87,12 @@ const useGraphStore = create<RFState>()(
87
  addNode: (node: Node) => {
88
  set({ nodes: get().nodes.concat(node) });
89
  },
90
- getNode: (id?: string) => {
91
  return get().nodes.find((x) => x.id === id);
92
  },
 
 
 
93
  addEdge: (connection: Connection) => {
94
  set({
95
  edges: addEdge(connection, get().edges),
@@ -101,7 +104,7 @@ const useGraphStore = create<RFState>()(
101
  },
102
  deletePreviousEdgeOfClassificationNode: (connection: Connection) => {
103
  // Delete the edge on the classification node anchor when the anchor is connected to other nodes
104
- const { edges } = get();
105
  if (getOperatorTypeFromId(connection.source) === Operator.Categorize) {
106
  const previousEdge = edges.find(
107
  (x) =>
 
20
  import { devtools } from 'zustand/middleware';
21
  import { Operator } from './constant';
22
  import { NodeData } from './interface';
 
23
 
24
  export type RFState = {
25
  nodes: Node<NodeData>[];
 
34
  updateNodeForm: (nodeId: string, values: any) => void;
35
  onSelectionChange: OnSelectionChangeFunc;
36
  addNode: (nodes: Node) => void;
37
+ getNode: (id?: string | null) => Node<NodeData> | undefined;
38
  addEdge: (connection: Connection) => void;
39
  getEdge: (id: string) => Edge | undefined;
40
  deletePreviousEdgeOfClassificationNode: (connection: Connection) => void;
 
45
  deleteEdgeBySourceAndSourceHandle: (connection: Partial<Connection>) => void;
46
  findNodeByName: (operatorName: Operator) => Node | undefined;
47
  updateMutableNodeFormItem: (id: string, field: string, value: any) => void;
48
+ getOperatorTypeFromId: (id?: string | null) => string | undefined;
49
  };
50
 
51
  // this is our useStore hook that we can use in our components to get parts of the store and call actions
 
87
  addNode: (node: Node) => {
88
  set({ nodes: get().nodes.concat(node) });
89
  },
90
+ getNode: (id?: string | null) => {
91
  return get().nodes.find((x) => x.id === id);
92
  },
93
+ getOperatorTypeFromId: (id?: string | null) => {
94
+ return get().getNode(id)?.data?.label;
95
+ },
96
  addEdge: (connection: Connection) => {
97
  set({
98
  edges: addEdge(connection, get().edges),
 
104
  },
105
  deletePreviousEdgeOfClassificationNode: (connection: Connection) => {
106
  // Delete the edge on the classification node anchor when the anchor is connected to other nodes
107
+ const { edges, getOperatorTypeFromId } = get();
108
  if (getOperatorTypeFromId(connection.source) === Operator.Categorize) {
109
  const previousEdge = edges.find(
110
  (x) =>
web/src/pages/flow/utils.test.ts CHANGED
@@ -1,3 +1,6 @@
 
 
 
1
  import { dsl } from './mock';
2
  import { buildNodesAndEdgesFromDSLComponents } from './utils';
3
 
@@ -28,3 +31,19 @@ test('buildNodesAndEdgesFromDSLComponents', () => {
28
  ]),
29
  );
30
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import headhunter_zh from '../../../../graph/test/dsl_examples/headhunter_zh.json';
4
  import { dsl } from './mock';
5
  import { buildNodesAndEdgesFromDSLComponents } from './utils';
6
 
 
31
  ]),
32
  );
33
  });
34
+
35
+ test('build nodes and edges from dsl', () => {
36
+ const { edges, nodes } = buildNodesAndEdgesFromDSLComponents(
37
+ headhunter_zh.components,
38
+ );
39
+ try {
40
+ fs.writeFileSync(
41
+ path.join(__dirname, 'headhunter_zh.json'),
42
+ JSON.stringify({ edges, nodes }, null, 4),
43
+ );
44
+ console.log('JSON data is saved.');
45
+ } catch (error) {
46
+ console.warn(error);
47
+ }
48
+ expect(nodes.length).toEqual(12);
49
+ });
web/src/pages/flow/utils.ts CHANGED
@@ -5,7 +5,7 @@ import { curry, isEmpty } from 'lodash';
5
  import pipe from 'lodash/fp/pipe';
6
  import { Edge, MarkerType, Node, Position } from 'reactflow';
7
  import { v4 as uuidv4 } from 'uuid';
8
- import { Operator, initialFormValuesMap } from './constant';
9
  import { NodeData } from './interface';
10
 
11
  const buildEdges = (
@@ -41,7 +41,7 @@ export const buildNodesAndEdgesFromDSLComponents = (data: DSLComponents) => {
41
  const upstream = [...value.upstream];
42
  nodes.push({
43
  id: key,
44
- type: 'ragNode',
45
  position: { x: 0, y: 0 },
46
  data: {
47
  label: value.obj.component_name,
@@ -162,7 +162,3 @@ export const buildDslComponentsByGraph = (
162
 
163
  return components;
164
  };
165
-
166
- export const getOperatorTypeFromId = (id: string | null) => {
167
- return id?.split(':')[0] as Operator | undefined;
168
- };
 
5
  import pipe from 'lodash/fp/pipe';
6
  import { Edge, MarkerType, Node, Position } from 'reactflow';
7
  import { v4 as uuidv4 } from 'uuid';
8
+ import { NodeMap, Operator, initialFormValuesMap } from './constant';
9
  import { NodeData } from './interface';
10
 
11
  const buildEdges = (
 
41
  const upstream = [...value.upstream];
42
  nodes.push({
43
  id: key,
44
+ type: NodeMap[value.obj.component_name as Operator] || 'ragNode',
45
  position: { x: 0, y: 0 },
46
  data: {
47
  label: value.obj.component_name,
 
162
 
163
  return components;
164
  };