balibabu commited on
Commit
be99f83
·
1 Parent(s): 92cae19

feat: add custom edge (#1061)

Browse files

### What problem does this PR solve?
feat: add custom edge
feat: add flow card
feat: add store for canvas
#918

### Type of change

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

Files changed (42) hide show
  1. web/package-lock.json +87 -203
  2. web/package.json +4 -1
  3. web/src/app.tsx +16 -11
  4. web/src/components/knowledge-base-item.tsx +37 -0
  5. web/src/components/llm-setting-items/index.less +6 -0
  6. web/src/components/llm-setting-items/index.tsx +259 -0
  7. web/src/components/top-n-item.tsx +23 -0
  8. web/src/hooks/flow-hooks.ts +70 -0
  9. web/src/hooks/userSettingHook.ts +8 -4
  10. web/src/interfaces/database/flow.ts +4 -4
  11. web/src/locales/en.ts +1 -0
  12. web/src/pages/chat/chat-configuration-modal/assistant-setting.tsx +2 -24
  13. web/src/pages/chat/chat-configuration-modal/model-setting.tsx +9 -15
  14. web/src/pages/chat/chat-configuration-modal/prompt-engine.tsx +2 -15
  15. web/src/pages/flow/answer-form/index.tsx +5 -0
  16. web/src/pages/flow/begin-form/index.tsx +47 -0
  17. web/src/pages/flow/canvas/context-menu/index.tsx +1 -1
  18. web/src/pages/flow/canvas/edge/index.less +15 -0
  19. web/src/pages/flow/canvas/edge/index.tsx +72 -0
  20. web/src/pages/flow/canvas/index.less +4 -0
  21. web/src/pages/flow/canvas/index.tsx +42 -41
  22. web/src/pages/flow/canvas/node/index.less +10 -1
  23. web/src/pages/flow/canvas/node/index.tsx +15 -3
  24. web/src/pages/flow/constant.ts +6 -0
  25. web/src/pages/flow/flow-drawer/index.tsx +32 -4
  26. web/src/pages/flow/generate-form/index.tsx +83 -0
  27. web/src/pages/flow/hooks.ts +63 -50
  28. web/src/pages/flow/index.tsx +4 -1
  29. web/src/pages/flow/interface.ts +58 -0
  30. web/src/pages/flow/list/flow-card/index.less +78 -0
  31. web/src/pages/flow/list/flow-card/index.tsx +94 -0
  32. web/src/pages/flow/list/hooks.ts +48 -0
  33. web/src/pages/flow/list/index.less +48 -0
  34. web/src/pages/flow/list/index.tsx +53 -0
  35. web/src/pages/flow/mock.tsx +15 -8
  36. web/src/pages/flow/retrieval-form/index.tsx +43 -0
  37. web/src/pages/flow/store.ts +106 -0
  38. web/src/pages/flow/utils.ts +33 -0
  39. web/src/routes.ts +4 -0
  40. web/src/services/flow-service.ts +43 -0
  41. web/src/utils/api.ts +8 -0
  42. web/src/utils/registerServer.ts +1 -1
web/package-lock.json CHANGED
@@ -10,6 +10,7 @@
10
  "@ant-design/pro-components": "^2.6.46",
11
  "@ant-design/pro-layout": "^7.17.16",
12
  "@js-preview/excel": "^1.7.8",
 
13
  "ahooks": "^3.7.10",
14
  "antd": "^5.12.7",
15
  "axios": "^1.6.3",
@@ -39,10 +40,12 @@
39
  "umi": "^4.0.90",
40
  "umi-request": "^1.4.0",
41
  "unist-util-visit-parents": "^6.0.1",
42
- "uuid": "^9.0.1"
 
43
  },
44
  "devDependencies": {
45
  "@react-dev-inspector/umi4-plugin": "^2.0.1",
 
46
  "@testing-library/jest-dom": "^6.4.5",
47
  "@testing-library/react": "^15.0.7",
48
  "@types/dagre": "^0.7.52",
@@ -3915,40 +3918,6 @@
3915
  "react-dom": ">=17"
3916
  }
3917
  },
3918
- "node_modules/@reactflow/background/node_modules/immer": {
3919
- "version": "10.1.1",
3920
- "resolved": "https://registry.npmmirror.com/immer/-/immer-10.1.1.tgz",
3921
- "integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==",
3922
- "optional": true,
3923
- "peer": true
3924
- },
3925
- "node_modules/@reactflow/background/node_modules/zustand": {
3926
- "version": "4.5.2",
3927
- "resolved": "https://registry.npmmirror.com/zustand/-/zustand-4.5.2.tgz",
3928
- "integrity": "sha512-2cN1tPkDVkwCy5ickKrI7vijSjPksFRfqS6237NzT0vqSsztTNnQdHw9mmN7uBdk3gceVXU0a+21jFzFzAc9+g==",
3929
- "dependencies": {
3930
- "use-sync-external-store": "1.2.0"
3931
- },
3932
- "engines": {
3933
- "node": ">=12.7.0"
3934
- },
3935
- "peerDependencies": {
3936
- "@types/react": ">=16.8",
3937
- "immer": ">=9.0.6",
3938
- "react": ">=16.8"
3939
- },
3940
- "peerDependenciesMeta": {
3941
- "@types/react": {
3942
- "optional": true
3943
- },
3944
- "immer": {
3945
- "optional": true
3946
- },
3947
- "react": {
3948
- "optional": true
3949
- }
3950
- }
3951
- },
3952
  "node_modules/@reactflow/controls": {
3953
  "version": "11.2.12",
3954
  "resolved": "https://registry.npmmirror.com/@reactflow/controls/-/controls-11.2.12.tgz",
@@ -3963,40 +3932,6 @@
3963
  "react-dom": ">=17"
3964
  }
3965
  },
3966
- "node_modules/@reactflow/controls/node_modules/immer": {
3967
- "version": "10.1.1",
3968
- "resolved": "https://registry.npmmirror.com/immer/-/immer-10.1.1.tgz",
3969
- "integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==",
3970
- "optional": true,
3971
- "peer": true
3972
- },
3973
- "node_modules/@reactflow/controls/node_modules/zustand": {
3974
- "version": "4.5.2",
3975
- "resolved": "https://registry.npmmirror.com/zustand/-/zustand-4.5.2.tgz",
3976
- "integrity": "sha512-2cN1tPkDVkwCy5ickKrI7vijSjPksFRfqS6237NzT0vqSsztTNnQdHw9mmN7uBdk3gceVXU0a+21jFzFzAc9+g==",
3977
- "dependencies": {
3978
- "use-sync-external-store": "1.2.0"
3979
- },
3980
- "engines": {
3981
- "node": ">=12.7.0"
3982
- },
3983
- "peerDependencies": {
3984
- "@types/react": ">=16.8",
3985
- "immer": ">=9.0.6",
3986
- "react": ">=16.8"
3987
- },
3988
- "peerDependenciesMeta": {
3989
- "@types/react": {
3990
- "optional": true
3991
- },
3992
- "immer": {
3993
- "optional": true
3994
- },
3995
- "react": {
3996
- "optional": true
3997
- }
3998
- }
3999
- },
4000
  "node_modules/@reactflow/core": {
4001
  "version": "11.11.2",
4002
  "resolved": "https://registry.npmmirror.com/@reactflow/core/-/core-11.11.2.tgz",
@@ -4017,40 +3952,6 @@
4017
  "react-dom": ">=17"
4018
  }
4019
  },
4020
- "node_modules/@reactflow/core/node_modules/immer": {
4021
- "version": "10.1.1",
4022
- "resolved": "https://registry.npmmirror.com/immer/-/immer-10.1.1.tgz",
4023
- "integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==",
4024
- "optional": true,
4025
- "peer": true
4026
- },
4027
- "node_modules/@reactflow/core/node_modules/zustand": {
4028
- "version": "4.5.2",
4029
- "resolved": "https://registry.npmmirror.com/zustand/-/zustand-4.5.2.tgz",
4030
- "integrity": "sha512-2cN1tPkDVkwCy5ickKrI7vijSjPksFRfqS6237NzT0vqSsztTNnQdHw9mmN7uBdk3gceVXU0a+21jFzFzAc9+g==",
4031
- "dependencies": {
4032
- "use-sync-external-store": "1.2.0"
4033
- },
4034
- "engines": {
4035
- "node": ">=12.7.0"
4036
- },
4037
- "peerDependencies": {
4038
- "@types/react": ">=16.8",
4039
- "immer": ">=9.0.6",
4040
- "react": ">=16.8"
4041
- },
4042
- "peerDependenciesMeta": {
4043
- "@types/react": {
4044
- "optional": true
4045
- },
4046
- "immer": {
4047
- "optional": true
4048
- },
4049
- "react": {
4050
- "optional": true
4051
- }
4052
- }
4053
- },
4054
  "node_modules/@reactflow/minimap": {
4055
  "version": "11.7.12",
4056
  "resolved": "https://registry.npmmirror.com/@reactflow/minimap/-/minimap-11.7.12.tgz",
@@ -4069,40 +3970,6 @@
4069
  "react-dom": ">=17"
4070
  }
4071
  },
4072
- "node_modules/@reactflow/minimap/node_modules/immer": {
4073
- "version": "10.1.1",
4074
- "resolved": "https://registry.npmmirror.com/immer/-/immer-10.1.1.tgz",
4075
- "integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==",
4076
- "optional": true,
4077
- "peer": true
4078
- },
4079
- "node_modules/@reactflow/minimap/node_modules/zustand": {
4080
- "version": "4.5.2",
4081
- "resolved": "https://registry.npmmirror.com/zustand/-/zustand-4.5.2.tgz",
4082
- "integrity": "sha512-2cN1tPkDVkwCy5ickKrI7vijSjPksFRfqS6237NzT0vqSsztTNnQdHw9mmN7uBdk3gceVXU0a+21jFzFzAc9+g==",
4083
- "dependencies": {
4084
- "use-sync-external-store": "1.2.0"
4085
- },
4086
- "engines": {
4087
- "node": ">=12.7.0"
4088
- },
4089
- "peerDependencies": {
4090
- "@types/react": ">=16.8",
4091
- "immer": ">=9.0.6",
4092
- "react": ">=16.8"
4093
- },
4094
- "peerDependenciesMeta": {
4095
- "@types/react": {
4096
- "optional": true
4097
- },
4098
- "immer": {
4099
- "optional": true
4100
- },
4101
- "react": {
4102
- "optional": true
4103
- }
4104
- }
4105
- },
4106
  "node_modules/@reactflow/node-resizer": {
4107
  "version": "2.2.12",
4108
  "resolved": "https://registry.npmmirror.com/@reactflow/node-resizer/-/node-resizer-2.2.12.tgz",
@@ -4119,40 +3986,6 @@
4119
  "react-dom": ">=17"
4120
  }
4121
  },
4122
- "node_modules/@reactflow/node-resizer/node_modules/immer": {
4123
- "version": "10.1.1",
4124
- "resolved": "https://registry.npmmirror.com/immer/-/immer-10.1.1.tgz",
4125
- "integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==",
4126
- "optional": true,
4127
- "peer": true
4128
- },
4129
- "node_modules/@reactflow/node-resizer/node_modules/zustand": {
4130
- "version": "4.5.2",
4131
- "resolved": "https://registry.npmmirror.com/zustand/-/zustand-4.5.2.tgz",
4132
- "integrity": "sha512-2cN1tPkDVkwCy5ickKrI7vijSjPksFRfqS6237NzT0vqSsztTNnQdHw9mmN7uBdk3gceVXU0a+21jFzFzAc9+g==",
4133
- "dependencies": {
4134
- "use-sync-external-store": "1.2.0"
4135
- },
4136
- "engines": {
4137
- "node": ">=12.7.0"
4138
- },
4139
- "peerDependencies": {
4140
- "@types/react": ">=16.8",
4141
- "immer": ">=9.0.6",
4142
- "react": ">=16.8"
4143
- },
4144
- "peerDependenciesMeta": {
4145
- "@types/react": {
4146
- "optional": true
4147
- },
4148
- "immer": {
4149
- "optional": true
4150
- },
4151
- "react": {
4152
- "optional": true
4153
- }
4154
- }
4155
- },
4156
  "node_modules/@reactflow/node-toolbar": {
4157
  "version": "1.3.12",
4158
  "resolved": "https://registry.npmmirror.com/@reactflow/node-toolbar/-/node-toolbar-1.3.12.tgz",
@@ -4167,38 +4000,17 @@
4167
  "react-dom": ">=17"
4168
  }
4169
  },
4170
- "node_modules/@reactflow/node-toolbar/node_modules/immer": {
4171
- "version": "10.1.1",
4172
- "resolved": "https://registry.npmmirror.com/immer/-/immer-10.1.1.tgz",
4173
- "integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==",
4174
- "optional": true,
4175
- "peer": true
4176
- },
4177
- "node_modules/@reactflow/node-toolbar/node_modules/zustand": {
4178
- "version": "4.5.2",
4179
- "resolved": "https://registry.npmmirror.com/zustand/-/zustand-4.5.2.tgz",
4180
- "integrity": "sha512-2cN1tPkDVkwCy5ickKrI7vijSjPksFRfqS6237NzT0vqSsztTNnQdHw9mmN7uBdk3gceVXU0a+21jFzFzAc9+g==",
4181
  "dependencies": {
4182
- "use-sync-external-store": "1.2.0"
4183
- },
4184
- "engines": {
4185
- "node": ">=12.7.0"
4186
  },
4187
  "peerDependencies": {
4188
- "@types/react": ">=16.8",
4189
- "immer": ">=9.0.6",
4190
- "react": ">=16.8"
4191
- },
4192
- "peerDependenciesMeta": {
4193
- "@types/react": {
4194
- "optional": true
4195
- },
4196
- "immer": {
4197
- "optional": true
4198
- },
4199
- "react": {
4200
- "optional": true
4201
- }
4202
  }
4203
  },
