Compare commits

...

43 Commits

Author SHA1 Message Date
Archer
02bf400bf3 Add workflow rename; Fix: userselect chatId unrefresh (#2672)
* feat: workflow node support rename

* perf: push data to training queue

* fix: userselect chatId unrefresh
2024-09-11 15:27:47 +08:00
Archer
11cbcca2d4 Update 4810.md (#2668) 2024-09-11 10:09:56 +08:00
Finley Ge
34422f9549 fix: sso (#2661) 2024-09-10 18:24:29 +08:00
Archer
aeba79267a Workflow deep and workflow connection performance (#2664)
* feat: Workflow dispatch deep

* perf: workflow connection
2024-09-10 16:57:59 +08:00
Archer
7473be5922 pay pre wrap (#2659) 2024-09-10 14:04:57 +08:00
Finley Ge
de59b3d2e5 fix: app select dataset (#2660) 2024-09-10 13:50:23 +08:00
Archer
08190c2f0d fix: toolNode max tokens and toolDescription i18n (#2655)
* fix: toolNode max tokens

* fix: toolNode max tokens

* fix: workflow  tool desc i18n
2024-09-09 22:26:20 +08:00
heheer
6a85c8c2b6 fix: cq template missing chatconfig (#2654) 2024-09-09 19:37:15 +08:00
Archer
bbdab1d40e fix: entry (#2652) 2024-09-09 16:21:22 +08:00
Finley Ge
78ad2791cd chore: openapi doc generator (#2644)
* chore: extract the type and comment from apis

* chore: template code

* feat: openapi

* pref: openapi generator. send into public/openapi folder
2024-09-09 15:43:09 +08:00
Archer
5f3c8e9046 Fix: workflow init status (#2649)
* i18n

* fix: entry
2024-09-09 15:37:20 +08:00
papapatrick
30057f01a6 fix: workflow i18n (#2645)
* fix: workflow i18n

* i18n completion
2024-09-09 14:36:35 +08:00
Archer
3ea185315d fix: workflow clear repeat check run (#2646) 2024-09-09 14:14:32 +08:00
Finley Ge
a1ae08f62b perf: logs, auth root as super admin, etc (#2615)
* chore: usePagination hook type

* feat: chat log show outlinkuid or tmb avatar and name

* fix: ts error for pagination

* feat: auth root
2024-09-09 10:05:18 +08:00
Archer
91ec895fd2 fix: oauth (#2640)
* fix: oauth

* remove log

* fix: oauth dispatch
2024-09-08 20:56:33 +08:00
xianlezheng
1a33642635 Fixs:当最后一条数据因大模型没返回数据时导致调试时前端报错。 (#2637)
在lastMessageValue取值时需要提前判断空值。

Co-authored-by: zhengxianle <nopanic@ilikejobs.com>
2024-09-08 15:26:05 +08:00
archer
e9681c8ed5 log 2024-09-08 15:03:29 +08:00
imgbot[bot]
69ff65973f [ImgBot] Optimize images (#2639)
*Total -- 6,985.44kb -> 4,501.25kb (35.56%)

/python/sensevoice/app/iic/SenseVoiceSmall/fig/inference.png -- 935.23kb -> 292.81kb (68.69%)
/python/sensevoice/app/iic/SenseVoiceSmall/fig/asr_results.png -- 238.19kb -> 76.62kb (67.83%)
/python/sensevoice/app/iic/SenseVoiceSmall/fig/sensevoice.png -- 879.78kb -> 332.45kb (62.21%)
/python/sensevoice/app/iic/SenseVoiceSmall/fig/ser_table.png -- 318.12kb -> 145.67kb (54.21%)
/docSite/assets/imgs/wechat6.png -- 208.98kb -> 119.19kb (42.97%)
/docSite/assets/imgs/collection-tags-2.png -- 83.02kb -> 52.57kb (36.67%)
/.github/imgs/intro3.png -- 258.74kb -> 167.94kb (35.09%)
/python/sensevoice/app/iic/speech_fsmn_vad_zh-cn-16k-common-pytorch/fig/struct.png -- 27.26kb -> 17.81kb (34.66%)
/.github/imgs/intro1.png -- 259.12kb -> 173.33kb (33.11%)
/docSite/assets/imgs/fileinpu-2.png -- 214.61kb -> 147.20kb (31.41%)
/.github/imgs/intro4.png -- 227.64kb -> 158.71kb (30.28%)
/docSite/assets/imgs/questionGuide.png -- 38.89kb -> 27.95kb (28.13%)
/python/sensevoice/app/iic/SenseVoiceSmall/fig/aed_figure.png -- 115.93kb -> 85.46kb (26.28%)
/.github/imgs/intro2.png -- 370.64kb -> 273.45kb (26.22%)
/docSite/assets/imgs/offiaccount-9.png -- 38.62kb -> 28.89kb (25.18%)
/docSite/assets/imgs/collection-tags-3.png -- 125.71kb -> 98.27kb (21.83%)
/docSite/assets/imgs/offiaccount-3.png -- 90.57kb -> 71.85kb (20.66%)
/docSite/assets/imgs/feishu-bot-4.png -- 85.73kb -> 68.44kb (20.17%)
/docSite/assets/imgs/feishu-bot-2.png -- 92.49kb -> 73.94kb (20.05%)
/docSite/assets/imgs/offiaccount-1.png -- 99.70kb -> 79.77kb (19.98%)
/docSite/assets/imgs/feishu-bot-1.png -- 154.50kb -> 126.89kb (17.87%)
/docSite/assets/imgs/feishu-bot-6.png -- 160.72kb -> 133.59kb (16.88%)
/docSite/assets/imgs/fileinpu-6.jpg -- 179.04kb -> 150.60kb (15.88%)
/docSite/assets/imgs/feishu-bot-8.png -- 43.64kb -> 36.83kb (15.61%)
/docSite/assets/imgs/offiaccount-7.png -- 73.88kb -> 62.58kb (15.29%)
/python/sensevoice/app/iic/SenseVoiceSmall/fig/ser_figure.png -- 194.25kb -> 167.12kb (13.97%)
/docSite/assets/imgs/feishu-bot-5.png -- 169.50kb -> 146.65kb (13.48%)
/docSite/assets/imgs/offiaccount-8.png -- 129.99kb -> 114.37kb (12.02%)
/docSite/assets/imgs/offiaccount-6.png -- 131.04kb -> 115.30kb (12.01%)
/docSite/assets/imgs/feishu-bot-3.png -- 165.20kb -> 145.86kb (11.71%)
/docSite/assets/imgs/offiaccount-4.png -- 158.75kb -> 140.69kb (11.38%)
/docSite/assets/imgs/feishu-bot-7.png -- 95.10kb -> 84.29kb (11.37%)
/docSite/assets/imgs/offiaccount-5.png -- 158.34kb -> 140.59kb (11.21%)
/docSite/assets/imgs/offiaccount-2.png -- 163.30kb -> 145.59kb (10.84%)
/docSite/assets/imgs/gpt-translate-example.png -- 299.24kb -> 297.98kb (0.42%)

Signed-off-by: ImgBotApp <ImgBotHelp@gmail.com>
Co-authored-by: ImgBotApp <ImgBotHelp@gmail.com>
2024-09-08 10:23:27 +08:00
Archer
52ac445557 fix: mobild sso login (#2636)
* fix: mobild sso login

* feat: max call tool times
2024-09-07 18:24:38 +08:00
Archer
d45cb2f84a Update 4810.md (#2634) 2024-09-06 17:29:23 +08:00
Archer
1cb71c6bfb 4.8.10 perf (#2633)
* perf: i18n init

* i18n

* 4.8.10 doc

* doc
2024-09-06 17:22:24 +08:00
papapatrick
fb59b60761 addition i18n (#2631) 2024-09-06 16:57:47 +08:00
Archer
9334a0dcf6 4.8.10 perf (#2630)
* perf: i18n init

* i18n

* fix: user select end status

* fix: interactive workflow

* fix: restart chat

* fix: oauth login
2024-09-06 15:45:02 +08:00
Archer
c614f8b9ca Perf: i18n change and captcha code. (#2625)
* perf: send captcha check

* perf: back router

* perf: i18n init

* perf: ui

* i18n

* perf: ui duration
2024-09-05 23:01:12 +08:00
heheer
478386c612 chore: template author name & icon border radius (#2624) 2024-09-05 20:44:31 +08:00
papapatrick
dfcffc7fc1 I18n: Completed i18n&&proofread some translations (#2619)
* i18n-1

* i18n: Completed the remaining parts of i18n and proofread some translations

* i18n: add default lang&&add app template i18n
2024-09-05 17:29:36 +08:00
Finley Ge
b4238257b6 feat: dynamic website crawler (#2609)
* feat: cheerio returns cherrio.load for pro version website sync

* chore: rename
2024-09-05 14:49:57 +08:00
Archer
38f47956cd fix: ts (#2621) 2024-09-05 14:04:45 +08:00
Archer
7fed4d697f perf: captcha code (#2620)
* perf:  captcha code

* perf: dockerfile
2024-09-05 13:41:11 +08:00
EthanD4869
5ed89130ef add sensevoice & cosevoice (#2562)
Signed-off-by: EthanD <EthanD4869@gmail.com>
Co-authored-by: EthanD <EthanD4869@gmail.com>
2024-09-05 13:36:11 +08:00
Archer
3671e55001 4.8.10 test (#2618)
* perf: menu arrow ui

* perf: http node placeholder

* perf: http node form input

* perf: chatBox performance
2024-09-05 11:49:13 +08:00
papapatrick
3bcc3430fb feat: add captcha (#2613)
* feat: captcha

* add borderRadius

* code perf
2024-09-05 11:46:51 +08:00
heheer
d6233cd7b1 fix: add http params focus & ui (#2611) 2024-09-04 13:37:29 +08:00
Archer
64708ea424 Update userselect ux (#2610)
* perf: user select ux and api

* perf: http variables replace code

* perf: http variables replace code

* perf: chat box question guide adapt interactive

* remove comment
2024-09-04 11:11:08 +08:00
heheer
85a11d08b2 feat: http body type & http input support editor variable (#2603)
* feat: http body type & http input support editor variable

* fix type

* chore: code

* code
2024-09-03 23:43:21 +08:00
papapatrick
a7569037fe perf: modal add size props&menu add menuItemStyles (#2600)
* perf: modal add size props&menu add menuItemStyles

* delete minH

* mobile adaptation

* 优化部分代码
2024-09-03 14:56:09 +08:00
Archer
4726034344 Update official_account.md (#2604) 2024-09-03 14:41:46 +08:00
Archer
9a57e94b79 4.8.10 test (#2601)
* perf: workflow children run params

* feat: workflow userId

* fix: ui size

* perf: Markdown whitespace and ai images split

* fix: openai sdk ts
2024-09-03 13:43:56 +08:00
Archer
761e35c226 4.8.10 workflow perf (#2596)
* perf: run plugin variables init

* perf: init free plan

* perf: dataset data ui

* perf: workflow theme

* perf: plugin input modal ui

* perf: workflow dispatch

* fix: account ui

* feat: 4810 doc
2024-09-03 09:56:33 +08:00
papapatrick
5ebe0017a0 perf: user default avatar (#2594)
* perf: user default avatar

* refactor: 修改文件路径
2024-09-02 21:04:49 +08:00
papapatrick
036097243a perf: workflow&plugins json config import and export (#2592) 2024-09-02 15:05:58 +08:00
heheer
84de95d294 fix: plugin input type options & save button (#2590) 2024-09-02 13:06:48 +08:00
papapatrick
fdab383b26 Style-dataset-2.5 (#2580)
* style: dataset detail page 2.5

* fix merge error

* fix: flash bug

* fix: build error

* revert: 滚动变回分页
2024-09-02 10:00:55 +08:00
312 changed files with 9315 additions and 5200 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 259 KiB

After

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 371 KiB

After

Width:  |  Height:  |  Size: 273 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 259 KiB

After

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 228 KiB

After

Width:  |  Height:  |  Size: 159 KiB

View File

@@ -13,7 +13,7 @@
"js",
"ts"
],
"i18n-ally.keystyle": "nested",
"i18n-ally.keystyle": "flat",
"i18n-ally.sortKeys": true,
"i18n-ally.keepFulfilled": false,
"i18n-ally.sourceLanguage": "zh", // 根据此语言文件翻译其他语言文件的变量和内容

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 KiB

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 154 KiB

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 165 KiB

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 170 KiB

After

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 161 KiB

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 215 KiB

After

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 179 KiB

After

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 299 KiB

After

Width:  |  Height:  |  Size: 298 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 163 KiB

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 159 KiB

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 158 KiB

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 KiB

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 KiB

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 209 KiB

After

Width:  |  Height:  |  Size: 119 KiB

View File

@@ -62,6 +62,23 @@ weight: 113
34.142.157.52
```
国内版用户fastgpt.cn)可以填写下面的 IP 白名单:
```
47.97.59.172
121.43.108.48
121.41.75.88
121.41.178.7
121.40.65.187
121.196.235.183
120.55.195.90
120.55.193.112
120.26.229.115
112.124.41.79
101.37.205.32
47.98.190.173
```
## 4. 获取AES Key选择加密方式
![图片](/imgs/offiaccount-5.png)

View File

@@ -1,5 +1,5 @@
---
title: 'V4.8.10(进行中)'
title: 'V4.8.10'
description: 'FastGPT V4.8.10 更新说明'
icon: 'upgrade'
draft: false
@@ -24,8 +24,8 @@ STORE_LOG_LEVEL=warn
### 3. 修改镜像tag
- 更新 FastGPT 镜像 tag: v4.8.10-alpha
- 更新 FastGPT 商业版镜像 tag: v4.8.10-alpha
- 更新 FastGPT 镜像 tag: v4.8.10-fix
- 更新 FastGPT 商业版镜像 tag: v4.8.10-fix
- Sandbox 镜像,可以不更新
## 4. 执行初始化
@@ -45,40 +45,60 @@ curl --location --request POST 'https://{{host}}/api/admin/initv4810' \
## V4.8.10 更新说明
完整内容请见:[4.8.10 release](https://github.com/labring/FastGPT/releases/tag/v4.8.10-alpha)
完整内容请见:[4.8.10 release](https://github.com/labring/FastGPT/releases/tag/v4.8.10)
1. 新增 - 模板市场
2. 新增 - 工作流节点拖动自动对齐吸附
3. 新增 - 用户选择节点Debug 模式暂未支持)
4. 新增 - 工作流撤销和重做
5. 新增 - 工作流本次编辑记录,取代自动保存
6. 新增 - 工作流版本支持重命名
7. 新增 - 应用调用迁移成单独节点,同时可以传递全局变量和用户的文件
8. 新增 - 插件增加使用说明配置
9. 商业版新增 - 飞书机器人接入
10. 商业版新增 - 公众号接入接入
11. 商业版新增 - 自助开票申请
12. 商业版新增 - SSO 定制
13. 优化 - SSE 响应优化
14. 优化 - 无 SSL 证书情况下,优化复制
15. 优化 - 单选框打开后自动滚动到选中的位置
16. 优化 - 知识库集合禁用,目录禁用会递归修改其下所有 children 的禁用状态
17. 优化 - 节点选择,避免切换 tab 时候path 加载报错
18. 优化 - 最新 React Markdown 组件,支持 Base64 图片
19. 优化 - 知识库列表 UI
20. 优化 - 知识库详情页 UI
21. 优化 - 支持无网络配置情况下运行
22. 优化 - 部分全局变量,增加数据类型约束
23. 修复 - 全局变量 key 可能重复
24. 修复 - Prompt 模式调用工具stream=false 模式下,会携带 0: 开头标记
25. 修复 - 对话日志鉴权问题:仅为 APP 管理员的用户,无法查看对话日志详情
26. 修复 - 选择 Milvus 部署时,无法导出知识库。
27. 修复 - 创建 APP 副本,无法复制系统配置
28. 修复 - 图片识别模式下,自动解析图片链接正则不够严谨问题
29. 修复 - 内容提取的数据类型与输出数据类型未一致
30. 修复 - 工作流运行时间统计错误
31. 修复 - stream 模式下,工具调用有可能出现 undefined
32. 修复 - 全局变量在 API 中无法持久化
33. 修复 - OpenAPIdetail=false模式下不应该返回 tool 调用结果,仅返回文字。(可解决 cow 不适配问题)
34. 修复 - 知识库标签重复加载
35. 修复 - Debug 模式下,循环调用边问题。
1. 新增 - 模板市场
2. 新增 - 工作流节点拖动自动对齐吸附
3. 新增 - 用户选择节点Debug 模式暂未支持)
4. 新增 - 工作流增加 uid 全局变量。
5. 新增 - 工作流撤销和重做。
6. 新增 - 工作流本次编辑记录,取代自动保存。
7. 新增 - 工作流版本支持重命名
8. 新增 - 工作流的“应用调用”节点弃用,迁移成单独节点,与插件使用方式相同,同时可以传递全局变量和用户上传的文件
9. 新增 - 插件增加使用说明配置。
10. 新增 - 插件自定义输入支持单选框。
11. 新增 - HTTP 节点支持 text/plain 模式。
12. 新增 - HTTP模块支持超时配置、支持更多的 Body 类型params 和 headers 支持新的变量选择模式。
13. 新增 - 工作流导出导入,支持直接导出和导入 JSON 文件,便于交流
14. 新增 - 发送验证码安全校验
15. 商业版新增 - 飞书机器人接入
16. 商业版新增 - 公众号接入接入
17. 商业版新增 - 自助开票申请
18. 商业版新增 - SSO 定制
19. 优化 - 工作流循环校验,避免 skip 循环空转。同时支持分支完全并发执行
20. 优化 - 工作流嵌套执行,参数可能存在的污染问题
21. 优化 - 部分全局变量,增加数据类型约束
22. 优化 - 节点选择,避免切换 tab 时候path 加载报错
23. 优化 - 最新 React Markdown 组件,支持 Base64 图片
24. 优化 - 对话框性能问题
25. 优化 - 单选框打开后自动滚动到选中的位置
26. 优化 - 知识库集合禁用,目录禁用会递归修改其下所有 children 的禁用状态。
27. 优化 - SSE 响应代码优化
28. 优化 - 无 SSL 证书情况下,优化复制
29. 优化 - 知识库列表 UI
30. 优化 - 知识库详情页 UI
31. 优化 - 支持无网络配置情况下运行。
32. 优化 - 调整.env.template关于mongodb的说明使得更易于理解
33. 优化 - 新的支付模式。
34. 优化 - 用户默认头像
35. 修复 - Prompt 模式调用工具stream=false 模式下,会携带 0: 开头标记。
36. 修复 - 对话日志鉴权问题:仅为 APP 管理员的用户,无法查看对话日志详情。
37. 修复 - 选择 Milvus 部署时,无法导出知识库。
38. 修复 - 创建 APP 副本,无法复制系统配置。
39. 修复 - 图片识别模式下,自动解析图片链接正则不够严谨问题。
40. 修复 - 内容提取的数据类型与输出数据类型未一致。
41. 修复 - 工作流运行时间统计错误。
42. 修复 - stream 模式下,工具调用有可能出现 undefined。
43. 修复 - reranker typo。
44. 修复 - home host typo。
45. 修复 - i18n display。
46. 修复 - 全局变量可重复定义 key。
47. 修复 - 全局变量在 Debug 模式下不可持久化。
48. 修复 - 全局变量在 API 中无法持久化。
49. 修复 - OpenAPIdetail=false模式下不应该返回 tool 调用结果,仅返回文字。(可解决 cow 不适配问题)。
50. 修复 - 知识库标签重复加载。
51. 修复 - 网络链接重新获取时,自定义分割符不生效。
52. 修复 - 插件运行时,会传递额外的全局变量,可能造成插件内变量污染。
53. 文档 - qa docs。
54. 文档 - Update feishu.md。
55. 文档 - update baseURL。

View File

@@ -16,8 +16,13 @@ weight: 813
## V4.8.11 更新预告
1.
2. 新增 - 插件自定义输入支持单选框
3. 新增 - 插件输出,支持指定某些字段为工具调用结果
4. 新增 - 插件支持配置使用引导、全局变量和文件输入
5. 优化 - SSE 响应代码
6. 优化 - 非 HTTPS 环境下支持复制(除非 textarea 复制也不支持)
2. 新增 - 工作流循环执行节点。
3. 新增 - 工作流用户表单输入节点。
4. 新增 - 插件输出,支持指定某些字段为工具调用结果。
5. 新增 - 插件支持配置使用引导、全局变量和文件输入
6. 新增 - 简易模式支持新的版本管理方式。
7. 新增 - 聊天记录滚动加载,不再只加载 30 条。
8. 优化 - 工作流嵌套层级限制 20 层,避免因编排不合理导致的无限死循环。
9. 优化 - 工作流 handler 性能优化。
10. 修复 - 知识库选择权限问题。
11. 修复 - 空 chatId 发起对话,首轮携带用户选择时会异常。

0
fastgpt@4.0 Normal file
View File

View File

@@ -10,8 +10,7 @@
"postinstall": "sh ./scripts/postinstall.sh",
"initIcon": "node ./scripts/icon/init.js",
"previewIcon": "node ./scripts/icon/index.js",
"i18n:delete-unused-keys": "node ./scripts/i18n/delete-unused-keys.js",
"i18n:query": "node ./scripts/i18n/query.js"
"api:gen": "tsc ./scripts/openapi/index.ts && node ./scripts/openapi/index.js && npx @redocly/cli build-docs ./scripts/openapi/openapi.json -o ./projects/app/public/openapi/index.html"
},
"devDependencies": {
"@chakra-ui/cli": "^2.4.1",

View File

@@ -1,15 +1,15 @@
import { PromptTemplateItem } from '../type.d';
import { i18nT } from '../../../../web/i18n/utils';
export const Prompt_QuoteTemplateList: PromptTemplateItem[] = [
{
title: '标准模板',
desc: '标准提示词,用于结构不固定的知识库。',
title: i18nT('app:template.standard_template'),
desc: i18nT('app:template.standard_template_des'),
value: `{{q}}
{{a}}`
},
{
title: '问答模板',
desc: '适合 QA 问答结构的知识库可以让AI较为严格的按预设内容回答',
title: i18nT('app:template.qa_template'),
desc: i18nT('app:template.qa_template_des'),
value: `<Question>
{{q}}
</Question>
@@ -18,14 +18,14 @@ export const Prompt_QuoteTemplateList: PromptTemplateItem[] = [
</Answer>`
},
{
title: '标准严格模板',
desc: '在标准模板基础上,对模型的回答做更严格的要求。',
title: i18nT('app:template.standard_strict'),
desc: i18nT('app:template.standard_strict_des'),
value: `{{q}}
{{a}}`
},
{
title: '严格问答模板',
desc: '在问答模板基础上,对模型的回答做更严格的要求。',
title: i18nT('app:template.hard_strict'),
desc: i18nT('app:template.hard_strict_des'),
value: `<Question>
{{q}}
</Question>
@@ -37,7 +37,7 @@ export const Prompt_QuoteTemplateList: PromptTemplateItem[] = [
export const Prompt_QuotePromptList: PromptTemplateItem[] = [
{
title: '标准模板',
title: i18nT('app:template.standard_template'),
desc: '',
value: `使用 <Data></Data> 标记中的内容作为你的知识:
@@ -55,7 +55,7 @@ export const Prompt_QuotePromptList: PromptTemplateItem[] = [
问题:"""{{question}}"""`
},
{
title: '问答模板',
title: i18nT('app:template.qa_template'),
desc: '',
value: `使用 <QA></QA> 标记中的问答对进行回答。
@@ -72,7 +72,7 @@ export const Prompt_QuotePromptList: PromptTemplateItem[] = [
问题:"""{{question}}"""`
},
{
title: '标准严格模板',
title: i18nT('app:template.standard_strict'),
desc: '',
value: `忘记你已有的知识,仅使用 <Data></Data> 标记中的内容作为你的知识:
@@ -94,7 +94,7 @@ export const Prompt_QuotePromptList: PromptTemplateItem[] = [
问题:"""{{question}}"""`
},
{
title: '严格问答模板',
title: i18nT('app:template.hard_strict'),
desc: '',
value: `忘记你已有的知识,仅使用 <QA></QA> 标记中的问答对进行回答。

View File

@@ -118,7 +118,7 @@ export const chats2GPTMessages = ({
tool_calls
})
.concat(toolResponse);
} else if (value.text) {
} else if (value.text?.content) {
results.push({
dataId,
role: ChatCompletionRequestMessageRoleEnum.Assistant,
@@ -142,7 +142,7 @@ export const GPTMessages2Chats = (
messages: ChatCompletionMessageParam[],
reserveTool = true
): ChatItemType[] => {
return messages
const chatMessages = messages
.map((item) => {
const value: ChatItemType['value'] = [];
const obj = GPT2Chat[item.role];
@@ -151,12 +151,23 @@ export const GPTMessages2Chats = (
obj === ChatRoleEnum.System &&
item.role === ChatCompletionRequestMessageRoleEnum.System
) {
value.push({
type: ChatItemValueTypeEnum.text,
text: {
content: item.content
}
});
if (Array.isArray(item.content)) {
item.content.forEach((item) => [
value.push({
type: ChatItemValueTypeEnum.text,
text: {
content: item.text
}
})
]);
} else {
value.push({
type: ChatItemValueTypeEnum.text,
text: {
content: item.content
}
});
}
} else if (
obj === ChatRoleEnum.Human &&
item.role === ChatCompletionRequestMessageRoleEnum.User
@@ -277,6 +288,22 @@ export const GPTMessages2Chats = (
} as ChatItemType;
})
.filter((item) => item.value.length > 0);
// Merge data with the same dataId
const result = chatMessages.reduce((result: ChatItemType[], currentItem) => {
const lastItem = result[result.length - 1];
if (lastItem && lastItem.dataId === currentItem.dataId && lastItem.obj === currentItem.obj) {
// @ts-ignore
lastItem.value = lastItem.value.concat(currentItem.value);
} else {
result.push(currentItem);
}
return result;
}, []);
return result;
};
export const chatValue2RuntimePrompt = (value: ChatItemValueItemType[]): RuntimeUserPromptType => {

View File

@@ -108,6 +108,8 @@ export enum NodeInputKeyEnum {
httpMethod = 'system_httpMethod',
httpParams = 'system_httpParams',
httpJsonBody = 'system_httpJsonBody',
httpFormBody = 'system_httpFormBody',
httpContentType = 'system_httpContentType',
httpTimeout = 'system_httpTimeout',
abandon_httpUrl = 'url',
@@ -217,3 +219,13 @@ export enum RuntimeEdgeStatusEnum {
export const VARIABLE_NODE_ID = 'VARIABLE_NODE_ID';
export const DYNAMIC_INPUT_REFERENCE_KEY = 'DYNAMIC_INPUT_REFERENCE_KEY';
// http node body content type
export enum ContentTypes {
none = 'none',
formData = 'form-data',
xWwwFormUrlencoded = 'x-www-form-urlencoded',
json = 'json',
xml = 'xml',
raw = 'raw-text'
}

View File

@@ -1,5 +1,5 @@
import { WorkflowIOValueTypeEnum } from '../constants';
import { i18nT } from '../../../../web/i18n/utils';
export enum FlowNodeInputTypeEnum { // render ui
reference = 'reference', // reference to other node output
input = 'input', // one line input
@@ -167,23 +167,23 @@ export const FlowValueTypeMap = {
value: WorkflowIOValueTypeEnum.any
},
[WorkflowIOValueTypeEnum.chatHistory]: {
label: '历史记录',
label: i18nT('common:core.chat.History'),
value: WorkflowIOValueTypeEnum.chatHistory
},
[WorkflowIOValueTypeEnum.datasetQuote]: {
label: '知识库引用',
label: i18nT('common:core.workflow.Dataset quote'),
value: WorkflowIOValueTypeEnum.datasetQuote
},
[WorkflowIOValueTypeEnum.selectApp]: {
label: '选择应用',
label: i18nT('common:plugin.App'),
value: WorkflowIOValueTypeEnum.selectApp
},
[WorkflowIOValueTypeEnum.selectDataset]: {
label: '选择知识库',
label: i18nT('common:core.chat.Select dataset'),
value: WorkflowIOValueTypeEnum.selectDataset
},
[WorkflowIOValueTypeEnum.dynamic]: {
label: '动态输入',
label: i18nT('common:core.workflow.dynamic_input'),
value: WorkflowIOValueTypeEnum.dynamic
}
};

View File

@@ -22,8 +22,10 @@ export enum DispatchNodeResponseKeyEnum {
childrenResponses = 'childrenResponses', // Some nodes make recursive calls that need to be returned
toolResponses = 'toolResponses', // The result is passed back to the tool node for use
assistantResponses = 'assistantResponses', // assistant response
rewriteHistories = 'rewriteHistories', // If have the response, workflow histories will be rewrite
interactive = 'INTERACTIVE' // is interactive
interactive = 'INTERACTIVE', // is interactive
runTimes = 'runTimes' // run times
}
export const needReplaceReferenceInputTypeList = [

View File

@@ -2,9 +2,9 @@ import { ChatNodeUsageType } from '../../../support/wallet/bill/type';
import {
ChatItemType,
UserChatItemValueItemType,
ChatItemValueItemType,
ToolRunResponseItemType,
NodeOutputItemType
NodeOutputItemType,
AIChatItemValueItemType
} from '../../chat/type';
import { FlowNodeInputItemType, FlowNodeOutputItemType } from '../type/io.d';
import { StoreNodeItemType } from '../type/node';
@@ -26,10 +26,15 @@ export type ChatDispatchProps = {
res?: NextApiResponse;
requestOrigin?: string;
mode: 'test' | 'chat' | 'debug';
teamId: string;
tmbId: string;
user: UserModelSchema;
app: AppDetailType | AppSchema;
runningAppInfo: {
id: string; // May be the id of the system plug-in (cannot be used directly to look up the table)
teamId: string;
tmbId: string; // App tmbId
};
uid: string; // Who run this workflow
chatId?: string;
responseChatItemId?: string;
histories: ChatItemType[];
@@ -40,6 +45,7 @@ export type ChatDispatchProps = {
maxRunTimes: number;
isToolCall?: boolean;
workflowStreamResponse?: WorkflowResponseType;
workflowDispatchDeep?: number;
};
export type ModuleDispatchProps<T> = ChatDispatchProps & {
@@ -50,10 +56,12 @@ export type ModuleDispatchProps<T> = ChatDispatchProps & {
};
export type SystemVariablesType = {
userId: string;
appId: string;
chatId?: string;
responseChatItemId?: string;
histories: ChatItemType[];
cTime: string;
};
/* node props */
@@ -69,7 +77,7 @@ export type RuntimeNodeItemType = {
inputs: FlowNodeInputItemType[];
outputs: FlowNodeOutputItemType[];
pluginId?: string;
pluginId?: string; // workflow id / plugin id
};
export type PluginRuntimeType = {
@@ -166,13 +174,15 @@ export type DispatchNodeResponseType = {
updateVarResult?: any[];
};
export type DispatchNodeResultType<T> = {
export type DispatchNodeResultType<T = {}> = {
[DispatchNodeResponseKeyEnum.skipHandleId]?: string[]; // skip some edge handle id
[DispatchNodeResponseKeyEnum.nodeResponse]?: DispatchNodeResponseType; // The node response detail
[DispatchNodeResponseKeyEnum.nodeDispatchUsages]?: ChatNodeUsageType[]; // Node total usage
[DispatchNodeResponseKeyEnum.childrenResponses]?: DispatchNodeResultType[]; // Children node response
[DispatchNodeResponseKeyEnum.toolResponses]?: ToolRunResponseItemType; // Tool response
[DispatchNodeResponseKeyEnum.assistantResponses]?: ChatItemValueItemType[]; // Assistant response(Store to db)
[DispatchNodeResponseKeyEnum.assistantResponses]?: AIChatItemValueItemType[]; // Assistant response(Store to db)
[DispatchNodeResponseKeyEnum.rewriteHistories]?: ChatItemType[];
[DispatchNodeResponseKeyEnum.runTimes]?: number;
} & T;
/* Single node props */

View File

@@ -27,16 +27,32 @@ export const getMaxHistoryLimitFromNodes = (nodes: StoreNodeItemType[]): number
return limit * 2;
};
/*
Get interaction information (if any) from the last AI message.
What can be done:
1. Get the interactive data
2. Check that the workflow starts at the interaction node
*/
export const getLastInteractiveValue = (histories: ChatItemType[]) => {
const lastAIMessage = histories.findLast((item) => item.obj === ChatRoleEnum.AI);
if (lastAIMessage) {
const interactiveValue = lastAIMessage.value.find(
(v) => v.type === ChatItemValueTypeEnum.interactive
);
const lastValue = lastAIMessage.value[lastAIMessage.value.length - 1];
if (interactiveValue && 'interactive' in interactiveValue) {
return interactiveValue.interactive;
if (
!lastValue ||
lastValue.type !== ChatItemValueTypeEnum.interactive ||
!lastValue.interactive
) {
return null;
}
// Check is user select
if (
lastValue.interactive.type === 'userSelect' &&
!lastValue.interactive.params.userSelectedVal
) {
return lastValue.interactive;
}
}

View File

@@ -17,7 +17,7 @@ import { RunAppModule } from './system/abandoned/runApp/index';
import { PluginInputModule } from './system/pluginInput';
import { PluginOutputModule } from './system/pluginOutput';
import { RunPluginModule } from './system/runPlugin';
import { RunAppPluginModule } from './system/runAppPlugin';
import { RunAppNode } from './system/runApp';
import { AiQueryExtension } from './system/queryExtension';
import type { FlowNodeTemplateType } from '../type/node';
@@ -73,6 +73,6 @@ export const moduleTemplatesFlat: FlowNodeTemplateType[] = [
),
EmptyNode,
RunPluginModule,
RunAppPluginModule,
RunAppNode,
RunAppModule
];

View File

@@ -51,15 +51,15 @@ export const ClassifyQuestionModule: FlowNodeTemplateType = {
label: '',
value: [
{
value: i18nT('workflow:greeting'),
value: 'Greeting',
key: 'wqre'
},
{
value: i18nT('workflow:about_xxx_question'),
value: 'Question regarding xxx',
key: 'sdfa'
},
{
value: i18nT('workflow:other_questions'),
value: 'Other Questions',
key: 'agex'
}
]

View File

@@ -8,7 +8,8 @@ import {
WorkflowIOValueTypeEnum,
NodeInputKeyEnum,
NodeOutputKeyEnum,
FlowNodeTemplateTypeEnum
FlowNodeTemplateTypeEnum,
ContentTypes
} from '../../constants';
import { Input_Template_DynamicInput } from '../input';
import { Output_Template_AddOutput } from '../output';
@@ -82,6 +83,7 @@ export const HttpNode468: FlowNodeTemplateType = {
label: '',
required: false
},
// json body data
{
key: NodeInputKeyEnum.httpJsonBody,
renderTypeList: [FlowNodeInputTypeEnum.hidden],
@@ -89,6 +91,24 @@ export const HttpNode468: FlowNodeTemplateType = {
value: '',
label: '',
required: false
},
// form body data
{
key: NodeInputKeyEnum.httpFormBody,
renderTypeList: [FlowNodeInputTypeEnum.hidden],
valueType: WorkflowIOValueTypeEnum.any,
value: [],
label: '',
required: false
},
// body data type
{
key: NodeInputKeyEnum.httpContentType,
renderTypeList: [FlowNodeInputTypeEnum.hidden],
valueType: WorkflowIOValueTypeEnum.string,
value: ContentTypes.json,
label: '',
required: false
}
],
outputs: [

View File

@@ -3,7 +3,7 @@ import { FlowNodeTypeEnum } from '../../node/constant';
import { FlowNodeTemplateType } from '../../type/node';
import { getHandleConfig } from '../utils';
export const RunAppPluginModule: FlowNodeTemplateType = {
export const RunAppNode: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.appModule,
templateType: FlowNodeTemplateTypeEnum.other,
flowNodeType: FlowNodeTypeEnum.appModule,

View File

@@ -15,7 +15,7 @@ type InteractiveBasicType = {
type UserSelectInteractive = {
type: 'userSelect';
params: {
// description: string;
description: string;
userSelectOptions: UserSelectOptionItemType[];
userSelectedVal?: string;
};

View File

@@ -326,27 +326,6 @@ export const updatePluginInputByVariables = (
);
};
/* Remove pluginInput variables from global variables
(completions api: Plugin input get value from global variables)
*/
export const removePluginInputVariables = (
variables: Record<string, any>,
nodes: RuntimeNodeItemType[]
) => {
const pluginInputNode = nodes.find((node) => node.flowNodeType === FlowNodeTypeEnum.pluginInput);
if (!pluginInputNode) return variables;
return Object.keys(variables).reduce(
(acc, key) => {
if (!pluginInputNode.inputs.find((input) => input.key === key)) {
acc[key] = variables[key];
}
return acc;
},
{} as Record<string, any>
);
};
// replace {{$xx.xx$}} variables for text
export function replaceEditorVariable({
text,

View File

@@ -11,7 +11,7 @@
"jschardet": "3.1.1",
"nanoid": "^4.0.1",
"next": "14.2.5",
"openai": "4.53.0",
"openai": "4.57.0",
"openapi-types": "^12.1.3",
"timezones-list": "^3.0.2"
},

View File

@@ -1,19 +1,19 @@
import { NullPermission, PermissionKeyEnum, PermissionList } from '../constant';
import { PermissionListType } from '../type';
import { i18nT } from '../../../../web/i18n/utils';
export enum AppPermissionKeyEnum {}
export const AppPermissionList: PermissionListType = {
[PermissionKeyEnum.read]: {
...PermissionList[PermissionKeyEnum.read],
description: '可使用该应用进行对话'
description: i18nT('app:permission.des.read')
},
[PermissionKeyEnum.write]: {
...PermissionList[PermissionKeyEnum.write],
description: '可查看和编辑应用'
description: i18nT('app:permission.des.write')
},
[PermissionKeyEnum.manage]: {
...PermissionList[PermissionKeyEnum.manage],
description: '写权限基础上,可配置发布渠道、查看对话日志、分配该应用权限'
description: i18nT('app:permission.des.manage')
}
};

View File

@@ -1,6 +1,6 @@
import { Permission } from './controller';
import { PermissionListType } from './type';
import { i18nT } from '../../../web/i18n/utils';
export enum AuthUserTypeEnum {
token = 'token',
root = 'root',
@@ -27,15 +27,15 @@ export const PermissionTypeMap = {
},
[PermissionTypeEnum.publicRead]: {
iconLight: 'support/permission/publicLight',
label: '团队可访问'
label: i18nT('user:permission.team_read')
},
[PermissionTypeEnum.publicWrite]: {
iconLight: 'support/permission/publicLight',
label: '团队可编辑'
label: i18nT('user:permission.team_write')
},
[PermissionTypeEnum.clbPrivate]: {
iconLight: 'support/permission/privateLight',
label: '仅协作者'
label: i18nT('user:permission.only_collaborators')
}
};
@@ -53,19 +53,19 @@ export enum PermissionKeyEnum {
}
export const PermissionList: PermissionListType = {
[PermissionKeyEnum.read]: {
name: '读权限',
name: i18nT('common:permission.read'),
description: '',
value: 0b100,
checkBoxType: 'single'
},
[PermissionKeyEnum.write]: {
name: '写权限',
name: i18nT('common:permission.write'),
description: '',
value: 0b110, // 如果某个资源有特殊要求,再重写这个值
checkBoxType: 'single'
},
[PermissionKeyEnum.manage]: {
name: '管理员',
name: i18nT('common:permission.manager'),
description: '',
value: 0b111,
checkBoxType: 'single'

View File

@@ -1,3 +1,4 @@
import { i18nT } from '../../../../web/i18n/utils';
import { NullPermission, PermissionKeyEnum, PermissionList } from '../constant';
export enum DatasetPermissionKeyEnum {}
@@ -5,15 +6,15 @@ export enum DatasetPermissionKeyEnum {}
export const DatasetPermissionList = {
[PermissionKeyEnum.read]: {
...PermissionList[PermissionKeyEnum.read],
description: '可查看知识库内容'
description: i18nT('dataset:permission.des.read')
},
[PermissionKeyEnum.write]: {
...PermissionList[PermissionKeyEnum.write],
description: '可增加和变更知识库内容'
description: i18nT('dataset:permission.des.write')
},
[PermissionKeyEnum.manage]: {
...PermissionList[PermissionKeyEnum.manage],
description: '可管理整个知识库数据和信息'
description: i18nT('dataset:permission.des.manage')
}
};

View File

@@ -1,18 +1,18 @@
import { PermissionKeyEnum, PermissionList, ReadPermissionVal } from '../constant';
import { PermissionListType } from '../type';
import { i18nT } from '../../../../web/i18n/utils';
export const TeamPermissionList: PermissionListType = {
[PermissionKeyEnum.read]: {
...PermissionList[PermissionKeyEnum.read],
description: '成员仅可阅读相关资源,无法新建资源'
description: i18nT('user:permission_des.read')
},
[PermissionKeyEnum.write]: {
...PermissionList[PermissionKeyEnum.write],
description: '除了可读资源外,还可以新建新的资源'
description: i18nT('user:permission_des.write')
},
[PermissionKeyEnum.manage]: {
...PermissionList[PermissionKeyEnum.manage],
description: '可创建资源、邀请、删除成员'
description: i18nT('user:permission_des.manage')
}
};

View File

@@ -2,12 +2,14 @@ export enum UserAuthTypeEnum {
register = 'register',
findPassword = 'findPassword',
wxLogin = 'wxLogin',
bindNotification = 'bindNotification'
bindNotification = 'bindNotification',
captcha = 'captcha'
}
export const userAuthTypeMap = {
[UserAuthTypeEnum.register]: 'register',
[UserAuthTypeEnum.findPassword]: 'findPassword',
[UserAuthTypeEnum.wxLogin]: 'wxLogin',
[UserAuthTypeEnum.bindNotification]: 'bindNotification'
[UserAuthTypeEnum.bindNotification]: 'bindNotification',
[UserAuthTypeEnum.captcha]: 'captcha'
};

View File

@@ -14,13 +14,13 @@ export enum UsageSourceEnum {
export const UsageSourceMap = {
[UsageSourceEnum.fastgpt]: {
label: '在线使用'
label: i18nT('common:core.chat.logs.online')
},
[UsageSourceEnum.api]: {
label: 'Api'
},
[UsageSourceEnum.shareLink]: {
label: '免登录链接'
label: i18nT('common:core.chat.logs.free_login')
},
[UsageSourceEnum.training]: {
label: 'dataset.Training Name'

View File

@@ -101,3 +101,5 @@ export const urlsFetch = async ({
return response;
};
export const loadContentByCheerio = async (content: string) => cheerio.load(content);

View File

@@ -48,7 +48,7 @@ export class PgVectorCtrl {
const { teamId, datasetId, collectionId, vector, retry = 3 } = props;
try {
const { rows } = await PgClient.insert(DatasetVectorTableName, {
const { rowCount, rows } = await PgClient.insert(DatasetVectorTableName, {
values: [
[
{ key: 'vector', value: `[${vector}]` },
@@ -58,6 +58,11 @@ export class PgVectorCtrl {
]
]
});
if (rowCount === 0) {
return Promise.reject('insertDatasetData: no insert');
}
return {
insertId: rows[0].id
};

View File

@@ -3,7 +3,7 @@ import { getAIApi } from '../config';
import { countGptMessagesTokens } from '../../../common/string/tiktoken/index';
import { loadRequestMessages } from '../../chat/utils';
export const Prompt_QuestionGuide = `你是一个AI智能助手可以回答和解决我的问题。请结合前面的对话记录帮我生成 3 个问题引导我继续提问。问题的长度应小于20个字符按 JSON 格式返回: ["问题1", "问题2", "问题3"]`;
export const Prompt_QuestionGuide = `你是一个AI智能助手可以回答和解决我的问题。请结合前面的对话记录帮我生成 3 个问题,引导我继续提问,生成问题的语言要与原问题相同。问题的长度应小于20个字符按 JSON 格式返回: ["问题1", "问题2", "问题3"]`;
export async function createQuestionGuide({
messages,
@@ -19,6 +19,7 @@ export async function createQuestionGuide({
content: Prompt_QuestionGuide
}
];
const ai = getAIApi({
timeout: 480000
});

View File

@@ -1,7 +1,7 @@
import type { ChatItemType, ChatItemValueItemType } from '@fastgpt/global/core/chat/type';
import { MongoChatItem } from './chatItemSchema';
import { addLog } from '../../common/system/log';
import { ChatItemValueTypeEnum, ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants';
import { delFileByFileIdList, getGFSCollection } from '../../common/file/gridfs/controller';
import { BucketNameEnum } from '@fastgpt/global/common/file/constants';
import { MongoChat } from './chatSchema';
@@ -80,52 +80,6 @@ export const addCustomFeedbacks = async ({
}
};
/*
Update the user selected index of the interactive module
*/
export const updateUserSelectedResult = async ({
appId,
chatId,
userSelectedVal
}: {
appId: string;
chatId?: string;
userSelectedVal: string;
}) => {
if (!chatId) return;
try {
const chatItem = await MongoChatItem.findOne(
{ appId, chatId, obj: ChatRoleEnum.AI },
'value'
).sort({ _id: -1 });
if (!chatItem) return;
const interactiveValue = chatItem.value.find(
(v) => v.type === ChatItemValueTypeEnum.interactive
);
if (
!interactiveValue ||
interactiveValue.type !== ChatItemValueTypeEnum.interactive ||
!interactiveValue.interactive?.params
)
return;
interactiveValue.interactive = {
...interactiveValue.interactive,
params: {
...interactiveValue.interactive.params,
userSelectedVal
}
};
await chatItem.save();
} catch (error) {
addLog.error('updateUserSelectedResult error', error);
}
};
/*
Delete chat files
1. ChatId: Delete one chat files

View File

@@ -1,6 +1,10 @@
import type { AIChatItemType, UserChatItemType } from '@fastgpt/global/core/chat/type.d';
import { MongoApp } from '../app/schema';
import { ChatSourceEnum } from '@fastgpt/global/core/chat/constants';
import {
ChatItemValueTypeEnum,
ChatRoleEnum,
ChatSourceEnum
} from '@fastgpt/global/core/chat/constants';
import { MongoChatItem } from './chatItemSchema';
import { MongoChat } from './chatSchema';
import { addLog } from '../../common/system/log';
@@ -111,3 +115,85 @@ export async function saveChat({
addLog.error(`update chat history error`, error);
}
}
export const updateInteractiveChat = async ({
chatId,
appId,
teamId,
tmbId,
userSelectedVal,
aiResponse,
newVariables,
newTitle
}: {
chatId: string;
appId: string;
teamId: string;
tmbId: string;
userSelectedVal: string;
aiResponse: AIChatItemType & { dataId?: string };
newVariables?: Record<string, any>;
newTitle: string;
}) => {
if (!chatId) return;
const chatItem = await MongoChatItem.findOne({ appId, chatId, obj: ChatRoleEnum.AI }).sort({
_id: -1
});
if (!chatItem || chatItem.obj !== ChatRoleEnum.AI) return;
const interactiveValue = chatItem.value[chatItem.value.length - 1];
if (
!interactiveValue ||
interactiveValue.type !== ChatItemValueTypeEnum.interactive ||
!interactiveValue.interactive?.params
) {
return;
}
interactiveValue.interactive = {
...interactiveValue.interactive,
params: {
...interactiveValue.interactive.params,
userSelectedVal
}
};
if (aiResponse.customFeedbacks) {
chatItem.customFeedbacks = chatItem.customFeedbacks
? [...chatItem.customFeedbacks, ...aiResponse.customFeedbacks]
: aiResponse.customFeedbacks;
}
if (aiResponse.responseData) {
chatItem.responseData = chatItem.responseData
? [...chatItem.responseData, ...aiResponse.responseData]
: aiResponse.responseData;
}
if (aiResponse.value) {
chatItem.value = chatItem.value ? [...chatItem.value, ...aiResponse.value] : aiResponse.value;
}
await mongoSessionRun(async (session) => {
await chatItem.save({ session });
await MongoChat.updateOne(
{
appId,
chatId
},
{
$set: {
variables: newVariables,
title: newTitle,
updateTime: new Date()
}
},
{
session
}
);
});
};

View File

@@ -121,46 +121,22 @@ export const loadRequestMessages = async ({
const imageRegex =
/(https?:\/\/[^\s/$.?#].[^\s]*\.(?:png|jpe?g|gif|webp|bmp|tiff?|svg|ico|heic|avif))/i;
const result: { type: 'text' | 'image'; value: string }[] = [];
let lastIndex = 0;
let match;
const result: ChatCompletionContentPart[] = [];
// 使用正则表达式查找所有匹配项
while ((match = imageRegex.exec(input.slice(lastIndex))) !== null) {
const textBefore = input.slice(lastIndex, lastIndex + match.index);
// 如果图片URL前有文本添加文本部分
if (textBefore) {
result.push({ type: 'text', value: textBefore });
}
// 添加图片URL
result.push({ type: 'image', value: match[0] });
lastIndex += match.index + match[0].length;
}
// 添加剩余的文本(如果有的话)
if (lastIndex < input.length) {
result.push({ type: 'text', value: input.slice(lastIndex) });
}
return result
.map((item) => {
if (item.type === 'text') {
return { type: 'text', text: item.value };
// 提取所有HTTPS图片URL并添加到result开头
const httpsImages = input.match(imageRegex) || [];
httpsImages.forEach((url) => {
result.push({
type: 'image_url',
image_url: {
url: url
}
if (item.type === 'image') {
return {
type: 'image_url',
image_url: {
url: item.value
}
};
}
return { type: 'text', text: item.value };
})
.filter(Boolean) as ChatCompletionContentPart[];
});
});
// 添加原始input作为文本
result.push({ type: 'text', text: input });
return result;
}
// Load image
const parseUserContent = async (content: string | ChatCompletionContentPart[]) => {
@@ -235,11 +211,40 @@ export const loadRequestMessages = async ({
};
}
}
if (item.role === ChatCompletionRequestMessageRoleEnum.Assistant) {
if (item.content !== undefined && !item.content) return;
if (Array.isArray(item.content) && item.content.length === 0) return;
}
return item;
})
.filter(Boolean) as ChatCompletionMessageParam[];
};
/*
Merge data for some consecutive roles
1. Contiguous assistant and both have content, merge content
*/
const mergeConsecutiveMessages = (
messages: ChatCompletionMessageParam[]
): ChatCompletionMessageParam[] => {
return messages.reduce((mergedMessages: ChatCompletionMessageParam[], currentMessage) => {
const lastMessage = mergedMessages[mergedMessages.length - 1];
if (
lastMessage &&
currentMessage.role === ChatCompletionRequestMessageRoleEnum.Assistant &&
lastMessage.role === ChatCompletionRequestMessageRoleEnum.Assistant &&
typeof lastMessage.content === 'string' &&
typeof currentMessage.content === 'string'
) {
lastMessage.content += currentMessage ? `\n${currentMessage.content}` : '';
} else {
mergedMessages.push(currentMessage);
}
return mergedMessages;
}, []);
};
if (messages.length === 0) {
return Promise.reject('core.chat.error.Messages empty');
@@ -269,11 +274,22 @@ export const loadRequestMessages = async ({
...item,
content: await parseUserContent(item.content)
};
} else if (item.role === ChatCompletionRequestMessageRoleEnum.Assistant) {
return {
role: item.role,
content: item.content,
function_call: item.function_call,
name: item.name,
refusal: item.refusal,
tool_calls: item.tool_calls
};
} else {
return item;
}
})
)) as ChatCompletionMessageParam[];
return clearInvalidMessages(loadMessages) as SdkChatCompletionMessageParam[];
return mergeConsecutiveMessages(
clearInvalidMessages(loadMessages)
) as SdkChatCompletionMessageParam[];
};

View File

@@ -10,6 +10,7 @@ import { ClientSession } from '../../../common/mongo';
import { getLLMModel, getVectorModel } from '../../ai/model';
import { addLog } from '../../../common/system/log';
import { getCollectionWithDataset } from '../controller';
import { mongoSessionRun } from '../../../common/mongo/sessionRun';
export const lockTrainingDataByTeamId = async (teamId: string): Promise<any> => {
try {
@@ -64,7 +65,7 @@ export async function pushDataListToTrainingQueue({
vectorModel: string;
session?: ClientSession;
} & PushDatasetDataProps): Promise<PushDatasetDataResponse> {
const checkModelValid = async () => {
const { model, maxToken, weight } = await (async () => {
const agentModelData = getLLMModel(agentModel);
if (!agentModelData) {
return Promise.reject(`File model ${agentModel} is inValid`);
@@ -91,9 +92,16 @@ export async function pushDataListToTrainingQueue({
}
return Promise.reject(`Training mode "${trainingMode}" is inValid`);
};
})();
const { model, maxToken, weight } = await checkModelValid();
// filter repeat or equal content
const set = new Set();
const filterResult: Record<string, PushDatasetDataChunkProps[]> = {
success: [],
overToken: [],
repeat: [],
error: []
};
// format q and a, remove empty char
data.forEach((item) => {
@@ -108,19 +116,8 @@ export async function pushDataListToTrainingQueue({
};
})
.filter(Boolean);
});
// filter repeat or equal content
const set = new Set();
const filterResult: Record<string, PushDatasetDataChunkProps[]> = {
success: [],
overToken: [],
repeat: [],
error: []
};
// filter repeat content
data.forEach((item) => {
// filter repeat content
if (!item.q) {
filterResult.error.push(item);
return;
@@ -150,40 +147,55 @@ export async function pushDataListToTrainingQueue({
const failedDocuments: PushDatasetDataChunkProps[] = [];
// 使用 insertMany 批量插入
try {
await MongoDatasetTraining.insertMany(
filterResult.success.map((item) => ({
teamId,
tmbId,
datasetId,
collectionId,
billId,
mode: trainingMode,
prompt,
model,
q: item.q,
a: item.a,
chunkIndex: item.chunkIndex ?? 0,
weight: weight ?? 0,
indexes: item.indexes
})),
{
session,
ordered: false
}
);
} catch (error: any) {
addLog.error(`Insert error`, error);
// 如果有错误,将失败的文档添加到失败列表中
error.writeErrors?.forEach((writeError: any) => {
failedDocuments.push(data[writeError.index]);
});
console.log('failed', failedDocuments);
}
const batchSize = 200;
const insertData = async (startIndex: number, session: ClientSession) => {
const list = filterResult.success.slice(startIndex, startIndex + batchSize);
// 对于失败的文档,尝试单独插入
for await (const item of failedDocuments) {
await MongoDatasetTraining.create(item);
if (list.length === 0) return;
try {
await MongoDatasetTraining.insertMany(
list.map((item) => ({
teamId,
tmbId,
datasetId,
collectionId,
billId,
mode: trainingMode,
prompt,
model,
q: item.q,
a: item.a,
chunkIndex: item.chunkIndex ?? 0,
weight: weight ?? 0,
indexes: item.indexes
})),
{
session,
ordered: true
}
);
} catch (error: any) {
addLog.error(`Insert error`, error);
// 如果有错误,将失败的文档添加到失败列表中
error.writeErrors?.forEach((writeError: any) => {
failedDocuments.push(data[writeError.index]);
});
console.log('failed', failedDocuments);
}
console.log(startIndex, '===');
// 对于失败的文档,尝试单独插入
await MongoDatasetTraining.create(failedDocuments, { session });
return insertData(startIndex + batchSize, session);
};
if (session) {
await insertData(0, session);
} else {
await mongoSessionRun(async (session) => {
await insertData(0, session);
});
}
delete filterResult.success;

View File

@@ -0,0 +1,3 @@
export const WORKFLOW_MAX_RUN_TIMES = process.env.WORKFLOW_MAX_RUN_TIMES
? parseInt(process.env.WORKFLOW_MAX_RUN_TIMES)
: 500;

View File

@@ -31,7 +31,7 @@ type Response = DispatchNodeResultType<{
export const dispatchAppRequest = async (props: Props): Promise<Response> => {
const {
app: workflowApp,
runningAppInfo,
workflowStreamResponse,
histories,
query,
@@ -45,7 +45,7 @@ export const dispatchAppRequest = async (props: Props): Promise<Response> => {
// 检查该工作流的tmb是否有调用该app的权限不是校验对话的人是否有权限
const { app: appData } = await authAppByTmbId({
appId: app.id,
tmbId: workflowApp.tmbId,
tmbId: runningAppInfo.tmbId,
per: ReadPermissionVal
});
@@ -61,7 +61,11 @@ export const dispatchAppRequest = async (props: Props): Promise<Response> => {
const { flowResponses, flowUsages, assistantResponses } = await dispatchWorkFlow({
...props,
app: appData,
runningAppInfo: {
id: String(appData._id),
teamId: String(appData.teamId),
tmbId: String(appData.tmbId)
},
runtimeNodes: storeNodes2RuntimeNodes(
appData.modules,
getWorkflowEntryNodeIds(appData.modules)

View File

@@ -194,7 +194,10 @@ export const runToolWithFunctionCall = async (
isEntry: true,
inputs: updateToolInputValue({ params: startParams, inputs: item.inputs })
}
: item
: {
...item,
isEntry: false
}
)
});
@@ -295,7 +298,10 @@ export const runToolWithFunctionCall = async (
dispatchFlowResponse,
totalTokens: response?.totalTokens ? response.totalTokens + tokens : tokens,
completeMessages: filterMessages,
assistantResponses: toolNodeAssistants
assistantResponses: toolNodeAssistants,
runTimes:
(response?.runTimes || 0) +
flatToolsResponseData.reduce((sum, item) => sum + item.runTimes, 0)
};
}
@@ -307,7 +313,10 @@ export const runToolWithFunctionCall = async (
{
dispatchFlowResponse,
totalTokens: response?.totalTokens ? response.totalTokens + tokens : tokens,
assistantResponses: toolNodeAssistants
assistantResponses: toolNodeAssistants,
runTimes:
(response?.runTimes || 0) +
flatToolsResponseData.reduce((sum, item) => sum + item.runTimes, 0)
}
);
} else {
@@ -327,7 +336,8 @@ export const runToolWithFunctionCall = async (
dispatchFlowResponse: response?.dispatchFlowResponse || [],
totalTokens: response?.totalTokens ? response.totalTokens + tokens : tokens,
completeMessages,
assistantResponses: [...assistantResponses, ...toolNodeAssistant.value]
assistantResponses: [...assistantResponses, ...toolNodeAssistant.value],
runTimes: (response?.runTimes || 0) + 1
};
}
};

View File

@@ -125,7 +125,8 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
dispatchFlowResponse, // tool flow response
totalTokens,
completeMessages = [], // The actual message sent to AI(just save text)
assistantResponses = [] // FastGPT system store assistant.value response
assistantResponses = [], // FastGPT system store assistant.value response
runTimes
} = await (async () => {
const adaptMessages = chats2GPTMessages({ messages, reserveId: false });
@@ -134,6 +135,7 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
...props,
toolNodes,
toolModel,
maxRunToolTimes: 30,
messages: adaptMessages
});
}
@@ -194,6 +196,7 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
const previewAssistantResponses = filterToolResponseToPreview(assistantResponses);
return {
[DispatchNodeResponseKeyEnum.runTimes]: runTimes,
[NodeOutputKeyEnum.answerText]: previewAssistantResponses
.filter((item) => item.text?.content)
.map((item) => item.text?.content || '')

View File

@@ -180,7 +180,8 @@ export const runToolWithPromptCall = async (
dispatchFlowResponse: response?.dispatchFlowResponse || [],
totalTokens: response?.totalTokens ? response.totalTokens + tokens : tokens,
completeMessages,
assistantResponses: [...assistantResponses, ...toolNodeAssistant.value]
assistantResponses: [...assistantResponses, ...toolNodeAssistant.value],
runTimes: (response?.runTimes || 0) + 1
};
}
@@ -226,7 +227,10 @@ export const runToolWithPromptCall = async (
isEntry: true,
inputs: updateToolInputValue({ params: startParams, inputs: item.inputs })
}
: item
: {
...item,
isEntry: false
}
)
});
@@ -315,7 +319,8 @@ ANSWER: `;
dispatchFlowResponse,
totalTokens: response?.totalTokens ? response.totalTokens + tokens : tokens,
completeMessages: filterMessages,
assistantResponses: toolNodeAssistants
assistantResponses: toolNodeAssistants,
runTimes: (response?.runTimes || 0) + toolsRunResponse.moduleRunResponse.runTimes
};
}
@@ -327,7 +332,8 @@ ANSWER: `;
{
dispatchFlowResponse,
totalTokens: response?.totalTokens ? response.totalTokens + tokens : tokens,
assistantResponses: toolNodeAssistants
assistantResponses: toolNodeAssistants,
runTimes: (response?.runTimes || 0) + toolsRunResponse.moduleRunResponse.runTimes
}
);
};

View File

@@ -25,7 +25,7 @@ import { GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt';
import { AIChatItemType } from '@fastgpt/global/core/chat/type';
import { updateToolInputValue } from './utils';
import { computedMaxToken, computedTemperature } from '../../../../ai/utils';
import { sliceStrStartEnd } from '@fastgpt/global/common/string/tools';
import { getNanoid, sliceStrStartEnd } from '@fastgpt/global/common/string/tools';
import { addLog } from '../../../../../common/system/log';
type ToolRunResponseType = {
@@ -45,13 +45,12 @@ export const runToolWithToolChoice = async (
messages: ChatCompletionMessageParam[];
toolNodes: ToolNodeItemType[];
toolModel: LLMModelItemType;
maxRunToolTimes: number;
},
response?: RunToolResponse
): Promise<RunToolResponse> => {
const { messages, toolNodes, toolModel, maxRunToolTimes, ...workflowProps } = props;
const {
toolModel,
toolNodes,
messages,
res,
requestOrigin,
runtimeNodes,
@@ -59,7 +58,12 @@ export const runToolWithToolChoice = async (
stream,
workflowStreamResponse,
params: { temperature = 0, maxToken = 4000, aiChatVision }
} = props;
} = workflowProps;
if (maxRunToolTimes <= 0 && response) {
return response;
}
const assistantResponses = response?.assistantResponses || [];
const tools: ChatCompletionTool[] = toolNodes.map((item) => {
@@ -196,7 +200,7 @@ export const runToolWithToolChoice = async (
})();
const toolRunResponse = await dispatchWorkFlow({
...props,
...workflowProps,
isToolCall: true,
runtimeNodes: runtimeNodes.map((item) =>
item.nodeId === toolNode.nodeId
@@ -205,7 +209,10 @@ export const runToolWithToolChoice = async (
isEntry: true,
inputs: updateToolInputValue({ params: startParams, inputs: item.inputs })
}
: item
: {
...item,
isEntry: false
}
)
});
@@ -252,11 +259,22 @@ export const runToolWithToolChoice = async (
role: ChatCompletionRequestMessageRoleEnum.Assistant,
tool_calls: toolCalls
};
/*
...
user
assistant: tool data
*/
const concatToolMessages = [
...requestMessages,
assistantToolMsgParams
] as ChatCompletionMessageParam[];
const tokens = await countGptMessagesTokens(concatToolMessages, tools);
/*
...
user
assistant: tool data
tool: tool response
*/
const completeMessages = [
...concatToolMessages,
...toolsRunResponse.map((item) => item?.toolMsgParams)
@@ -307,19 +325,26 @@ export const runToolWithToolChoice = async (
dispatchFlowResponse,
totalTokens: response?.totalTokens ? response.totalTokens + tokens : tokens,
completeMessages,
assistantResponses: toolNodeAssistants
assistantResponses: toolNodeAssistants,
runTimes:
(response?.runTimes || 0) +
flatToolsResponseData.reduce((sum, item) => sum + item.runTimes, 0)
};
}
return runToolWithToolChoice(
{
...props,
maxRunToolTimes: maxRunToolTimes - 1,
messages: completeMessages
},
{
dispatchFlowResponse,
totalTokens: response?.totalTokens ? response.totalTokens + tokens : tokens,
assistantResponses: toolNodeAssistants
assistantResponses: toolNodeAssistants,
runTimes:
(response?.runTimes || 0) +
flatToolsResponseData.reduce((sum, item) => sum + item.runTimes, 0)
}
);
} else {
@@ -339,10 +364,12 @@ export const runToolWithToolChoice = async (
dispatchFlowResponse: response?.dispatchFlowResponse || [],
totalTokens: response?.totalTokens ? response.totalTokens + tokens : tokens,
completeMessages,
assistantResponses: [...assistantResponses, ...toolNodeAssistant.value]
assistantResponses: [...assistantResponses, ...toolNodeAssistant.value],
runTimes: (response?.runTimes || 0) + 1
};
}
} catch (error) {
console.log(error);
addLog.warn(`LLM response error`, {
requestBody
});
@@ -367,6 +394,7 @@ async function streamResponse({
});
let textAnswer = '';
let callingTool: { name: string; arguments: string } | null = null;
let toolCalls: ChatCompletionMessageToolCall[] = [];
for await (const part of stream) {
@@ -390,69 +418,71 @@ async function streamResponse({
});
} else if (responseChoice?.tool_calls?.[0]) {
const toolCall: ChatCompletionMessageToolCall = responseChoice.tool_calls[0];
// In a stream response, only one tool is returned at a time. If have id, description is executing a tool
if (toolCall.id) {
const toolNode = toolNodes.find((item) => item.nodeId === toolCall.function?.name);
if (toolCall.id || callingTool) {
// Start call tool
if (toolCall.id) {
callingTool = {
name: toolCall.function.name || '',
arguments: toolCall.function.arguments || ''
};
} else if (callingTool) {
// Continue call
callingTool.name += toolCall.function.name || '';
callingTool.arguments += toolCall.function.arguments || '';
}
const toolFunction = callingTool!;
const toolNode = toolNodes.find((item) => item.nodeId === toolFunction.name);
if (toolNode) {
if (toolCall.function?.arguments === undefined) {
toolCall.function.arguments = '';
}
// New tool, add to list.
const toolId = getNanoid();
toolCalls.push({
...toolCall,
id: toolId,
function: toolFunction,
toolName: toolNode.name,
toolAvatar: toolNode.avatar
});
// Get last tool call
const lastToolCall = toolCalls[toolCalls.length - 1];
// new tool
if (lastToolCall?.id !== toolCall.id) {
toolCalls.push({
...toolCall,
toolName: toolNode.name,
toolAvatar: toolNode.avatar
});
workflowStreamResponse?.({
event: SseResponseEventEnum.toolCall,
data: {
tool: {
id: toolCall.id,
toolName: toolNode.name,
toolAvatar: toolNode.avatar,
functionName: toolCall.function.name,
params: toolCall.function.arguments,
response: ''
}
workflowStreamResponse?.({
event: SseResponseEventEnum.toolCall,
data: {
tool: {
id: toolId,
toolName: toolNode.name,
toolAvatar: toolNode.avatar,
functionName: toolFunction.name,
params: toolFunction?.arguments ?? '',
response: ''
}
});
continue;
}
// last tool, update params
} else {
continue;
}
}
/* arg 插入最后一个工具的参数里 */
const arg: string = toolCall?.function?.arguments ?? '';
const currentTool = toolCalls[toolCalls.length - 1];
if (currentTool) {
currentTool.function.arguments += arg;
workflowStreamResponse?.({
write,
event: SseResponseEventEnum.toolParams,
data: {
tool: {
id: currentTool.id,
toolName: '',
toolAvatar: '',
params: arg,
response: ''
}
}
});
});
callingTool = null;
}
} else {
/* arg 插入最后一个工具的参数里 */
const arg: string = toolCall?.function?.arguments ?? '';
const currentTool = toolCalls[toolCalls.length - 1];
if (currentTool && arg) {
currentTool.function.arguments += arg;
workflowStreamResponse?.({
write,
event: SseResponseEventEnum.toolParams,
data: {
tool: {
id: currentTool.id,
toolName: '',
toolAvatar: '',
params: arg,
response: ''
}
}
});
}
}
}
}

View File

@@ -8,6 +8,7 @@ import type { RuntimeNodeItemType } from '@fastgpt/global/core/workflow/runtime/
import { ChatNodeUsageType } from '@fastgpt/global/support/wallet/bill/type';
import type { DispatchFlowResponse } from '../../type.d';
import { AIChatItemValueItemType, ChatItemValueItemType } from '@fastgpt/global/core/chat/type';
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
export type DispatchToolModuleProps = ModuleDispatchProps<{
[NodeInputKeyEnum.history]?: ChatItemType[];
@@ -25,6 +26,7 @@ export type RunToolResponse = {
totalTokens: number;
completeMessages?: ChatCompletionMessageParam[];
assistantResponses?: AIChatItemValueItemType[];
[DispatchNodeResponseKeyEnum.runTimes]: number;
};
export type ToolNodeItemType = RuntimeNodeItemType & {
toolParams: RuntimeNodeItemType['inputs'];

View File

@@ -37,7 +37,7 @@ export async function dispatchDatasetSearch(
props: DatasetSearchProps
): Promise<DatasetSearchResponse> {
const {
teamId,
runningAppInfo: { teamId },
histories,
node,
params: {

View File

@@ -6,7 +6,9 @@ import {
import { NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
import type {
ChatDispatchProps,
ModuleDispatchProps
DispatchNodeResultType,
ModuleDispatchProps,
SystemVariablesType
} from '@fastgpt/global/core/workflow/runtime/type';
import type { RuntimeNodeItemType } from '@fastgpt/global/core/workflow/runtime/type.d';
import type {
@@ -63,7 +65,7 @@ import {
InteractiveNodeResponseItemType,
UserSelectInteractive
} from '@fastgpt/global/core/workflow/template/system/userSelect/type';
import { dispatchRunAppNode } from './agent/runApp';
import { dispatchRunAppNode } from './plugin/runApp';
const callbackMap: Record<FlowNodeTypeEnum, Function> = {
[FlowNodeTypeEnum.workflowStart]: dispatchWorkflowStart,
@@ -117,6 +119,31 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
...props
} = data;
// 初始化深度和自动增加深度,避免无限嵌套
if (!props.workflowDispatchDeep) {
props.workflowDispatchDeep = 1;
} else {
props.workflowDispatchDeep += 1;
}
if (props.workflowDispatchDeep > 20) {
return {
flowResponses: [],
flowUsages: [],
debugResponse: {
finishedNodes: [],
finishedEdges: [],
nextStepRunNodes: []
},
[DispatchNodeResponseKeyEnum.runTimes]: 1,
[DispatchNodeResponseKeyEnum.assistantResponses]: [],
[DispatchNodeResponseKeyEnum.toolResponses]: null,
newVariables: removeSystemVariable(variables)
};
}
let workflowRunTimes = 0;
// set sse response headers
if (stream && res) {
res.setHeader('Content-Type', 'text/event-stream;charset=utf-8');
@@ -135,6 +162,13 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
let chatNodeUsages: ChatNodeUsageType[] = [];
let toolRunResponse: ToolRunResponseItemType;
let debugNextStepRunNodes: RuntimeNodeItemType[] = [];
// 记录交互节点,交互节点需要在工作流完全结束后再进行计算
let workflowInteractiveResponse:
| {
entryNodeIds: string[];
interactiveResponse: UserSelectInteractive;
}
| undefined;
/* Store special response field */
function pushStore(
@@ -144,15 +178,21 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
responseData,
nodeDispatchUsages,
toolResponses,
assistantResponses
}: {
[NodeOutputKeyEnum.answerText]?: string;
[DispatchNodeResponseKeyEnum.nodeResponse]?: ChatHistoryItemResType;
[DispatchNodeResponseKeyEnum.nodeDispatchUsages]?: ChatNodeUsageType[];
[DispatchNodeResponseKeyEnum.toolResponses]?: ToolRunResponseItemType;
[DispatchNodeResponseKeyEnum.assistantResponses]?: AIChatItemValueItemType[]; // tool module, save the response value
}
assistantResponses,
rewriteHistories,
runTimes = 1
}: Omit<
DispatchNodeResultType<{
[NodeOutputKeyEnum.answerText]?: string;
[DispatchNodeResponseKeyEnum.nodeResponse]?: ChatHistoryItemResType;
}>,
'nodeResponse'
>
) {
// Add run times
workflowRunTimes += runTimes;
props.maxRunTimes -= runTimes;
if (responseData) {
chatResponses.push(responseData);
}
@@ -181,12 +221,19 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
});
}
}
if (rewriteHistories) {
histories = rewriteHistories;
}
}
/* Pass the output of the node, to get next nodes and update edge status */
function nodeOutput(
node: RuntimeNodeItemType,
result: Record<string, any> = {}
): RuntimeNodeItemType[] {
): {
nextStepActiveNodes: RuntimeNodeItemType[];
nextStepSkipNodes: RuntimeNodeItemType[];
} {
pushStore(node, result);
// Assign the output value to the next node
@@ -211,16 +258,32 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
}
});
const nextStepNodes = runtimeNodes.filter((node) => {
return targetEdges.some((item) => item.target === node.nodeId);
const nextStepActiveNodes: RuntimeNodeItemType[] = [];
const nextStepSkipNodes: RuntimeNodeItemType[] = [];
runtimeNodes.forEach((node) => {
if (targetEdges.some((item) => item.target === node.nodeId && item.status === 'active')) {
nextStepActiveNodes.push(node);
}
if (targetEdges.some((item) => item.target === node.nodeId && item.status === 'skipped')) {
nextStepSkipNodes.push(node);
}
});
if (props.mode === 'debug') {
debugNextStepRunNodes = debugNextStepRunNodes.concat(nextStepNodes);
return [];
debugNextStepRunNodes = debugNextStepRunNodes.concat([
...nextStepActiveNodes,
...nextStepSkipNodes
]);
return {
nextStepActiveNodes: [],
nextStepSkipNodes: []
};
}
return nextStepNodes;
return {
nextStepActiveNodes,
nextStepSkipNodes
};
}
/* Have interactive result, computed edges and node outputs */
@@ -270,10 +333,8 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
};
}
// 每个节点 运行/跳过 ,初始化边的状态
function nodeRunAfterHook(node: RuntimeNodeItemType) {
node.isEntry = false;
// 每个节点确定 运行/跳过 ,初始化边的状态
function nodeRunBeforeHook(node: RuntimeNodeItemType) {
runtimeEdges.forEach((item) => {
if (item.target === node.nodeId) {
item.status = 'waiting';
@@ -281,69 +342,81 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
});
}
/* Check node run/skip or wait */
function checkNodeCanRun(nodes: RuntimeNodeItemType[] = []): Promise<any> {
return Promise.all(
nodes.map(async (node) => {
const status = checkNodeRunStatus({
node,
runtimeEdges
});
async function checkNodeCanRun(
node: RuntimeNodeItemType,
skippedNodeIdList = new Set<string>()
): Promise<RuntimeNodeItemType[]> {
if (res?.closed || props.maxRunTimes <= 0) return [];
// Thread avoidance
await surrenderProcess();
if (res?.closed || props.maxRunTimes <= 0) return;
addLog.debug(`Run node`, { maxRunTimes: props.maxRunTimes, appId: props.runningAppInfo.id });
addLog.debug(`Run node`, { maxRunTimes: props.maxRunTimes, uid: user._id });
// Thread avoidance
await surrenderProcess();
if (status === 'run') {
addLog.debug(`[dispatchWorkFlow] nodeRunWithActive: ${node.name}`);
return nodeRunWithActive(node);
}
if (status === 'skip') {
addLog.debug(`[dispatchWorkFlow] nodeRunWithSkip: ${node.name}`);
return nodeRunWithSkip(node);
}
return;
})
).then((result) => {
props.maxRunTimes--;
const flat = result.flat().filter(Boolean) as unknown as {
node: RuntimeNodeItemType;
runStatus: 'run' | 'skip';
result: Record<string, any>;
}[];
// If there are no running nodes, the workflow is complete
if (flat.length === 0) return;
// Update the node output at the end of the run and get the next nodes
const nextNodes = flat.map((item) => nodeOutput(item.node, item.result)).flat();
// Remove repeat nodes(Make sure that the node is only executed once)
const filterNextNodes = nextNodes.filter(
(node, index, self) => self.findIndex((t) => t.nodeId === node.nodeId) === index
);
// In the current version, only one interactive node is allowed at the same time
const haveInteractiveResponse = flat
.map((response) => {
const interactiveResponse = response.result?.[DispatchNodeResponseKeyEnum.interactive];
if (interactiveResponse) {
chatAssistantResponse.push(
handleInteractiveResult({
entryNodeIds: [response.node.nodeId],
interactiveResponse
})
);
return 1;
}
})
.filter(Boolean);
if (haveInteractiveResponse.length > 0) return;
return checkNodeCanRun(filterNextNodes);
// Get node run status by edges
const status = checkNodeRunStatus({
node,
runtimeEdges
});
const nodeRunResult = await (() => {
if (status === 'run') {
nodeRunBeforeHook(node);
addLog.debug(`[dispatchWorkFlow] nodeRunWithActive: ${node.name}`);
return nodeRunWithActive(node);
}
if (status === 'skip' && !skippedNodeIdList.has(node.nodeId)) {
nodeRunBeforeHook(node);
props.maxRunTimes -= 0.1;
skippedNodeIdList.add(node.nodeId);
addLog.debug(`[dispatchWorkFlow] nodeRunWithSkip: ${node.name}`);
return nodeRunWithSkip(node);
}
})();
if (!nodeRunResult) return [];
// In the current version, only one interactive node is allowed at the same time
const interactiveResponse = nodeRunResult.result?.[DispatchNodeResponseKeyEnum.interactive];
if (interactiveResponse) {
workflowInteractiveResponse = {
entryNodeIds: [nodeRunResult.node.nodeId],
interactiveResponse
};
return [];
}
// Update the node output at the end of the run and get the next nodes
let { nextStepActiveNodes, nextStepSkipNodes } = nodeOutput(
nodeRunResult.node,
nodeRunResult.result
);
// Remove repeat nodes(Make sure that the node is only executed once)
nextStepActiveNodes = nextStepActiveNodes.filter(
(node, index, self) => self.findIndex((t) => t.nodeId === node.nodeId) === index
);
nextStepSkipNodes = nextStepSkipNodes.filter(
(node, index, self) => self.findIndex((t) => t.nodeId === node.nodeId) === index
);
// Run next nodes先运行 run 的,再运行 skip 的)
const nextStepActiveNodesResults = (
await Promise.all(nextStepActiveNodes.map((node) => checkNodeCanRun(node)))
).flat();
// 如果已经 active 运行过,不再执行 skipactive 中有闭环)
nextStepSkipNodes = nextStepSkipNodes.filter(
(node) => !nextStepActiveNodesResults.some((item) => item.nodeId === node.nodeId)
);
const nextStepSkipNodesResults = (
await Promise.all(nextStepSkipNodes.map((node) => checkNodeCanRun(node, skippedNodeIdList)))
).flat();
return [
...nextStepActiveNodes,
...nextStepSkipNodes,
...nextStepActiveNodesResults,
...nextStepSkipNodesResults
];
}
/* Inject data into module input */
function getNodeRunParams(node: RuntimeNodeItemType) {
@@ -396,7 +469,11 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
return params;
}
async function nodeRunWithActive(node: RuntimeNodeItemType) {
async function nodeRunWithActive(node: RuntimeNodeItemType): Promise<{
node: RuntimeNodeItemType;
runStatus: 'run';
result: Record<string, any>;
}> {
// push run status messages
if (node.showStatus) {
props.workflowStreamResponse?.({
@@ -454,8 +531,6 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
dispatchRes[item.key] = valueTypeFormat(item.defaultValue, item.valueType);
});
nodeRunAfterHook(node);
return {
node,
runStatus: 'run',
@@ -465,10 +540,13 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
}
};
}
async function nodeRunWithSkip(node: RuntimeNodeItemType) {
// 其后所有target的节点都设置为skip
async function nodeRunWithSkip(node: RuntimeNodeItemType): Promise<{
node: RuntimeNodeItemType;
runStatus: 'skip';
result: Record<string, any>;
}> {
// Set target edges status to skipped
const targetEdges = runtimeEdges.filter((item) => item.source === node.nodeId);
nodeRunAfterHook(node);
return {
node,
@@ -483,10 +561,13 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
const entryNodes = runtimeNodes.filter((item) => item.isEntry);
// reset entry
// runtimeNodes.forEach((item) => {
// item.isEntry = false;
// });
await checkNodeCanRun(entryNodes);
runtimeNodes.forEach((item) => {
// Interactive node is not the entry node, return interactive result
if (item.flowNodeType !== FlowNodeTypeEnum.userSelect) {
item.isEntry = false;
}
});
await Promise.all(entryNodes.map((node) => checkNodeCanRun(node)));
// focus try to run pluginOutput
const pluginOutputModule = runtimeNodes.find(
@@ -496,6 +577,15 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
await nodeRunWithActive(pluginOutputModule);
}
// Interactive node
if (workflowInteractiveResponse) {
const interactiveResult = handleInteractiveResult({
entryNodeIds: workflowInteractiveResponse.entryNodeIds,
interactiveResponse: workflowInteractiveResponse.interactiveResponse
});
chatAssistantResponse.push(interactiveResult);
}
return {
flowResponses: chatResponses,
flowUsages: chatNodeUsages,
@@ -504,6 +594,7 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
finishedEdges: runtimeEdges,
nextStepRunNodes: debugNextStepRunNodes
},
[DispatchNodeResponseKeyEnum.runTimes]: workflowRunTimes,
[DispatchNodeResponseKeyEnum.assistantResponses]:
mergeAssistantResponseAnswerText(chatAssistantResponse),
[DispatchNodeResponseKeyEnum.toolResponses]: toolRunResponse,
@@ -514,13 +605,15 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
/* get system variable */
export function getSystemVariable({
user,
app,
runningAppInfo,
chatId,
responseChatItemId,
histories = []
}: Props) {
histories = [],
uid
}: Props): SystemVariablesType {
return {
appId: String(app._id),
userId: uid,
appId: String(runningAppInfo.id),
chatId,
responseChatItemId,
histories,

View File

@@ -1,7 +1,4 @@
import {
DispatchNodeResponseKeyEnum,
SseResponseEventEnum
} from '@fastgpt/global/core/workflow/runtime/constants';
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
import {
DispatchNodeResultType,
ModuleDispatchProps
@@ -12,8 +9,6 @@ import type {
UserSelectInteractive,
UserSelectOptionItemType
} from '@fastgpt/global/core/workflow/template/system/userSelect/type';
import { updateUserSelectedResult } from '../../../chat/controller';
import { textAdaptGptResponse } from '@fastgpt/global/core/workflow/runtime/utils';
import { chatValue2RuntimePrompt } from '@fastgpt/global/core/chat/adapt';
type Props = ModuleDispatchProps<{
@@ -28,37 +23,28 @@ type UserSelectResponse = DispatchNodeResultType<{
export const dispatchUserSelect = async (props: Props): Promise<UserSelectResponse> => {
const {
workflowStreamResponse,
app: { _id: appId },
chatId,
node: { nodeId, isEntry },
histories,
node,
params: { description, userSelectOptions },
query
} = props;
const { nodeId, isEntry } = node;
// Interactive node is not the entry node, return interactive result
if (!isEntry) {
const answerText = description ? `\n${description}` : undefined;
if (answerText) {
workflowStreamResponse?.({
event: SseResponseEventEnum.fastAnswer,
data: textAdaptGptResponse({
text: answerText
})
});
}
return {
[NodeOutputKeyEnum.answerText]: answerText,
[DispatchNodeResponseKeyEnum.interactive]: {
type: 'userSelect',
params: {
description,
userSelectOptions
}
}
};
}
node.isEntry = false;
const { text: userSelectedVal } = chatValue2RuntimePrompt(query);
// Error status
@@ -70,14 +56,8 @@ export const dispatchUserSelect = async (props: Props): Promise<UserSelectRespon
};
}
// Update db
updateUserSelectedResult({
appId,
chatId,
userSelectedVal
});
return {
[DispatchNodeResponseKeyEnum.rewriteHistories]: histories.slice(0, -2), // Removes the current session record as the history of subsequent nodes
[DispatchNodeResponseKeyEnum.skipHandleId]: userSelectOptions
.filter((item) => item.value !== userSelectedVal)
.map((item: any) => getHandleId(nodeId, 'source', item.key)),

View File

@@ -13,6 +13,7 @@ import { authPluginByTmbId } from '../../../../support/permission/app/auth';
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
import { computedPluginUsage } from '../../../app/plugin/utils';
import { filterSystemVariables } from '../utils';
import { getPluginRunUserQuery } from '../../utils';
type RunPluginProps = ModuleDispatchProps<{
[key: string]: any;
@@ -22,9 +23,8 @@ type RunPluginResponse = DispatchNodeResultType<{}>;
export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPluginResponse> => {
const {
node: { pluginId },
app: workflowApp,
runningAppInfo,
mode,
teamId,
params: data // Plugin input
} = props;
@@ -33,9 +33,9 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi
}
// auth plugin
await authPluginByTmbId({
const pluginData = await authPluginByTmbId({
appId: pluginId,
tmbId: workflowApp.tmbId,
tmbId: runningAppInfo.tmbId,
per: ReadPermissionVal
});
@@ -61,10 +61,21 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi
showStatus: false
};
});
const runtimeVariables = {
...filterSystemVariables(props.variables),
appId: String(plugin.id)
};
const { flowResponses, flowUsages, assistantResponses } = await dispatchWorkFlow({
const { flowResponses, flowUsages, assistantResponses, runTimes } = await dispatchWorkFlow({
...props,
variables: filterSystemVariables(props.variables),
runningAppInfo: {
id: String(plugin.id),
teamId: plugin.teamId || '',
tmbId: pluginData?.tmbId || ''
},
variables: runtimeVariables,
query: getPluginRunUserQuery(plugin.nodes, runtimeVariables).value,
chatConfig: {},
runtimeNodes,
runtimeEdges: initWorkflowEdgeStatus(plugin.edges)
});
@@ -81,12 +92,13 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi
return {
assistantResponses,
// responseData, // debug
[DispatchNodeResponseKeyEnum.runTimes]: runTimes,
[DispatchNodeResponseKeyEnum.nodeResponse]: {
moduleLogo: plugin.avatar,
totalPoints: usagePoints,
pluginOutput: output?.pluginOutput,
pluginDetail:
mode === 'test' && plugin.teamId === teamId
mode === 'test' && plugin.teamId === runningAppInfo.teamId
? flowResponses.filter((item) => {
const filterArr = [FlowNodeTypeEnum.pluginOutput];
return !filterArr.includes(item.moduleType as any);

View File

@@ -16,6 +16,7 @@ import { chatValue2RuntimePrompt, runtimePrompt2ChatsValue } from '@fastgpt/glob
import { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type';
import { authAppByTmbId } from '../../../../support/permission/app/auth';
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
import { getAppLatestVersion } from '../../../app/controller';
type Props = ModuleDispatchProps<{
[NodeInputKeyEnum.userChatInput]: string;
@@ -29,7 +30,7 @@ type Response = DispatchNodeResultType<{
export const dispatchRunAppNode = async (props: Props): Promise<Response> => {
const {
app: workflowApp,
runningAppInfo,
histories,
query,
node: { pluginId },
@@ -49,9 +50,10 @@ export const dispatchRunAppNode = async (props: Props): Promise<Response> => {
// Auth the app by tmbId(Not the user, but the workflow user)
const { app: appData } = await authAppByTmbId({
appId: pluginId,
tmbId: workflowApp.tmbId,
tmbId: runningAppInfo.tmbId,
per: ReadPermissionVal
});
const { nodes, edges, chatConfig } = await getAppLatestVersion(pluginId);
// Auto line
workflowStreamResponse?.({
@@ -64,27 +66,31 @@ export const dispatchRunAppNode = async (props: Props): Promise<Response> => {
const chatHistories = getHistories(history, histories);
const { files } = chatValue2RuntimePrompt(query);
// Concat variables
// Rewrite children app variables
const systemVariables = filterSystemVariables(variables);
const childrenRunVariables = {
...systemVariables,
...childrenAppVariables
...childrenAppVariables,
histories: chatHistories,
appId: String(appData._id)
};
const { flowResponses, flowUsages, assistantResponses } = await dispatchWorkFlow({
const { flowResponses, flowUsages, assistantResponses, runTimes } = await dispatchWorkFlow({
...props,
app: appData,
runtimeNodes: storeNodes2RuntimeNodes(
appData.modules,
getWorkflowEntryNodeIds(appData.modules)
),
runtimeEdges: initWorkflowEdgeStatus(appData.edges),
runningAppInfo: {
id: String(appData._id),
teamId: String(appData.teamId),
tmbId: String(appData.tmbId)
},
runtimeNodes: storeNodes2RuntimeNodes(nodes, getWorkflowEntryNodeIds(nodes)),
runtimeEdges: initWorkflowEdgeStatus(edges),
histories: chatHistories,
variables: childrenRunVariables,
query: runtimePrompt2ChatsValue({
files,
text: userChatInput
}),
variables: childrenRunVariables
chatConfig
});
const completeMessages = chatHistories.concat([
@@ -101,6 +107,7 @@ export const dispatchRunAppNode = async (props: Props): Promise<Response> => {
const { text } = chatValue2RuntimePrompt(assistantResponses);
return {
[DispatchNodeResponseKeyEnum.runTimes]: runTimes,
[DispatchNodeResponseKeyEnum.nodeResponse]: {
moduleLogo: appData.avatar,
query: userChatInput,

View File

@@ -15,7 +15,7 @@ type Response = DispatchNodeResultType<{}>;
export const dispatchCustomFeedback = (props: Record<string, any>): Response => {
const {
app: { _id: appId },
runningAppInfo: { id: appId },
chatId,
responseChatItemId: chatItemId,
stream,

View File

@@ -16,6 +16,8 @@ import { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/ty
import { getErrText } from '@fastgpt/global/common/error/utils';
import { textAdaptGptResponse } from '@fastgpt/global/core/workflow/runtime/utils';
import { getSystemPluginCb } from '../../../../../plugins/register';
import { ContentTypes } from '@fastgpt/global/core/workflow/constants';
import { replaceEditorVariable } from '@fastgpt/global/core/workflow/utils';
type PropsArrType = {
key: string;
@@ -29,6 +31,8 @@ type HttpRequestProps = ModuleDispatchProps<{
[NodeInputKeyEnum.httpHeaders]: PropsArrType[];
[NodeInputKeyEnum.httpParams]: PropsArrType[];
[NodeInputKeyEnum.httpJsonBody]: string;
[NodeInputKeyEnum.httpFormBody]: PropsArrType[];
[NodeInputKeyEnum.httpContentType]: ContentTypes;
[NodeInputKeyEnum.addInputParam]: Record<string, any>;
[NodeInputKeyEnum.httpTimeout]?: number;
[key: string]: any;
@@ -40,13 +44,23 @@ type HttpResponse = DispatchNodeResultType<{
const UNDEFINED_SIGN = 'UNDEFINED_SIGN';
const contentTypeMap = {
[ContentTypes.none]: '',
[ContentTypes.formData]: '',
[ContentTypes.xWwwFormUrlencoded]: 'application/x-www-form-urlencoded',
[ContentTypes.json]: 'application/json',
[ContentTypes.xml]: 'application/xml',
[ContentTypes.raw]: 'text/plain'
};
export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<HttpResponse> => {
let {
app: { _id: appId },
runningAppInfo: { id: appId },
chatId,
responseChatItemId,
variables,
node: { outputs },
node,
runtimeNodes,
histories,
workflowStreamResponse,
params: {
@@ -55,6 +69,8 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H
system_httpHeader: httpHeader,
system_httpParams: httpParams = [],
system_httpJsonBody: httpJsonBody,
system_httpFormBody: httpFormBody,
system_httpContentType: httpContentType = ContentTypes.json,
system_httpTimeout: httpTimeout = 60,
[NodeInputKeyEnum.addInputParam]: dynamicInput,
...body
@@ -77,21 +93,37 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H
// ...dynamicInput,
...systemVariables
};
const allVariables = {
[NodeInputKeyEnum.addInputParam]: concatVariables,
...concatVariables
};
httpReqUrl = replaceVariable(httpReqUrl, allVariables);
const replaceStringVariables = (text: string) => {
return replaceVariable(
replaceEditorVariable({
text,
nodes: runtimeNodes,
variables: allVariables,
runningNode: node
}),
allVariables
);
};
// parse header
const headers = await (() => {
try {
const contentType = contentTypeMap[httpContentType];
if (contentType) {
httpHeader = [{ key: 'Content-Type', value: contentType, type: 'string' }, ...httpHeader];
}
if (!httpHeader || httpHeader.length === 0) return {};
// array
return httpHeader.reduce((acc: Record<string, string>, item) => {
const key = replaceVariable(item.key, allVariables);
const value = replaceVariable(item.value, allVariables);
const key = replaceStringVariables(item.key);
const value = replaceStringVariables(item.value);
acc[key] = valueTypeFormat(value, WorkflowIOValueTypeEnum.string);
return acc;
}, {});
@@ -99,28 +131,53 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H
return Promise.reject('Header 为非法 JSON 格式');
}
})();
const params = httpParams.reduce((acc: Record<string, string>, item) => {
const key = replaceVariable(item.key, allVariables);
const value = replaceVariable(item.value, allVariables);
const key = replaceStringVariables(item.key);
const value = replaceStringVariables(item.value);
acc[key] = valueTypeFormat(value, WorkflowIOValueTypeEnum.string);
return acc;
}, {});
const requestBody = await (() => {
if (!httpJsonBody) return {};
if (httpContentType === ContentTypes.none) return {};
try {
// Replace all variables in the string body
httpJsonBody = replaceVariable(httpJsonBody, allVariables);
// Text body, return directly
if (headers['Content-Type']?.includes('text/plain')) {
return httpJsonBody?.replaceAll(UNDEFINED_SIGN, 'null');
if (httpContentType === ContentTypes.formData) {
if (!Array.isArray(httpFormBody)) return {};
httpFormBody = httpFormBody.map((item) => ({
key: replaceStringVariables(item.key),
type: item.type,
value: replaceStringVariables(item.value)
}));
const formData = new FormData();
for (const { key, value } of httpFormBody) {
formData.append(key, value);
}
return formData;
}
// Json body, parse and return
const jsonParse = JSON.parse(httpJsonBody);
const removeSignJson = removeUndefinedSign(jsonParse);
return removeSignJson;
if (httpContentType === ContentTypes.xWwwFormUrlencoded) {
if (!Array.isArray(httpFormBody)) return {};
httpFormBody = httpFormBody.map((item) => ({
key: replaceStringVariables(item.key),
type: item.type,
value: replaceStringVariables(item.value)
}));
const urlSearchParams = new URLSearchParams();
for (const { key, value } of httpFormBody) {
urlSearchParams.append(key, value);
}
return urlSearchParams;
}
if (!httpJsonBody) return {};
if (httpContentType === ContentTypes.json) {
httpJsonBody = replaceVariable(httpJsonBody, allVariables);
// Json body, parse and return
const jsonParse = JSON.parse(httpJsonBody);
const removeSignJson = removeUndefinedSign(jsonParse);
return removeSignJson;
}
httpJsonBody = replaceStringVariables(httpJsonBody);
return httpJsonBody.replaceAll(UNDEFINED_SIGN, 'null');
} catch (error) {
console.log(error);
return Promise.reject(`Invalid JSON body: ${httpJsonBody}`);
@@ -150,7 +207,7 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H
// format output value type
const results: Record<string, any> = {};
for (const key in formatResponse) {
const output = outputs.find((item) => item.key === key);
const output = node.outputs.find((item) => item.key === key);
if (!output) continue;
results[key] = valueTypeFormat(formatResponse[key], output.valueType);
}
@@ -213,7 +270,6 @@ async function fetchData({
baseURL: `http://${SERVICE_LOCAL_HOST}`,
url,
headers: {
'Content-Type': 'application/json',
...headers
},
timeout: timeout * 1000,

View File

@@ -44,7 +44,7 @@ ${content.slice(0, 100)}${content.length > 100 ? '......' : ''}
export const dispatchReadFiles = async (props: Props): Promise<Response> => {
const {
requestOrigin,
teamId,
runningAppInfo: { teamId },
histories,
chatConfig,
params: { fileUrlList = [] }

View File

@@ -21,7 +21,7 @@ const UNDEFINED_SIGN = 'UNDEFINED_SIGN';
export const dispatchLafRequest = async (props: LafRequestProps): Promise<LafResponse> => {
let {
app: { _id: appId },
runningAppInfo: { id: appId },
chatId,
responseChatItemId,
variables,

View File

@@ -22,6 +22,7 @@ export type DispatchFlowResponse = {
};
[DispatchNodeResponseKeyEnum.toolResponses]: ToolRunResponseItemType;
[DispatchNodeResponseKeyEnum.assistantResponses]: AIChatItemValueItemType[];
[DispatchNodeResponseKeyEnum.runTimes]: number;
newVariables: Record<string, string>;
};

View File

@@ -5,7 +5,10 @@ import {
WorkflowIOValueTypeEnum,
NodeOutputKeyEnum
} from '@fastgpt/global/core/workflow/constants';
import { RuntimeEdgeItemType } from '@fastgpt/global/core/workflow/runtime/type';
import {
RuntimeEdgeItemType,
SystemVariablesType
} from '@fastgpt/global/core/workflow/runtime/type';
import { responseWrite } from '../../../common/response';
import { NextApiResponse } from 'next';
import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants';
@@ -144,8 +147,9 @@ export const removeSystemVariable = (variables: Record<string, any>) => {
return copyVariables;
};
export const filterSystemVariables = (variables: Record<string, any>) => {
export const filterSystemVariables = (variables: Record<string, any>): SystemVariablesType => {
return {
userId: variables.userId,
appId: variables.appId,
chatId: variables.chatId,
responseChatItemId: variables.responseChatItemId,

View File

@@ -1,5 +1,13 @@
import { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type';
import { countPromptTokens } from '../../common/string/tiktoken/index';
import { getNanoid } from '@fastgpt/global/common/string/tools';
import { ChatItemValueTypeEnum, ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
import {
getPluginInputsFromStoreNodes,
getPluginRunContent
} from '@fastgpt/global/core/app/plugin/utils';
import { StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node';
import { UserChatItemType } from '@fastgpt/global/core/chat/type';
/* filter search result */
export const filterSearchResultsByMaxChars = async (
@@ -23,3 +31,25 @@ export const filterSearchResultsByMaxChars = async (
return results.length === 0 ? list.slice(0, 1) : results;
};
/* Get plugin runtime input user query */
export const getPluginRunUserQuery = (
nodes: StoreNodeItemType[],
variables: Record<string, any>
): UserChatItemType & { dataId: string } => {
return {
dataId: getNanoid(24),
obj: ChatRoleEnum.Human,
value: [
{
type: ChatItemValueTypeEnum.text,
text: {
content: getPluginRunContent({
pluginInputs: getPluginInputsFromStoreNodes(nodes),
variables
})
}
}
]
};
};

View File

@@ -25,22 +25,26 @@ export const authPluginByTmbId = async ({
}) => {
const { source } = await splitCombinePluginId(appId);
if (source === PluginSourceEnum.personal) {
await authAppByTmbId({
const { app } = await authAppByTmbId({
appId,
tmbId,
per
});
return app;
}
};
export const authAppByTmbId = async ({
tmbId,
appId,
per
per,
isRoot
}: {
tmbId: string;
appId: string;
per: PermissionValueType;
isRoot?: boolean;
}): Promise<{
app: AppDetailType;
}> => {
@@ -53,6 +57,14 @@ export const authAppByTmbId = async ({
return Promise.reject(AppErrEnum.unExist);
}
if (isRoot) {
return {
...app,
defaultPermission: app.defaultPermission,
permission: new AppPermission({ isOwner: true })
};
}
if (String(app.teamId) !== teamId) {
return Promise.reject(AppErrEnum.unAuthApp);
}
@@ -134,7 +146,8 @@ export const authApp = async ({
const { app } = await authAppByTmbId({
tmbId,
appId,
per
per,
isRoot: result.isRoot
});
return {

View File

@@ -78,13 +78,18 @@ export const delResourcePermission = ({
/* 下面代码等迁移 */
/* create token */
export function createJWT(user: { _id?: string; team?: { teamId?: string; tmbId: string } }) {
export function createJWT(user: {
_id?: string;
team?: { teamId?: string; tmbId: string };
isRoot?: boolean;
}) {
const key = process.env.TOKEN_KEY as string;
const token = jwt.sign(
{
userId: String(user._id),
teamId: String(user.team?.teamId),
tmbId: String(user.team?.tmbId),
isRoot: user.isRoot,
exp: Math.floor(Date.now() / 1000) + 60 * 60 * 24 * 7
},
key
@@ -98,6 +103,7 @@ export function authJWT(token: string) {
userId: string;
teamId: string;
tmbId: string;
isRoot: boolean;
}>((resolve, reject) => {
const key = process.env.TOKEN_KEY as string;
@@ -110,7 +116,8 @@ export function authJWT(token: string) {
resolve({
userId: decoded.userId,
teamId: decoded.teamId || '',
tmbId: decoded.tmbId
tmbId: decoded.tmbId,
isRoot: decoded.isRoot
});
});
});
@@ -183,7 +190,7 @@ export async function parseHeaderCert({
const { cookie, token, rootkey, authorization } = (req.headers || {}) as ReqHeaderAuthType;
const { uid, teamId, tmbId, appId, openApiKey, authType } = await (async () => {
const { uid, teamId, tmbId, appId, openApiKey, authType, isRoot } = await (async () => {
if (authApiKey && authorization) {
// apikey from authorization
const authResponse = await parseAuthorization(authorization);
@@ -205,7 +212,8 @@ export async function parseHeaderCert({
tmbId: res.tmbId,
appId: '',
openApiKey: '',
authType: AuthUserTypeEnum.token
authType: AuthUserTypeEnum.token,
isRoot: res.isRoot
};
}
if (authRoot && rootkey) {
@@ -217,7 +225,8 @@ export async function parseHeaderCert({
tmbId: '',
appId: '',
openApiKey: '',
authType: AuthUserTypeEnum.root
authType: AuthUserTypeEnum.root,
isRoot: true
};
}
@@ -234,7 +243,8 @@ export async function parseHeaderCert({
tmbId: String(tmbId),
appId,
authType,
apikey: openApiKey
apikey: openApiKey,
isRoot: !!isRoot
};
}

View File

@@ -24,11 +24,13 @@ import { ParentIdType } from '@fastgpt/global/common/parentFolder/type';
export const authDatasetByTmbId = async ({
tmbId,
datasetId,
per
per,
isRoot = false
}: {
tmbId: string;
datasetId: string;
per: PermissionValueType;
isRoot?: boolean;
}): Promise<{
dataset: DatasetSchemaType & {
permission: DatasetPermission;
@@ -44,6 +46,15 @@ export const authDatasetByTmbId = async ({
return Promise.reject(DatasetErrEnum.unExist);
}
if (isRoot) {
return {
...dataset,
permission: new DatasetPermission({
isOwner: true
})
};
}
if (String(dataset.teamId) !== teamId) {
return Promise.reject(DatasetErrEnum.unAuthDataset);
}
@@ -79,13 +90,15 @@ export const authDatasetByTmbId = async ({
const { dataset: parent } = await authDatasetByTmbId({
tmbId,
datasetId: dataset.parentId,
per
per,
isRoot
});
const Per = new DatasetPermission({
per: parent.permission.value,
isOwner
});
return {
Per,
defaultPermission: parent.defaultPermission
@@ -131,7 +144,8 @@ export const authDataset = async ({
const { dataset } = await authDatasetByTmbId({
tmbId,
datasetId,
per
per,
isRoot: result.isRoot
});
return {
@@ -144,15 +158,17 @@ export const authDataset = async ({
export async function authDatasetCollection({
collectionId,
per = NullPermission,
isRoot = false,
...props
}: AuthModeType & {
collectionId: string;
isRoot?: boolean;
}): Promise<
AuthResponseType<DatasetPermission> & {
collection: CollectionWithDatasetType;
}
> {
const { teamId, tmbId } = await parseHeaderCert(props);
const { teamId, tmbId, isRoot: isRootFromHeader } = await parseHeaderCert(props);
const collection = await getCollectionWithDataset(collectionId);
if (!collection) {
@@ -162,7 +178,8 @@ export async function authDatasetCollection({
const { dataset } = await authDatasetByTmbId({
tmbId,
datasetId: collection.datasetId._id,
per
per,
isRoot: isRootFromHeader || isRoot
});
return {
@@ -184,7 +201,7 @@ export async function authDatasetFile({
file: DatasetFileSchema;
}
> {
const { teamId, tmbId } = await parseHeaderCert(props);
const { teamId, tmbId, isRoot } = await parseHeaderCert(props);
const [file, collection] = await Promise.all([
getFileById({ bucketName: BucketNameEnum.dataset, fileId }),
@@ -206,7 +223,8 @@ export async function authDatasetFile({
const { permission } = await authDatasetCollection({
...props,
collectionId: collection._id,
per
per,
isRoot
});
return {

View File

@@ -4,6 +4,7 @@ import { getTmbInfoByTmbId } from '../../user/team/controller';
import { TeamErrEnum } from '@fastgpt/global/common/error/code/team';
import { AuthModeType, AuthResponseType } from '../type';
import { NullPermission } from '@fastgpt/global/support/permission/constant';
import { TeamPermission } from '@fastgpt/global/support/permission/user/controller';
/* auth user role */
export async function authUserPer(props: AuthModeType): Promise<
@@ -14,6 +15,15 @@ export async function authUserPer(props: AuthModeType): Promise<
const result = await parseHeaderCert(props);
const tmb = await getTmbInfoByTmbId({ tmbId: result.tmbId });
if (result.isRoot) {
return {
...result,
permission: new TeamPermission({
isOwner: true
}),
tmb
};
}
if (!tmb.permission.checkPer(props.per ?? NullPermission)) {
return Promise.reject(TeamErrEnum.unAuthTeam);
}

View File

@@ -6,6 +6,19 @@ import { UserStatusEnum, userStatusMap } from '@fastgpt/global/support/user/cons
export const userCollectionName = 'users';
const defaultAvatars = [
'/imgs/avatar/RoyalBlueAvatar.svg',
'/imgs/avatar/PurpleAvatar.svg',
'/imgs/avatar/AdoraAvatar.svg',
'/imgs/avatar/OrangeAvatar.svg',
'/imgs/avatar/RedAvatar.svg',
'/imgs/avatar/GrayModernAvatar.svg',
'/imgs/avatar/TealAvatar.svg',
'/imgs/avatar/GreenAvatar.svg',
'/imgs/avatar/BrightBlueAvatar.svg',
'/imgs/avatar/BlueAvatar.svg'
];
const UserSchema = new Schema({
status: {
type: String,
@@ -34,7 +47,7 @@ const UserSchema = new Schema({
},
avatar: {
type: String,
default: '/icon/human.svg'
default: defaultAvatars[Math.floor(Math.random() * defaultAvatars.length)]
},
inviterId: {
// 谁邀请注册的

View File

@@ -50,7 +50,7 @@ export const getTeamStandPlan = async ({ teamId }: { teamId: string }) => {
};
};
export const initTeamStandardPlan2Free = async ({
export const initTeamFreePlan = async ({
teamId,
session
}: {
@@ -59,23 +59,28 @@ export const initTeamStandardPlan2Free = async ({
}) => {
const freePoints = global?.subPlans?.standard?.[StandardSubLevelEnum.free]?.totalPoints || 100;
const teamStandardSub = await MongoTeamSub.findOne({ teamId, type: SubTypeEnum.standard });
const freePlan = await MongoTeamSub.findOne({
teamId,
type: SubTypeEnum.standard,
currentSubLevel: StandardSubLevelEnum.free
});
if (teamStandardSub) {
teamStandardSub.currentMode = SubModeEnum.month;
teamStandardSub.nextMode = SubModeEnum.month;
teamStandardSub.startTime = new Date();
teamStandardSub.expiredTime = addMonths(new Date(), 1);
// Reset one month free plan
if (freePlan) {
freePlan.currentMode = SubModeEnum.month;
freePlan.nextMode = SubModeEnum.month;
freePlan.startTime = new Date();
freePlan.expiredTime = addMonths(new Date(), 1);
teamStandardSub.currentSubLevel = StandardSubLevelEnum.free;
teamStandardSub.nextSubLevel = StandardSubLevelEnum.free;
freePlan.currentSubLevel = StandardSubLevelEnum.free;
freePlan.nextSubLevel = StandardSubLevelEnum.free;
teamStandardSub.totalPoints = freePoints;
teamStandardSub.surplusPoints =
teamStandardSub.surplusPoints && teamStandardSub.surplusPoints < 0
? teamStandardSub.surplusPoints + freePoints
freePlan.totalPoints = freePoints;
freePlan.surplusPoints =
freePlan.surplusPoints && freePlan.surplusPoints < 0
? freePlan.surplusPoints + freePoints
: freePoints;
return teamStandardSub.save({ session });
return freePlan.save({ session });
}
return MongoTeamSub.create(
@@ -123,13 +128,14 @@ export const getTeamPlanStatus = async ({
// Free user, first login after expiration. The free subscription plan will be reset
if (
standardPlan &&
standardPlan.expiredTime &&
standardPlan.currentSubLevel === StandardSubLevelEnum.free &&
dayjs(standardPlan.expiredTime).isBefore(new Date())
(standardPlan &&
standardPlan.expiredTime &&
standardPlan.currentSubLevel === StandardSubLevelEnum.free &&
dayjs(standardPlan.expiredTime).isBefore(new Date())) ||
teamStandardPlans.length === 0
) {
console.log('Init free stand plan', { teamId });
await initTeamStandardPlan2Free({ teamId });
await initTeamFreePlan({ teamId });
return getTeamPlanStatus({ teamId });
}

View File

@@ -2,8 +2,10 @@
export const iconPaths = {
book: () => import('./icons/book.svg'),
visible: () => import('./icons/visible.svg'),
change: () => import('./icons/change.svg'),
chatSend: () => import('./icons/chatSend.svg'),
configmap: () => import('./icons/configmap.svg'),
closeSolid: () => import('./icons/closeSolid.svg'),
collectionLight: () => import('./icons/collectionLight.svg'),
collectionSolid: () => import('./icons/collectionSolid.svg'),

View File

@@ -1,3 +1,3 @@
<svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.18118 2.74428L4.49007 2.74428C5.17179 2.74427 5.72618 2.74427 6.17608 2.78102C6.64077 2.81899 7.0558 2.89966 7.44194 3.09642C7.85435 3.30655 8.21445 3.60159 8.50012 3.95943C8.78579 3.60159 9.14588 3.30655 9.55829 3.09642C9.94444 2.89966 10.3595 2.81899 10.8242 2.78102C11.2741 2.74427 11.8284 2.74427 12.5102 2.74428L12.8191 2.74428C13.1499 2.74426 13.4396 2.74425 13.6792 2.76382C13.9333 2.78459 14.1925 2.83086 14.4436 2.95881C14.8139 3.14753 15.1151 3.44864 15.3038 3.81901C15.4317 4.07013 15.478 4.32926 15.4988 4.58343C15.5183 4.82296 15.5183 5.11271 15.5183 5.44351V11.5565C15.5183 11.8873 15.5183 12.177 15.4988 12.4166C15.478 12.6707 15.4317 12.9299 15.3038 13.181C15.1151 13.5514 14.8139 13.8525 14.4436 14.0412C14.1925 14.1691 13.9333 14.2154 13.6792 14.2362C13.4396 14.2557 13.1499 14.2557 12.819 14.2557H4.18119C3.85038 14.2557 3.56062 14.2557 3.32108 14.2362C3.06691 14.2154 2.80778 14.1691 2.55666 14.0412C2.1863 13.8525 1.88518 13.5514 1.69647 13.181C1.56852 12.9299 1.52225 12.6707 1.50148 12.4166C1.48191 12.177 1.48192 11.8873 1.48193 11.5565V5.44352C1.48192 5.11272 1.48191 4.82296 1.50148 4.58343C1.52225 4.32926 1.56852 4.07013 1.69647 3.81901C1.88518 3.44864 2.1863 3.14753 2.55666 2.95881C2.80778 2.83086 3.06691 2.78459 3.32108 2.76382C3.56062 2.74425 3.85037 2.74426 4.18118 2.74428ZM7.79425 7.49003C7.79425 6.77134 7.7937 6.27477 7.76219 5.88914C7.73136 5.51176 7.67443 5.3032 7.59598 5.14924C7.42158 4.80696 7.1433 4.52869 6.80103 4.35429C6.64706 4.27584 6.43851 4.21891 6.06112 4.18808C5.6755 4.15657 5.17893 4.15602 4.46024 4.15602H4.20775C3.84258 4.15602 3.61115 4.15657 3.43604 4.17088C3.26918 4.18451 3.21652 4.20704 3.19758 4.21669C3.09285 4.27005 3.0077 4.3552 2.95434 4.45993C2.94469 4.47886 2.92217 4.53152 2.90853 4.69839C2.89423 4.87349 2.89368 5.10492 2.89368 5.47009V11.5299C2.89368 11.8951 2.89423 12.1265 2.90853 12.3016C2.92217 12.4685 2.94469 12.5211 2.95434 12.5401C3.0077 12.6448 3.09285 12.73 3.19758 12.7833C3.21652 12.793 3.26918 12.8155 3.43604 12.8291C3.61115 12.8434 3.84258 12.844 4.20775 12.844H7.79425V7.49003ZM9.20599 12.844V7.49003C9.20599 6.77134 9.20654 6.27477 9.23805 5.88914C9.26888 5.51176 9.32581 5.3032 9.40425 5.14924C9.57865 4.80696 9.85693 4.52869 10.1992 4.35429C10.3532 4.27584 10.5617 4.21891 10.9391 4.18808C11.3247 4.15657 11.8213 4.15602 12.54 4.15602H12.7925C13.1577 4.15602 13.3891 4.15657 13.5642 4.17088C13.7311 4.18451 13.7837 4.20704 13.8027 4.21669C13.9074 4.27005 13.9925 4.3552 14.0459 4.45993C14.0555 4.47886 14.0781 4.53152 14.0917 4.69839C14.106 4.87349 14.1066 5.10492 14.1066 5.47009V11.5299C14.1066 11.8951 14.106 12.1265 14.0917 12.3016C14.0781 12.4685 14.0555 12.5211 14.0459 12.5401C13.9925 12.6448 13.9074 12.73 13.8027 12.7833C13.7837 12.793 13.7311 12.8155 13.5642 12.8291C13.3891 12.8434 13.1577 12.844 12.7925 12.844H9.20599Z" fill="#2B5FD9"/>
<svg viewBox="0 0 17 17" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.18118 2.74428L4.49007 2.74428C5.17179 2.74427 5.72618 2.74427 6.17608 2.78102C6.64077 2.81899 7.0558 2.89966 7.44194 3.09642C7.85435 3.30655 8.21445 3.60159 8.50012 3.95943C8.78579 3.60159 9.14588 3.30655 9.55829 3.09642C9.94444 2.89966 10.3595 2.81899 10.8242 2.78102C11.2741 2.74427 11.8284 2.74427 12.5102 2.74428L12.8191 2.74428C13.1499 2.74426 13.4396 2.74425 13.6792 2.76382C13.9333 2.78459 14.1925 2.83086 14.4436 2.95881C14.8139 3.14753 15.1151 3.44864 15.3038 3.81901C15.4317 4.07013 15.478 4.32926 15.4988 4.58343C15.5183 4.82296 15.5183 5.11271 15.5183 5.44351V11.5565C15.5183 11.8873 15.5183 12.177 15.4988 12.4166C15.478 12.6707 15.4317 12.9299 15.3038 13.181C15.1151 13.5514 14.8139 13.8525 14.4436 14.0412C14.1925 14.1691 13.9333 14.2154 13.6792 14.2362C13.4396 14.2557 13.1499 14.2557 12.819 14.2557H4.18119C3.85038 14.2557 3.56062 14.2557 3.32108 14.2362C3.06691 14.2154 2.80778 14.1691 2.55666 14.0412C2.1863 13.8525 1.88518 13.5514 1.69647 13.181C1.56852 12.9299 1.52225 12.6707 1.50148 12.4166C1.48191 12.177 1.48192 11.8873 1.48193 11.5565V5.44352C1.48192 5.11272 1.48191 4.82296 1.50148 4.58343C1.52225 4.32926 1.56852 4.07013 1.69647 3.81901C1.88518 3.44864 2.1863 3.14753 2.55666 2.95881C2.80778 2.83086 3.06691 2.78459 3.32108 2.76382C3.56062 2.74425 3.85037 2.74426 4.18118 2.74428ZM7.79425 7.49003C7.79425 6.77134 7.7937 6.27477 7.76219 5.88914C7.73136 5.51176 7.67443 5.3032 7.59598 5.14924C7.42158 4.80696 7.1433 4.52869 6.80103 4.35429C6.64706 4.27584 6.43851 4.21891 6.06112 4.18808C5.6755 4.15657 5.17893 4.15602 4.46024 4.15602H4.20775C3.84258 4.15602 3.61115 4.15657 3.43604 4.17088C3.26918 4.18451 3.21652 4.20704 3.19758 4.21669C3.09285 4.27005 3.0077 4.3552 2.95434 4.45993C2.94469 4.47886 2.92217 4.53152 2.90853 4.69839C2.89423 4.87349 2.89368 5.10492 2.89368 5.47009V11.5299C2.89368 11.8951 2.89423 12.1265 2.90853 12.3016C2.92217 12.4685 2.94469 12.5211 2.95434 12.5401C3.0077 12.6448 3.09285 12.73 3.19758 12.7833C3.21652 12.793 3.26918 12.8155 3.43604 12.8291C3.61115 12.8434 3.84258 12.844 4.20775 12.844H7.79425V7.49003ZM9.20599 12.844V7.49003C9.20599 6.77134 9.20654 6.27477 9.23805 5.88914C9.26888 5.51176 9.32581 5.3032 9.40425 5.14924C9.57865 4.80696 9.85693 4.52869 10.1992 4.35429C10.3532 4.27584 10.5617 4.21891 10.9391 4.18808C11.3247 4.15657 11.8213 4.15602 12.54 4.15602H12.7925C13.1577 4.15602 13.3891 4.15657 13.5642 4.17088C13.7311 4.18451 13.7837 4.20704 13.8027 4.21669C13.9074 4.27005 13.9925 4.3552 14.0459 4.45993C14.0555 4.47886 14.0781 4.53152 14.0917 4.69839C14.106 4.87349 14.1066 5.10492 14.1066 5.47009V11.5299C14.1066 11.8951 14.106 12.1265 14.0917 12.3016C14.0781 12.4685 14.0555 12.5211 14.0459 12.5401C13.9925 12.6448 13.9074 12.73 13.8027 12.7833C13.7837 12.793 13.7311 12.8155 13.5642 12.8291C13.3891 12.8434 13.1577 12.844 12.7925 12.844H9.20599Z" />
</svg>

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@@ -1,4 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" >
<path
d="M4.25845 0.738983C4.25845 0.606996 4.15146 0.5 4.01947 0.5C2.11725 0.5 0.575195 2.04205 0.575195 3.94428V12.0557C0.575195 13.9579 2.11725 15.5 4.01947 15.5H12.1309C14.0331 15.5 15.5752 13.9579 15.5752 12.0557V3.94428C15.5752 2.04205 14.0331 0.5 12.1309 0.5H10.4017C9.98744 0.5 9.65166 0.835786 9.65166 1.25C9.65166 1.66421 9.98744 2 10.4017 2H12.1309C13.2047 2 14.0752 2.87048 14.0752 3.94428V12.0557C14.0752 13.1295 13.2047 14 12.1309 14H4.01947C2.94568 14 2.0752 13.1295 2.0752 12.0557V3.94428C2.0752 2.87048 2.94568 2 4.01947 2C4.15146 2 4.25845 1.893 4.25845 1.76102V0.738983Z" />
<path

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,4 @@
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M13 3.63151C12.8097 3.61563 12.5318 3.61309 12.0118 3.61309H8.8C7.94442 3.61309 7.35921 3.61378 6.90608 3.64853C6.46289 3.68252 6.23164 3.7446 6.0695 3.82215C5.68754 4.00483 5.38889 4.29054 5.20585 4.62774C5.13457 4.75906 5.07214 4.95341 5.03711 5.35592C5.00088 5.7721 5 6.31265 5 7.11879V16.8811C5 17.6873 5.00088 18.2278 5.03711 18.644C5.07214 19.0465 5.13457 19.2409 5.20585 19.3722C5.38889 19.7094 5.68754 19.9951 6.0695 20.1778C6.23164 20.2553 6.46289 20.3174 6.90608 20.3514C7.35921 20.3861 7.94442 20.3868 8.8 20.3868H11.3166V20.3831H12.0402C12.5935 20.3831 13.0421 20.8317 13.0421 21.385C13.0421 21.9383 12.5935 22.3868 12.0402 22.3868H8.76082C7.95401 22.3868 7.29182 22.3869 6.75314 22.3455C6.19582 22.3028 5.68616 22.2114 5.20656 21.982C4.45954 21.6247 3.84031 21.0488 3.44811 20.3263C3.19241 19.8552 3.09135 19.3541 3.04464 18.8174C2.99997 18.3042 2.99999 17.6761 3 16.9243V7.07562C2.99999 6.32377 2.99997 5.69569 3.04464 5.18249C3.09135 4.64583 3.19241 4.1447 3.44811 3.67363C3.84031 2.95108 4.45955 2.37518 5.20656 2.0179C5.68616 1.78851 6.19582 1.69713 6.75314 1.65438C7.29183 1.61307 7.95404 1.61308 8.76087 1.61309L12.1104 1.61306C12.7488 1.6128 13.2216 1.61261 13.6775 1.71536C13.9143 1.76871 14.1455 1.84257 14.3684 1.93605C14.3845 1.94245 14.4004 1.94926 14.4162 1.95646C14.5556 2.01705 14.6916 2.08538 14.8235 2.16124C15.2292 2.3946 15.5693 2.71422 16.0188 3.13657L19.3536 6.26681C19.7996 6.68508 20.1428 7.00687 20.3953 7.39362C20.4782 7.52058 20.5532 7.65209 20.6199 7.78747C20.6275 7.8022 20.6347 7.81715 20.6416 7.8323C20.7445 8.04841 20.8265 8.27406 20.8859 8.50648C21.0007 8.95522 21.0004 9.42011 21 10.0095L21.0001 12.9707C21.0001 13.524 20.5515 13.9725 19.9982 13.9725C19.4449 13.9725 18.9964 13.524 18.9964 12.9707V12.2471H19V10.1115C19 9.65934 18.9973 9.41026 18.985 9.24521L15.5694 9.24522C15.3159 9.24524 15.0723 9.24526 14.8667 9.2295C14.6429 9.21233 14.3802 9.1721 14.1145 9.04503C13.7438 8.86773 13.4306 8.57903 13.2301 8.20974C13.0834 7.93947 13.0369 7.67016 13.0174 7.44655C12.9999 7.24586 13 7.01037 13 6.77572L13 3.63151ZM15 4.92332V6.74331C15 6.99036 15.0007 7.13329 15.007 7.23432L15.0197 7.23535C15.1393 7.24453 15.3045 7.24521 15.6 7.24521H17.4736L15 4.92332Z" />
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.6589 16.1948C17.9477 15.0725 19.5415 15.0725 19.8304 16.1948C19.9868 16.8028 20.6122 17.1639 21.217 16.9953C22.3333 16.6843 23.1303 18.0647 22.3028 18.8759C21.8544 19.3155 21.8544 20.0375 22.3028 20.4771C23.1303 21.2883 22.3333 22.6687 21.217 22.3577C20.6122 22.1891 19.9868 22.5502 19.8304 23.1582C19.5415 24.2805 17.9477 24.2805 17.6589 23.1582C17.5024 22.5502 16.8771 22.1891 16.2722 22.3577C15.1559 22.6687 14.3589 21.2883 15.1865 20.4771C15.6348 20.0375 15.6348 19.3155 15.1865 18.8759C14.3589 18.0647 15.1559 16.6843 16.2722 16.9953C16.8771 17.1639 17.5024 16.8028 17.6589 16.1948ZM20.1433 19.6765C20.1433 20.449 19.5171 21.0752 18.7446 21.0752C17.9721 21.0752 17.3459 20.449 17.3459 19.6765C17.3459 18.904 17.9721 18.2778 18.7446 18.2778C19.5171 18.2778 20.1433 18.904 20.1433 19.6765Z" />
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -1 +1,4 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1689491332665" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5892" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M393.2 701.7c-8.2 0-16.4-3.1-22.6-9.4-12.5-12.5-12.5-32.8 0-45.3l115.3-115.3c14-14 36.9-14 50.9 0L652.1 647c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0l-95.5-95.5-95.5 95.5c-6.2 6.2-14.4 9.4-22.6 9.4z" fill="" p-id="5893"></path><path d="M511.3 921.1c-17.7 0-32-14.3-32-32v-276c0-17.7 14.3-32 32-32s32 14.3 32 32v276c0 17.6-14.3 32-32 32z" fill="" p-id="5894"></path><path d="M732.7 784.9c-17.7 0-32-14.3-32-32s14.3-32 32-32c90.6 0 164.3-73.7 164.3-164.3 0-82.9-61.9-153-144-163.1l-22.7-2.8-4.7-22.4c-20.8-99.9-110.1-172.4-212.4-172.4-102.2 0-191.5 72.5-212.4 172.4l-4.7 22.4-22.7 2.8c-82.1 10.1-144 80.2-144 163.1 0 90.6 73.7 164.3 164.3 164.3 17.7 0 32 14.3 32 32s-14.3 32-32 32c-61 0-118.3-23.8-161.5-66.9-43.1-43.1-66.9-100.5-66.9-161.5 0-107.6 75.2-199.7 178.2-222.8 15.8-53.8 47.7-102.2 91.3-138.1 50.1-41.2 113.4-63.9 178.3-63.9s128.3 22.7 178.3 63.9c43.6 35.9 75.5 84.3 91.3 138.1C885.9 356.8 961 448.9 961 556.5c0 61-23.8 118.3-66.9 161.5-43.1 43.1-100.4 66.9-161.4 66.9z" fill="" p-id="5895"></path></svg>
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<path d="M11.9689 12.7335C13.8022 12.6748 15.2703 11.1701 15.2703 9.32256C15.2703 7.72206 14.1686 6.37888 12.6821 6.01009C12.1325 3.93852 10.2445 2.41187 8.00003 2.41187C5.75552 2.41187 3.86757 3.93852 3.31794 6.01009C1.83149 6.37888 0.729736 7.72206 0.729736 9.32256C0.729736 11.1827 2.2179 12.6952 4.06858 12.7345V12.7353H4.33524C4.70343 12.7353 5.00191 12.4368 5.00191 12.0686C5.00191 11.7004 4.70343 11.402 4.33524 11.402H4.14248C2.99405 11.402 2.06307 10.471 2.06307 9.32256C2.06307 8.34915 2.73317 7.52893 3.63901 7.30419L4.40444 7.11428L4.60668 6.35202C5.00513 4.85028 6.37523 3.7452 8.00003 3.7452C9.62484 3.7452 10.9949 4.85028 11.3934 6.35202L11.5956 7.11428L12.361 7.30419C13.2669 7.52893 13.937 8.34915 13.937 9.32256C13.937 10.4672 13.0121 11.3958 11.8689 11.4019L11.7009 11.402C11.3327 11.402 11.0342 11.7004 11.0342 12.0686C11.0342 12.4368 11.3327 12.7353 11.7009 12.7353H11.9689V12.7335Z" />
<path d="M6.17498 8.73195L7.50552 7.40141C7.52051 7.38484 7.53633 7.36902 7.5529 7.35403L7.60322 7.30371L7.6041 7.30283C7.82262 7.08431 8.17682 7.08417 8.39551 7.30241C8.39595 7.30284 8.39638 7.30327 8.39682 7.30371L8.44704 7.35393C8.46368 7.36899 8.47957 7.38487 8.49462 7.40151L9.82417 8.73107C10.0433 8.95021 10.0433 9.30552 9.82417 9.52466C9.60503 9.74381 9.24972 9.74381 9.03058 9.52466L8.66669 9.16077V13.0339C8.66669 13.402 8.36821 13.7005 8.00002 13.7005C7.63183 13.7005 7.33336 13.402 7.33336 13.0339V9.15724L6.96682 9.52378C6.74816 9.74244 6.39364 9.74244 6.17498 9.52378C5.95632 9.30512 5.95632 8.95061 6.17498 8.73195Z" />
</svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,3 @@
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.00011 6.3335C8.44214 6.3335 8.86606 6.50909 9.17863 6.82165C9.49119 7.13421 9.66678 7.55814 9.66678 8.00016C9.66678 8.44219 9.49119 8.86611 9.17863 9.17867C8.86606 9.49124 8.44214 9.66683 8.00011 9.66683C7.55809 9.66683 7.13416 9.49124 6.8216 9.17867C6.50904 8.86611 6.33345 8.44219 6.33345 8.00016C6.33345 7.55814 6.50904 7.13421 6.8216 6.82165C7.13416 6.50909 7.55809 6.3335 8.00011 6.3335ZM8.00011 3.8335C10.4794 3.8335 12.6356 5.20988 13.7537 7.23977C13.862 7.43633 13.9161 7.53461 13.9567 7.73345C13.9835 7.86447 13.9835 8.13586 13.9567 8.26688C13.9161 8.46572 13.862 8.564 13.7537 8.76055C12.6356 10.7905 10.4794 12.1668 8.00011 12.1668C5.52085 12.1668 3.36466 10.7905 2.2465 8.76055C2.13823 8.564 2.0841 8.46572 2.0435 8.26688C2.01675 8.13586 2.01675 7.86447 2.0435 7.73345C2.0841 7.53461 2.13823 7.43633 2.2465 7.23977C3.36466 5.20988 5.52085 3.8335 8.00011 3.8335ZM3.64644 7.11141C3.49828 7.30756 3.4242 7.40563 3.35714 7.65897C3.31559 7.81594 3.31559 8.18438 3.35714 8.34135C3.4242 8.5947 3.49828 8.69277 3.64644 8.88891C4.04722 9.41949 4.54344 9.87474 5.1126 10.2297C5.97881 10.77 6.97922 11.0565 8.00011 11.0565C9.021 11.0565 10.0214 10.77 10.8876 10.2297C11.4568 9.87474 11.953 9.41949 12.3538 8.88891C12.5019 8.69277 12.576 8.5947 12.6431 8.34135C12.6846 8.18438 12.6846 7.81594 12.6431 7.65897C12.576 7.40563 12.5019 7.30756 12.3538 7.11141C11.953 6.58083 11.4568 6.12558 10.8876 5.77058C10.0214 5.2303 9.021 4.94387 8.00011 4.94387C6.97922 4.94387 5.97881 5.2303 5.1126 5.77058C4.54344 6.12558 4.04722 6.58083 3.64644 7.11141Z" />
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -17,39 +17,40 @@ import { Box, Flex } from '@chakra-ui/react';
import styles from './index.module.scss';
import { EditorState, LexicalEditor } from 'lexical';
import { getNanoid } from '@fastgpt/global/common/string/tools';
import { EditorVariablePickerType } from '../../Textarea/PromptEditor/type';
import {
EditorVariableLabelPickerType,
EditorVariablePickerType
} from '../../Textarea/PromptEditor/type';
import { VariableNode } from '../../Textarea/PromptEditor/plugins/VariablePlugin/node';
import { textToEditorState } from '../../Textarea/PromptEditor/utils';
import DropDownMenu from '../../Textarea/PromptEditor/modules/DropDownMenu';
import { SingleLinePlugin } from '../../Textarea/PromptEditor/plugins/SingleLinePlugin';
import OnBlurPlugin from '../../Textarea/PromptEditor/plugins/OnBlurPlugin';
import VariablePlugin from '../../Textarea/PromptEditor/plugins/VariablePlugin';
import VariablePickerPlugin from '../../Textarea/PromptEditor/plugins/VariablePickerPlugin';
import FocusPlugin from '../../Textarea/PromptEditor/plugins/FocusPlugin';
import VariableLabelPlugin from '../../Textarea/PromptEditor/plugins/VariableLabelPlugin';
import { VariableLabelNode } from '../../Textarea/PromptEditor/plugins/VariableLabelPlugin/node';
import VariableLabelPickerPlugin from '../../Textarea/PromptEditor/plugins/VariableLabelPickerPlugin';
export default function Editor({
h = 40,
hasVariablePlugin = true,
hasDropDownPlugin = false,
variables,
variableLabels,
onChange,
onBlur,
value,
currentValue,
placeholder = '',
setDropdownValue,
updateTrigger
}: {
h?: number;
hasVariablePlugin?: boolean;
hasDropDownPlugin?: boolean;
variables: EditorVariablePickerType[];
variableLabels: EditorVariableLabelPickerType[];
onChange?: (editorState: EditorState, editor: LexicalEditor) => void;
onBlur?: (editor: LexicalEditor) => void;
value?: string;
currentValue?: string;
placeholder?: string;
setDropdownValue?: (value: string) => void;
updateTrigger?: boolean;
}) {
const [key, setKey] = useState(getNanoid(6));
@@ -58,7 +59,7 @@ export default function Editor({
const initialConfig = {
namespace: 'HttpInput',
nodes: [VariableNode],
nodes: [VariableNode, VariableLabelNode],
editorState: textToEditorState(value),
onError: (error: Error) => {
throw error;
@@ -75,16 +76,6 @@ export default function Editor({
setFocus(false);
}, [updateTrigger]);
const dropdownVariables = useMemo(
() =>
variables.filter((item) => {
const key = item.key.toLowerCase();
const current = currentValue?.toLowerCase();
return key.includes(current || '') && item.key !== currentValue;
}),
[currentValue, variables]
);
return (
<Flex
position={'relative'}
@@ -133,14 +124,12 @@ export default function Editor({
});
}}
/>
{hasVariablePlugin ? <VariablePickerPlugin variables={variables} /> : ''}
<VariablePlugin variables={variables} />
<VariableLabelPlugin variables={variableLabels} />
<VariableLabelPickerPlugin variables={variableLabels} isFocus={focus} />
<OnBlurPlugin onBlur={onBlur} />
<SingleLinePlugin />
</LexicalComposer>
{focus && hasDropDownPlugin && (
<DropDownMenu variables={dropdownVariables} setDropdownValue={setDropdownValue} />
)}
</Flex>
);
}

View File

@@ -1,58 +1,61 @@
import React, { useEffect } from 'react';
import { $getRoot, EditorState, type LexicalEditor } from 'lexical';
import React from 'react';
import { EditorState, type LexicalEditor } from 'lexical';
import { useCallback } from 'react';
import { editorStateToText } from '../../Textarea/PromptEditor/utils';
import { EditorVariablePickerType } from '../../Textarea/PromptEditor/type';
import {
EditorVariableLabelPickerType,
EditorVariablePickerType
} from '../../Textarea/PromptEditor/type';
import Editor from './Editor';
const HttpInput = ({
hasVariablePlugin = true,
hasDropDownPlugin = false,
variables = [],
variableLabels = [],
value,
onChange,
onBlur,
h,
placeholder,
setDropdownValue,
updateTrigger
}: {
hasVariablePlugin?: boolean;
hasDropDownPlugin?: boolean;
variables?: EditorVariablePickerType[];
variableLabels?: EditorVariableLabelPickerType[];
value?: string;
onChange?: (text: string) => void;
onBlur?: (text: string) => void;
h?: number;
placeholder?: string;
setDropdownValue?: (value: string) => void;
updateTrigger?: boolean;
}) => {
const [currentValue, setCurrentValue] = React.useState(value);
const onChangeInput = useCallback((editorState: EditorState, editor: LexicalEditor) => {
const text = editorStateToText(editor).replaceAll('}}{{', '}} {{');
setCurrentValue(text);
onChange?.(text);
}, []);
const onBlurInput = useCallback((editor: LexicalEditor) => {
const text = editorStateToText(editor).replaceAll('}}{{', '}} {{');
onBlur?.(text);
}, []);
const onChangeInput = useCallback(
(editorState: EditorState, editor: LexicalEditor) => {
const text = editorStateToText(editor).replaceAll('}}{{', '}} {{');
setCurrentValue(text);
onChange?.(text);
},
[onChange]
);
const onBlurInput = useCallback(
(editor: LexicalEditor) => {
const text = editorStateToText(editor).replaceAll('}}{{', '}} {{');
onBlur?.(text);
},
[onBlur]
);
return (
<>
<Editor
hasVariablePlugin={hasVariablePlugin}
hasDropDownPlugin={hasDropDownPlugin}
variables={variables}
variableLabels={variableLabels}
h={h}
value={value}
currentValue={currentValue}
onChange={onChangeInput}
onBlur={onBlurInput}
placeholder={placeholder}
setDropdownValue={setDropdownValue}
updateTrigger={updateTrigger}
/>
</>

View File

@@ -23,6 +23,7 @@ export type Props = {
trigger?: 'hover' | 'click';
iconSize?: string;
iconRadius?: string;
placement?: PlacementWithLogical;
menuList: {
label?: string;
@@ -32,7 +33,8 @@ export type Props = {
icon?: IconNameType | string;
label: string | React.ReactNode;
description?: string;
onClick: () => any;
onClick?: () => any;
menuItemStyles?: MenuItemProps;
}[];
}[];
};
@@ -75,14 +77,6 @@ const MyMenu = ({
}
}
};
const menuItemStyles: MenuItemProps = {
borderRadius: 'sm',
py: 2,
px: 3,
display: 'flex',
alignItems: 'center',
fontSize: 'sm'
};
const { isPc } = useSystem();
const ref = useRef<HTMLDivElement>(null);
@@ -166,16 +160,23 @@ const MyMenu = ({
{item.children.map((child, index) => (
<MenuItem
key={index}
{...menuItemStyles}
onClickCapture={(e) => {
borderRadius={'sm'}
onClick={(e) => {
e.stopPropagation();
setIsOpen(false);
child.onClick && child.onClick();
if (child.onClick) {
setIsOpen(false);
child.onClick();
}
}}
py={2}
px={3}
alignItems={'center'}
fontSize={'sm'}
color={child.isActive ? 'primary.700' : 'myGray.600'}
whiteSpace={'pre-wrap'}
_notLast={{ mb: 0.5 }}
{...typeMapStyle[child.type || 'primary']}
{...child.menuItemStyles}
>
{!!child.icon && (
<Avatar
@@ -185,12 +186,16 @@ const MyMenu = ({
mr={3}
/>
)}
<Box>
<Box color={child.description ? 'myGray.900' : 'inherit'} fontSize={'sm'}>
<Box w={'100%'}>
<Box
w={'100%'}
color={child.description ? 'myGray.900' : 'inherit'}
fontSize={'sm'}
>
{child.label}
</Box>
{child.description && (
<Box color={'myGray.500'} fontSize={'mini'}>
<Box color={'myGray.500'} fontSize={'mini'} w={'100%'}>
{child.description}
</Box>
)}

View File

@@ -21,6 +21,7 @@ export interface MyModalProps extends ModalContentProps {
isOpen?: boolean;
onClose?: () => void;
closeOnOverlayClick?: boolean;
size?: 'md' | 'lg';
}
const MyModal = ({
@@ -35,6 +36,7 @@ const MyModal = ({
maxW = ['90vw', '600px'],
closeOnOverlayClick = true,
iconColor,
size = 'md',
...props
}: MyModalProps) => {
const { isPc } = useSystem();
@@ -43,6 +45,7 @@ const MyModal = ({
<Modal
isOpen={isOpen}
onClose={() => onClose && onClose()}
size={size}
autoFocus={false}
isCentered={isPc ? isCentered : true}
blockScrollOnMount={false}

View File

@@ -105,7 +105,7 @@ const MultipleRowSelect = ({
justifyContent={'space-between'}
width={'100%'}
rightIcon={<ChevronDownIcon />}
variant={'whiteFlow'}
variant={'whiteBase'}
_active={{
transform: 'none'
}}

View File

@@ -10,6 +10,7 @@ type Props<ValueType = string> = Omit<GridProps, 'onChange'> & {
size?: 'sm' | 'md' | 'lg';
inlineStyles?: FlexProps;
activeColor?: string;
defaultColor?: string;
onChange: (value: ValueType) => void;
};
@@ -18,6 +19,7 @@ const LightRowTabs = <ValueType = string,>({
size = 'md',
value,
activeColor = 'primary.600',
defaultColor = 'transparent',
onChange,
inlineStyles,
...props
@@ -63,7 +65,8 @@ const LightRowTabs = <ValueType = string,>({
py={sizeMap.inlineP}
alignItems={'center'}
justifyContent={'center'}
borderBottom={'2px solid transparent'}
borderBottom={'2px solid'}
borderColor={defaultColor}
px={3}
whiteSpace={'nowrap'}
{...(value === item.value

View File

@@ -106,6 +106,7 @@ export default function Editor({
cursor={'text'}
color={'myGray.700'}
bg={focus ? 'white' : bg}
borderRadius={'md'}
>
<LexicalComposer initialConfig={initialConfig} key={key}>
<PlainTextPlugin

View File

@@ -77,10 +77,7 @@ export default function VariableLabelPickerPlugin({
onSelectOption={onSelectOption}
triggerFn={checkForTriggerMatch}
options={variableFilter(variables, queryString || '')}
menuRenderFn={(
anchorElementRef,
{ selectedIndex, selectOptionAndCleanUp, setHighlightedIndex }
) => {
menuRenderFn={(anchorElementRef, { selectedIndex, selectOptionAndCleanUp }) => {
if (anchorElementRef.current == null) {
return null;
}

Some files were not shown because too many files have changed in this diff Show More