4204
  "node_modules/@rgrove/parse-xml": {
@@ -4441,6 +4253,30 @@
4441
  "integrity": "sha512-DJSilV5+ytBP1FbFcEJovv4rnnm/CokuVvrBEtW/Va9DvuJ3HksbXUJEpI0aV1KtuL4ZoO9AVE6PyNLzF7tLeA==",
4442
  "dev": true
4443
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4444
  "node_modules/@testing-library/dom": {
4445
  "version": "10.1.0",
4446
  "resolved": "https://registry.npmmirror.com/@testing-library/dom/-/dom-10.1.0.tgz",
@@ -6692,6 +6528,16 @@
6692
  "value-equal": "^1.0.1"
6693
  }
6694
  },
 
 
 
 
 
 
 
 
 
 
6695
  "node_modules/@umijs/plugins/node_modules/isarray": {
6696
  "version": "0.0.1",
6697
  "resolved": "https://registry.npmmirror.com/isarray/-/isarray-0.0.1.tgz",
@@ -13621,9 +13467,20 @@
13621
  "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
13622
  },
13623
  "node_modules/immer": {
13624
- "version": "8.0.4",
13625
- "resolved": "https://registry.npmmirror.com/immer/-/immer-8.0.4.tgz",
13626
- "integrity": "sha512-jMfL18P+/6P6epANRvRk6q8t+3gGhqsJ9EuJ25AXE+9bNTYtssvzeYbEd0mXRYWCmmXSIbnlpz6vd6iJlmGGGQ==",
 
 
 
 
 
 
 
 
 
 
 
13627
  "dev": true
13628
  },
13629
  "node_modules/import-fresh": {
@@ -26064,6 +25921,33 @@
26064
  "node": ">=10"
26065
  }
26066
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26067
  "node_modules/zwitch": {
26068
  "version": "2.0.4",
26069
  "resolved": "https://registry.npmmirror.com/zwitch/-/zwitch-2.0.4.tgz",
 
10
  "@ant-design/pro-components": "^2.6.46",
11
  "@ant-design/pro-layout": "^7.17.16",
12
  "@js-preview/excel": "^1.7.8",
13
+ "@tanstack/react-query": "^5.40.0",
14
  "ahooks": "^3.7.10",
15
  "antd": "^5.12.7",
16
  "axios": "^1.6.3",
 
40
  "umi": "^4.0.90",
41
  "umi-request": "^1.4.0",
42
  "unist-util-visit-parents": "^6.0.1",
43
+ "uuid": "^9.0.1",
44
+ "zustand": "^4.5.2"
45
  },
46
  "devDependencies": {
47
  "@react-dev-inspector/umi4-plugin": "^2.0.1",
48
+ "@redux-devtools/extension": "^3.3.0",
49
  "@testing-library/jest-dom": "^6.4.5",
50
  "@testing-library/react": "^15.0.7",
51
  "@types/dagre": "^0.7.52",
 
3918
  "react-dom": ">=17"
3919
  }
3920
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3921
  "node_modules/@reactflow/controls": {
3922
  "version": "11.2.12",
3923
  "resolved": "https://registry.npmmirror.com/@reactflow/controls/-/controls-11.2.12.tgz",
 
3932
  "react-dom": ">=17"
3933
  }
3934
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3935
  "node_modules/@reactflow/core": {
3936
  "version": "11.11.2",
3937
  "resolved": "https://registry.npmmirror.com/@reactflow/core/-/core-11.11.2.tgz",
 
3952
  "react-dom": ">=17"
3953
  }
3954
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3955
  "node_modules/@reactflow/minimap": {
3956
  "version": "11.7.12",
3957
  "resolved": "https://registry.npmmirror.com/@reactflow/minimap/-/minimap-11.7.12.tgz",
 
3970
  "react-dom": ">=17"
3971
  }
3972
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3973
  "node_modules/@reactflow/node-resizer": {
3974
  "version": "2.2.12",
3975
  "resolved": "https://registry.npmmirror.com/@reactflow/node-resizer/-/node-resizer-2.2.12.tgz",
 
3986
  "react-dom": ">=17"
3987
  }
3988
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3989
  "node_modules/@reactflow/node-toolbar": {
3990
  "version": "1.3.12",
3991
  "resolved": "https://registry.npmmirror.com/@reactflow/node-toolbar/-/node-toolbar-1.3.12.tgz",
 
4000
  "react-dom": ">=17"
4001
  }
4002
  },
4003
+ "node_modules/@redux-devtools/extension": {
4004
+ "version": "3.3.0",
4005
+ "resolved": "https://registry.npmmirror.com/@redux-devtools/extension/-/extension-3.3.0.tgz",
4006
+ "integrity": "sha512-X34S/rC8S/M1BIrkYD1mJ5f8vlH0BDqxXrs96cvxSBo4FhMdbhU+GUGsmNYov1xjSyLMHgo8NYrUG8bNX7525g==",
4007
+ "dev": true,
 
 
 
 
 
 
4008
  "dependencies": {
4009
+ "@babel/runtime": "^7.23.2",
4010
+ "immutable": "^4.3.4"
 
 
4011
  },
4012
  "peerDependencies": {
4013
+ "redux": "^3.1.0 || ^4.0.0 || ^5.0.0"
 
 
 
 
 
 
 
 
 
 
 
 
 
4014
  }
4015
  },
4016
  "node_modules/@rgrove/parse-xml": {
 
4253
  "integrity": "sha512-DJSilV5+ytBP1FbFcEJovv4rnnm/CokuVvrBEtW/Va9DvuJ3HksbXUJEpI0aV1KtuL4ZoO9AVE6PyNLzF7tLeA==",
4254
  "dev": true
4255
  },
4256
+ "node_modules/@tanstack/react-query": {
4257
+ "version": "5.40.0",
4258
+ "resolved": "https://registry.npmmirror.com/@tanstack/react-query/-/react-query-5.40.0.tgz",
4259
+ "integrity": "sha512-iv/W0Axc4aXhFzkrByToE1JQqayxTPNotCoSCnarR/A1vDIHaoKpg7FTIfP3Ev2mbKn1yrxq0ZKYUdLEJxs6Tg==",
4260
+ "dependencies": {
4261
+ "@tanstack/query-core": "5.40.0"
4262
+ },
4263
+ "funding": {
4264
+ "type": "github",
4265
+ "url": "https://github.com/sponsors/tannerlinsley"
4266
+ },
4267
+ "peerDependencies": {
4268
+ "react": "^18.0.0"
4269
+ }
4270
+ },
4271
+ "node_modules/@tanstack/react-query/node_modules/@tanstack/query-core": {
4272
+ "version": "5.40.0",
4273
+ "resolved": "https://registry.npmmirror.com/@tanstack/query-core/-/query-core-5.40.0.tgz",
4274
+ "integrity": "sha512-eD8K8jsOIq0Z5u/QbvOmfvKKE/XC39jA7yv4hgpl/1SRiU+J8QCIwgM/mEHuunQsL87dcvnHqSVLmf9pD4CiaA==",
4275
+ "funding": {
4276
+ "type": "github",
4277
+ "url": "https://github.com/sponsors/tannerlinsley"
4278
+ }
4279
+ },
4280
  "node_modules/@testing-library/dom": {
4281
  "version": "10.1.0",
4282
  "resolved": "https://registry.npmmirror.com/@testing-library/dom/-/dom-10.1.0.tgz",
 
6528
  "value-equal": "^1.0.1"
6529
  }
6530
  },
6531
+ "node_modules/@umijs/plugins/node_modules/immer": {
6532
+ "version": "8.0.4",
6533
+ "resolved": "https://registry.npmmirror.com/immer/-/immer-8.0.4.tgz",
6534
+ "integrity": "sha512-jMfL18P+/6P6epANRvRk6q8t+3gGhqsJ9EuJ25AXE+9bNTYtssvzeYbEd0mXRYWCmmXSIbnlpz6vd6iJlmGGGQ==",
6535
+ "dev": true,
6536
+ "funding": {
6537
+ "type": "opencollective",
6538
+ "url": "https://opencollective.com/immer"
6539
+ }
6540
+ },
6541
  "node_modules/@umijs/plugins/node_modules/isarray": {
6542
  "version": "0.0.1",
6543
  "resolved": "https://registry.npmmirror.com/isarray/-/isarray-0.0.1.tgz",
 
13467
  "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
13468
  },
13469
  "node_modules/immer": {
13470
+ "version": "10.1.1",
13471
+ "resolved": "https://registry.npmmirror.com/immer/-/immer-10.1.1.tgz",
13472
+ "integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==",
13473
+ "optional": true,
13474
+ "peer": true,
13475
+ "funding": {
13476
+ "type": "opencollective",
13477
+ "url": "https://opencollective.com/immer"
13478
+ }
13479
+ },
13480
+ "node_modules/immutable": {
13481
+ "version": "4.3.6",
13482
+ "resolved": "https://registry.npmmirror.com/immutable/-/immutable-4.3.6.tgz",
13483
+ "integrity": "sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==",
13484
  "dev": true
13485
  },
13486
  "node_modules/import-fresh": {
 
25921
  "node": ">=10"
25922
  }
25923
  },
25924
+ "node_modules/zustand": {
25925
+ "version": "4.5.2",
25926
+ "resolved": "https://registry.npmmirror.com/zustand/-/zustand-4.5.2.tgz",
25927
+ "integrity": "sha512-2cN1tPkDVkwCy5ickKrI7vijSjPksFRfqS6237NzT0vqSsztTNnQdHw9mmN7uBdk3gceVXU0a+21jFzFzAc9+g==",
25928
+ "dependencies": {
25929
+ "use-sync-external-store": "1.2.0"
25930
+ },
25931
+ "engines": {
25932
+ "node": ">=12.7.0"
25933
+ },
25934
+ "peerDependencies": {
25935
+ "@types/react": ">=16.8",
25936
+ "immer": ">=9.0.6",
25937
+ "react": ">=16.8"
25938
+ },
25939
+ "peerDependenciesMeta": {
25940
+ "@types/react": {
25941
+ "optional": true
25942
+ },
25943
+ "immer": {
25944
+ "optional": true
25945
+ },
25946
+ "react": {
25947
+ "optional": true
25948
+ }
25949
+ }
25950
+ },
25951
  "node_modules/zwitch": {
25952
  "version": "2.0.4",
25953
  "resolved": "https://registry.npmmirror.com/zwitch/-/zwitch-2.0.4.tgz",
web/package.json CHANGED
@@ -15,6 +15,7 @@
15
  "@ant-design/pro-components": "^2.6.46",
16
  "@ant-design/pro-layout": "^7.17.16",
17
  "@js-preview/excel": "^1.7.8",
 
18
  "ahooks": "^3.7.10",
19
  "antd": "^5.12.7",
20
  "axios": "^1.6.3",
@@ -44,10 +45,12 @@
44
  "umi": "^4.0.90",
45
  "umi-request": "^1.4.0",
46
  "unist-util-visit-parents": "^6.0.1",
47
- "uuid": "^9.0.1"
 
48
  },
49
  "devDependencies": {
50
  "@react-dev-inspector/umi4-plugin": "^2.0.1",
 
51
  "@testing-library/jest-dom": "^6.4.5",
52
  "@testing-library/react": "^15.0.7",
53
  "@types/dagre": "^0.7.52",
 
15
  "@ant-design/pro-components": "^2.6.46",
16
  "@ant-design/pro-layout": "^7.17.16",
17
  "@js-preview/excel": "^1.7.8",
18
+ "@tanstack/react-query": "^5.40.0",
19
  "ahooks": "^3.7.10",
20
  "antd": "^5.12.7",
21
  "axios": "^1.6.3",
 
45
  "umi": "^4.0.90",
46
  "umi-request": "^1.4.0",
47
  "unist-util-visit-parents": "^6.0.1",
48
+ "uuid": "^9.0.1",
49
+ "zustand": "^4.5.2"
50
  },
51
  "devDependencies": {
52
  "@react-dev-inspector/umi4-plugin": "^2.0.1",
53
+ "@redux-devtools/extension": "^3.3.0",
54
  "@testing-library/jest-dom": "^6.4.5",
55
  "@testing-library/react": "^15.0.7",
56
  "@types/dagre": "^0.7.52",
web/src/app.tsx CHANGED
@@ -6,13 +6,14 @@ import zh_HK from 'antd/locale/zh_HK';
6
  import React, { ReactNode, useEffect, useState } from 'react';
7
  import storage from './utils/authorizationUtil';
8
 
 
9
  import dayjs from 'dayjs';
10
  import advancedFormat from 'dayjs/plugin/advancedFormat';
11
  import customParseFormat from 'dayjs/plugin/customParseFormat';
12
  import localeData from 'dayjs/plugin/localeData';
13
- import weekday from 'dayjs/plugin/weekday';
14
  import weekOfYear from 'dayjs/plugin/weekOfYear';
15
  import weekYear from 'dayjs/plugin/weekYear';
 
16
 
17
  dayjs.extend(customParseFormat);
18
  dayjs.extend(advancedFormat);
@@ -27,6 +28,8 @@ const AntLanguageMap = {
27
  'zh-TRADITIONAL': zh_HK,
28
  };
29
 
 
 
30
  type Locale = ConfigProviderProps['locale'];
31
 
32
  const RootProvider = ({ children }: React.PropsWithChildren) => {
@@ -49,16 +52,18 @@ const RootProvider = ({ children }: React.PropsWithChildren) => {
49
  }, []);
50
 
51
  return (
52
- <ConfigProvider
53
- theme={{
54
- token: {
55
- fontFamily: 'Inter',
56
- },
57
- }}
58
- locale={locale}
59
- >
60
- <App> {children}</App>
61
- </ConfigProvider>
 
 
62
  );
63
  };
64
 
 
6
  import React, { ReactNode, useEffect, useState } from 'react';
7
  import storage from './utils/authorizationUtil';
8
 
9
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
10
  import dayjs from 'dayjs';
11
  import advancedFormat from 'dayjs/plugin/advancedFormat';
12
  import customParseFormat from 'dayjs/plugin/customParseFormat';
13
  import localeData from 'dayjs/plugin/localeData';
 
14
  import weekOfYear from 'dayjs/plugin/weekOfYear';
15
  import weekYear from 'dayjs/plugin/weekYear';
16
+ import weekday from 'dayjs/plugin/weekday';
17
 
18
  dayjs.extend(customParseFormat);
19
  dayjs.extend(advancedFormat);
 
28
  'zh-TRADITIONAL': zh_HK,
29
  };
30
 
31
+ const queryClient = new QueryClient();
32
+
33
  type Locale = ConfigProviderProps['locale'];
34
 
35
  const RootProvider = ({ children }: React.PropsWithChildren) => {
 
52
  }, []);
53
 
54
  return (
55
+ <QueryClientProvider client={queryClient}>
56
+ <ConfigProvider
57
+ theme={{
58
+ token: {
59
+ fontFamily: 'Inter',
60
+ },
61
+ }}
62
+ locale={locale}
63
+ >
64
+ <App> {children}</App>
65
+ </ConfigProvider>
66
+ </QueryClientProvider>
67
  );
68
  };
69
 
web/src/components/knowledge-base-item.tsx ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useTranslate } from '@/hooks/commonHooks';
2
+ import { useFetchKnowledgeList } from '@/hooks/knowledgeHook';
3
+ import { Form, Select } from 'antd';
4
+
5
+ const KnowledgeBaseItem = () => {
6
+ const { t } = useTranslate('chat');
7
+
8
+ const { list: knowledgeList } = useFetchKnowledgeList(true);
9
+
10
+ const knowledgeOptions = knowledgeList.map((x) => ({
11
+ label: x.name,
12
+ value: x.id,
13
+ }));
14
+
15
+ return (
16
+ <Form.Item
17
+ label={t('knowledgeBases')}
18
+ name="kb_ids"
19
+ tooltip={t('knowledgeBasesTip')}
20
+ rules={[
21
+ {
22
+ required: true,
23
+ message: t('knowledgeBasesMessage'),
24
+ type: 'array',
25
+ },
26
+ ]}
27
+ >
28
+ <Select
29
+ mode="multiple"
30
+ options={knowledgeOptions}
31
+ placeholder={t('knowledgeBasesMessage')}
32
+ ></Select>
33
+ </Form.Item>
34
+ );
35
+ };
36
+
37
+ export default KnowledgeBaseItem;
web/src/components/llm-setting-items/index.less ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ .sliderInputNumber {
2
+ width: 80px;
3
+ }
4
+ .variableSlider {
5
+ width: 100%;
6
+ }
web/src/components/llm-setting-items/index.tsx ADDED
@@ -0,0 +1,259 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { LlmModelType, ModelVariableType } from '@/constants/knowledge';
2
+ import { Divider, Flex, Form, InputNumber, Select, Slider, Switch } from 'antd';
3
+ import camelCase from 'lodash/camelCase';
4
+
5
+ import { useTranslate } from '@/hooks/commonHooks';
6
+ import { useSelectLlmOptionsByModelType } from '@/hooks/llmHooks';
7
+ import { useMemo } from 'react';
8
+ import styles from './index.less';
9
+
10
+ interface IProps {
11
+ prefix?: string;
12
+ handleParametersChange(value: ModelVariableType): void;
13
+ }
14
+
15
+ const LlmSettingItems = ({ prefix, handleParametersChange }: IProps) => {
16
+ const { t } = useTranslate('chat');
17
+ const parameterOptions = Object.values(ModelVariableType).map((x) => ({
18
+ label: t(camelCase(x)),
19
+ value: x,
20
+ }));
21
+
22
+ const memorizedPrefix = useMemo(() => (prefix ? [prefix] : []), [prefix]);
23
+
24
+ const modelOptions = useSelectLlmOptionsByModelType();
25
+
26
+ return (
27
+ <>
28
+ <Form.Item
29
+ label={t('model')}
30
+ name="llm_id"
31
+ tooltip={t('modelTip')}
32
+ rules={[{ required: true, message: t('modelMessage') }]}
33
+ >
34
+ <Select options={modelOptions[LlmModelType.Chat]} showSearch />
35
+ </Form.Item>
36
+ <Divider></Divider>
37
+ <Form.Item
38
+ label={t('freedom')}
39
+ name="parameters"
40
+ tooltip={t('freedomTip')}
41
+ initialValue={ModelVariableType.Precise}
42
+ >
43
+ <Select<ModelVariableType>
44
+ options={parameterOptions}
45
+ onChange={handleParametersChange}
46
+ />
47
+ </Form.Item>
48
+ <Form.Item label={t('temperature')} tooltip={t('temperatureTip')}>
49
+ <Flex gap={20} align="center">
50
+ <Form.Item
51
+ name={'temperatureEnabled'}
52
+ valuePropName="checked"
53
+ noStyle
54
+ >
55
+ <Switch size="small" />
56
+ </Form.Item>
57
+ <Form.Item noStyle dependencies={['temperatureEnabled']}>
58
+ {({ getFieldValue }) => {
59
+ const disabled = !getFieldValue('temperatureEnabled');
60
+ return (
61
+ <>
62
+ <Flex flex={1}>
63
+ <Form.Item
64
+ name={[...memorizedPrefix, 'temperature']}
65
+ noStyle
66
+ >
67
+ <Slider
68
+ className={styles.variableSlider}
69
+ max={1}
70
+ step={0.01}
71
+ disabled={disabled}
72
+ />
73
+ </Form.Item>
74
+ </Flex>
75
+ <Form.Item name={[...memorizedPrefix, 'temperature']} noStyle>
76
+ <InputNumber
77
+ className={styles.sliderInputNumber}
78
+ max={1}
79
+ min={0}
80
+ step={0.01}
81
+ disabled={disabled}
82
+ />
83
+ </Form.Item>
84
+ </>
85
+ );
86
+ }}
87
+ </Form.Item>
88
+ </Flex>
89
+ </Form.Item>
90
+ <Form.Item label={t('topP')} tooltip={t('topPTip')}>
91
+ <Flex gap={20} align="center">
92
+ <Form.Item name={'topPEnabled'} valuePropName="checked" noStyle>
93
+ <Switch size="small" />
94
+ </Form.Item>
95
+ <Form.Item noStyle dependencies={['topPEnabled']}>
96
+ {({ getFieldValue }) => {
97
+ const disabled = !getFieldValue('topPEnabled');
98
+ return (
99
+ <>
100
+ <Flex flex={1}>
101
+ <Form.Item name={[...memorizedPrefix, 'top_p']} noStyle>
102
+ <Slider
103
+ className={styles.variableSlider}
104
+ max={1}
105
+ step={0.01}
106
+ disabled={disabled}
107
+ />
108
+ </Form.Item>
109
+ </Flex>
110
+ <Form.Item name={[...memorizedPrefix, 'top_p']} noStyle>
111
+ <InputNumber
112
+ className={styles.sliderInputNumber}
113
+ max={1}
114
+ min={0}
115
+ step={0.01}
116
+ disabled={disabled}
117
+ />
118
+ </Form.Item>
119
+ </>
120
+ );
121
+ }}
122
+ </Form.Item>
123
+ </Flex>
124
+ </Form.Item>
125
+ <Form.Item label={t('presencePenalty')} tooltip={t('presencePenaltyTip')}>
126
+ <Flex gap={20} align="center">
127
+ <Form.Item
128
+ name={'presencePenaltyEnabled'}
129
+ valuePropName="checked"
130
+ noStyle
131
+ >
132
+ <Switch size="small" />
133
+ </Form.Item>
134
+ <Form.Item noStyle dependencies={['presencePenaltyEnabled']}>
135
+ {({ getFieldValue }) => {
136
+ const disabled = !getFieldValue('presencePenaltyEnabled');
137
+ return (
138
+ <>
139
+ <Flex flex={1}>
140
+ <Form.Item
141
+ name={[...memorizedPrefix, 'presence_penalty']}
142
+ noStyle
143
+ >
144
+ <Slider
145
+ className={styles.variableSlider}
146
+ max={1}
147
+ step={0.01}
148
+ disabled={disabled}
149
+ />
150
+ </Form.Item>
151
+ </Flex>
152
+ <Form.Item
153
+ name={[...memorizedPrefix, 'presence_penalty']}
154
+ noStyle
155
+ >
156
+ <InputNumber
157
+ className={styles.sliderInputNumber}
158
+ max={1}
159
+ min={0}
160
+ step={0.01}
161
+ disabled={disabled}
162
+ />
163
+ </Form.Item>
164
+ </>
165
+ );
166
+ }}
167
+ </Form.Item>
168
+ </Flex>
169
+ </Form.Item>
170
+ <Form.Item
171
+ label={t('frequencyPenalty')}
172
+ tooltip={t('frequencyPenaltyTip')}
173
+ >
174
+ <Flex gap={20} align="center">
175
+ <Form.Item
176
+ name={'frequencyPenaltyEnabled'}
177
+ valuePropName="checked"
178
+ noStyle
179
+ >
180
+ <Switch size="small" />
181
+ </Form.Item>
182
+ <Form.Item noStyle dependencies={['frequencyPenaltyEnabled']}>
183
+ {({ getFieldValue }) => {
184
+ const disabled = !getFieldValue('frequencyPenaltyEnabled');
185
+ return (
186
+ <>
187
+ <Flex flex={1}>
188
+ <Form.Item
189
+ name={[...memorizedPrefix, 'frequency_penalty']}
190
+ noStyle
191
+ >
192
+ <Slider
193
+ className={styles.variableSlider}
194
+ max={1}
195
+ step={0.01}
196
+ disabled={disabled}
197
+ />
198
+ </Form.Item>
199
+ </Flex>
200
+ <Form.Item
201
+ name={[...memorizedPrefix, 'frequency_penalty']}
202
+ noStyle
203
+ >
204
+ <InputNumber
205
+ className={styles.sliderInputNumber}
206
+ max={1}
207
+ min={0}
208
+ step={0.01}
209
+ disabled={disabled}
210
+ />
211
+ </Form.Item>
212
+ </>
213
+ );
214
+ }}
215
+ </Form.Item>
216
+ </Flex>
217
+ </Form.Item>
218
+ <Form.Item label={t('maxTokens')} tooltip={t('maxTokensTip')}>
219
+ <Flex gap={20} align="center">
220
+ <Form.Item name={'maxTokensEnabled'} valuePropName="checked" noStyle>
221
+ <Switch size="small" />
222
+ </Form.Item>
223
+ <Form.Item noStyle dependencies={['maxTokensEnabled']}>
224
+ {({ getFieldValue }) => {
225
+ const disabled = !getFieldValue('maxTokensEnabled');
226
+
227
+ return (
228
+ <>
229
+ <Flex flex={1}>
230
+ <Form.Item
231
+ name={[...memorizedPrefix, 'max_tokens']}
232
+ noStyle
233
+ >
234
+ <Slider
235
+ className={styles.variableSlider}
236
+ max={2048}
237
+ disabled={disabled}
238
+ />
239
+ </Form.Item>
240
+ </Flex>
241
+ <Form.Item name={[...memorizedPrefix, 'max_tokens']} noStyle>
242
+ <InputNumber
243
+ disabled={disabled}
244
+ className={styles.sliderInputNumber}
245
+ max={2048}
246
+ min={0}
247
+ />
248
+ </Form.Item>
249
+ </>
250
+ );
251
+ }}
252
+ </Form.Item>
253
+ </Flex>
254
+ </Form.Item>
255
+ </>
256
+ );
257
+ };
258
+
259
+ export default LlmSettingItems;
web/src/components/top-n-item.tsx ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useTranslate } from '@/hooks/commonHooks';
2
+ import { Form, Slider } from 'antd';
3
+
4
+ type FieldType = {
5
+ top_n?: number;
6
+ };
7
+
8
+ const TopNItem = () => {
9
+ const { t } = useTranslate('chat');
10
+
11
+ return (
12
+ <Form.Item<FieldType>
13
+ label={t('topN')}
14
+ name={'top_n'}
15
+ initialValue={8}
16
+ tooltip={t('topNTip')}
17
+ >
18
+ <Slider max={30} />
19
+ </Form.Item>
20
+ );
21
+ };
22
+
23
+ export default TopNItem;
web/src/hooks/flow-hooks.ts ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import flowService from '@/services/flow-service';
2
+ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
3
+
4
+ export const useFetchFlowTemplates = () => {
5
+ const { data } = useQuery({
6
+ queryKey: ['fetchFlowTemplates'],
7
+ initialData: [],
8
+ queryFn: async () => {
9
+ const { data } = await flowService.listTemplates();
10
+
11
+ return data;
12
+ },
13
+ });
14
+
15
+ return data;
16
+ };
17
+
18
+ export const useFetchFlowList = () => {
19
+ const { data, isFetching: loading } = useQuery({
20
+ queryKey: ['fetchFlowList'],
21
+ initialData: [],
22
+ queryFn: async () => {
23
+ const { data } = await flowService.listCanvas();
24
+
25
+ return data?.data ?? [];
26
+ },
27
+ });
28
+
29
+ return { data, loading };
30
+ };
31
+
32
+ export const useSetFlow = () => {
33
+ const queryClient = useQueryClient();
34
+ const {
35
+ data,
36
+ isPending: loading,
37
+ mutateAsync,
38
+ } = useMutation({
39
+ mutationKey: ['setFlow'],
40
+ mutationFn: async (params: any) => {
41
+ const { data } = await flowService.setCanvas(params);
42
+ if (data.retcode === 0) {
43
+ queryClient.invalidateQueries({ queryKey: ['fetchFlowList'] });
44
+ }
45
+ return data?.retcode;
46
+ },
47
+ });
48
+
49
+ return { data, loading, setFlow: mutateAsync };
50
+ };
51
+
52
+ export const useDeleteFlow = () => {
53
+ const queryClient = useQueryClient();
54
+ const {
55
+ data,
56
+ isPending: loading,
57
+ mutateAsync,
58
+ } = useMutation({
59
+ mutationKey: ['deleteFlow'],
60
+ mutationFn: async (canvasIds: string[]) => {
61
+ const { data } = await flowService.removeCanvas({ canvasIds });
62
+ if (data.retcode === 0) {
63
+ queryClient.invalidateQueries({ queryKey: ['fetchFlowList'] });
64
+ }
65
+ return data?.data ?? [];
66
+ },
67
+ });
68
+
69
+ return { data, loading, deleteFlow: mutateAsync };
70
+ };
web/src/hooks/userSettingHook.ts CHANGED
@@ -99,10 +99,14 @@ export const useFetchSystemVersion = () => {
99
  const [loading, setLoading] = useState(false);
100
 
101
  const fetchSystemVersion = useCallback(async () => {
102
- setLoading(true);
103
- const { data } = await userService.getSystemVersion();
104
- if (data.retcode === 0) {
105
- setVersion(data.data);
 
 
 
 
106
  setLoading(false);
107
  }
108
  }, []);
 
99
  const [loading, setLoading] = useState(false);
100
 
101
  const fetchSystemVersion = useCallback(async () => {
102
+ try {
103
+ setLoading(true);
104
+ const { data } = await userService.getSystemVersion();
105
+ if (data.retcode === 0) {
106
+ setVersion(data.data);
107
+ setLoading(false);
108
+ }
109
+ } catch (error) {
110
  setLoading(false);
111
  }
112
  }, []);
web/src/interfaces/database/flow.ts CHANGED
@@ -1,4 +1,4 @@
1
- export type DSLComponents = Record<string, Operator>;
2
 
3
  export interface DSL {
4
  components: DSLComponents;
@@ -7,13 +7,13 @@ export interface DSL {
7
  answer: any[];
8
  }
9
 
10
- export interface Operator {
11
- obj: OperatorNode;
12
  downstream: string[];
13
  upstream: string[];
14
  }
15
 
16
- export interface OperatorNode {
17
  component_name: string;
18
  params: Record<string, unknown>;
19
  }
 
1
+ export type DSLComponents = Record<string, IOperator>;
2
 
3
  export interface DSL {
4
  components: DSLComponents;
 
7
  answer: any[];
8
  }
9
 
10
+ export interface IOperator {
11
+ obj: IOperatorNode;
12
  downstream: string[];
13
  upstream: string[];
14
  }
15
 
16
+ export interface IOperatorNode {
17
  component_name: string;
18
  params: Record<string, unknown>;
19
  }
web/src/locales/en.ts CHANGED
@@ -541,6 +541,7 @@ The above is the content you need to summarize.`,
541
  preview: 'Preview',
542
  fileError: 'File error',
543
  },
 
544
  footer: {
545
  profile: 'All rights reserved @ React',
546
  },
 
541
  preview: 'Preview',
542
  fileError: 'File error',
543
  },
544
+ flow: { cite: 'Cite', citeTip: 'citeTip' },
545
  footer: {
546
  profile: 'All rights reserved @ React',
547
  },
web/src/pages/chat/chat-configuration-modal/assistant-setting.tsx CHANGED
@@ -1,18 +1,13 @@
1
- import { useFetchKnowledgeList } from '@/hooks/knowledgeHook';
2
  import { PlusOutlined } from '@ant-design/icons';
3
  import { Form, Input, Select, Switch, Upload } from 'antd';
4
  import classNames from 'classnames';
5
  import { ISegmentedContentProps } from '../interface';
6
 
 
7
  import { useTranslate } from '@/hooks/commonHooks';
8
  import styles from './index.less';
9
 
10
  const AssistantSetting = ({ show }: ISegmentedContentProps) => {
11
- const { list: knowledgeList } = useFetchKnowledgeList(true);
12
- const knowledgeOptions = knowledgeList.map((x) => ({
13
- label: x.name,
14
- value: x.id,
15
- }));
16
  const { t } = useTranslate('chat');
17
 
18
  const normFile = (e: any) => {
@@ -95,24 +90,7 @@ const AssistantSetting = ({ show }: ISegmentedContentProps) => {
95
  >
96
  <Switch />
97
  </Form.Item>
98
- <Form.Item
99
- label={t('knowledgeBases')}
100
- name="kb_ids"
101
- tooltip={t('knowledgeBasesTip')}
102
- rules={[
103
- {
104
- required: true,
105
- message: t('knowledgeBasesMessage'),
106
- type: 'array',
107
- },
108
- ]}
109
- >
110
- <Select
111
- mode="multiple"
112
- options={knowledgeOptions}
113
- placeholder={t('knowledgeBasesMessage')}
114
- ></Select>
115
- </Form.Item>
116
  </section>
117
  );
118
  };
 
 
1
  import { PlusOutlined } from '@ant-design/icons';
2
  import { Form, Input, Select, Switch, Upload } from 'antd';
3
  import classNames from 'classnames';
4
  import { ISegmentedContentProps } from '../interface';
5
 
6
+ import KnowledgeBaseItem from '@/components/knowledge-base-item';
7
  import { useTranslate } from '@/hooks/commonHooks';
8
  import styles from './index.less';
9
 
10
  const AssistantSetting = ({ show }: ISegmentedContentProps) => {
 
 
 
 
 
11
  const { t } = useTranslate('chat');
12
 
13
  const normFile = (e: any) => {
 
90
  >
91
  <Switch />
92
  </Form.Item>
93
+ <KnowledgeBaseItem></KnowledgeBaseItem>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  </section>
95
  );
96
  };
web/src/pages/chat/chat-configuration-modal/model-setting.tsx CHANGED
@@ -1,16 +1,12 @@
1
  import {
2
- LlmModelType,
3
  ModelVariableType,
4
  settledModelVariableMap,
5
  } from '@/constants/knowledge';
6
- import { Divider, Flex, Form, InputNumber, Select, Slider, Switch } from 'antd';
7
  import classNames from 'classnames';
8
- import camelCase from 'lodash/camelCase';
9
  import { useEffect } from 'react';
10
  import { ISegmentedContentProps } from '../interface';
11
 
12
- import { useTranslate } from '@/hooks/commonHooks';
13
- import { useSelectLlmOptionsByModelType } from '@/hooks/llmHooks';
14
  import { Variable } from '@/interfaces/database/chat';
15
  import { variableEnabledFieldMap } from '../constants';
16
  import styles from './index.less';
@@ -24,14 +20,6 @@ const ModelSetting = ({
24
  initialLlmSetting?: Variable;
25
  visible?: boolean;
26
  }) => {
27
- const { t } = useTranslate('chat');
28
- const parameterOptions = Object.values(ModelVariableType).map((x) => ({
29
- label: t(camelCase(x)),
30
- value: x,
31
- }));
32
-
33
- const modelOptions = useSelectLlmOptionsByModelType();
34
-
35
  const handleParametersChange = (value: ModelVariableType) => {
36
  const variable = settledModelVariableMap[value];
37
  form.setFieldsValue({ llm_setting: variable });
@@ -62,7 +50,13 @@ const ModelSetting = ({
62
  [styles.segmentedHidden]: !show,
63
  })}
64
  >
65
- <Form.Item
 
 
 
 
 
 
66
  label={t('model')}
67
  name="llm_id"
68
  tooltip={t('modelTip')}
@@ -279,7 +273,7 @@ const ModelSetting = ({
279
  }}
280
  </Form.Item>
281
  </Flex>
282
- </Form.Item>
283
  </section>
284
  );
285
  };
 
1
  import {
 
2
  ModelVariableType,
3
  settledModelVariableMap,
4
  } from '@/constants/knowledge';
 
5
  import classNames from 'classnames';
 
6
  import { useEffect } from 'react';
7
  import { ISegmentedContentProps } from '../interface';
8
 
9
+ import LlmSettingItems from '@/components/llm-setting-items';
 
10
  import { Variable } from '@/interfaces/database/chat';
11
  import { variableEnabledFieldMap } from '../constants';
12
  import styles from './index.less';
 
20
  initialLlmSetting?: Variable;
21
  visible?: boolean;
22
  }) => {
 
 
 
 
 
 
 
 
23
  const handleParametersChange = (value: ModelVariableType) => {
24
  const variable = settledModelVariableMap[value];
25
  form.setFieldsValue({ llm_setting: variable });
 
50
  [styles.segmentedHidden]: !show,
51
  })}
52
  >
53
+ {visible && (
54
+ <LlmSettingItems
55
+ prefix="llm_setting"
56
+ handleParametersChange={handleParametersChange}
57
+ ></LlmSettingItems>
58
+ )}
59
+ {/* <Form.Item
60
  label={t('model')}
61
  name="llm_id"
62
  tooltip={t('modelTip')}
 
273
  }}
274
  </Form.Item>
275
  </Flex>
276
+ </Form.Item> */}
277
  </section>
278
  );
279
  };
web/src/pages/chat/chat-configuration-modal/prompt-engine.tsx CHANGED
@@ -7,7 +7,6 @@ import {
7
  Form,
8
  Input,
9
  Row,
10
- Slider,
11
  Switch,
12
  Table,
13
  TableProps,
@@ -30,16 +29,11 @@ import {
30
  import { EditableCell, EditableRow } from './editable-cell';
31
 
32
  import Rerank from '@/components/rerank';
 
33
  import { useTranslate } from '@/hooks/commonHooks';
34
  import { useSelectPromptConfigParameters } from '../hooks';
35
  import styles from './index.less';
36
 
37
- type FieldType = {
38
- similarity_threshold?: number;
39
- vector_similarity_weight?: number;
40
- top_n?: number;
41
- };
42
-
43
  const PromptEngine = (
44
  { show }: ISegmentedContentProps,
45
  ref: ForwardedRef<Array<IPromptConfigParameters>>,
@@ -165,14 +159,7 @@ const PromptEngine = (
165
  </Form.Item>
166
  <Divider></Divider>
167
  <SimilaritySlider isTooltipShown></SimilaritySlider>
168
- <Form.Item<FieldType>
169
- label={t('topN')}
170
- name={'top_n'}
171
- initialValue={8}
172
- tooltip={t('topNTip')}
173
- >
174
- <Slider max={30} />
175
- </Form.Item>
176
  <Rerank></Rerank>
177
  <section className={classNames(styles.variableContainer)}>
178
  <Row align={'middle'} justify="end">
 
7
  Form,
8
  Input,
9
  Row,
 
10
  Switch,
11
  Table,
12
  TableProps,
 
29
  import { EditableCell, EditableRow } from './editable-cell';
30
 
31
  import Rerank from '@/components/rerank';
32
+ import TopNItem from '@/components/top-n-item';
33
  import { useTranslate } from '@/hooks/commonHooks';
34
  import { useSelectPromptConfigParameters } from '../hooks';
35
  import styles from './index.less';
36
 
 
 
 
 
 
 
37
  const PromptEngine = (
38
  { show }: ISegmentedContentProps,
39
  ref: ForwardedRef<Array<IPromptConfigParameters>>,
 
159
  </Form.Item>
160
  <Divider></Divider>
161
  <SimilaritySlider isTooltipShown></SimilaritySlider>
162
+ <TopNItem></TopNItem>
 
 
 
 
 
 
 
163
  <Rerank></Rerank>
164
  <section className={classNames(styles.variableContainer)}>
165
  <Row align={'middle'} justify="end">
web/src/pages/flow/answer-form/index.tsx ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ const AnswerForm = () => {
2
+ return <div>AnswerForm</div>;
3
+ };
4
+
5
+ export default AnswerForm;
web/src/pages/flow/begin-form/index.tsx ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useTranslate } from '@/hooks/commonHooks';
2
+ import type { FormProps } from 'antd';
3
+ import { Form, Input } from 'antd';
4
+ import { IOperatorForm } from '../interface';
5
+
6
+ type FieldType = {
7
+ prologue?: string;
8
+ };
9
+
10
+ const onFinish: FormProps<FieldType>['onFinish'] = (values) => {
11
+ console.log('Success:', values);
12
+ };
13
+
14
+ const onFinishFailed: FormProps<FieldType>['onFinishFailed'] = (errorInfo) => {
15
+ console.log('Failed:', errorInfo);
16
+ };
17
+
18
+ const BeginForm = ({ onValuesChange }: IOperatorForm) => {
19
+ const { t } = useTranslate('chat');
20
+ const [form] = Form.useForm();
21
+
22
+ return (
23
+ <Form
24
+ name="basic"
25
+ labelCol={{ span: 8 }}
26
+ wrapperCol={{ span: 16 }}
27
+ style={{ maxWidth: 600 }}
28
+ initialValues={{ remember: true }}
29
+ onFinish={onFinish}
30
+ onFinishFailed={onFinishFailed}
31
+ onValuesChange={onValuesChange}
32
+ autoComplete="off"
33
+ form={form}
34
+ >
35
+ <Form.Item<FieldType>
36
+ name={'prologue'}
37
+ label={t('setAnOpener')}
38
+ tooltip={t('setAnOpenerTip')}
39
+ initialValue={t('setAnOpenerInitial')}
40
+ >
41
+ <Input.TextArea autoSize={{ minRows: 5 }} />
42
+ </Form.Item>
43
+ </Form>
44
+ );
45
+ };
46
+
47
+ export default BeginForm;
web/src/pages/flow/canvas/context-menu/index.tsx CHANGED
@@ -86,7 +86,7 @@ export const useHandleNodeContextMenu = (sideWidth: number) => {
86
 
87
  setMenu({
88
  id: node.id,
89
- top: event.clientY - 72,
90
  left: event.clientX - sideWidth,
91
  // top: event.clientY < pane.height - 200 ? event.clientY - 72 : 0,
92
  // left: event.clientX < pane.width - 200 ? event.clientX : 0,
 
86
 
87
  setMenu({
88
  id: node.id,
89
+ top: event.clientY - 144,
90
  left: event.clientX - sideWidth,
91
  // top: event.clientY < pane.height - 200 ? event.clientY - 72 : 0,
92
  // left: event.clientX < pane.width - 200 ? event.clientX : 0,
web/src/pages/flow/canvas/edge/index.less ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .edgeButton {
2
+ width: 14px;
3
+ height: 14px;
4
+ background: #eee;
5
+ border: 1px solid #fff;
6
+ padding: 0;
7
+ cursor: pointer;
8
+ border-radius: 50%;
9
+ font-size: 10px;
10
+ line-height: 1;
11
+ }
12
+
13
+ .edgeButton:hover {
14
+ box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.08);
15
+ }
web/src/pages/flow/canvas/edge/index.tsx ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {
2
+ BaseEdge,
3
+ EdgeLabelRenderer,
4
+ EdgeProps,
5
+ getBezierPath,
6
+ } from 'reactflow';
7
+ import useStore from '../../store';
8
+
9
+ import { useMemo } from 'react';
10
+ import styles from './index.less';
11
+
12
+ export function ButtonEdge({
13
+ id,
14
+ sourceX,
15
+ sourceY,
16
+ targetX,
17
+ targetY,
18
+ sourcePosition,
19
+ targetPosition,
20
+ style = {},
21
+ markerEnd,
22
+ selected,
23
+ }: EdgeProps) {
24
+ const deleteEdgeById = useStore((state) => state.deleteEdgeById);
25
+ const [edgePath, labelX, labelY] = getBezierPath({
26
+ sourceX,
27
+ sourceY,
28
+ sourcePosition,
29
+ targetX,
30
+ targetY,
31
+ targetPosition,
32
+ });
33
+
34
+ const selectedStyle = useMemo(() => {
35
+ return selected ? { strokeWidth: 1, stroke: '#1677ff' } : {};
36
+ }, [selected]);
37
+
38
+ const onEdgeClick = () => {
39
+ deleteEdgeById(id);
40
+ };
41
+
42
+ return (
43
+ <>
44
+ <BaseEdge
45
+ path={edgePath}
46
+ markerEnd={markerEnd}
47
+ style={{ ...style, ...selectedStyle }}
48
+ />
49
+ <EdgeLabelRenderer>
50
+ <div
51
+ style={{
52
+ position: 'absolute',
53
+ transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,
54
+ fontSize: 12,
55
+ // everything inside EdgeLabelRenderer has no pointer events by default
56
+ // if you have an interactive element, set pointer-events: all
57
+ pointerEvents: 'all',
58
+ }}
59
+ className="nodrag nopan"
60
+ >
61
+ <button
62
+ className={styles.edgeButton}
63
+ type="button"
64
+ onClick={onEdgeClick}
65
+ >
66
+ ×
67
+ </button>
68
+ </div>
69
+ </EdgeLabelRenderer>
70
+ </>
71
+ );
72
+ }
web/src/pages/flow/canvas/index.less ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ .canvasWrapper {
2
+ position: relative;
3
+ height: 100%;
4
+ }
web/src/pages/flow/canvas/index.tsx CHANGED
@@ -1,76 +1,64 @@
1
- import { useCallback, useEffect, useState } from 'react';
2
  import ReactFlow, {
3
  Background,
4
  Controls,
5
- Edge,
6
- Node,
7
  NodeMouseHandler,
8
- OnConnect,
9
- OnEdgesChange,
10
- OnNodesChange,
11
- addEdge,
12
- applyEdgeChanges,
13
- applyNodeChanges,
14
  } from 'reactflow';
15
  import 'reactflow/dist/style.css';
16
 
17
  import { NodeContextMenu, useHandleNodeContextMenu } from './context-menu';
 
18
 
19
  import FlowDrawer from '../flow-drawer';
20
  import {
21
  useHandleDrop,
22
  useHandleKeyUp,
23
- useHandleSelectionChange,
24
  useShowDrawer,
25
  } from '../hooks';
26
- import { dsl } from '../mock';
27
  import { TextUpdaterNode } from './node';
28
 
 
 
29
  const nodeTypes = { textUpdater: TextUpdaterNode };
30
 
 
 
 
 
31
  interface IProps {
32
  sideWidth: number;
33
  }
34
 
35
  function FlowCanvas({ sideWidth }: IProps) {
36
- const [nodes, setNodes] = useState<Node[]>(dsl.graph.nodes);
37
- const [edges, setEdges] = useState<Edge[]>(dsl.graph.edges);
38
-
39
- const { selectedEdges, selectedNodes } = useHandleSelectionChange();
 
 
 
 
40
 
41
  const { ref, menu, onNodeContextMenu, onPaneClick } =
42
  useHandleNodeContextMenu(sideWidth);
43
- const { drawerVisible, hideDrawer, showDrawer } = useShowDrawer();
44
-
45
- const onNodesChange: OnNodesChange = useCallback(
46
- (changes) => setNodes((nds) => applyNodeChanges(changes, nds)),
47
- [],
48
- );
49
- const onEdgesChange: OnEdgesChange = useCallback(
50
- (changes) => setEdges((eds) => applyEdgeChanges(changes, eds)),
51
- [],
52
- );
53
 
54
- const onConnect: OnConnect = useCallback(
55
- (params) => setEdges((eds) => addEdge(params, eds)),
56
- [],
 
 
57
  );
58
 
59
- const onNodeClick: NodeMouseHandler = useCallback(() => {
60
- showDrawer();
61
- }, [showDrawer]);
62
-
63
- const { onDrop, onDragOver, setReactFlowInstance } = useHandleDrop(setNodes);
64
-
65
- const { handleKeyUp } = useHandleKeyUp(selectedEdges, selectedNodes);
66
 
67
- useEffect(() => {
68
- console.info('nodes:', nodes);
69
- console.info('edges:', edges);
70
- }, [nodes, edges]);
71
 
72
  return (
73
- <div style={{ height: '100%', width: '100%' }}>
74
  <ReactFlow
75
  ref={ref}
76
  nodes={nodes}
@@ -81,12 +69,21 @@ function FlowCanvas({ sideWidth }: IProps) {
81
  fitView
82
  onConnect={onConnect}
83
  nodeTypes={nodeTypes}
 
84
  onPaneClick={onPaneClick}
85
  onDrop={onDrop}
86
  onDragOver={onDragOver}
87
  onNodeClick={onNodeClick}
88
  onInit={setReactFlowInstance}
89
  onKeyUp={handleKeyUp}
 
 
 
 
 
 
 
 
90
  >
91
  <Background />
92
  <Controls />
@@ -94,7 +91,11 @@ function FlowCanvas({ sideWidth }: IProps) {
94
  <NodeContextMenu onClick={onPaneClick} {...(menu as any)} />
95
  )}
96
  </ReactFlow>
97
- <FlowDrawer visible={drawerVisible} hideModal={hideDrawer}></FlowDrawer>
 
 
 
 
98
  </div>
99
  );
100
  }
 
1
+ import { useCallback } from 'react';
2
  import ReactFlow, {
3
  Background,
4
  Controls,
5
+ MarkerType,
 
6
  NodeMouseHandler,
 
 
 
 
 
 
7
  } from 'reactflow';
8
  import 'reactflow/dist/style.css';
9
 
10
  import { NodeContextMenu, useHandleNodeContextMenu } from './context-menu';
11
+ import { ButtonEdge } from './edge';
12
 
13
  import FlowDrawer from '../flow-drawer';
14
  import {
15
  useHandleDrop,
16
  useHandleKeyUp,
17
+ useSelectCanvasData,
18
  useShowDrawer,
19
  } from '../hooks';
 
20
  import { TextUpdaterNode } from './node';
21
 
22
+ import styles from './index.less';
23
+
24
  const nodeTypes = { textUpdater: TextUpdaterNode };
25
 
26
+ const edgeTypes = {
27
+ buttonEdge: ButtonEdge,
28
+ };
29
+
30
  interface IProps {
31
  sideWidth: number;
32
  }
33
 
34
  function FlowCanvas({ sideWidth }: IProps) {
35
+ const {
36
+ nodes,
37
+ edges,
38
+ onConnect,
39
+ onEdgesChange,
40
+ onNodesChange,
41
+ onSelectionChange,
42
+ } = useSelectCanvasData();
43
 
44
  const { ref, menu, onNodeContextMenu, onPaneClick } =
45
  useHandleNodeContextMenu(sideWidth);
46
+ const { drawerVisible, hideDrawer, showDrawer, clickedNode } =
47
+ useShowDrawer();
 
 
 
 
 
 
 
 
48
 
49
+ const onNodeClick: NodeMouseHandler = useCallback(
50
+ (e, node) => {
51
+ showDrawer(node);
52
+ },
53
+ [showDrawer],
54
  );
55
 
56
+ const { onDrop, onDragOver, setReactFlowInstance } = useHandleDrop();
 
 
 
 
 
 
57
 
58
+ const { handleKeyUp } = useHandleKeyUp();
 
 
 
59
 
60
  return (
61
+ <div className={styles.canvasWrapper}>
62
  <ReactFlow
63
  ref={ref}
64
  nodes={nodes}
 
69
  fitView
70
  onConnect={onConnect}
71
  nodeTypes={nodeTypes}
72
+ edgeTypes={edgeTypes}
73
  onPaneClick={onPaneClick}
74
  onDrop={onDrop}
75
  onDragOver={onDragOver}
76
  onNodeClick={onNodeClick}
77
  onInit={setReactFlowInstance}
78
  onKeyUp={handleKeyUp}
79
+ onSelectionChange={onSelectionChange}
80
+ nodeOrigin={[0.5, 0]}
81
+ defaultEdgeOptions={{
82
+ type: 'buttonEdge',
83
+ markerEnd: {
84
+ type: MarkerType.ArrowClosed,
85
+ },
86
+ }}
87
  >
88
  <Background />
89
  <Controls />
 
91
  <NodeContextMenu onClick={onPaneClick} {...(menu as any)} />
92
  )}
93
  </ReactFlow>
94
+ <FlowDrawer
95
+ node={clickedNode}
96
+ visible={drawerVisible}
97
+ hideModal={hideDrawer}
98
+ ></FlowDrawer>
99
  </div>
100
  );
101
  }
web/src/pages/flow/canvas/node/index.less CHANGED
@@ -1,6 +1,6 @@
1
  .textUpdaterNode {
2
  // height: 50px;
3
- border: 1px solid black;
4
  padding: 5px;
5
  border-radius: 5px;
6
  background: white;
@@ -10,3 +10,12 @@
10
  font-size: 12px;
11
  }
12
  }
 
 
 
 
 
 
 
 
 
 
1
  .textUpdaterNode {
2
  // height: 50px;
3
+ border: 1px solid gray;
4
  padding: 5px;
5
  border-radius: 5px;
6
  background: white;
 
10
  font-size: 12px;
11
  }
12
  }
13
+ .selectedNode {
14
+ border-color: #1677ff;
15
+ }
16
+
17
+ .handle {
18
+ display: inline-flex;
19
+ text-align: center;
20
+ // align-items: center;
21
+ }
web/src/pages/flow/canvas/node/index.tsx CHANGED
@@ -1,3 +1,4 @@
 
1
  import { Handle, NodeProps, Position } from 'reactflow';
2
 
3
  import styles from './index.less';
@@ -5,19 +6,30 @@ import styles from './index.less';
5
  export function TextUpdaterNode({
6
  data,
7
  isConnectable = true,
 
8
  }: NodeProps<{ label: string }>) {
9
  return (
10
- <div className={styles.textUpdaterNode}>
 
 
 
 
11
  <Handle
12
  type="target"
13
  position={Position.Left}
14
  isConnectable={isConnectable}
15
- />
 
 
 
16
  <Handle
17
  type="source"
18
  position={Position.Right}
19
  isConnectable={isConnectable}
20
- />
 
 
 
21
  <div>{data.label}</div>
22
  </div>
23
  );
 
1
+ import classNames from 'classnames';
2
  import { Handle, NodeProps, Position } from 'reactflow';
3
 
4
  import styles from './index.less';
 
6
  export function TextUpdaterNode({
7
  data,
8
  isConnectable = true,
9
+ selected,
10
  }: NodeProps<{ label: string }>) {
11
  return (
12
+ <div
13
+ className={classNames(styles.textUpdaterNode, {
14
+ [styles.selectedNode]: selected,
15
+ })}
16
+ >
17
  <Handle
18
  type="target"
19
  position={Position.Left}
20
  isConnectable={isConnectable}
21
+ className={styles.handle}
22
+ >
23
+ {/* <PlusCircleOutlined style={{ fontSize: 10 }} /> */}
24
+ </Handle>
25
  <Handle
26
  type="source"
27
  position={Position.Right}
28
  isConnectable={isConnectable}
29
+ className={styles.handle}
30
+ >
31
+ {/* <PlusCircleOutlined style={{ fontSize: 10 }} /> */}
32
+ </Handle>
33
  <div>{data.label}</div>
34
  </div>
35
  );
web/src/pages/flow/constant.ts ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ export enum Operator {
2
+ Begin = 'Begin',
3
+ Retrieval = 'Retrieval',
4
+ Generate = 'Generate',
5
+ Answer = 'Answer',
6
+ }
web/src/pages/flow/flow-drawer/index.tsx CHANGED
@@ -1,18 +1,46 @@
1
  import { IModalProps } from '@/interfaces/common';
2
  import { Drawer } from 'antd';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
 
4
- const FlowDrawer = ({ visible, hideModal }: IModalProps<any>) => {
5
  return (
6
  <Drawer
7
- title="Basic Drawer"
8
  placement="right"
9
- // closable={false}
10
  onClose={hideModal}
11
  open={visible}
12
  getContainer={false}
13
  mask={false}
 
14
  >
15
- <p>Some contents...</p>
 
 
16
  </Drawer>
17
  );
18
  };
 
1
  import { IModalProps } from '@/interfaces/common';
2
  import { Drawer } from 'antd';
3
+ import { Node } from 'reactflow';
4
+ import AnswerForm from '../answer-form';
5
+ import BeginForm from '../begin-form';
6
+ import { Operator } from '../constant';
7
+ import GenerateForm from '../generate-form';
8
+ import { useHandleFormValuesChange } from '../hooks';
9
+ import RetrievalForm from '../retrieval-form';
10
+
11
+ interface IProps {
12
+ node?: Node;
13
+ }
14
+
15
+ const FormMap = {
16
+ [Operator.Begin]: BeginForm,
17
+ [Operator.Retrieval]: RetrievalForm,
18
+ [Operator.Generate]: GenerateForm,
19
+ [Operator.Answer]: AnswerForm,
20
+ };
21
+
22
+ const FlowDrawer = ({
23
+ visible,
24
+ hideModal,
25
+ node,
26
+ }: IModalProps<any> & IProps) => {
27
+ const operatorName: Operator = node?.data.label;
28
+ const OperatorForm = FormMap[operatorName];
29
+ const { handleValuesChange } = useHandleFormValuesChange(node?.id);
30
 
 
31
  return (
32
  <Drawer
33
+ title={node?.data.label}
34
  placement="right"
 
35
  onClose={hideModal}
36
  open={visible}
37
  getContainer={false}
38
  mask={false}
39
+ width={470}
40
  >
41
+ {visible && (
42
+ <OperatorForm onValuesChange={handleValuesChange}></OperatorForm>
43
+ )}
44
  </Drawer>
45
  );
46
  };
web/src/pages/flow/generate-form/index.tsx ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import LlmSettingItems from '@/components/llm-setting-items';
2
+ import {
3
+ ModelVariableType,
4
+ settledModelVariableMap,
5
+ } from '@/constants/knowledge';
6
+ import { useTranslate } from '@/hooks/commonHooks';
7
+ import { Variable } from '@/interfaces/database/chat';
8
+ import { variableEnabledFieldMap } from '@/pages/chat/constants';
9
+ import { Form, Input, Switch } from 'antd';
10
+ import { useCallback, useEffect } from 'react';
11
+ import { IOperatorForm } from '../interface';
12
+
13
+ const GenerateForm = ({ onValuesChange }: IOperatorForm) => {
14
+ const { t } = useTranslate('flow');
15
+ const [form] = Form.useForm();
16
+ const initialLlmSetting = undefined;
17
+
18
+ const handleParametersChange = useCallback(
19
+ (value: ModelVariableType) => {
20
+ const variable = settledModelVariableMap[value];
21
+ form.setFieldsValue(variable);
22
+ },
23
+ [form],
24
+ );
25
+
26
+ useEffect(() => {
27
+ const switchBoxValues = Object.keys(variableEnabledFieldMap).reduce<
28
+ Record<string, boolean>
29
+ >((pre, field) => {
30
+ pre[field] =
31
+ initialLlmSetting === undefined
32
+ ? true
33
+ : !!initialLlmSetting[
34
+ variableEnabledFieldMap[
35
+ field as keyof typeof variableEnabledFieldMap
36
+ ] as keyof Variable
37
+ ];
38
+ return pre;
39
+ }, {});
40
+ const otherValues = settledModelVariableMap[ModelVariableType.Precise];
41
+ form.setFieldsValue({ ...switchBoxValues, ...otherValues });
42
+ }, [form, initialLlmSetting]);
43
+
44
+ return (
45
+ <Form
46
+ name="basic"
47
+ labelCol={{ span: 9 }}
48
+ wrapperCol={{ span: 15 }}
49
+ autoComplete="off"
50
+ form={form}
51
+ onValuesChange={onValuesChange}
52
+ >
53
+ <LlmSettingItems
54
+ handleParametersChange={handleParametersChange}
55
+ ></LlmSettingItems>
56
+ <Form.Item
57
+ name={['prompt']}
58
+ label={t('prompt', { keyPrefix: 'knowledgeConfiguration' })}
59
+ initialValue={t('promptText', { keyPrefix: 'knowledgeConfiguration' })}
60
+ tooltip={t('promptTip', { keyPrefix: 'knowledgeConfiguration' })}
61
+ rules={[
62
+ {
63
+ required: true,
64
+ message: t('promptMessage'),
65
+ },
66
+ ]}
67
+ >
68
+ <Input.TextArea rows={8} />
69
+ </Form.Item>
70
+ <Form.Item
71
+ name={['cite']}
72
+ label={t('cite')}
73
+ initialValue={true}
74
+ valuePropName="checked"
75
+ tooltip={t('citeTip')}
76
+ >
77
+ <Switch />
78
+ </Form.Item>
79
+ </Form>
80
+ );
81
+ };
82
+
83
+ export default GenerateForm;
web/src/pages/flow/hooks.ts CHANGED
@@ -1,19 +1,26 @@
1
  import { useSetModalState } from '@/hooks/commonHooks';
2
- import React, {
3
- Dispatch,
4
- KeyboardEventHandler,
5
- SetStateAction,
6
- useCallback,
7
- useState,
8
- } from 'react';
9
- import {
10
- Node,
11
- Position,
12
- ReactFlowInstance,
13
- useOnSelectionChange,
14
- useReactFlow,
15
- } from 'reactflow';
16
  import { v4 as uuidv4 } from 'uuid';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
 
18
  export const useHandleDrag = () => {
19
  const handleDragStart = useCallback(
@@ -27,7 +34,8 @@ export const useHandleDrag = () => {
27
  return { handleDragStart };
28
  };
29
 
30
- export const useHandleDrop = (setNodes: Dispatch<SetStateAction<Node[]>>) => {
 
31
  const [reactFlowInstance, setReactFlowInstance] =
32
  useState<ReactFlowInstance<any, any>>();
33
 
@@ -66,59 +74,40 @@ export const useHandleDrop = (setNodes: Dispatch<SetStateAction<Node[]>>) => {
66
  targetPosition: Position.Left,
67
  };
68
 
69
- setNodes((nds) => nds.concat(newNode));
70
  },
71
- [reactFlowInstance, setNodes],
72
  );
73
 
74
  return { onDrop, onDragOver, setReactFlowInstance };
75
  };
76
 
77
  export const useShowDrawer = () => {
 
78
  const {
79
  visible: drawerVisible,
80
  hideModal: hideDrawer,
81
  showModal: showDrawer,
82
  } = useSetModalState();
83
 
 
 
 
 
 
 
 
 
84
  return {
85
  drawerVisible,
86
  hideDrawer,
87
- showDrawer,
 
88
  };
89
  };
90
 
91
- export const useHandleSelectionChange = () => {
92
- const [selectedNodes, setSelectedNodes] = useState<string[]>([]);
93
- const [selectedEdges, setSelectedEdges] = useState<string[]>([]);
94
-
95
- useOnSelectionChange({
96
- onChange: ({ nodes, edges }) => {
97
- setSelectedNodes(nodes.map((node) => node.id));
98
- setSelectedEdges(edges.map((edge) => edge.id));
99
- },
100
- });
101
-
102
- return { selectedEdges, selectedNodes };
103
- };
104
-
105
- export const useDeleteEdge = (selectedEdges: string[]) => {
106
- const { setEdges } = useReactFlow();
107
-
108
- const deleteEdge = useCallback(() => {
109
- setEdges((edges) =>
110
- edges.filter((edge) => selectedEdges.every((x) => x !== edge.id)),
111
- );
112
- }, [setEdges, selectedEdges]);
113
-
114
- return deleteEdge;
115
- };
116
-
117
- export const useHandleKeyUp = (
118
- selectedEdges: string[],
119
- selectedNodes: string[],
120
- ) => {
121
- const deleteEdge = useDeleteEdge(selectedEdges);
122
  const handleKeyUp: KeyboardEventHandler = useCallback(
123
  (e) => {
124
  if (e.code === 'Delete') {
@@ -132,7 +121,31 @@ export const useHandleKeyUp = (
132
  };
133
 
134
  export const useSaveGraph = () => {
135
- const saveGraph = useCallback(() => {}, []);
 
 
 
 
136
 
137
  return { saveGraph };
138
  };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import { useSetModalState } from '@/hooks/commonHooks';
2
+ import { useFetchFlowTemplates } from '@/hooks/flow-hooks';
3
+ import { useFetchLlmList } from '@/hooks/llmHooks';
4
+ import React, { KeyboardEventHandler, useCallback, useState } from 'react';
5
+ import { Node, Position, ReactFlowInstance } from 'reactflow';
 
 
 
 
 
 
 
 
 
 
6
  import { v4 as uuidv4 } from 'uuid';
7
+ import useStore, { RFState } from './store';
8
+ import { buildDslComponentsByGraph } from './utils';
9
+
10
+ const selector = (state: RFState) => ({
11
+ nodes: state.nodes,
12
+ edges: state.edges,
13
+ onNodesChange: state.onNodesChange,
14
+ onEdgesChange: state.onEdgesChange,
15
+ onConnect: state.onConnect,
16
+ setNodes: state.setNodes,
17
+ onSelectionChange: state.onSelectionChange,
18
+ });
19
+
20
+ export const useSelectCanvasData = () => {
21
+ // return useStore(useShallow(selector)); throw error
22
+ return useStore(selector);
23
+ };
24
 
25
  export const useHandleDrag = () => {
26
  const handleDragStart = useCallback(
 
34
  return { handleDragStart };
35
  };
36
 
37
+ export const useHandleDrop = () => {
38
+ const addNode = useStore((state) => state.addNode);
39
  const [reactFlowInstance, setReactFlowInstance] =
40
  useState<ReactFlowInstance<any, any>>();
41
 
 
74
  targetPosition: Position.Left,
75
  };
76
 
77
+ addNode(newNode);
78
  },
79
+ [reactFlowInstance, addNode],
80
  );
81
 
82
  return { onDrop, onDragOver, setReactFlowInstance };
83
  };
84
 
85
  export const useShowDrawer = () => {
86
+ const [clickedNode, setClickedNode] = useState<Node>();
87
  const {
88
  visible: drawerVisible,
89
  hideModal: hideDrawer,
90
  showModal: showDrawer,
91
  } = useSetModalState();
92
 
93
+ const handleShow = useCallback(
94
+ (node: Node) => {
95
+ setClickedNode(node);
96
+ showDrawer();
97
+ },
98
+ [showDrawer],
99
+ );
100
+
101
  return {
102
  drawerVisible,
103
  hideDrawer,
104
+ showDrawer: handleShow,
105
+ clickedNode,
106
  };
107
  };
108
 
109
+ export const useHandleKeyUp = () => {
110
+ const deleteEdge = useStore((state) => state.deleteEdge);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
  const handleKeyUp: KeyboardEventHandler = useCallback(
112
  (e) => {
113
  if (e.code === 'Delete') {
 
121
  };
122
 
123
  export const useSaveGraph = () => {
124
+ const { nodes, edges } = useStore((state) => state);
125
+ const saveGraph = useCallback(() => {
126
+ const x = buildDslComponentsByGraph(nodes, edges);
127
+ console.info('components:', x);
128
+ }, [nodes, edges]);
129
 
130
  return { saveGraph };
131
  };
132
+
133
+ export const useHandleFormValuesChange = (id?: string) => {
134
+ const updateNodeForm = useStore((state) => state.updateNodeForm);
135
+ const handleValuesChange = useCallback(
136
+ (changedValues: any, values: any) => {
137
+ console.info(changedValues, values);
138
+ if (id) {
139
+ updateNodeForm(id, values);
140
+ }
141
+ },
142
+ [updateNodeForm, id],
143
+ );
144
+
145
+ return { handleValuesChange };
146
+ };
147
+
148
+ export const useFetchDataOnMount = () => {
149
+ useFetchFlowTemplates();
150
+ useFetchLlmList();
151
+ };
web/src/pages/flow/index.tsx CHANGED
@@ -4,19 +4,22 @@ import { ReactFlowProvider } from 'reactflow';
4
  import FlowCanvas from './canvas';
5
  import Sider from './flow-sider';
6
  import FlowHeader from './header';
 
7
 
8
  const { Content } = Layout;
9
 
10
  function RagFlow() {
11
  const [collapsed, setCollapsed] = useState(false);
12
 
 
 
13
  return (
14
  <Layout>
15
  <ReactFlowProvider>
16
  <Sider setCollapsed={setCollapsed} collapsed={collapsed}></Sider>
17
  <Layout>
18
  <FlowHeader></FlowHeader>
19
- <Content style={{ margin: '0 16px' }}>
20
  <FlowCanvas sideWidth={collapsed ? 0 : 200}></FlowCanvas>
21
  </Content>
22
  </Layout>
 
4
  import FlowCanvas from './canvas';
5
  import Sider from './flow-sider';
6
  import FlowHeader from './header';
7
+ import { useFetchDataOnMount } from './hooks';
8
 
9
  const { Content } = Layout;
10
 
11
  function RagFlow() {
12
  const [collapsed, setCollapsed] = useState(false);
13
 
14
+ useFetchDataOnMount();
15
+
16
  return (
17
  <Layout>
18
  <ReactFlowProvider>
19
  <Sider setCollapsed={setCollapsed} collapsed={collapsed}></Sider>
20
  <Layout>
21
  <FlowHeader></FlowHeader>
22
+ <Content style={{ margin: 0 }}>
23
  <FlowCanvas sideWidth={collapsed ? 0 : 200}></FlowCanvas>
24
  </Content>
25
  </Layout>
web/src/pages/flow/interface.ts CHANGED
@@ -1,4 +1,62 @@
 
 
1
  export interface DSLComponentList {
2
  id: string;
3
  name: string;
4
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Edge, Node } from 'reactflow';
2
+
3
  export interface DSLComponentList {
4
  id: string;
5
  name: string;
6
  }
7
+
8
+ export interface IOperatorForm {
9
+ onValuesChange?(changedValues: any, values: any): void;
10
+ }
11
+
12
+ export interface IBeginForm {
13
+ prologue?: string;
14
+ }
15
+
16
+ export interface IRetrievalForm {
17
+ similarity_threshold?: number;
18
+ keywords_similarity_weight?: number;
19
+ top_n?: number;
20
+ top_k?: number;
21
+ rerank_id?: string;
22
+ empty_response?: string;
23
+ kb_ids: string[];
24
+ }
25
+
26
+ export interface IGenerateForm {
27
+ max_tokens?: number;
28
+ temperature?: number;
29
+ top_p?: number;
30
+ presence_penalty?: number;
31
+ frequency_penalty?: number;
32
+ cite?: boolean;
33
+ prompt: number;
34
+ llm_id: string;
35
+ parameters: { key: string; component_id: string };
36
+ }
37
+
38
+ export type NodeData = {
39
+ label: string;
40
+ color: string;
41
+ form: IBeginForm | IRetrievalForm | IGenerateForm;
42
+ };
43
+
44
+ export interface IFlow {
45
+ avatar: null;
46
+ canvas_type: null;
47
+ create_date: string;
48
+ create_time: number;
49
+ description: null;
50
+ dsl: {
51
+ answer: any[];
52
+ components: DSLComponentList;
53
+ graph: { nodes: Node[]; edges: Edge[] };
54
+ history: any[];
55
+ path: string[];
56
+ };
57
+ id: string;
58
+ title: string;
59
+ update_date: string;
60
+ update_time: number;
61
+ user_id: string;
62
+ }
web/src/pages/flow/list/flow-card/index.less ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .container {
2
+ height: 251px;
3
+ display: flex;
4
+ flex-direction: column;
5
+ justify-content: space-between;
6
+
7
+ .delete {
8
+ height: 24px;
9
+ }
10
+
11
+ .content {
12
+ display: flex;
13
+ justify-content: space-between;
14
+
15
+ .context {
16
+ flex: 1;
17
+ }
18
+ }
19
+
20
+ .footer {
21
+ // text-align: left;
22
+ }
23
+ .footerTop {
24
+ padding-bottom: 2px;
25
+ }
26
+ }
27
+
28
+ .card {
29
+ border-radius: 12px;
30
+ border: 1px solid rgba(234, 236, 240, 1);
31
+ box-shadow: 0px 1px 2px 0px rgba(16, 24, 40, 0.05);
32
+ padding: 24px;
33
+ width: 300px;
34
+ cursor: pointer;
35
+
36
+ .titleWrapper {
37
+ // flex: 1;
38
+ .title {
39
+ font-size: 24px;
40
+ line-height: 32px;
41
+ font-weight: 600;
42
+ color: rgba(0, 0, 0, 0.88);
43
+ word-break: break-all;
44
+ }
45
+ .description {
46
+ font-size: 12px;
47
+ font-weight: 600;
48
+ line-height: 20px;
49
+ color: rgba(0, 0, 0, 0.45);
50
+ }
51
+ }
52
+
53
+ :global {
54
+ .ant-card-body {
55
+ padding: 0;
56
+ margin: 0;
57
+ }
58
+ }
59
+ .bottom {
60
+ display: flex;
61
+ align-items: center;
62
+ justify-content: space-between;
63
+ }
64
+ .bottomLeft {
65
+ vertical-align: middle;
66
+ }
67
+ .leftIcon {
68
+ margin-right: 10px;
69
+ font-size: 18px;
70
+ vertical-align: middle;
71
+ }
72
+ .rightText {
73
+ font-size: 12px;
74
+ font-weight: 600;
75
+ color: rgba(0, 0, 0, 0.65);
76
+ vertical-align: middle;
77
+ }
78
+ }
web/src/pages/flow/list/flow-card/index.tsx ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { ReactComponent as MoreIcon } from '@/assets/svg/more.svg';
2
+ import { useShowDeleteConfirm } from '@/hooks/commonHooks';
3
+ import { formatDate } from '@/utils/date';
4
+ import {
5
+ CalendarOutlined,
6
+ DeleteOutlined,
7
+ UserOutlined,
8
+ } from '@ant-design/icons';
9
+ import { Avatar, Card, Dropdown, MenuProps, Space } from 'antd';
10
+ import { useTranslation } from 'react-i18next';
11
+ import { useNavigate } from 'umi';
12
+
13
+ import { useDeleteFlow } from '@/hooks/flow-hooks';
14
+ import { IFlow } from '../../interface';
15
+ import styles from './index.less';
16
+
17
+ interface IProps {
18
+ item: IFlow;
19
+ }
20
+
21
+ const FlowCard = ({ item }: IProps) => {
22
+ const navigate = useNavigate();
23
+ const showDeleteConfirm = useShowDeleteConfirm();
24
+ const { t } = useTranslation();
25
+ const { deleteFlow } = useDeleteFlow();
26
+
27
+ const removeKnowledge = () => {
28
+ return deleteFlow([item.id]);
29
+ };
30
+
31
+ const handleDelete = () => {
32
+ showDeleteConfirm({ onOk: removeKnowledge });
33
+ };
34
+
35
+ const items: MenuProps['items'] = [
36
+ {
37
+ key: '1',
38
+ label: (
39
+ <Space>
40
+ {t('common.delete')}
41
+ <DeleteOutlined />
42
+ </Space>
43
+ ),
44
+ },
45
+ ];
46
+
47
+ const handleDropdownMenuClick: MenuProps['onClick'] = ({ domEvent, key }) => {
48
+ domEvent.preventDefault();
49
+ domEvent.stopPropagation();
50
+ if (key === '1') {
51
+ handleDelete();
52
+ }
53
+ };
54
+
55
+ const handleCardClick = () => {
56
+ navigate(`/flow/${item.id}`);
57
+ };
58
+
59
+ return (
60
+ <Card className={styles.card} onClick={handleCardClick}>
61
+ <div className={styles.container}>
62
+ <div className={styles.content}>
63
+ <Avatar size={34} icon={<UserOutlined />} src={item.avatar} />
64
+ <Dropdown
65
+ menu={{
66
+ items,
67
+ onClick: handleDropdownMenuClick,
68
+ }}
69
+ >
70
+ <span className={styles.delete}>
71
+ <MoreIcon />
72
+ </span>
73
+ </Dropdown>
74
+ </div>
75
+ <div className={styles.titleWrapper}>
76
+ <span className={styles.title}>{item.title}</span>
77
+ <p>{item.description}</p>
78
+ </div>
79
+ <div className={styles.footer}>
80
+ <div className={styles.bottom}>
81
+ <div className={styles.bottomLeft}>
82
+ <CalendarOutlined className={styles.leftIcon} />
83
+ <span className={styles.rightText}>
84
+ {formatDate(item.update_time)}
85
+ </span>
86
+ </div>
87
+ </div>
88
+ </div>
89
+ </div>
90
+ </Card>
91
+ );
92
+ };
93
+
94
+ export default FlowCard;
web/src/pages/flow/list/hooks.ts ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useSetModalState } from '@/hooks/commonHooks';
2
+ import { useFetchFlowList, useSetFlow } from '@/hooks/flow-hooks';
3
+ import { useCallback, useState } from 'react';
4
+ import { dsl } from '../mock';
5
+
6
+ export const useFetchDataOnMount = () => {
7
+ const { data, loading } = useFetchFlowList();
8
+
9
+ return { list: data, loading };
10
+ };
11
+
12
+ export const useSaveFlow = () => {
13
+ const [currentFlow, setCurrentFlow] = useState({});
14
+ const {
15
+ visible: flowSettingVisible,
16
+ hideModal: hideFlowSettingModal,
17
+ showModal: showFileRenameModal,
18
+ } = useSetModalState();
19
+ const { loading, setFlow } = useSetFlow();
20
+
21
+ const onFlowOk = useCallback(
22
+ async (title: string) => {
23
+ const ret = await setFlow({ title, dsl });
24
+
25
+ if (ret === 0) {
26
+ hideFlowSettingModal();
27
+ }
28
+ },
29
+ [setFlow, hideFlowSettingModal],
30
+ );
31
+
32
+ const handleShowFlowSettingModal = useCallback(
33
+ async (record: any) => {
34
+ setCurrentFlow(record);
35
+ showFileRenameModal();
36
+ },
37
+ [showFileRenameModal],
38
+ );
39
+
40
+ return {
41
+ flowSettingLoading: loading,
42
+ initialFlowName: '',
43
+ onFlowOk,
44
+ flowSettingVisible,
45
+ hideFlowSettingModal,
46
+ showFlowSettingModal: handleShowFlowSettingModal,
47
+ };
48
+ };
web/src/pages/flow/list/index.less ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .flowListWrapper {
2
+ padding: 48px;
3
+ }
4
+
5
+ .topWrapper {
6
+ display: flex;
7
+ justify-content: space-between;
8
+ align-items: flex-start;
9
+ padding: 0 60px 72px;
10
+
11
+ .title {
12
+ font-family: Inter;
13
+ font-size: 30px;
14
+ font-style: normal;
15
+ font-weight: @fontWeight600;
16
+ line-height: 38px;
17
+ color: rgba(16, 24, 40, 1);
18
+ }
19
+ .description {
20
+ font-family: Inter;
21
+ font-size: 16px;
22
+ font-style: normal;
23
+ font-weight: 400;
24
+ line-height: 24px;
25
+ color: rgba(71, 84, 103, 1);
26
+ }
27
+
28
+ .topButton {
29
+ font-family: Inter;
30
+ font-size: 14px;
31
+ font-style: normal;
32
+ font-weight: @fontWeight600;
33
+ line-height: 20px;
34
+ }
35
+
36
+ .filterButton {
37
+ display: flex;
38
+ align-items: center;
39
+ .topButton();
40
+ }
41
+ }
42
+ .flowCardContainer {
43
+ padding: 0 60px;
44
+ overflow: auto;
45
+ .knowledgeEmpty {
46
+ width: 100%;
47
+ }
48
+ }
web/src/pages/flow/list/index.tsx ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import RenameModal from '@/components/rename-modal';
2
+ import { PlusOutlined } from '@ant-design/icons';
3
+ import { Button, Empty, Flex, Spin } from 'antd';
4
+ import FlowCard from './flow-card';
5
+ import { useFetchDataOnMount, useSaveFlow } from './hooks';
6
+
7
+ import styles from './index.less';
8
+
9
+ const FlowList = () => {
10
+ const {
11
+ showFlowSettingModal,
12
+ hideFlowSettingModal,
13
+ flowSettingVisible,
14
+ flowSettingLoading,
15
+ onFlowOk,
16
+ } = useSaveFlow();
17
+
18
+ const { list, loading } = useFetchDataOnMount();
19
+
20
+ return (
21
+ <Flex className={styles.flowListWrapper} vertical flex={1} gap={'large'}>
22
+ <Flex justify={'end'}>
23
+ <Button
24
+ type="primary"
25
+ icon={<PlusOutlined />}
26
+ onClick={showFlowSettingModal}
27
+ >
28
+ create canvas
29
+ </Button>
30
+ </Flex>
31
+ <Spin spinning={loading}>
32
+ <Flex gap={'large'} wrap="wrap" className={styles.flowCardContainer}>
33
+ {list.length > 0 ? (
34
+ list.map((item: any) => {
35
+ return <FlowCard item={item} key={item.name}></FlowCard>;
36
+ })
37
+ ) : (
38
+ <Empty className={styles.knowledgeEmpty}></Empty>
39
+ )}
40
+ </Flex>
41
+ </Spin>
42
+ <RenameModal
43
+ visible={flowSettingVisible}
44
+ onOk={onFlowOk}
45
+ loading={flowSettingLoading}
46
+ hideModal={hideFlowSettingModal}
47
+ initialName=""
48
+ ></RenameModal>
49
+ </Flex>
50
+ );
51
+ };
52
+
53
+ export default FlowList;
web/src/pages/flow/mock.tsx CHANGED
@@ -1,12 +1,7 @@
1
- import {
2
- MergeCellsOutlined,
3
- RocketOutlined,
4
- SendOutlined,
5
- } from '@ant-design/icons';
6
  import { Position } from 'reactflow';
7
 
8
  export const componentList = [
9
- { name: 'Begin', icon: <SendOutlined />, description: '' },
10
  { name: 'Retrieval', icon: <RocketOutlined />, description: '' },
11
  { name: 'Generate', icon: <MergeCellsOutlined />, description: '' },
12
  ];
@@ -159,7 +154,14 @@ export const dsl = {
159
  'Retrieval:China': {
160
  obj: {
161
  component_name: 'Retrieval',
162
- params: {},
 
 
 
 
 
 
 
163
  },
164
  downstream: ['Generate:China'],
165
  upstream: ['Answer:China'],
@@ -167,7 +169,12 @@ export const dsl = {
167
  'Generate:China': {
168
  obj: {
169
  component_name: 'Generate',
170
- params: {},
 
 
 
 
 
171
  },
172
  downstream: ['Answer:China'],
173
  upstream: ['Retrieval:China'],
 
1
+ import { MergeCellsOutlined, RocketOutlined } from '@ant-design/icons';
 
 
 
 
2
  import { Position } from 'reactflow';
3
 
4
  export const componentList = [
 
5
  { name: 'Retrieval', icon: <RocketOutlined />, description: '' },
6
  { name: 'Generate', icon: <MergeCellsOutlined />, description: '' },
7
  ];
 
154
  'Retrieval:China': {
155
  obj: {
156
  component_name: 'Retrieval',
157
+ params: {
158
+ similarity_threshold: 0.2,
159
+ keywords_similarity_weight: 0.3,
160
+ top_n: 6,
161
+ top_k: 1024,
162
+ rerank_id: 'BAAI/bge-reranker-v2-m3',
163
+ kb_ids: ['568aa82603b611efa9d9fa163e197198'],
164
+ },
165
  },
166
  downstream: ['Generate:China'],
167
  upstream: ['Answer:China'],
 
169
  'Generate:China': {
170
  obj: {
171
  component_name: 'Generate',
172
+ params: {
173
+ llm_id: 'deepseek-chat',
174
+ prompt:
175
+ 'You are an intelligent assistant. Please summarize the content of the knowledge base to answer the question. Please list the data in the knowledge base and answer in detail. When all knowledge base content is irrelevant to the question, your answer must include the sentence "The answer you are looking for is not found in the knowledge base!" Answers need to consider chat history.\n Here is the knowledge base:\n {input}\n The above is the knowledge base.',
176
+ temperature: 0.2,
177
+ },
178
  },
179
  downstream: ['Answer:China'],
180
  upstream: ['Retrieval:China'],
web/src/pages/flow/retrieval-form/index.tsx ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import KnowledgeBaseItem from '@/components/knowledge-base-item';
2
+ import Rerank from '@/components/rerank';
3
+ import SimilaritySlider from '@/components/similarity-slider';
4
+ import TopNItem from '@/components/top-n-item';
5
+ import type { FormProps } from 'antd';
6
+ import { Form } from 'antd';
7
+ import { IOperatorForm } from '../interface';
8
+
9
+ type FieldType = {
10
+ top_n?: number;
11
+ };
12
+
13
+ const onFinish: FormProps<FieldType>['onFinish'] = (values) => {
14
+ console.log('Success:', values);
15
+ };
16
+
17
+ const onFinishFailed: FormProps<FieldType>['onFinishFailed'] = (errorInfo) => {
18
+ console.log('Failed:', errorInfo);
19
+ };
20
+
21
+ const RetrievalForm = ({ onValuesChange }: IOperatorForm) => {
22
+ const [form] = Form.useForm();
23
+
24
+ return (
25
+ <Form
26
+ name="basic"
27
+ labelCol={{ span: 12 }}
28
+ wrapperCol={{ span: 12 }}
29
+ onFinish={onFinish}
30
+ onFinishFailed={onFinishFailed}
31
+ autoComplete="off"
32
+ onValuesChange={onValuesChange}
33
+ form={form}
34
+ >
35
+ <SimilaritySlider isTooltipShown></SimilaritySlider>
36
+ <TopNItem></TopNItem>
37
+ <Rerank></Rerank>
38
+ <KnowledgeBaseItem></KnowledgeBaseItem>
39
+ </Form>
40
+ );
41
+ };
42
+
43
+ export default RetrievalForm;
web/src/pages/flow/store.ts ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import type {} from '@redux-devtools/extension';
2
+ import {
3
+ Connection,
4
+ Edge,
5
+ EdgeChange,
6
+ Node,
7
+ NodeChange,
8
+ OnConnect,
9
+ OnEdgesChange,
10
+ OnNodesChange,
11
+ OnSelectionChangeFunc,
12
+ OnSelectionChangeParams,
13
+ addEdge,
14
+ applyEdgeChanges,
15
+ applyNodeChanges,
16
+ } from 'reactflow';
17
+ import { create } from 'zustand';
18
+ import { devtools } from 'zustand/middleware';
19
+ import { NodeData } from './interface';
20
+ import { dsl } from './mock';
21
+
22
+ const { nodes: initialNodes, edges: initialEdges } = dsl.graph;
23
+
24
+ export type RFState = {
25
+ nodes: Node<NodeData>[];
26
+ edges: Edge[];
27
+ selectedNodeIds: string[];
28
+ selectedEdgeIds: string[];
29
+ onNodesChange: OnNodesChange;
30
+ onEdgesChange: OnEdgesChange;
31
+ onConnect: OnConnect;
32
+ setNodes: (nodes: Node[]) => void;
33
+ setEdges: (edges: Edge[]) => void;
34
+ updateNodeForm: (nodeId: string, values: any) => void;
35
+ onSelectionChange: OnSelectionChangeFunc;
36
+ addNode: (nodes: Node) => void;
37
+ deleteEdge: () => void;
38
+ deleteEdgeById: (id: string) => void;
39
+ };
40
+
41
+ // this is our useStore hook that we can use in our components to get parts of the store and call actions
42
+ const useStore = create<RFState>()(
43
+ devtools((set, get) => ({
44
+ nodes: initialNodes as Node[],
45
+ edges: initialEdges as Edge[],
46
+ selectedNodeIds: [],
47
+ selectedEdgeIds: [],
48
+ onNodesChange: (changes: NodeChange[]) => {
49
+ set({
50
+ nodes: applyNodeChanges(changes, get().nodes),
51
+ });
52
+ },
53
+ onEdgesChange: (changes: EdgeChange[]) => {
54
+ set({
55
+ edges: applyEdgeChanges(changes, get().edges),
56
+ });
57
+ },
58
+ onConnect: (connection: Connection) => {
59
+ set({
60
+ edges: addEdge(connection, get().edges),
61
+ });
62
+ },
63
+ onSelectionChange: ({ nodes, edges }: OnSelectionChangeParams) => {
64
+ set({
65
+ selectedEdgeIds: edges.map((x) => x.id),
66
+ selectedNodeIds: nodes.map((x) => x.id),
67
+ });
68
+ },
69
+ setNodes: (nodes: Node[]) => {
70
+ set({ nodes });
71
+ },
72
+ setEdges: (edges: Edge[]) => {
73
+ set({ edges });
74
+ },
75
+ addNode: (node: Node) => {
76
+ set({ nodes: get().nodes.concat(node) });
77
+ },
78
+ deleteEdge: () => {
79
+ const { edges, selectedEdgeIds } = get();
80
+ set({
81
+ edges: edges.filter((edge) =>
82
+ selectedEdgeIds.every((x) => x !== edge.id),
83
+ ),
84
+ });
85
+ },
86
+ deleteEdgeById: (id: string) => {
87
+ const { edges } = get();
88
+ set({
89
+ edges: edges.filter((edge) => edge.id !== id),
90
+ });
91
+ },
92
+ updateNodeForm: (nodeId: string, values: any) => {
93
+ set({
94
+ nodes: get().nodes.map((node) => {
95
+ if (node.id === nodeId) {
96
+ node.data = { ...node.data, form: values };
97
+ }
98
+
99
+ return node;
100
+ }),
101
+ });
102
+ },
103
+ })),
104
+ );
105
+
106
+ export default useStore;
web/src/pages/flow/utils.ts CHANGED
@@ -2,6 +2,7 @@ import { DSLComponents } from '@/interfaces/database/flow';
2
  import dagre from 'dagre';
3
  import { Edge, MarkerType, Node, Position } from 'reactflow';
4
  import { v4 as uuidv4 } from 'uuid';
 
5
 
6
  const buildEdges = (
7
  operatorIds: string[],
@@ -96,3 +97,35 @@ export const getLayoutedElements = (
96
 
97
  return { nodes, edges };
98
  };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  import dagre from 'dagre';
3
  import { Edge, MarkerType, Node, Position } from 'reactflow';
4
  import { v4 as uuidv4 } from 'uuid';
5
+ import { NodeData } from './interface';
6
 
7
  const buildEdges = (
8
  operatorIds: string[],
 
97
 
98
  return { nodes, edges };
99
  };
100
+
101
+ const buildComponentDownstreamOrUpstream = (
102
+ edges: Edge[],
103
+ nodeId: string,
104
+ isBuildDownstream = true,
105
+ ) => {
106
+ return edges
107
+ .filter((y) => y[isBuildDownstream ? 'source' : 'target'] === nodeId)
108
+ .map((y) => y[isBuildDownstream ? 'target' : 'source']);
109
+ };
110
+
111
+ // construct a dsl based on the node information of the graph
112
+ export const buildDslComponentsByGraph = (
113
+ nodes: Node<NodeData>[],
114
+ edges: Edge[],
115
+ ): DSLComponents => {
116
+ const components: DSLComponents = {};
117
+
118
+ nodes.forEach((x) => {
119
+ const id = x.id;
120
+ components[id] = {
121
+ obj: {
122
+ component_name: x.data.label,
123
+ params: x.data.form as Record<string, unknown>,
124
+ },
125
+ downstream: buildComponentDownstreamOrUpstream(edges, id, true),
126
+ upstream: buildComponentDownstreamOrUpstream(edges, id, false),
127
+ };
128
+ });
129
+
130
+ return components;
131
+ };
web/src/routes.ts CHANGED
@@ -90,6 +90,10 @@ const routes = [
90
  },
91
  {
92
  path: '/flow',
 
 
 
 
93
  component: '@/pages/flow',
94
  },
95
  ],
 
90
  },
91
  {
92
  path: '/flow',
93
+ component: '@/pages/flow/list',
94
+ },
95
+ {
96
+ path: '/flow/:id',
97
  component: '@/pages/flow',
98
  },
99
  ],
web/src/services/flow-service.ts ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import api from '@/utils/api';
2
+ import registerServer from '@/utils/registerServer';
3
+ import request from '@/utils/request';
4
+
5
+ const {
6
+ getCanvas,
7
+ setCanvas,
8
+ listCanvas,
9
+ resetCanvas,
10
+ removeCanvas,
11
+ listTemplates,
12
+ } = api;
13
+
14
+ const methods = {
15
+ getCanvas: {
16
+ url: getCanvas,
17
+ method: 'get',
18
+ },
19
+ setCanvas: {
20
+ url: setCanvas,
21
+ method: 'post',
22
+ },
23
+ listCanvas: {
24
+ url: listCanvas,
25
+ method: 'get',
26
+ },
27
+ resetCanvas: {
28
+ url: resetCanvas,
29
+ method: 'post',
30
+ },
31
+ removeCanvas: {
32
+ url: removeCanvas,
33
+ method: 'post',
34
+ },
35
+ listTemplates: {
36
+ url: listTemplates,
37
+ method: 'get',
38
+ },
39
+ } as const;
40
+
41
+ const chatService = registerServer<keyof typeof methods>(methods, request);
42
+
43
+ export default chatService;
web/src/utils/api.ts CHANGED
@@ -81,4 +81,12 @@ export default {
81
  // system
82
  getSystemVersion: `${api_host}/system/version`,
83
  getSystemStatus: `${api_host}/system/status`,
 
 
 
 
 
 
 
 
84
  };
 
81
  // system
82
  getSystemVersion: `${api_host}/system/version`,
83
  getSystemStatus: `${api_host}/system/status`,
84
+
85
+ // flow
86
+ listTemplates: `${api_host}/canvas/templates`,
87
+ listCanvas: `${api_host}/canvas/list`,
88
+ getCanvas: `${api_host}/canvas/get`,
89
+ removeCanvas: `${api_host}/canvas/rm`,
90
+ setCanvas: `${api_host}/canvas/set`,
91
+ resetCanvas: `${api_host}/canvas/reset`,
92
  };
web/src/utils/registerServer.ts CHANGED
@@ -1,7 +1,7 @@
1
  import omit from 'lodash/omit';
2
  import { RequestMethod } from 'umi-request';
3
 
4
- type Service<T extends string> = Record<T, (params: any) => any>;
5
 
6
  const registerServer = <T extends string>(
7
  opt: Record<T, { url: string; method: string }>,
 
1
  import omit from 'lodash/omit';
2
  import { RequestMethod } from 'umi-request';
3
 
4
+ type Service<T extends string> = Record<T, (params?: any) => any>;
5
 
6
  const registerServer = <T extends string>(
7
  opt: Record<T, { url: string; method: string }>,