Compare commits

...

50 Commits

Author SHA1 Message Date
gru-agent[bot]
90db5514fd Add unit tests for dataset API functions in api.test.ts 2025-05-26 10:29:11 +00:00
Archer
874300a56a fix: chinese name export (#4890)
* fix: chinese name export

* fix: xlsx white space

* doc

* doc
2025-05-25 21:19:29 +08:00
Archer
1dea2b71b4 perf: human check;perf: recursion get node response (#4888)
* perf: human check

* version

* perf: recursion get node response
2025-05-25 20:55:29 +08:00
Archer
a8673344b1 Test add menu (#4887)
* Feature: Add additional dataset options and their descriptions, updat… (#4874)

* Feature: Add additional dataset options and their descriptions, update menu components to support submenu functionality

* Optimize the menu component by removing the sub-menu position attribute, introducing the MyPopover component to support sub-menu functionality, and adding new dataset options and their descriptions in the dataset list.

---------

Co-authored-by: dreamer6680 <146868355@qq.com>

* api dataset tip

* remove invalid code

---------

Co-authored-by: dreamer6680 <1468683855@qq.com>
Co-authored-by: dreamer6680 <146868355@qq.com>
2025-05-25 20:16:03 +08:00
Archer
9709ae7a4f feat: The workflow quickly adds applications (#4882)
* feat: add node by handle (#4860)

* feat: add node by handle

* fix

* fix edge filter

* fix

* move utils

* move context

* scale handle

* move postion to handle params & optimize handle scale (#4878)

* move position to handle params

* close button scale

* perf: node template ui

* remove handle scale (#4880)

* feat: handle connect

* add mouse down duration check (#4881)

* perf: long press time

* tool handle size

* optimize add node by handle (#4883)

---------

Co-authored-by: heheer <heheer@sealos.io>
2025-05-23 19:20:12 +08:00
Archer
fae76e887a perf: dataset import params code (#4875)
* perf: dataset import params code

* perf: api dataset code

* model
2025-05-23 10:40:25 +08:00
dreamer6680
9af92d1eae Open Yufu Feishu Knowledge Base Permissions (#4867)
* add feishu yuque dataset

* Open Yufu Feishu Knowledge Base Permissions

* Refactor the dataset request module, optimize the import path, and fix the type definition

---------

Co-authored-by: dreamer6680 <146868355@qq.com>
2025-05-22 23:19:55 +08:00
Archer
6a6719e93d perf: isPc check;perf: dataset max token checker (#4872)
* perf: isPc check

* perf: dataset max token checker

* perf: dataset max token checker
2025-05-22 18:40:29 +08:00
Compasafe
50481f4ca8 fix: 修改语音组件中判断isPc的逻辑 (#4854)
* fix: 修改语音组件中判断isPc的逻辑

* fix: 修改语音组件中判断isPc的逻辑
2025-05-22 16:29:53 +08:00
Archer
88bd3aaa9e perf: backup import (#4866)
* i18n

* remove invalid code

* perf: backup import

* backup tip

* fix: indexsize invalid
2025-05-22 15:53:51 +08:00
Archer
dd3c251603 fix: stream response (#4853) 2025-05-21 10:21:20 +08:00
Archer
aa55f059d4 perf: chat history api;perf: full text error (#4852)
* perf: chat history api

* perf: i18n

* perf: full text
2025-05-20 22:31:32 +08:00
dreamer6680
89c9a02650 change ui of price (#4851)
Co-authored-by: dreamer6680 <146868355@qq.com>
2025-05-20 20:51:07 +08:00
heheer
0f3bfa280a fix quote reader duplicate rendering (#4845) 2025-05-20 20:21:00 +08:00
dependabot[bot]
593ebfd269 chore(deps): bump multer from 1.4.5-lts.1 to 2.0.0 (#4839)
Bumps [multer](https://github.com/expressjs/multer) from 1.4.5-lts.1 to 2.0.0.
- [Release notes](https://github.com/expressjs/multer/releases)
- [Changelog](https://github.com/expressjs/multer/blob/v2.0.0/CHANGELOG.md)
- [Commits](https://github.com/expressjs/multer/compare/v1.4.5-lts.1...v2.0.0)

---
updated-dependencies:
- dependency-name: multer
  dependency-version: 2.0.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-20 13:58:47 +08:00
John Chen
f6dc2204f5 fix:修正docker-compose-pgvecto.yml文件中,健康检查参数错误 (#4841) 2025-05-20 13:57:32 +08:00
Archer
d44c338059 perf: confirm ux (#4843)
* perf: delete tip ux

* perf: confirm ux
2025-05-20 13:41:56 +08:00
Archer
1dac2b70ec perf: stream timeout;feat: hnsw max_scan_tuples config;fix: fulltext search merge error (#4838)
* perf: stream timeout

* feat: hnsw max_scan_tuples config

* fix: fulltext search merge error

* perf: jieba code
2025-05-20 09:59:24 +08:00
Archer
9fef3e15fb Update doc (#4831)
* doc

* doc

* version update
2025-05-18 23:16:31 +08:00
Archer
2d2d0fffe9 Test apidataset (#4830)
* Dataset (#4822)

* apidataset support to basepath

* Resolve the error of the Feishu Knowledge Base modification configuration page not supporting baseurl bug.

* apibasepath

* add

* perf: api dataset

---------

Co-authored-by: dreamer6680 <1468683855@qq.com>
2025-05-17 22:41:10 +08:00
heheer
c6e0b5a1e7 offiaccount welcome text (#4827)
* offiaccount welcome text

* fix

* Update Image.tsx

---------

Co-authored-by: Archer <545436317@qq.com>
2025-05-17 22:03:18 +08:00
dependabot[bot]
932aa28a1f chore(deps): bump undici in /plugins/webcrawler/SPIDER (#4825)
Bumps [undici](https://github.com/nodejs/undici) from 6.21.1 to 6.21.3.
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](https://github.com/nodejs/undici/compare/v6.21.1...v6.21.3)

---
updated-dependencies:
- dependency-name: undici
  dependency-version: 6.21.3
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-17 01:16:31 +08:00
heheer
9c59bc2c17 fix: handle optional indexes in InputDataModal (#4828) 2025-05-16 15:07:33 +08:00
Archer
e145f63554 feat: chat error msg (#4826)
* perf: i18n

* feat: chat error msg

* feat: doc
2025-05-16 12:07:11 +08:00
Archer
554b2ca8dc perf: mcp tool type (#4820) 2025-05-15 18:14:32 +08:00
Archer
4e83840c14 perf: tool call check (#4818)
* i18n

* tool call

* fix: mcp create permission;Plugin unauth tip

* fix: mcp create permission;Plugin unauth tip

* fix: Cite modal permission

* remove invalide cite

* perf: prompt

* filter fulltext search

* fix: ts

* fix: ts

* fix: ts
2025-05-15 15:51:34 +08:00
heheer
a6c80684d1 fix version match (#4814) 2025-05-14 17:45:31 +08:00
Archer
a4db03a3b7 feat: session id (#4817)
* feat: session id

* feat: Add default index
2025-05-14 17:24:02 +08:00
Archer
cba8f773fe New license (#4809)
* feat: new-license

* perf: volumn watch

* Set use client
2025-05-14 13:55:09 +08:00
Archer
bd93f28d6f update doc (#4806) 2025-05-13 21:24:35 +08:00
Archer
2063cb6314 i18n (#4805)
* i18n

* version

* copy node
2025-05-13 18:58:57 +08:00
dreamer6680
12acaf491c change password rule (#4804)
* change password rule

* change password.tset.ts
2025-05-13 18:20:11 +08:00
heheer
3688842cc7 filter tool type version & fix unpublished version (#4803) 2025-05-13 17:58:51 +08:00
Archer
398d131bac fix api_dataset.md (#4791) (#4801)
Co-authored-by: dreamer6680 <1468683855@qq.com>
2025-05-13 12:28:50 +08:00
Archer
d5f188a1a4 doc (#4798)
* doc

* fix: i18n

* fix: scroll load
2025-05-13 12:16:32 +08:00
heheer
1edca309c4 remove system plugin node version (#4797) 2025-05-13 11:04:48 +08:00
Archer
1470c37ef1 Test media tag (#4796)
* feat: add html video tag convertion (#4784)

Co-authored-by: Zhenyi Wang <zhenyiwang@intl.zju.edu.cn>

* perf: media tag

---------

Co-authored-by: Zhenyi-Wang <47094597+Zhenyi-Wang@users.noreply.github.com>
Co-authored-by: Zhenyi Wang <zhenyiwang@intl.zju.edu.cn>
2025-05-13 10:46:49 +08:00
heheer
bdb1221d94 optimize editor default value code (#4794) 2025-05-12 23:52:22 +08:00
heheer
cac4b1d435 fix monaco editor default value (#4793)
* fix monaco editor default value

* fix
2025-05-12 23:09:15 +08:00
Archer
0ef3d40296 Test version (#4792)
* plugin node version select (#4760)

* plugin node version select

* type

* fix

* fix

* perf: version list

* fix node version (#4787)

* change my select

* fix-ui

* fix test

* add test

* fix

* remove invalid version field

* filter deprecated field

* fix: claude tool call

* fix: test

---------

Co-authored-by: heheer <heheer@sealos.io>
2025-05-12 22:27:01 +08:00
Theresa
3cc6b8a17a fix: improve handling of interactive node responses in workflow dispatch (#4786)
* fix: improve handling of interactive node responses in workflow dispatch

* fix: simplify interactive response handling in dispatch functions
2025-05-12 18:18:05 +08:00
Archer
681ec30c38 4.9.8 test (#4790)
* fix: doc url

* doc
2025-05-12 18:13:42 +08:00
Archer
24cd1c98dc Update official_account.md (#4789) 2025-05-12 17:01:18 +08:00
Archer
eaceabcc43 Update official_account.md (#4788) 2025-05-12 16:56:40 +08:00
dreamer6680
a7f9411dca feat: Update the system configuration type, add visibility controls for datasets and publishing channels (#4778) 2025-05-12 13:51:58 +08:00
Archer
657fa32217 feat: system config type;fix: retraining permission (#4772)
* feat: system config type

* fix: retraining permission
2025-05-08 22:09:55 +08:00
Archer
12d6948ba7 Feat: prelogin (#4773)
* add prelogin api (#4762)

* add prelogin api

* move type.d.ts

* perf: prelogin code

* doc

* fix: ts

---------

Co-authored-by: dreamer6680 <1468683855@qq.com>
2025-05-08 22:09:02 +08:00
Archer
83d54d046d perf: replace cite;perf: app card ui (#4768)
* perf: replace cite

* perf: app card ui

* fix: test
2025-05-08 13:35:08 +08:00
Archer
c75f154728 Password security policy (#4765)
* Psw (#4748)

* feat: 添加重置密码功能及相关接口

- 在用户模型中新增 passwordUpdateTime 字段以记录密码更新时间。
- 更新用户模式以支持密码更新时间的存储。
- 新增重置密码的模态框组件,允许用户重置密码。
- 实现重置密码的 API 接口,支持根据用户 ID 更新密码。
- 更新相关国际化文件,添加重置密码的提示信息。

* 更新国际化文件,添加重置密码相关提示信息,并优化重置密码模态框的实现。修复部分代码逻辑,确保用户体验流畅。

* 更新国际化文件,添加重置密码相关提示信息,优化重置密码模态框的实现,修复部分代码逻辑,确保用户体验流畅。新增获取用户密码更新时间的API接口,并调整相关逻辑以支持密码重置功能。

* update

* fix

* fix

* Added environment variables NEXT_PUBLIC_PASSWORD_UPDATETIME to support password update time configuration, update related logic to implement password mandatory update function, and optimize the implementation of reset password modal box to improve user experience.

* update index

* 更新用户密码重置功能,调整相关API接口,优化重置密码模态框的实现,确保用户体验流畅。修复部分代码逻辑,更新国际化提示信息。

* 删除获取用户密码更新时间的API接口,并在布局组件中移除不必要的重置密码模态框。优化代码结构,提升可维护性。

* update

* perf: reset expired password code

* perf: layout child components

* doc

* remove invalid env

* perf: update password code

---------

Co-authored-by: dreamer6680 <1468683855@qq.com>
2025-05-08 12:11:08 +08:00
Archer
96e7dd581e fix: json schema parse error;fix: retraining image reset (#4757)
* i18n

* fix: json schema parse error

* fix: retraining image reset

* update doc
2025-05-07 15:38:03 +08:00
369 changed files with 8225 additions and 5294 deletions

View File

@@ -21,7 +21,7 @@
"i18n-ally.namespace": true, "i18n-ally.namespace": true,
"i18n-ally.pathMatcher": "{locale}/{namespaces}.json", "i18n-ally.pathMatcher": "{locale}/{namespaces}.json",
"i18n-ally.extract.targetPickingStrategy": "most-similar-by-key", "i18n-ally.extract.targetPickingStrategy": "most-similar-by-key",
"i18n-ally.translate.engines": ["google"], "i18n-ally.translate.engines": ["deepl","google"],
"[typescript]": { "[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "esbenp.prettier-vscode"
}, },

View File

@@ -132,15 +132,15 @@ services:
# fastgpt # fastgpt
sandbox: sandbox:
container_name: sandbox container_name: sandbox
image: ghcr.io/labring/fastgpt-sandbox:v4.9.7-fix2 # git image: ghcr.io/labring/fastgpt-sandbox:v4.9.9 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.7-fix2 # 阿里云 # image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.9 # 阿里云
networks: networks:
- fastgpt - fastgpt
restart: always restart: always
fastgpt-mcp-server: fastgpt-mcp-server:
container_name: fastgpt-mcp-server container_name: fastgpt-mcp-server
image: ghcr.io/labring/fastgpt-mcp_server:v4.9.7-fix2 # git image: ghcr.io/labring/fastgpt-mcp_server:v4.9.9 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.9.7-fix2 # 阿里云 # image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.9.9 # 阿里云
ports: ports:
- 3005:3000 - 3005:3000
networks: networks:
@@ -150,8 +150,8 @@ services:
- FASTGPT_ENDPOINT=http://fastgpt:3000 - FASTGPT_ENDPOINT=http://fastgpt:3000
fastgpt: fastgpt:
container_name: fastgpt container_name: fastgpt
image: ghcr.io/labring/fastgpt:v4.9.7-fix2 # git image: ghcr.io/labring/fastgpt:v4.9.9 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.7-fix2 # 阿里云 # image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.9 # 阿里云
ports: ports:
- 3000:3000 - 3000:3000
networks: networks:

View File

@@ -109,15 +109,15 @@ services:
# fastgpt # fastgpt
sandbox: sandbox:
container_name: sandbox container_name: sandbox
image: ghcr.io/labring/fastgpt-sandbox:v4.9.7-fix2 # git image: ghcr.io/labring/fastgpt-sandbox:v4.9.9 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.7-fix2 # 阿里云 # image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.9 # 阿里云
networks: networks:
- fastgpt - fastgpt
restart: always restart: always
fastgpt-mcp-server: fastgpt-mcp-server:
container_name: fastgpt-mcp-server container_name: fastgpt-mcp-server
image: ghcr.io/labring/fastgpt-mcp_server:v4.9.7-fix2 # git image: ghcr.io/labring/fastgpt-mcp_server:v4.9.9 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.9.7-fix2 # 阿里云 # image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.9.9 # 阿里云
ports: ports:
- 3005:3000 - 3005:3000
networks: networks:
@@ -127,8 +127,8 @@ services:
- FASTGPT_ENDPOINT=http://fastgpt:3000 - FASTGPT_ENDPOINT=http://fastgpt:3000
fastgpt: fastgpt:
container_name: fastgpt container_name: fastgpt
image: ghcr.io/labring/fastgpt:v4.9.7-fix2 # git image: ghcr.io/labring/fastgpt:v4.9.9 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.7-fix2 # 阿里云 # image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.9 # 阿里云
ports: ports:
- 3000:3000 - 3000:3000
networks: networks:

View File

@@ -23,7 +23,7 @@ services:
volumes: volumes:
- ./pg/data:/var/lib/postgresql/data - ./pg/data:/var/lib/postgresql/data
healthcheck: healthcheck:
test: ['CMD', 'pg_isready', '-U', 'postgres', '-d', 'aiproxy'] test: ['CMD', 'pg_isready', '-U', 'postgres', '-d', 'postgres']
interval: 5s interval: 5s
timeout: 5s timeout: 5s
retries: 10 retries: 10
@@ -96,15 +96,15 @@ services:
# fastgpt # fastgpt
sandbox: sandbox:
container_name: sandbox container_name: sandbox
image: ghcr.io/labring/fastgpt-sandbox:v4.9.7-fix2 # git image: ghcr.io/labring/fastgpt-sandbox:v4.9.9 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.7-fix2 # 阿里云 # image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.9 # 阿里云
networks: networks:
- fastgpt - fastgpt
restart: always restart: always
fastgpt-mcp-server: fastgpt-mcp-server:
container_name: fastgpt-mcp-server container_name: fastgpt-mcp-server
image: ghcr.io/labring/fastgpt-mcp_server:v4.9.7-fix2 # git image: ghcr.io/labring/fastgpt-mcp_server:v4.9.9 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.9.7-fix2 # 阿里云 # image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.9.9 # 阿里云
ports: ports:
- 3005:3000 - 3005:3000
networks: networks:
@@ -114,8 +114,8 @@ services:
- FASTGPT_ENDPOINT=http://fastgpt:3000 - FASTGPT_ENDPOINT=http://fastgpt:3000
fastgpt: fastgpt:
container_name: fastgpt container_name: fastgpt
image: ghcr.io/labring/fastgpt:v4.9.7-fix2 # git image: ghcr.io/labring/fastgpt:v4.9.9 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.7-fix2 # 阿里云 # image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.9 # 阿里云
ports: ports:
- 3000:3000 - 3000:3000
networks: networks:

View File

@@ -72,15 +72,15 @@ services:
sandbox: sandbox:
container_name: sandbox container_name: sandbox
image: ghcr.io/labring/fastgpt-sandbox:v4.9.7-fix2 # git image: ghcr.io/labring/fastgpt-sandbox:v4.9.9 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.7-fix2 # 阿里云 # image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.9 # 阿里云
networks: networks:
- fastgpt - fastgpt
restart: always restart: always
fastgpt-mcp-server: fastgpt-mcp-server:
container_name: fastgpt-mcp-server container_name: fastgpt-mcp-server
image: ghcr.io/labring/fastgpt-mcp_server:v4.9.7-fix2 # git image: ghcr.io/labring/fastgpt-mcp_server:v4.9.9 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.9.7-fix2 # 阿里云 # image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.9.9 # 阿里云
ports: ports:
- 3005:3000 - 3005:3000
networks: networks:
@@ -90,8 +90,8 @@ services:
- FASTGPT_ENDPOINT=http://fastgpt:3000 - FASTGPT_ENDPOINT=http://fastgpt:3000
fastgpt: fastgpt:
container_name: fastgpt container_name: fastgpt
image: ghcr.io/labring/fastgpt:v4.9.7-fix2 # git image: ghcr.io/labring/fastgpt:v4.9.9 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.7-fix2 # 阿里云 # image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.9 # 阿里云
ports: ports:
- 3000:3000 - 3000:3000
networks: networks:

Binary file not shown.

After

Width:  |  Height:  |  Size: 386 KiB

View File

@@ -959,10 +959,16 @@ curl --location --request POST 'http://localhost:3000/api/core/chat/getHistories
{{< markdownify >}} {{< markdownify >}}
{{% alert icon=" " context="success" %}} {{% alert icon=" " context="success" %}}
目前仅能获取到当前 API key 的创建者的对话。
- appId - 应用 Id - appId - 应用 Id
- offset - 偏移量,即从第几条数据开始取 - offset - 偏移量,即从第几条数据开始取
- pageSize - 记录数量 - pageSize - 记录数量
- source - 对话源。source=api表示获取通过 API 创建的对话(不会获取到页面上的对话记录) - source - 对话源。source=api表示获取通过 API 创建的对话(不会获取到页面上的对话记录)
- startCreateTime - 开始创建时间(可选)
- endCreateTime - 结束创建时间(可选)
- startUpdateTime - 开始更新时间(可选)
- endUpdateTime - 结束更新时间(可选)
{{% /alert %}} {{% /alert %}}
{{< /markdownify >}} {{< /markdownify >}}

View File

@@ -0,0 +1,34 @@
---
title: 'V4.9.10(进行中)'
description: 'FastGPT V4.9.10 更新说明'
icon: 'upgrade'
draft: false
toc: true
weight: 790
---
## 🚀 新增内容
1. 支持 PG 设置`systemEnv.hnswMaxScanTuples`参数,提高迭代搜索的数据总量。
2. 工作流调整为单向接入和接出,支持快速的添加下一步节点。
3. 开放飞书和语雀知识库到开源版。
4. gemini 和 claude 最新模型预设。
## ⚙️ 优化
1. LLM stream调用默认超时调大。
2. 部分确认交互优化。
3. 纠正原先知识库的“表格数据集”名称,改成“备份导入”。同时支持知识库索引的导出和导入。
4. 工作流知识库引用上限,如果工作流中没有相关 AI 节点,则交互模式改成纯手动输入,并且上限为 1000万。
5. 语音输入,移动端判断逻辑,准确判断是否为手机,而不是小屏。
6. 优化上下文截取算法,至少保证留下一组 Human 信息。
## 🐛 修复
1. 全文检索多知识库时排序得分排序不正确。
2. 流响应捕获 finish_reason 可能不正确。
3. 工具调用模式,未保存思考输出。
4. 知识库 indexSize 参数未生效。
5. 工作流嵌套 2 层后,获取预览引用、上下文不正确。
6. xlsx 转成 Markdown 时候,前面会多出一个空格。

View File

@@ -1,5 +1,5 @@
--- ---
title: 'V4.9.8(进行中)' title: 'V4.9.8'
description: 'FastGPT V4.9.8 更新说明' description: 'FastGPT V4.9.8 更新说明'
icon: 'upgrade' icon: 'upgrade'
draft: false draft: false
@@ -7,6 +7,17 @@ toc: true
weight: 792 weight: 792
--- ---
## 升级指南
### 1. 做好数据备份
### 2. 更新镜像 tag
- 更新 FastGPT 镜像 tag: v4.9.8
- 更新 FastGPT 商业版镜像 tag: v4.9.8
- mcp_server 无需更新
- Sandbox 无需更新
- AIProxy 无需更新
## 🚀 新增内容 ## 🚀 新增内容
@@ -14,14 +25,25 @@ weight: 792
2. 将所有内置任务,从非 stream 模式调整成 stream 模式,避免部分模型不支持非 stream 模式。如需覆盖,则可以在模型`额外 Body`参数中,强制指定`stream=false` 2. 将所有内置任务,从非 stream 模式调整成 stream 模式,避免部分模型不支持非 stream 模式。如需覆盖,则可以在模型`额外 Body`参数中,强制指定`stream=false`
3. qwen3 模型预设 3. qwen3 模型预设
4. 语雀知识库支持设置根目录。 4. 语雀知识库支持设置根目录。
5. 可配置密码过期时间,过期后下次登录会强制要求修改密码。
6. 密码登录增加 preLogin 临时密钥校验。
7. 支持 Admin 后台配置发布渠道和第三方知识库的显示隐藏。
## ⚙️ 优化 ## ⚙️ 优化
1. Chat log list 优化,避免大数据时超出内存限制。 1. Chat log list 优化,避免大数据时超出内存限制。
2. 预加载 token 计算 worker避免主任务中并发创建导致线程阻塞。 2. 预加载 token 计算 worker避免主任务中并发创建导致线程阻塞。
3. 工作流节点版本控制交互优化。
4. 网络获取以及 html2md 优化,支持视频和音频标签的转换。
## 🐛 修复 ## 🐛 修复
1. 应用列表/知识库列表,删除行权限展示问题。 1. 应用列表/知识库列表,删除行权限展示问题。
2. 打开知识库搜索参数后,重排选项自动被打开。 2. 打开知识库搜索参数后,重排选项自动被打开。
3. LLM json_schema 模式 API 请求格式错误。
4. 重新训练时,图片过期索引未成功清除,导致图片会丢失。
5. 重新训练权限问题。
6. 文档链接地址。
7. Claude 工具调用,由于 index 为空,导致工具调用失败。
8. 嵌套工作流,工具调用下包含交互节点时,流程异常。

View File

@@ -0,0 +1,43 @@
---
title: 'V4.9.9'
description: 'FastGPT V4.9.9 更新说明'
icon: 'upgrade'
draft: false
toc: true
weight: 791
---
## 升级指南
### 1. 做好数据备份
### 2. 商业版用户替换新 License
商业版用户可以联系 FastGPT 团队支持同学,获取 License 替换方案。替换后,可以直接升级系统,管理后台会提示输入新 License。
### 3. 更新镜像 tag
- 更新 FastGPT 镜像 tag: v4.9.9
- 更新 FastGPT 商业版镜像 tag: v4.9.9
- mcp_server 无需更新
- Sandbox 无需更新
- AIProxy 无需更新
## 🚀 新增内容
1. 切换 SessionId 来替代 JWT 实现登录鉴权,可控制最大登录客户端数量。
2. 新的商业版 License 管理模式。
3. 公众号调用,显示记录 chat 对话错误,方便排查。
4. API 知识库支持 BasePath 选择,需增加 API 接口,具体可见[API 知识库介绍](/docs/guide/knowledge_base/api_dataset/#4-获取文件详细信息用于获取文件信息)
## ⚙️ 优化
1. 优化工具调用,新工具的判断逻辑。
2. 调整 Cite 引用提示词。
## 🐛 修复
1. 无法正常获取应用历史保存/发布记录。
2. 成员创建 MCP 工具权限问题。
3. 来源引用展示,存在 ID 传递错误,导致提示无权操作该文件。
4. 回答标注前端数据报错。

View File

@@ -43,7 +43,7 @@ type ResponseType = {
// 文件列表中,单项的文件类型 // 文件列表中,单项的文件类型
type FileListItem = { type FileListItem = {
id: string; id: string;
parentId: string | null; parentId: string //也可能为 null 或者 undefined 类型;
name: string; name: string;
type: 'file' | 'folder'; type: 'file' | 'folder';
updateTime: Date; updateTime: Date;
@@ -59,7 +59,7 @@ type FileListItem = {
{{< markdownify >}} {{< markdownify >}}
{{% alert icon=" " context="success" %}} {{% alert icon=" " context="success" %}}
- parentId - 父级 id可选或者 null。 - parentId - 父级 id可选或者 null | undefined
- searchKey - 检索词,可选 - searchKey - 检索词,可选
{{% /alert %}} {{% /alert %}}
@@ -68,7 +68,7 @@ curl --location --request POST '{{baseURL}}/v1/file/list' \
--header 'Authorization: Bearer {{authorization}}' \ --header 'Authorization: Bearer {{authorization}}' \
--header 'Content-Type: application/json' \ --header 'Content-Type: application/json' \
--data-raw '{ --data-raw '{
"parentId": null, "parentId": "",
"searchKey": "" "searchKey": ""
}' }'
``` ```
@@ -185,3 +185,40 @@ curl --location --request GET '{{baseURL}}/v1/file/read?id=xx' \
{{< /tabs >}} {{< /tabs >}}
### 4. 获取文件详细信息(用于获取文件信息)
{{< tabs tabTotal="2" >}}
{{< tab tabName="请求示例" >}}
{{< markdownify >}}
id 为文件的 id。
```bash
curl --location --request GET '{{baseURL}}/v1/file/detail?id=xx' \
--header 'Authorization: Bearer {{authorization}}'
```
{{< /markdownify >}}
{{< /tab >}}
{{< tab tabName="响应示例" >}}
{{< markdownify >}}
```json
{
"code": 200,
"success": true,
"message": "",
"data": {
"id": "docs",
"parentId": "",
"name": "docs"
}
}
```
{{< /markdownify >}}
{{< /tab >}}
{{< /tabs >}}

View File

@@ -28,7 +28,6 @@ FastGPT 商业版是基于 FastGPT 开源版的增强版本,增加了一些独
| 应用发布安全配置 | ❌ | ✅ | ✅ | | 应用发布安全配置 | ❌ | ✅ | ✅ |
| 内容审核 | ❌ | ✅ | ✅ | | 内容审核 | ❌ | ✅ | ✅ |
| web站点同步 | ❌ | ✅ | ✅ | | web站点同步 | ❌ | ✅ | ✅ |
| 主流文档库接入(目前支持:语雀、飞书) | ❌ | ✅ | ✅ |
| 增强训练模式 | ❌ | ✅ | ✅ | | 增强训练模式 | ❌ | ✅ | ✅ |
| 第三方应用快速接入(飞书、公众号) | ❌ | ✅ | ✅ | | 第三方应用快速接入(飞书、公众号) | ❌ | ✅ | ✅ |
| 管理后台 | ❌ | ✅ | 不需要 | | 管理后台 | ❌ | ✅ | 不需要 |

View File

@@ -95,6 +95,10 @@ weight: 506
121.196.228.45 121.196.228.45
121.43.126.202 121.43.126.202
120.26.144.37 120.26.144.37
47.98.196.113
47.97.204.90
118.31.41.236
118.178.185.61
``` ```
## 4. 获取AES Key选择加密方式 ## 4. 获取AES Key选择加密方式
@@ -125,6 +129,13 @@ weight: 506
## FAQ ## FAQ
### 公众号没响应
检查应用对话日志,如果有对话日志,但是微信公众号无响应,则是白名单 IP未成功。
添加白名单IP 后,通常需要等待几分钟微信更新。可以在对话日志中,找点错误日志。
![](/imgs/official_account_faq.png)
### 如何新开一个聊天记录 ### 如何新开一个聊天记录
如果你想重置你的聊天记录,可以给机器人发送 `Reset` 消息(注意大小写),机器人会新开一个聊天记录。 如果你想重置你的聊天记录,可以给机器人发送 `Reset` 消息(注意大小写),机器人会新开一个聊天记录。

3
env.d.ts vendored
View File

@@ -4,7 +4,6 @@ declare global {
LOG_DEPTH: string; LOG_DEPTH: string;
DEFAULT_ROOT_PSW: string; DEFAULT_ROOT_PSW: string;
DB_MAX_LINK: string; DB_MAX_LINK: string;
TOKEN_KEY: string;
FILE_TOKEN_KEY: string; FILE_TOKEN_KEY: string;
ROOT_KEY: string; ROOT_KEY: string;
OPENAI_BASE_URL: string; OPENAI_BASE_URL: string;
@@ -36,6 +35,8 @@ declare global {
SHOW_COUPON?: string; SHOW_COUPON?: string;
CONFIG_JSON_PATH?: string; CONFIG_JSON_PATH?: string;
PASSWORD_LOGIN_LOCK_SECONDS?: string; PASSWORD_LOGIN_LOCK_SECONDS?: string;
PASSWORD_EXPIRED_MONTH?: string;
MAX_LOGIN_SESSION?: string;
} }
} }
} }

View File

@@ -27,7 +27,7 @@ const datasetErr = [
}, },
{ {
statusText: DatasetErrEnum.unExist, statusText: DatasetErrEnum.unExist,
message: 'core.dataset.error.unExistDataset' message: i18nT('common:core.dataset.error.unExistDataset')
}, },
{ {
statusText: DatasetErrEnum.unExistCollection, statusText: DatasetErrEnum.unExistCollection,

View File

@@ -2,13 +2,28 @@ import { type ErrType } from '../errorCode';
import { i18nT } from '../../../../web/i18n/utils'; import { i18nT } from '../../../../web/i18n/utils';
/* dataset: 509000 */ /* dataset: 509000 */
export enum SystemErrEnum { export enum SystemErrEnum {
communityVersionNumLimit = 'communityVersionNumLimit' communityVersionNumLimit = 'communityVersionNumLimit',
licenseAppAmountLimit = 'licenseAppAmountLimit',
licenseDatasetAmountLimit = 'licenseDatasetAmountLimit',
licenseUserAmountLimit = 'licenseUserAmountLimit'
} }
const systemErr = [ const systemErr = [
{ {
statusText: SystemErrEnum.communityVersionNumLimit, statusText: SystemErrEnum.communityVersionNumLimit,
message: i18nT('common:code_error.system_error.community_version_num_limit') message: i18nT('common:code_error.system_error.community_version_num_limit')
},
{
statusText: SystemErrEnum.licenseAppAmountLimit,
message: i18nT('common:code_error.system_error.license_app_amount_limit')
},
{
statusText: SystemErrEnum.licenseDatasetAmountLimit,
message: i18nT('common:code_error.system_error.license_dataset_amount_limit')
},
{
statusText: SystemErrEnum.licenseUserAmountLimit,
message: i18nT('common:code_error.system_error.license_user_amount_limit')
} }
]; ];

View File

@@ -5,7 +5,7 @@ export const checkPasswordRule = (password: string) => {
/[A-Z]/, // Contains uppercase letters /[A-Z]/, // Contains uppercase letters
/[!@#$%^&*()_+=-]/ // Contains special characters /[!@#$%^&*()_+=-]/ // Contains special characters
]; ];
const validChars = /^[\dA-Za-z!@#$%^&*()_+=-]{6,100}$/; const validChars = /^[\dA-Za-z!@#$%^&*()_+=-]{8,100}$/;
// Check length and valid characters // Check length and valid characters
if (!validChars.test(password)) return false; if (!validChars.test(password)) return false;

View File

@@ -1,7 +1,8 @@
export enum SystemConfigsTypeEnum { export enum SystemConfigsTypeEnum {
fastgpt = 'fastgpt', fastgpt = 'fastgpt',
fastgptPro = 'fastgptPro', fastgptPro = 'fastgptPro',
systemMsgModal = 'systemMsgModal' systemMsgModal = 'systemMsgModal',
license = 'license'
} }
export const SystemConfigsTypeMap = { export const SystemConfigsTypeMap = {
@@ -13,5 +14,8 @@ export const SystemConfigsTypeMap = {
}, },
[SystemConfigsTypeEnum.systemMsgModal]: { [SystemConfigsTypeEnum.systemMsgModal]: {
label: 'systemMsgModal' label: 'systemMsgModal'
},
[SystemConfigsTypeEnum.license]: {
label: 'license'
} }
}; };

View File

@@ -64,6 +64,15 @@ export type FastGPTFeConfigsType = {
show_coupon?: boolean; show_coupon?: boolean;
concatMd?: string; concatMd?: string;
show_dataset_feishu?: boolean;
show_dataset_yuque?: boolean;
show_publish_feishu?: boolean;
show_publish_dingtalk?: boolean;
show_publish_offiaccount?: boolean;
show_dataset_enhance?: boolean;
show_batch_eval?: boolean;
concatMd?: string; concatMd?: string;
docUrl?: string; docUrl?: string;
openAPIDocUrl?: string; openAPIDocUrl?: string;
@@ -121,9 +130,11 @@ export type SystemEnvType = {
vectorMaxProcess: number; vectorMaxProcess: number;
qaMaxProcess: number; qaMaxProcess: number;
vlmMaxProcess: number; vlmMaxProcess: number;
hnswEfSearch: number;
tokenWorkers: number; // token count max worker tokenWorkers: number; // token count max worker
hnswEfSearch: number;
hnswMaxScanTuples: number;
oneapiUrl?: string; oneapiUrl?: string;
chatApiKey?: string; chatApiKey?: string;
@@ -136,3 +147,21 @@ export type customPdfParseType = {
doc2xKey?: string; doc2xKey?: string;
price?: number; price?: number;
}; };
export type LicenseDataType = {
startTime: string;
expiredTime: string;
company: string;
description?: string; // 描述
hosts?: string[]; // 管理端有效域名
maxUsers?: number; // 最大用户数,不填默认不上限
maxApps?: number; // 最大应用数,不填默认不上限
maxDatasets?: number; // 最大数据集数,不填默认不上限
functions: {
sso: boolean;
pay: boolean;
customTemplates: boolean;
datasetEnhance: boolean;
batchEval: boolean;
};
};

View File

@@ -2,6 +2,248 @@ import { type PromptTemplateItem } from '../type.d';
import { i18nT } from '../../../../web/i18n/utils'; import { i18nT } from '../../../../web/i18n/utils';
import { getPromptByVersion } from './utils'; import { getPromptByVersion } from './utils';
export const Prompt_userQuotePromptList: PromptTemplateItem[] = [
{
title: i18nT('app:template.standard_template'),
desc: '',
value: {
['4.9.7']: `## 任务描述
你是一个知识库回答助手,可以使用 <Cites></Cites> 中的内容作为你本次回答的参考。
同时,为了使回答结果更加可信并且可追溯,你需要在每段话结尾添加引用标记,标识参考了哪些内容。
## 追溯展示规则
- 使用 [id](CITE) 的格式来引用 <Cites></Cites> 中的知识,其中 CITE 是固定常量, id 为引文中的 id。
- 在 **每段话结尾** 自然地整合引用。例如: "Nginx是一款轻量级的Web服务器、反向代理服务器[67e517e74767063e882d6861](CITE)。"。
- 每段话**至少包含一个引用**,多个引用时按顺序排列,例如:"Nginx是一款轻量级的Web服务器、反向代理服务器[67e517e74767063e882d6861](CITE)[67e517e74767063e882d6862](CITE)。\n 它的特点是非常轻量[67e517e74767063e882d6863](CITE)。"
- 不要把示例作为知识点。
- 不要伪造 id返回的 id 必须都存在 <Cites></Cites> 中!
## 通用规则
- 如果你不清楚答案,你需要澄清。
- 避免提及你是从 <Cites></Cites> 获取的知识。
- 保持答案与 <Cites></Cites> 中描述的一致。
- 使用 Markdown 语法优化回答格式。尤其是图片、表格、序列号等内容,需严格完整输出。
- 使用与问题相同的语言回答。
<Cites>
{{quote}}
</Cites>
## 用户问题
{{question}}
## 回答
`
}
},
{
title: i18nT('app:template.qa_template'),
desc: '',
value: {
['4.9.7']: `## 任务描述
作为一个问答助手,你会使用 <QA></QA> 标记中的提供的数据对进行内容回答。
## 回答要求
- 选择其中一个或多个问答对进行回答。
- 回答的内容应尽可能与 <Answer></Answer> 中的内容一致。
- 如果没有相关的问答对,你需要澄清。
- 避免提及你是从 <QA></QA> 获取的知识,只需要回复答案。
- 使用与问题相同的语言回答。
<QA>
{{quote}}
</QA>
## 用户问题
{{question}}
## 回答
`
}
},
{
title: i18nT('app:template.standard_strict'),
desc: '',
value: {
['4.9.7']: `## 任务描述
你是一个知识库回答助手,可以使用 <Cites></Cites> 中的内容作为你本次回答的参考。
同时,为了使回答结果更加可信并且可追溯,你需要在每段话结尾添加引用标记,标识参考了哪些内容。
## 追溯展示规则
- 使用 [id](CITE) 的格式来引用 <Cites></Cites> 中的知识,其中 CITE 是固定常量, id 为引文中的 id。
- 在 **每段话结尾** 自然地整合引用。例如: "Nginx是一款轻量级的Web服务器、反向代理服务器[67e517e74767063e882d6861](CITE)。"。
- 每段话**至少包含一个引用**,多个引用时按顺序排列,例如:"Nginx是一款轻量级的Web服务器、反向代理服务器[67e517e74767063e882d6861](CITE)[67e517e74767063e882d6862](CITE)。\n 它的特点是非常轻量[67e517e74767063e882d6863](CITE)。"
- 不要把示例作为知识点。
- 不要伪造 id返回的 id 必须都存在 <Cites></Cites> 中!
## 通用规则
- 如果你不清楚答案,你需要澄清。
- 避免提及你是从 <Cites></Cites> 获取的知识。
- 保持答案与 <Cites></Cites> 中描述的一致。
- 使用 Markdown 语法优化回答格式。尤其是图片、表格、序列号等内容,需严格完整输出。
- 使用与问题相同的语言回答。
## 严格要求
你只能使用 <Cites></Cites> 标记中的内容作为参考,不能使用自身的知识,并且回答的内容需严格与 <Cites></Cites> 中的内容一致。
<Cites>
{{quote}}
</Cites>
## 用户问题
{{question}}
## 回答
`
}
},
{
title: i18nT('app:template.hard_strict'),
desc: '',
value: {
['4.9.7']: `## 任务描述
作为一个问答助手,你会使用 <QA></QA> 标记中的提供的数据对进行内容回答。
## 回答要求
- 选择其中一个或多个问答对进行回答。
- 回答的内容应尽可能与 <Answer></Answer> 中的内容一致。
- 如果没有相关的问答对,你需要澄清。
- 避免提及你是从 <QA></QA> 获取的知识,只需要回复答案。
- 使用与问题相同的语言回答。
## 严格要求
你只能使用 <QA></QA> 标记中的内容作为参考,不能使用自身的知识,并且回答的内容需严格与 <QA></QA> 中的内容一致。
<QA>
{{quote}}
</QA>
## 用户问题
{{question}}
## 回答
`
}
}
];
export const Prompt_systemQuotePromptList: PromptTemplateItem[] = [
{
title: i18nT('app:template.standard_template'),
desc: '',
value: {
['4.9.7']: `## 任务描述
你是一个知识库回答助手,可以使用 <Cites></Cites> 中的内容作为你本次回答的参考。
同时,为了使回答结果更加可信并且可追溯,你需要在每段话结尾添加引用标记,标识参考了哪些内容。
## 追溯展示规则
- 使用 [id](CITE) 的格式来引用 <Cites></Cites> 中的知识,其中 CITE 是固定常量, id 为引文中的 id。
- 在 **每段话结尾** 自然地整合引用。例如: "Nginx是一款轻量级的Web服务器、反向代理服务器[67e517e74767063e882d6861](CITE)。"。
- 每段话**至少包含一个引用**,多个引用时按顺序排列,例如:"Nginx是一款轻量级的Web服务器、反向代理服务器[67e517e74767063e882d6861](CITE)[67e517e74767063e882d6862](CITE)。\n 它的特点是非常轻量[67e517e74767063e882d6863](CITE)。"
- 不要把示例作为知识点。
- 不要伪造 id返回的 id 必须都存在 <Cites></Cites> 中!
## 通用规则
- 如果你不清楚答案,你需要澄清。
- 避免提及你是从 <Cites></Cites> 获取的知识。
- 保持答案与 <Cites></Cites> 中描述的一致。
- 使用 Markdown 语法优化回答格式。尤其是图片、表格、序列号等内容,需严格完整输出。
- 使用与问题相同的语言回答。
<Cites>
{{quote}}
</Cites>`
}
},
{
title: i18nT('app:template.qa_template'),
desc: '',
value: {
['4.9.8']: `## 任务描述
作为一个问答助手,你会使用 <QA></QA> 标记中的提供的数据对进行内容回答。
## 回答要求
- 选择其中一个或多个问答对进行回答。
- 回答的内容应尽可能与 <Answer></Answer> 中的内容一致。
- 如果没有相关的问答对,你需要澄清。
- 避免提及你是从 <QA></QA> 获取的知识,只需要回复答案。
- 使用与问题相同的语言回答。
<QA>
{{quote}}
</QA>`
}
},
{
title: i18nT('app:template.standard_strict'),
desc: '',
value: {
['4.9.7']: `## 任务描述
你是一个知识库回答助手,可以使用 <Cites></Cites> 中的内容作为你本次回答的参考。
同时,为了使回答结果更加可信并且可追溯,你需要在每段话结尾添加引用标记,标识参考了哪些内容。
## 追溯展示规则
- 使用 [id](CITE) 的格式来引用 <Cites></Cites> 中的知识,其中 CITE 是固定常量, id 为引文中的 id。
- 在 **每段话结尾** 自然地整合引用。例如: "Nginx是一款轻量级的Web服务器、反向代理服务器[67e517e74767063e882d6861](CITE)。"。
- 每段话**至少包含一个引用**,多个引用时按顺序排列,例如:"Nginx是一款轻量级的Web服务器、反向代理服务器[67e517e74767063e882d6861](CITE)[67e517e74767063e882d6862](CITE)。\n 它的特点是非常轻量[67e517e74767063e882d6863](CITE)。"
- 不要把示例作为知识点。
- 不要伪造 id返回的 id 必须都存在 <Cites></Cites> 中!
## 通用规则
- 如果你不清楚答案,你需要澄清。
- 避免提及你是从 <Cites></Cites> 获取的知识。
- 保持答案与 <Cites></Cites> 中描述的一致。
- 使用 Markdown 语法优化回答格式。尤其是图片、表格、序列号等内容,需严格完整输出。
- 使用与问题相同的语言回答。
## 严格要求
你只能使用 <Cites></Cites> 标记中的内容作为参考,不能使用自身的知识,并且回答的内容需严格与 <Cites></Cites> 中的内容一致。
<Cites>
{{quote}}
</Cites>`
}
},
{
title: i18nT('app:template.hard_strict'),
desc: '',
value: {
['4.9.7']: `## 任务描述
作为一个问答助手,你会使用 <QA></QA> 标记中的提供的数据对进行内容回答。
## 回答要求
- 选择其中一个或多个问答对进行回答。
- 回答的内容应尽可能与 <Answer></Answer> 中的内容一致。
- 如果没有相关的问答对,你需要澄清。
- 避免提及你是从 <QA></QA> 获取的知识,只需要回复答案。
- 使用与问题相同的语言回答。
## 严格要求
你只能使用 <QA></QA> 标记中的内容作为参考,不能使用自身的知识,并且回答的内容需严格与 <QA></QA> 中的内容一致。
<QA>
{{quote}}
</QA>`
}
}
];
export const Prompt_QuoteTemplateList: PromptTemplateItem[] = [ export const Prompt_QuoteTemplateList: PromptTemplateItem[] = [
{ {
title: i18nT('app:template.standard_template'), title: i18nT('app:template.standard_template'),
@@ -10,11 +252,6 @@ export const Prompt_QuoteTemplateList: PromptTemplateItem[] = [
['4.9.7']: `{ ['4.9.7']: `{
"id": "{{id}}", "id": "{{id}}",
"sourceName": "{{source}}", "sourceName": "{{source}}",
"content": "{{q}}\n{{a}}"
}
`,
['4.9.2']: `{
"sourceName": "{{source}}",
"updateTime": "{{updateTime}}", "updateTime": "{{updateTime}}",
"content": "{{q}}\n{{a}}" "content": "{{q}}\n{{a}}"
} }
@@ -25,7 +262,7 @@ export const Prompt_QuoteTemplateList: PromptTemplateItem[] = [
title: i18nT('app:template.qa_template'), title: i18nT('app:template.qa_template'),
desc: i18nT('app:template.qa_template_des'), desc: i18nT('app:template.qa_template_des'),
value: { value: {
['4.9.2']: `<Question> ['4.9.7']: `<Question>
{{q}} {{q}}
</Question> </Question>
<Answer> <Answer>
@@ -40,11 +277,6 @@ export const Prompt_QuoteTemplateList: PromptTemplateItem[] = [
['4.9.7']: `{ ['4.9.7']: `{
"id": "{{id}}", "id": "{{id}}",
"sourceName": "{{source}}", "sourceName": "{{source}}",
"content": "{{q}}\n{{a}}"
}
`,
['4.9.2']: `{
"sourceName": "{{source}}",
"updateTime": "{{updateTime}}", "updateTime": "{{updateTime}}",
"content": "{{q}}\n{{a}}" "content": "{{q}}\n{{a}}"
} }
@@ -55,7 +287,7 @@ export const Prompt_QuoteTemplateList: PromptTemplateItem[] = [
title: i18nT('app:template.hard_strict'), title: i18nT('app:template.hard_strict'),
desc: i18nT('app:template.hard_strict_des'), desc: i18nT('app:template.hard_strict_des'),
value: { value: {
['4.9.2']: `<Question> ['4.9.7']: `<Question>
{{q}} {{q}}
</Question> </Question>
<Answer> <Answer>
@@ -64,263 +296,12 @@ export const Prompt_QuoteTemplateList: PromptTemplateItem[] = [
} }
} }
]; ];
export const getQuoteTemplate = (version?: string) => { export const getQuoteTemplate = (version?: string) => {
const defaultTemplate = Prompt_QuoteTemplateList[0].value; const defaultTemplate = Prompt_QuoteTemplateList[0].value;
return getPromptByVersion(version, defaultTemplate); return getPromptByVersion(version, defaultTemplate);
}; };
export const Prompt_userQuotePromptList: PromptTemplateItem[] = [
{
title: i18nT('app:template.standard_template'),
desc: '',
value: {
['4.9.7']: `使用 <Reference></Reference> 标记中的内容作为本次对话的参考:
<Reference>
{{quote}}
</Reference>
回答要求:
- 如果你不清楚答案,你需要澄清。
- 避免提及你是从 <Reference></Reference> 获取的知识。
- 保持答案与 <Reference></Reference> 中描述的一致。
- 使用 Markdown 语法优化回答格式。
- 使用与问题相同的语言回答。
- 使用 [id](CITE) 格式来引用<Reference></Reference>中的知识,其中 CITE 是固定常量, id 为引文中的 id。
- 在每段结尾自然地整合引用。例如: "FastGPT 是一个基于大语言模型(LLM)的知识库问答系统[67e517e74767063e882d6861](CITE)。"
- 每段至少包含一个引用,也可根据内容需要加入多个引用,按顺序排列。`,
['4.9.2']: `使用 <Reference></Reference> 标记中的内容作为本次对话的参考:
<Reference>
{{quote}}
</Reference>
回答要求:
- 如果你不清楚答案,你需要澄清。
- 避免提及你是从 <Reference></Reference> 获取的知识。
- 保持答案与 <Reference></Reference> 中描述的一致。
- 使用 Markdown 语法优化回答格式。
- 使用与问题相同的语言回答。
问题:"""{{question}}"""`
}
},
{
title: i18nT('app:template.qa_template'),
desc: '',
value: {
['4.9.2']: `使用 <QA></QA> 标记中的问答对进行回答。
<QA>
{{quote}}
</QA>
回答要求:
- 选择其中一个或多个问答对进行回答。
- 回答的内容应尽可能与 <答案></答案> 中的内容一致。
- 如果没有相关的问答对,你需要澄清。
- 避免提及你是从 QA 获取的知识,只需要回复答案。
问题:"""{{question}}"""`
}
},
{
title: i18nT('app:template.standard_strict'),
desc: '',
value: {
['4.9.7']: `忘记你已有的知识,仅使用 <Reference></Reference> 标记中的内容作为本次对话的参考:
<Reference>
{{quote}}
</Reference>
思考流程:
1. 判断问题是否与 <Reference></Reference> 标记中的内容有关。
2. 如果有关,你按下面的要求回答。
3. 如果无关,你直接拒绝回答本次问题。
回答要求:
- 避免提及你是从 <Reference></Reference> 获取的知识。
- 保持答案与 <Reference></Reference> 中描述的一致。
- 使用 Markdown 语法优化回答格式。
- 使用与问题相同的语言回答。
- 使用 [id](CITE) 格式来引用<Reference></Reference>中的知识,其中 CITE 是固定常量, id 为引文中的 id。
- 在每段结尾自然地整合引用。例如: "FastGPT 是一个基于大语言模型(LLM)的知识库问答系统[67e517e74767063e882d6861](CITE)。"
- 每段至少包含一个引用,也可根据内容需要加入多个引用,按顺序排列。
问题:"""{{question}}"""`,
['4.9.2']: `忘记你已有的知识,仅使用 <Reference></Reference> 标记中的内容作为本次对话的参考:
<Reference>
{{quote}}
</Reference>
思考流程:
1. 判断问题是否与 <Reference></Reference> 标记中的内容有关。
2. 如果有关,你按下面的要求回答。
3. 如果无关,你直接拒绝回答本次问题。
回答要求:
- 避免提及你是从 <Reference></Reference> 获取的知识。
- 保持答案与 <Reference></Reference> 中描述的一致。
- 使用 Markdown 语法优化回答格式。
- 使用与问题相同的语言回答。
问题:"""{{question}}"""`
}
},
{
title: i18nT('app:template.hard_strict'),
desc: '',
value: {
['4.9.2']: `忘记你已有的知识,仅使用 <QA></QA> 标记中的问答对进行回答。
<QA>
{{quote}}
</QA>
思考流程:
1. 判断问题是否与 <QA></QA> 标记中的内容有关。
2. 如果无关,你直接拒绝回答本次问题。
3. 判断是否有相近或相同的问题。
4. 如果有相同的问题,直接输出对应答案。
5. 如果只有相近的问题,请把相近的问题和答案一起输出。
回答要求:
- 如果没有相关的问答对,你需要澄清。
- 回答的内容应尽可能与 <QA></QA> 标记中的内容一致。
- 避免提及你是从 QA 获取的知识,只需要回复答案。
- 使用 Markdown 语法优化回答格式。
- 使用与问题相同的语言回答。
问题:"""{{question}}"""`
}
}
];
export const Prompt_systemQuotePromptList: PromptTemplateItem[] = [
{
title: i18nT('app:template.standard_template'),
desc: '',
value: {
['4.9.7']: `使用 <Reference></Reference> 标记中的内容作为本次对话的参考:
<Reference>
{{quote}}
</Reference>
回答要求:
- 如果你不清楚答案,你需要澄清。
- 避免提及你是从 <Reference></Reference> 获取的知识。
- 保持答案与 <Reference></Reference> 中描述的一致。
- 使用 Markdown 语法优化回答格式。
- 使用与问题相同的语言回答。
- 使用 [id](CITE) 格式来引用<Reference></Reference>中的知识,其中 CITE 是固定常量, id 为引文中的 id。
- 在每段结尾自然地整合引用。例如: "FastGPT 是一个基于大语言模型(LLM)的知识库问答系统[67e517e74767063e882d6861](CITE)。"
- 每段至少包含一个引用,也可根据内容需要加入多个引用,按顺序排列。`,
['4.9.2']: `使用 <Reference></Reference> 标记中的内容作为本次对话的参考:
<Reference>
{{quote}}
</Reference>
回答要求:
- 如果你不清楚答案,你需要澄清。
- 避免提及你是从 <Reference></Reference> 获取的知识。
- 保持答案与 <Reference></Reference> 中描述的一致。
- 使用 Markdown 语法优化回答格式。
- 使用与问题相同的语言回答。`
}
},
{
title: i18nT('app:template.qa_template'),
desc: '',
value: {
['4.9.2']: `使用 <QA></QA> 标记中的问答对进行回答。
<QA>
{{quote}}
</QA>
回答要求:
- 选择其中一个或多个问答对进行回答。
- 回答的内容应尽可能与 <答案></答案> 中的内容一致。
- 如果没有相关的问答对,你需要澄清。
- 避免提及你是从 QA 获取的知识,只需要回复答案。`
}
},
{
title: i18nT('app:template.standard_strict'),
desc: '',
value: {
['4.9.7']: `忘记你已有的知识,仅使用 <Reference></Reference> 标记中的内容作为本次对话的参考:
<Reference>
{{quote}}
</Reference>
思考流程:
1. 判断问题是否与 <Reference></Reference> 标记中的内容有关。
2. 如果有关,你按下面的要求回答。
3. 如果无关,你直接拒绝回答本次问题。
回答要求:
- 避免提及你是从 <Reference></Reference> 获取的知识。
- 保持答案与 <Reference></Reference> 中描述的一致。
- 使用 Markdown 语法优化回答格式。
- 使用与问题相同的语言回答。
- 使用 [id](CITE) 格式来引用<Reference></Reference>中的知识,其中 CITE 是固定常量, id 为引文中的 id。
- 在每段结尾自然地整合引用。例如: "FastGPT 是一个基于大语言模型(LLM)的知识库问答系统[67e517e74767063e882d6861](CITE)。"
- 每段至少包含一个引用,也可根据内容需要加入多个引用,按顺序排列。
问题:"""{{question}}"""`,
['4.9.2']: `忘记你已有的知识,仅使用 <Reference></Reference> 标记中的内容作为本次对话的参考:
<Reference>
{{quote}}
</Reference>
思考流程:
1. 判断问题是否与 <Reference></Reference> 标记中的内容有关。
2. 如果有关,你按下面的要求回答。
3. 如果无关,你直接拒绝回答本次问题。
回答要求:
- 避免提及你是从 <Reference></Reference> 获取的知识。
- 保持答案与 <Reference></Reference> 中描述的一致。
- 使用 Markdown 语法优化回答格式。
- 使用与问题相同的语言回答。`
}
},
{
title: i18nT('app:template.hard_strict'),
desc: '',
value: {
['4.9.2']: `忘记你已有的知识,仅使用 <QA></QA> 标记中的问答对进行回答。
<QA>
{{quote}}
</QA>
思考流程:
1. 判断问题是否与 <QA></QA> 标记中的内容有关。
2. 如果无关,你直接拒绝回答本次问题。
3. 判断是否有相近或相同的问题。
4. 如果有相同的问题,直接输出对应答案。
5. 如果只有相近的问题,请把相近的问题和答案一起输出。
回答要求:
- 如果没有相关的问答对,你需要澄清。
- 回答的内容应尽可能与 <QA></QA> 标记中的内容一致。
- 避免提及你是从 QA 获取的知识,只需要回复答案。
- 使用 Markdown 语法优化回答格式。
- 使用与问题相同的语言回答。`
}
}
];
export const getQuotePrompt = (version?: string, role: 'user' | 'system' = 'user') => { export const getQuotePrompt = (version?: string, role: 'user' | 'system' = 'user') => {
const quotePromptTemplates = const quotePromptTemplates =
role === 'user' ? Prompt_userQuotePromptList : Prompt_systemQuotePromptList; role === 'user' ? Prompt_userQuotePromptList : Prompt_systemQuotePromptList;
@@ -331,9 +312,9 @@ export const getQuotePrompt = (version?: string, role: 'user' | 'system' = 'user
}; };
// Document quote prompt // Document quote prompt
export const getDocumentQuotePrompt = (version: string) => { export const getDocumentQuotePrompt = (version?: string) => {
const promptMap = { const promptMap = {
['4.9.2']: `将 <FilesContent></FilesContent> 中的内容作为本次对话的参考: ['4.9.7']: `将 <FilesContent></FilesContent> 中的内容作为本次对话的参考:
<FilesContent> <FilesContent>
{{quote}} {{quote}}
</FilesContent> </FilesContent>

View File

@@ -1,14 +1,19 @@
export const getDatasetSearchToolResponsePrompt = () => { export const getDatasetSearchToolResponsePrompt = () => {
return `## Role return `## Role
你是一个知识库回答助手,可以 "quotes" 中的内容作为本次对话的参考。为了使回答结果更加可信并且可追溯,你需要在每段话结尾添加引用标记。 你是一个知识库回答助手,可以 "cites" 中的内容作为本次对话的参考。为了使回答结果更加可信并且可追溯,你需要在每段话结尾添加引用标记,标识参考了哪些内容
## Rules ## 追溯展示规则
- 使用 **[id](CITE)** 格式来引用 "cites" 中的知识,其中 CITE 是固定常量, id 为引文中的 id。
- 在 **每段话结尾** 自然地整合引用。例如: "Nginx是一款轻量级的Web服务器、反向代理服务器[67e517e74767063e882d6861](CITE)。"。
- 每段话**至少包含一个引用**,多个引用时按顺序排列,例如:"Nginx是一款轻量级的Web服务器、反向代理服务器[67e517e74767063e882d6861](CITE)[67e517e74767063e882d6862](CITE)。\n 它的特点是非常轻量[67e517e74767063e882d6863](CITE)。"
- 不要把示例作为知识点。
- 不要伪造 id返回的 id 必须都存在 cites 中!
## 通用规则
- 如果你不清楚答案,你需要澄清。 - 如果你不清楚答案,你需要澄清。
- 避免提及你是从 "quotes" 获取的知识。 - 避免提及你是从 "cites" 获取的知识。
- 保持答案与 "quotes" 中描述的一致。 - 保持答案与 "cites" 中描述的一致。
- 使用 Markdown 语法优化回答格式。尤其是图片、表格、序列号等内容,需严格完整输出。 - 使用 Markdown 语法优化回答格式。尤其是图片、表格、序列号等内容,需严格完整输出。
- 使用与问题相同的语言回答。 - 使用与问题相同的语言回答。`;
- 使用 [id](CITE) 格式来引用 "quotes" 中的知识,其中 CITE 是固定常量, id 为引文中的 id。
- 在每段话结尾自然地整合引用。例如: "FastGPT 是一个基于大语言模型(LLM)的知识库问答系统[67e517e74767063e882d6861](CITE)。"
- 每段话至少包含一个引用,也可根据内容需要加入多个引用,按顺序排列。`;
}; };

View File

@@ -1,4 +1,5 @@
export const getPromptByVersion = (version?: string, promptMap: Record<string, string> = {}) => { export const getPromptByVersion = (version?: string, promptMap: Record<string, string> = {}) => {
// 版本号大的在前面
const versions = Object.keys(promptMap).sort((a, b) => { const versions = Object.keys(promptMap).sort((a, b) => {
const [majorA, minorA, patchA] = a.split('.').map(Number); const [majorA, minorA, patchA] = a.split('.').map(Number);
const [majorB, minorB, patchB] = b.split('.').map(Number); const [majorB, minorB, patchB] = b.split('.').map(Number);
@@ -15,5 +16,5 @@ export const getPromptByVersion = (version?: string, promptMap: Record<string, s
if (version in promptMap) { if (version in promptMap) {
return promptMap[version]; return promptMap[version];
} }
return promptMap[versions[versions.length - 1]]; return promptMap[versions[0]];
}; };

View File

@@ -60,5 +60,3 @@ export enum AppTemplateTypeEnum {
// special type // special type
contribute = 'contribute' contribute = 'contribute'
} }
export const defaultDatasetMaxTokens = 16000;

View File

@@ -5,7 +5,7 @@ import {
FlowNodeTypeEnum FlowNodeTypeEnum
} from '../../workflow/node/constant'; } from '../../workflow/node/constant';
import { nanoid } from 'nanoid'; import { nanoid } from 'nanoid';
import { type ToolType } from '../type'; import { type McpToolConfigType } from '../type';
import { i18nT } from '../../../../web/i18n/utils'; import { i18nT } from '../../../../web/i18n/utils';
import { type RuntimeNodeItemType } from '../../workflow/runtime/type'; import { type RuntimeNodeItemType } from '../../workflow/runtime/type';
@@ -16,7 +16,7 @@ export const getMCPToolSetRuntimeNode = ({
avatar avatar
}: { }: {
url: string; url: string;
toolList: ToolType[]; toolList: McpToolConfigType[];
name?: string; name?: string;
avatar?: string; avatar?: string;
}): RuntimeNodeItemType => { }): RuntimeNodeItemType => {
@@ -45,7 +45,7 @@ export const getMCPToolRuntimeNode = ({
url, url,
avatar = 'core/app/type/mcpToolsFill' avatar = 'core/app/type/mcpToolsFill'
}: { }: {
tool: ToolType; tool: McpToolConfigType;
url: string; url: string;
avatar?: string; avatar?: string;
}): RuntimeNodeItemType => { }): RuntimeNodeItemType => {
@@ -65,7 +65,7 @@ export const getMCPToolRuntimeNode = ({
...Object.entries(tool.inputSchema?.properties || {}).map(([key, value]) => ({ ...Object.entries(tool.inputSchema?.properties || {}).map(([key, value]) => ({
key, key,
label: key, label: key,
valueType: value.type as WorkflowIOValueTypeEnum, valueType: value.type as WorkflowIOValueTypeEnum, // TODO: 这里需要做一个映射
description: value.description, description: value.description,
toolDescription: value.description || key, toolDescription: value.description || key,
required: tool.inputSchema?.required?.includes(key) || false, required: tool.inputSchema?.required?.includes(key) || false,

View File

@@ -16,16 +16,6 @@ import { FlowNodeInputTypeEnum } from '../../core/workflow/node/constant';
import type { WorkflowTemplateBasicType } from '@fastgpt/global/core/workflow/type'; import type { WorkflowTemplateBasicType } from '@fastgpt/global/core/workflow/type';
import type { SourceMemberType } from '../../support/user/type'; import type { SourceMemberType } from '../../support/user/type';
export type ToolType = {
name: string;
description: string;
inputSchema: {
type: string;
properties?: Record<string, { type: string; description?: string }>;
required?: string[];
};
};
export type AppSchema = { export type AppSchema = {
_id: string; _id: string;
parentId?: ParentIdType; parentId?: ParentIdType;
@@ -117,6 +107,16 @@ export type AppSimpleEditFormType = {
chatConfig: AppChatConfigType; chatConfig: AppChatConfigType;
}; };
export type McpToolConfigType = {
name: string;
description: string;
inputSchema: {
type: string;
properties?: Record<string, { type: string; description?: string }>;
required?: string[];
};
};
/* app chat config type */ /* app chat config type */
export type AppChatConfigType = { export type AppChatConfigType = {
welcomeText?: string; welcomeText?: string;

View File

@@ -9,6 +9,7 @@ import { type WorkflowTemplateBasicType } from '../workflow/type';
import { AppTypeEnum } from './constants'; import { AppTypeEnum } from './constants';
import { AppErrEnum } from '../../common/error/code/app'; import { AppErrEnum } from '../../common/error/code/app';
import { PluginErrEnum } from '../../common/error/code/plugin'; import { PluginErrEnum } from '../../common/error/code/plugin';
import { i18nT } from '../../../web/i18n/utils';
export const getDefaultAppForm = (): AppSimpleEditFormType => { export const getDefaultAppForm = (): AppSimpleEditFormType => {
return { return {
@@ -189,7 +190,7 @@ export const getAppType = (config?: WorkflowTemplateBasicType | AppSimpleEditFor
return ''; return '';
}; };
export const checkAppUnExistError = (error?: string) => { export const formatToolError = (error?: string) => {
const unExistError: Array<string> = [ const unExistError: Array<string> = [
AppErrEnum.unAuthApp, AppErrEnum.unAuthApp,
AppErrEnum.unExist, AppErrEnum.unExist,
@@ -197,9 +198,9 @@ export const checkAppUnExistError = (error?: string) => {
PluginErrEnum.unExist PluginErrEnum.unExist
]; ];
if (!!error && unExistError.includes(error)) { if (error && unExistError.includes(error)) {
return error; return i18nT('app:un_auth');
} else { } else {
return undefined; return error;
} }
}; };

View File

@@ -26,6 +26,7 @@ export type ChatSchema = {
teamId: string; teamId: string;
tmbId: string; tmbId: string;
appId: string; appId: string;
createTime: Date;
updateTime: Date; updateTime: Date;
title: string; title: string;
customTitle: string; customTitle: string;
@@ -112,6 +113,7 @@ export type ChatItemSchema = (UserChatItemType | SystemChatItemType | AIChatItem
appId: string; appId: string;
time: Date; time: Date;
durationSeconds?: number; durationSeconds?: number;
errorMsg?: string;
}; };
export type AdminFbkType = { export type AdminFbkType = {
@@ -143,6 +145,7 @@ export type ChatSiteItemType = (UserChatItemType | SystemChatItemType | AIChatIt
responseData?: ChatHistoryItemResType[]; responseData?: ChatHistoryItemResType[];
time?: Date; time?: Date;
durationSeconds?: number; durationSeconds?: number;
errorMsg?: string;
} & ChatBoxInputType & } & ChatBoxInputType &
ResponseTagItemType; ResponseTagItemType;

View File

@@ -1,9 +1,11 @@
import type { DatasetDataIndexItemType, DatasetSchemaType } from './type'; import type { ChunkSettingsType, DatasetDataIndexItemType, DatasetSchemaType } from './type';
import type { import type {
DatasetCollectionTypeEnum, DatasetCollectionTypeEnum,
DatasetCollectionDataProcessModeEnum, DatasetCollectionDataProcessModeEnum,
ChunkSettingModeEnum, ChunkSettingModeEnum,
DataChunkSplitModeEnum DataChunkSplitModeEnum,
ChunkTriggerConfigTypeEnum,
ParagraphChunkAIModeEnum
} from './constants'; } from './constants';
import type { LLMModelItemType } from '../ai/model.d'; import type { LLMModelItemType } from '../ai/model.d';
import type { ParentIdType } from 'common/parentFolder/type'; import type { ParentIdType } from 'common/parentFolder/type';
@@ -32,26 +34,16 @@ export type DatasetUpdateBody = {
}; };
/* ================= collection ===================== */ /* ================= collection ===================== */
export type DatasetCollectionChunkMetadataType = { // Input + store params
type DatasetCollectionStoreDataType = ChunkSettingsType & {
parentId?: string; parentId?: string;
customPdfParse?: boolean;
trainingType?: DatasetCollectionDataProcessModeEnum;
imageIndex?: boolean;
autoIndexes?: boolean;
chunkSettingMode?: ChunkSettingModeEnum;
chunkSplitMode?: DataChunkSplitModeEnum;
chunkSize?: number;
indexSize?: number;
chunkSplitter?: string;
qaPrompt?: string;
metadata?: Record<string, any>; metadata?: Record<string, any>;
customPdfParse?: boolean;
}; };
// create collection params // create collection params
export type CreateDatasetCollectionParams = DatasetCollectionChunkMetadataType & { export type CreateDatasetCollectionParams = DatasetCollectionStoreDataType & {
datasetId: string; datasetId: string;
name: string; name: string;
type: DatasetCollectionTypeEnum; type: DatasetCollectionTypeEnum;
@@ -72,7 +64,7 @@ export type CreateDatasetCollectionParams = DatasetCollectionChunkMetadataType &
nextSyncTime?: Date; nextSyncTime?: Date;
}; };
export type ApiCreateDatasetCollectionParams = DatasetCollectionChunkMetadataType & { export type ApiCreateDatasetCollectionParams = DatasetCollectionStoreDataType & {
datasetId: string; datasetId: string;
tags?: string[]; tags?: string[];
}; };
@@ -90,7 +82,7 @@ export type ApiDatasetCreateDatasetCollectionParams = ApiCreateDatasetCollection
export type FileIdCreateDatasetCollectionParams = ApiCreateDatasetCollectionParams & { export type FileIdCreateDatasetCollectionParams = ApiCreateDatasetCollectionParams & {
fileId: string; fileId: string;
}; };
export type reTrainingDatasetFileCollectionParams = DatasetCollectionChunkMetadataType & { export type reTrainingDatasetFileCollectionParams = DatasetCollectionStoreDataType & {
datasetId: string; datasetId: string;
collectionId: string; collectionId: string;
}; };
@@ -147,6 +139,7 @@ export type PushDatasetDataProps = {
collectionId: string; collectionId: string;
data: PushDatasetDataChunkProps[]; data: PushDatasetDataChunkProps[];
trainingType?: DatasetCollectionDataProcessModeEnum; trainingType?: DatasetCollectionDataProcessModeEnum;
indexSize?: number;
autoIndexes?: boolean; autoIndexes?: boolean;
imageIndex?: boolean; imageIndex?: boolean;
prompt?: string; prompt?: string;

View File

@@ -120,6 +120,8 @@ export const DatasetCollectionSyncResultMap = {
export enum DatasetCollectionDataProcessModeEnum { export enum DatasetCollectionDataProcessModeEnum {
chunk = 'chunk', chunk = 'chunk',
qa = 'qa', qa = 'qa',
backup = 'backup',
auto = 'auto' // abandon auto = 'auto' // abandon
} }
export const DatasetCollectionDataProcessModeMap = { export const DatasetCollectionDataProcessModeMap = {
@@ -131,21 +133,35 @@ export const DatasetCollectionDataProcessModeMap = {
label: i18nT('common:core.dataset.training.QA mode'), label: i18nT('common:core.dataset.training.QA mode'),
tooltip: i18nT('common:core.dataset.import.QA Import Tip') tooltip: i18nT('common:core.dataset.import.QA Import Tip')
}, },
[DatasetCollectionDataProcessModeEnum.backup]: {
label: i18nT('dataset:backup_mode'),
tooltip: i18nT('dataset:backup_mode')
},
[DatasetCollectionDataProcessModeEnum.auto]: { [DatasetCollectionDataProcessModeEnum.auto]: {
label: i18nT('common:core.dataset.training.Auto mode'), label: i18nT('common:core.dataset.training.Auto mode'),
tooltip: i18nT('common:core.dataset.training.Auto mode Tip') tooltip: i18nT('common:core.dataset.training.Auto mode Tip')
} }
}; };
export enum ChunkTriggerConfigTypeEnum {
minSize = 'minSize',
forceChunk = 'forceChunk',
maxSize = 'maxSize'
}
export enum ChunkSettingModeEnum { export enum ChunkSettingModeEnum {
auto = 'auto', auto = 'auto',
custom = 'custom' custom = 'custom'
} }
export enum DataChunkSplitModeEnum { export enum DataChunkSplitModeEnum {
paragraph = 'paragraph',
size = 'size', size = 'size',
char = 'char' char = 'char'
} }
export enum ParagraphChunkAIModeEnum {
auto = 'auto',
force = 'force'
}
/* ------------ data -------------- */ /* ------------ data -------------- */
@@ -154,7 +170,6 @@ export enum ImportDataSourceEnum {
fileLocal = 'fileLocal', fileLocal = 'fileLocal',
fileLink = 'fileLink', fileLink = 'fileLink',
fileCustom = 'fileCustom', fileCustom = 'fileCustom',
csvTable = 'csvTable',
externalFile = 'externalFile', externalFile = 'externalFile',
apiDataset = 'apiDataset', apiDataset = 'apiDataset',
reTraining = 'reTraining' reTraining = 'reTraining'

View File

@@ -32,7 +32,7 @@ export const DatasetDataIndexMap: Record<
color: 'red' color: 'red'
}, },
[DatasetDataIndexTypeEnum.image]: { [DatasetDataIndexTypeEnum.image]: {
label: i18nT('common:data_index_image'), label: i18nT('dataset:data_index_image'),
color: 'purple' color: 'purple'
} }
}; };

View File

@@ -118,7 +118,7 @@ export const computeChunkSize = (params: {
return getLLMMaxChunkSize(params.llmModel); return getLLMMaxChunkSize(params.llmModel);
} }
return Math.min(params.chunkSize || chunkAutoChunkSize, getLLMMaxChunkSize(params.llmModel)); return Math.min(params.chunkSize ?? chunkAutoChunkSize, getLLMMaxChunkSize(params.llmModel));
}; };
export const computeChunkSplitter = (params: { export const computeChunkSplitter = (params: {

View File

@@ -8,26 +8,42 @@ import type {
DatasetStatusEnum, DatasetStatusEnum,
DatasetTypeEnum, DatasetTypeEnum,
SearchScoreTypeEnum, SearchScoreTypeEnum,
TrainingModeEnum TrainingModeEnum,
ChunkSettingModeEnum
} from './constants'; } from './constants';
import type { DatasetPermission } from '../../support/permission/dataset/controller'; import type { DatasetPermission } from '../../support/permission/dataset/controller';
import { Permission } from '../../support/permission/controller';
import type { APIFileServer, FeishuServer, YuqueServer } from './apiDataset'; import type { APIFileServer, FeishuServer, YuqueServer } from './apiDataset';
import type { SourceMemberType } from 'support/user/type'; import type { SourceMemberType } from 'support/user/type';
import type { DatasetDataIndexTypeEnum } from './data/constants'; import type { DatasetDataIndexTypeEnum } from './data/constants';
import type { ChunkSettingModeEnum } from './constants';
export type ChunkSettingsType = { export type ChunkSettingsType = {
trainingType: DatasetCollectionDataProcessModeEnum; trainingType?: DatasetCollectionDataProcessModeEnum;
autoIndexes?: boolean;
// Chunk trigger
chunkTriggerType?: ChunkTriggerConfigTypeEnum;
chunkTriggerMinSize?: number; // maxSize from agent model, not store
// Data enhance
dataEnhanceCollectionName?: boolean; // Auto add collection name to data
// Index enhance
imageIndex?: boolean; imageIndex?: boolean;
autoIndexes?: boolean;
chunkSettingMode?: ChunkSettingModeEnum; // Chunk setting
chunkSettingMode?: ChunkSettingModeEnum; // 系统参数/自定义参数
chunkSplitMode?: DataChunkSplitModeEnum; chunkSplitMode?: DataChunkSplitModeEnum;
// Paragraph split
paragraphChunkAIMode?: ParagraphChunkAIModeEnum;
paragraphChunkDeep?: number; // Paragraph deep
paragraphChunkMinSize?: number; // Paragraph min size, if too small, it will merge
paragraphChunkMaxSize?: number; // Paragraph max size, if too large, it will split
// Size split
chunkSize?: number; chunkSize?: number;
indexSize?: number; // Char split
chunkSplitter?: string; chunkSplitter?: string;
indexSize?: number;
qaPrompt?: string; qaPrompt?: string;
}; };
@@ -66,7 +82,7 @@ export type DatasetSchemaType = {
defaultPermission?: number; defaultPermission?: number;
}; };
export type DatasetCollectionSchemaType = { export type DatasetCollectionSchemaType = ChunkSettingsType & {
_id: string; _id: string;
teamId: string; teamId: string;
tmbId: string; tmbId: string;
@@ -101,18 +117,7 @@ export type DatasetCollectionSchemaType = {
// Parse settings // Parse settings
customPdfParse?: boolean; customPdfParse?: boolean;
// Chunk settings
autoIndexes?: boolean;
imageIndex?: boolean;
trainingType: DatasetCollectionDataProcessModeEnum; trainingType: DatasetCollectionDataProcessModeEnum;
chunkSettingMode?: ChunkSettingModeEnum;
chunkSplitMode?: DataChunkSplitModeEnum;
chunkSize?: number;
indexSize?: number;
chunkSplitter?: string;
qaPrompt?: string;
}; };
export type DatasetCollectionTagsSchemaType = { export type DatasetCollectionTagsSchemaType = {
@@ -175,6 +180,7 @@ export type DatasetTrainingSchemaType = {
q: string; q: string;
a: string; a: string;
chunkIndex: number; chunkIndex: number;
indexSize?: number;
weight: number; weight: number;
indexes: Omit<DatasetDataIndexItemType, 'dataId'>[]; indexes: Omit<DatasetDataIndexItemType, 'dataId'>[];
retryCount: number; retryCount: number;

View File

@@ -218,7 +218,6 @@ export const FlowValueTypeMap: Record<
}; };
export const EDGE_TYPE = 'default'; export const EDGE_TYPE = 'default';
export const defaultNodeVersion = '481';
export const chatHistoryValueDesc = `{ export const chatHistoryValueDesc = `{
obj: System | Human | AI; obj: System | Human | AI;
@@ -236,3 +235,10 @@ export const datasetQuoteValueDesc = `{
export const datasetSelectValueDesc = `{ export const datasetSelectValueDesc = `{
datasetId: string; datasetId: string;
}[]`; }[]`;
export const AppNodeFlowNodeTypeMap: Record<any, boolean> = {
[FlowNodeTypeEnum.pluginModule]: true,
[FlowNodeTypeEnum.appModule]: true,
[FlowNodeTypeEnum.tool]: true,
[FlowNodeTypeEnum.toolSet]: true
};

View File

@@ -7,7 +7,7 @@ import type {
} from '../../chat/type'; } from '../../chat/type';
import { NodeOutputItemType } from '../../chat/type'; import { NodeOutputItemType } from '../../chat/type';
import type { FlowNodeInputItemType, FlowNodeOutputItemType } from '../type/io.d'; import type { FlowNodeInputItemType, FlowNodeOutputItemType } from '../type/io.d';
import type { StoreNodeItemType } from '../type/node'; import type { NodeToolConfigType, StoreNodeItemType } from '../type/node';
import type { DispatchNodeResponseKeyEnum } from './constants'; import type { DispatchNodeResponseKeyEnum } from './constants';
import type { StoreEdgeItemType } from '../type/edge'; import type { StoreEdgeItemType } from '../type/edge';
import type { NodeInputKeyEnum } from '../constants'; import type { NodeInputKeyEnum } from '../constants';
@@ -101,7 +101,10 @@ export type RuntimeNodeItemType = {
outputs: FlowNodeOutputItemType[]; outputs: FlowNodeOutputItemType[];
pluginId?: string; // workflow id / plugin id pluginId?: string; // workflow id / plugin id
version: string; version?: string;
// tool
toolConfig?: NodeToolConfigType;
}; };
export type RuntimeEdgeItemType = StoreEdgeItemType & { export type RuntimeEdgeItemType = StoreEdgeItemType & {
@@ -114,7 +117,7 @@ export type DispatchNodeResponseType = {
runningTime?: number; runningTime?: number;
query?: string; query?: string;
textOutput?: string; textOutput?: string;
error?: Record<string, any>; error?: Record<string, any> | string;
customInputs?: Record<string, any>; customInputs?: Record<string, any>;
customOutputs?: Record<string, any>; customOutputs?: Record<string, any>;
nodeInputs?: Record<string, any>; nodeInputs?: Record<string, any>;

View File

@@ -25,7 +25,6 @@ export const RunAppModule: FlowNodeTemplateType = {
name: i18nT('workflow:application_call'), name: i18nT('workflow:application_call'),
intro: i18nT('workflow:select_another_application_to_call'), intro: i18nT('workflow:select_another_application_to_call'),
showStatus: true, showStatus: true,
version: '481',
isTool: true, isTool: true,
inputs: [ inputs: [
{ {

View File

@@ -19,7 +19,6 @@ import {
Input_Template_UserChatInput, Input_Template_UserChatInput,
Input_Template_File_Link Input_Template_File_Link
} from '../../input'; } from '../../input';
import { chatNodeSystemPromptTip, systemPromptTip } from '../../tip';
import { getHandleConfig } from '../../utils'; import { getHandleConfig } from '../../utils';
import { i18nT } from '../../../../../../web/i18n/utils'; import { i18nT } from '../../../../../../web/i18n/utils';
@@ -54,7 +53,7 @@ export const AiChatModule: FlowNodeTemplateType = {
intro: i18nT('workflow:template.ai_chat_intro'), intro: i18nT('workflow:template.ai_chat_intro'),
showStatus: true, showStatus: true,
isTool: true, isTool: true,
courseUrl: '/docs/guide/workbench/workflow/ai_chat/', courseUrl: '/docs/guide/dashboard/workflow/ai_chat/',
version: '4.9.7', version: '4.9.7',
inputs: [ inputs: [
Input_Template_SettingAiModel, Input_Template_SettingAiModel,
@@ -121,12 +120,7 @@ export const AiChatModule: FlowNodeTemplateType = {
valueType: WorkflowIOValueTypeEnum.string valueType: WorkflowIOValueTypeEnum.string
}, },
// settings modal --- // settings modal ---
{ Input_Template_System_Prompt,
...Input_Template_System_Prompt,
label: i18nT('common:core.ai.Prompt'),
description: systemPromptTip,
placeholder: chatNodeSystemPromptTip
},
Input_Template_History, Input_Template_History,
Input_Template_Dataset_Quote, Input_Template_Dataset_Quote,
Input_Template_File_Link, Input_Template_File_Link,

View File

@@ -17,8 +17,7 @@ export const AssignedAnswerModule: FlowNodeTemplateType = {
avatar: 'core/workflow/template/reply', avatar: 'core/workflow/template/reply',
name: i18nT('workflow:assigned_reply'), name: i18nT('workflow:assigned_reply'),
intro: i18nT('workflow:intro_assigned_reply'), intro: i18nT('workflow:intro_assigned_reply'),
courseUrl: '/docs/guide/workbench/workflow/reply/', courseUrl: '/docs/guide/dashboard/workflow/reply/',
version: '481',
isTool: true, isTool: true,
inputs: [ inputs: [
{ {

View File

@@ -31,7 +31,7 @@ export const ClassifyQuestionModule: FlowNodeTemplateType = {
intro: i18nT('workflow:intro_question_classification'), intro: i18nT('workflow:intro_question_classification'),
showStatus: true, showStatus: true,
version: '4.9.2', version: '4.9.2',
courseUrl: '/docs/guide/workbench/workflow/question_classify/', courseUrl: '/docs/guide/dashboard/workflow/question_classify/',
inputs: [ inputs: [
{ {
...Input_Template_SelectAIModel, ...Input_Template_SelectAIModel,

View File

@@ -26,7 +26,7 @@ export const ContextExtractModule: FlowNodeTemplateType = {
intro: i18nT('workflow:intro_text_content_extraction'), intro: i18nT('workflow:intro_text_content_extraction'),
showStatus: true, showStatus: true,
isTool: true, isTool: true,
courseUrl: '/docs/guide/workbench/workflow/content_extract/', courseUrl: '/docs/guide/dashboard/workflow/content_extract/',
version: '4.9.2', version: '4.9.2',
inputs: [ inputs: [
{ {

View File

@@ -17,8 +17,7 @@ export const CustomFeedbackNode: FlowNodeTemplateType = {
avatar: 'core/workflow/template/customFeedback', avatar: 'core/workflow/template/customFeedback',
name: i18nT('workflow:custom_feedback'), name: i18nT('workflow:custom_feedback'),
intro: i18nT('workflow:intro_custom_feedback'), intro: i18nT('workflow:intro_custom_feedback'),
courseUrl: '/docs/guide/workbench/workflow/custom_feedback/', courseUrl: '/docs/guide/dashboard/workflow/custom_feedback/',
version: '486',
inputs: [ inputs: [
{ {
key: NodeInputKeyEnum.textareaInput, key: NodeInputKeyEnum.textareaInput,

View File

@@ -42,8 +42,7 @@ export const DatasetConcatModule: FlowNodeTemplateType = {
intro: i18nT('workflow:intro_knowledge_base_search_merge'), intro: i18nT('workflow:intro_knowledge_base_search_merge'),
showStatus: false, showStatus: false,
version: '486', courseUrl: '/docs/guide/dashboard/workflow/knowledge_base_search_merge/',
courseUrl: '/docs/guide/workbench/workflow/knowledge_base_search_merge/',
inputs: [ inputs: [
{ {
key: NodeInputKeyEnum.datasetMaxTokens, key: NodeInputKeyEnum.datasetMaxTokens,

View File

@@ -30,7 +30,7 @@ export const DatasetSearchModule: FlowNodeTemplateType = {
intro: Dataset_SEARCH_DESC, intro: Dataset_SEARCH_DESC,
showStatus: true, showStatus: true,
isTool: true, isTool: true,
courseUrl: '/docs/guide/workbench/workflow/dataset_search/', courseUrl: '/docs/guide/dashboard/workflow/dataset_search/',
version: '4.9.2', version: '4.9.2',
inputs: [ inputs: [
{ {

View File

@@ -27,8 +27,7 @@ export const HttpNode468: FlowNodeTemplateType = {
intro: i18nT('workflow:intro_http_request'), intro: i18nT('workflow:intro_http_request'),
showStatus: true, showStatus: true,
isTool: true, isTool: true,
courseUrl: '/docs/guide/workbench/workflow/http/', courseUrl: '/docs/guide/dashboard/workflow/http/',
version: '481',
inputs: [ inputs: [
{ {
...Input_Template_DynamicInput, ...Input_Template_DynamicInput,

View File

@@ -23,8 +23,7 @@ export const IfElseNode: FlowNodeTemplateType = {
name: i18nT('workflow:condition_checker'), name: i18nT('workflow:condition_checker'),
intro: i18nT('workflow:execute_different_branches_based_on_conditions'), intro: i18nT('workflow:execute_different_branches_based_on_conditions'),
showStatus: true, showStatus: true,
courseUrl: '/docs/guide/workbench/workflow/tfswitch/', courseUrl: '/docs/guide/dashboard/workflow/tfswitch/',
version: '481',
inputs: [ inputs: [
{ {
key: NodeInputKeyEnum.ifElseList, key: NodeInputKeyEnum.ifElseList,

View File

@@ -23,7 +23,6 @@ export const FormInputNode: FlowNodeTemplateType = {
name: i18nT('app:workflow.form_input'), name: i18nT('app:workflow.form_input'),
intro: i18nT(`app:workflow.form_input_tip`), intro: i18nT(`app:workflow.form_input_tip`),
isTool: true, isTool: true,
version: '4811',
inputs: [ inputs: [
{ {
key: NodeInputKeyEnum.description, key: NodeInputKeyEnum.description,

View File

@@ -24,8 +24,7 @@ export const UserSelectNode: FlowNodeTemplateType = {
name: i18nT('app:workflow.user_select'), name: i18nT('app:workflow.user_select'),
intro: i18nT(`app:workflow.user_select_tip`), intro: i18nT(`app:workflow.user_select_tip`),
isTool: true, isTool: true,
version: '489', courseUrl: '/docs/guide/dashboard/workflow/user-selection/',
courseUrl: '/docs/guide/workbench/workflow/user-selection/',
inputs: [ inputs: [
{ {
key: NodeInputKeyEnum.description, key: NodeInputKeyEnum.description,

View File

@@ -32,8 +32,7 @@ export const LafModule: FlowNodeTemplateType = {
intro: i18nT('workflow:intro_laf_function_call'), intro: i18nT('workflow:intro_laf_function_call'),
showStatus: true, showStatus: true,
isTool: true, isTool: true,
courseUrl: '/docs/guide/workbench/workflow/laf/', courseUrl: '/docs/guide/dashboard/workflow/laf/',
version: '481',
inputs: [ inputs: [
{ {
...Input_Template_DynamicInput, ...Input_Template_DynamicInput,

View File

@@ -29,8 +29,7 @@ export const LoopNode: FlowNodeTemplateType = {
name: i18nT('workflow:loop'), name: i18nT('workflow:loop'),
intro: i18nT('workflow:intro_loop'), intro: i18nT('workflow:intro_loop'),
showStatus: true, showStatus: true,
version: '4811', courseUrl: '/docs/guide/dashboard/workflow/loop/',
courseUrl: '/docs/guide/workbench/workflow/loop/',
inputs: [ inputs: [
{ {
key: NodeInputKeyEnum.loopInputArray, key: NodeInputKeyEnum.loopInputArray,

View File

@@ -19,7 +19,6 @@ export const LoopEndNode: FlowNodeTemplateType = {
avatar: 'core/workflow/template/loopEnd', avatar: 'core/workflow/template/loopEnd',
name: i18nT('workflow:loop_end'), name: i18nT('workflow:loop_end'),
showStatus: false, showStatus: false,
version: '4811',
inputs: [ inputs: [
{ {
key: NodeInputKeyEnum.loopEndInput, key: NodeInputKeyEnum.loopEndInput,

View File

@@ -24,7 +24,6 @@ export const LoopStartNode: FlowNodeTemplateType = {
unique: true, unique: true,
forbidDelete: true, forbidDelete: true,
showStatus: false, showStatus: false,
version: '4811',
inputs: [ inputs: [
{ {
key: NodeInputKeyEnum.loopStartInput, key: NodeInputKeyEnum.loopStartInput,

View File

@@ -15,7 +15,6 @@ export const PluginConfigNode: FlowNodeTemplateType = {
intro: '', intro: '',
unique: true, unique: true,
forbidDelete: true, forbidDelete: true,
version: '4811',
inputs: [], inputs: [],
outputs: [] outputs: []
}; };

View File

@@ -16,7 +16,6 @@ export const PluginInputModule: FlowNodeTemplateType = {
name: i18nT('workflow:plugin_input'), name: i18nT('workflow:plugin_input'),
intro: i18nT('workflow:intro_plugin_input'), intro: i18nT('workflow:intro_plugin_input'),
showStatus: false, showStatus: false,
version: '481',
inputs: [], inputs: [],
outputs: [] outputs: []
}; };

View File

@@ -16,7 +16,6 @@ export const PluginOutputModule: FlowNodeTemplateType = {
name: i18nT('workflow:template.plugin_output'), name: i18nT('workflow:template.plugin_output'),
intro: i18nT('workflow:intro_custom_plugin_output'), intro: i18nT('workflow:intro_custom_plugin_output'),
showStatus: false, showStatus: false,
version: '481',
inputs: [], inputs: [],
outputs: [] outputs: []
}; };

View File

@@ -13,7 +13,6 @@ export const RunAppNode: FlowNodeTemplateType = {
name: '', name: '',
showStatus: false, showStatus: false,
isTool: false, isTool: false,
version: '481',
inputs: [], // [{key:'pluginId'},...] inputs: [], // [{key:'pluginId'},...]
outputs: [] outputs: []
}; };

View File

@@ -13,7 +13,6 @@ export const RunPluginModule: FlowNodeTemplateType = {
name: '', name: '',
showStatus: false, showStatus: false,
isTool: true, isTool: true,
version: '481',
inputs: [], // [{key:'pluginId'},...] inputs: [], // [{key:'pluginId'},...]
outputs: [] outputs: []
}; };

View File

@@ -13,7 +13,6 @@ export const RunToolNode: FlowNodeTemplateType = {
name: '', name: '',
showStatus: false, showStatus: false,
isTool: true, isTool: true,
version: '4.9.6',
inputs: [], inputs: [],
outputs: [] outputs: []
}; };

View File

@@ -13,7 +13,6 @@ export const RunToolSetNode: FlowNodeTemplateType = {
name: '', name: '',
showStatus: false, showStatus: false,
isTool: true, isTool: true,
version: '4.9.6',
inputs: [], inputs: [],
outputs: [] outputs: []
}; };

View File

@@ -26,8 +26,7 @@ export const CodeNode: FlowNodeTemplateType = {
name: i18nT('workflow:code_execution'), name: i18nT('workflow:code_execution'),
intro: i18nT('workflow:execute_a_simple_script_code_usually_for_complex_data_processing'), intro: i18nT('workflow:execute_a_simple_script_code_usually_for_complex_data_processing'),
showStatus: true, showStatus: true,
courseUrl: '/docs/guide/workbench/workflow/sandbox/', courseUrl: '/docs/guide/dashboard/workflow/sandbox/',
version: '482',
inputs: [ inputs: [
{ {
...Input_Template_DynamicInput, ...Input_Template_DynamicInput,

View File

@@ -13,7 +13,6 @@ export const StopToolNode: FlowNodeTemplateType = {
avatar: 'core/workflow/template/stopTool', avatar: 'core/workflow/template/stopTool',
name: i18nT('workflow:tool_call_termination'), name: i18nT('workflow:tool_call_termination'),
intro: i18nT('workflow:intro_tool_call_termination'), intro: i18nT('workflow:intro_tool_call_termination'),
version: '481',
inputs: [], inputs: [],
outputs: [] outputs: []
}; };

View File

@@ -15,7 +15,6 @@ export const SystemConfigNode: FlowNodeTemplateType = {
intro: '', intro: '',
unique: true, unique: true,
forbidDelete: true, forbidDelete: true,
version: '481',
inputs: [], inputs: [],
outputs: [] outputs: []
}; };

View File

@@ -23,8 +23,7 @@ export const TextEditorNode: FlowNodeTemplateType = {
avatar: 'core/workflow/template/textConcat', avatar: 'core/workflow/template/textConcat',
name: i18nT('workflow:text_concatenation'), name: i18nT('workflow:text_concatenation'),
intro: i18nT('workflow:intro_text_concatenation'), intro: i18nT('workflow:intro_text_concatenation'),
courseUrl: '/docs/guide/workbench/workflow/text_editor/', courseUrl: '/docs/guide/dashboard/workflow/text_editor/',
version: '4813',
inputs: [ inputs: [
{ {
key: NodeInputKeyEnum.textareaInput, key: NodeInputKeyEnum.textareaInput,

View File

@@ -13,7 +13,6 @@ export const ToolParamsNode: FlowNodeTemplateType = {
avatar: 'core/workflow/template/toolParams', avatar: 'core/workflow/template/toolParams',
name: i18nT('workflow:tool_custom_field'), name: i18nT('workflow:tool_custom_field'),
intro: i18nT('workflow:intro_tool_params_config'), intro: i18nT('workflow:intro_tool_params_config'),
version: '4811',
isTool: true, isTool: true,
inputs: [], inputs: [],
outputs: [] outputs: []

View File

@@ -32,7 +32,7 @@ export const ToolModule: FlowNodeTemplateType = {
name: i18nT('workflow:template.tool_call'), name: i18nT('workflow:template.tool_call'),
intro: i18nT('workflow:template.tool_call_intro'), intro: i18nT('workflow:template.tool_call_intro'),
showStatus: true, showStatus: true,
courseUrl: '/docs/guide/workbench/workflow/tool/', courseUrl: '/docs/guide/dashboard/workflow/tool/',
version: '4.9.2', version: '4.9.2',
inputs: [ inputs: [
{ {

View File

@@ -19,8 +19,7 @@ export const VariableUpdateNode: FlowNodeTemplateType = {
intro: i18nT('workflow:update_specified_node_output_or_global_variable'), intro: i18nT('workflow:update_specified_node_output_or_global_variable'),
showStatus: false, showStatus: false,
isTool: true, isTool: true,
version: '481', courseUrl: '/docs/guide/dashboard/workflow/variable_update/',
courseUrl: '/docs/guide/workbench/workflow/variable_update/',
inputs: [ inputs: [
{ {
key: NodeInputKeyEnum.updateList, key: NodeInputKeyEnum.updateList,

View File

@@ -30,7 +30,6 @@ export const WorkflowStart: FlowNodeTemplateType = {
intro: '', intro: '',
forbidDelete: true, forbidDelete: true,
unique: true, unique: true,
version: '481',
inputs: [{ ...Input_Template_UserChatInput, toolDescription: i18nT('workflow:user_question') }], inputs: [{ ...Input_Template_UserChatInput, toolDescription: i18nT('workflow:user_question') }],
outputs: [ outputs: [
{ {

View File

@@ -37,7 +37,10 @@ export type WorkflowTemplateType = {
intro?: string; intro?: string;
author?: string; author?: string;
courseUrl?: string; courseUrl?: string;
version: string;
version?: string;
versionLabel?: string;
isLatestVersion?: boolean;
showStatus?: boolean; showStatus?: boolean;
weight?: number; weight?: number;

View File

@@ -63,6 +63,8 @@ export type FlowNodeInputItemType = InputComponentPropsType & {
canSelectFile?: boolean; canSelectFile?: boolean;
canSelectImg?: boolean; canSelectImg?: boolean;
maxFiles?: number; maxFiles?: number;
deprecated?: boolean;
}; };
export type FlowNodeOutputItemType = { export type FlowNodeOutputItemType = {
@@ -86,6 +88,8 @@ export type FlowNodeOutputItemType = {
// component params // component params
customFieldConfig?: CustomFieldConfigType; customFieldConfig?: CustomFieldConfigType;
deprecated?: boolean;
}; };
export type ReferenceItemValueType = [string, string | undefined]; export type ReferenceItemValueType = [string, string | undefined];

View File

@@ -20,11 +20,17 @@ import { RuntimeNodeItemType } from '../runtime/type';
import { PluginTypeEnum } from '../../plugin/constants'; import { PluginTypeEnum } from '../../plugin/constants';
import { RuntimeEdgeItemType, StoreEdgeItemType } from './edge'; import { RuntimeEdgeItemType, StoreEdgeItemType } from './edge';
import { NextApiResponse } from 'next'; import { NextApiResponse } from 'next';
import { AppDetailType, AppSchema } from '../../app/type'; import type { AppDetailType, AppSchema, McpToolConfigType } from '../../app/type';
import type { ParentIdType } from 'common/parentFolder/type'; import type { ParentIdType } from 'common/parentFolder/type';
import { AppTypeEnum } from 'core/app/constants'; import { AppTypeEnum } from '../../app/constants';
import type { WorkflowInteractiveResponseType } from '../template/system/interactive/type'; import type { WorkflowInteractiveResponseType } from '../template/system/interactive/type';
export type NodeToolConfigType = {
mcpTool?: McpToolConfigType & {
url: string;
};
};
export type FlowNodeCommonType = { export type FlowNodeCommonType = {
parentNodeId?: string; parentNodeId?: string;
flowNodeType: FlowNodeTypeEnum; // render node card flowNodeType: FlowNodeTypeEnum; // render node card
@@ -34,7 +40,10 @@ export type FlowNodeCommonType = {
name: string; name: string;
intro?: string; // template list intro intro?: string; // template list intro
showStatus?: boolean; // chatting response step status showStatus?: boolean; // chatting response step status
version: string;
version?: string;
versionLabel?: string; // Just ui show
isLatestVersion?: boolean; // Just ui show
// data // data
inputs: FlowNodeInputItemType[]; inputs: FlowNodeInputItemType[];
@@ -43,12 +52,14 @@ export type FlowNodeCommonType = {
// plugin data // plugin data
pluginId?: string; pluginId?: string;
isFolder?: boolean; isFolder?: boolean;
// pluginType?: AppTypeEnum;
pluginData?: PluginDataType; pluginData?: PluginDataType;
// tool data
toolData?: NodeToolConfigType;
}; };
export type PluginDataType = { export type PluginDataType = {
version: string; version?: string;
diagram?: string; diagram?: string;
userGuide?: string; userGuide?: string;
courseUrl?: string; courseUrl?: string;

View File

@@ -9,6 +9,7 @@ import type { TeamMemberItemType } from './team/type';
export type PostLoginProps = { export type PostLoginProps = {
username: string; username: string;
password: string; password: string;
code: string;
}; };
export type OauthLoginProps = { export type OauthLoginProps = {

View File

@@ -3,7 +3,8 @@ export enum UserAuthTypeEnum {
findPassword = 'findPassword', findPassword = 'findPassword',
wxLogin = 'wxLogin', wxLogin = 'wxLogin',
bindNotification = 'bindNotification', bindNotification = 'bindNotification',
captcha = 'captcha' captcha = 'captcha',
login = 'login'
} }
export const userAuthTypeMap = { export const userAuthTypeMap = {
@@ -11,5 +12,6 @@ export const userAuthTypeMap = {
[UserAuthTypeEnum.findPassword]: 'findPassword', [UserAuthTypeEnum.findPassword]: 'findPassword',
[UserAuthTypeEnum.wxLogin]: 'wxLogin', [UserAuthTypeEnum.wxLogin]: 'wxLogin',
[UserAuthTypeEnum.bindNotification]: 'bindNotification', [UserAuthTypeEnum.bindNotification]: 'bindNotification',
[UserAuthTypeEnum.captcha]: 'captcha' [UserAuthTypeEnum.captcha]: 'captcha',
[UserAuthTypeEnum.login]: 'login'
}; };

View File

@@ -0,0 +1,10 @@
import type { UserAuthTypeEnum } from '@fastgpt/global/support/user/auth/constants';
export type UserAuthSchemaType = {
key: string;
type: `${UserAuthTypeEnum}`;
code?: string;
openid?: string;
createTime: Date;
expiredTime: Date;
};

View File

@@ -14,6 +14,7 @@ export type UserModelSchema = {
timezone: string; timezone: string;
status: `${UserStatusEnum}`; status: `${UserStatusEnum}`;
lastLoginTmbId?: string; lastLoginTmbId?: string;
passwordUpdateTime?: Date;
fastgpt_sem?: { fastgpt_sem?: {
keyword: string; keyword: string;
}; };

View File

@@ -6,12 +6,6 @@ import type {
} from '../../core/dataset/search/controller'; } from '../../core/dataset/search/controller';
import type { AuthOpenApiLimitProps } from '../../support/openapi/auth'; import type { AuthOpenApiLimitProps } from '../../support/openapi/auth';
import type { CreateUsageProps, ConcatUsageProps } from '@fastgpt/global/support/wallet/usage/api'; import type { CreateUsageProps, ConcatUsageProps } from '@fastgpt/global/support/wallet/usage/api';
import type {
GetProApiDatasetFileContentParams,
GetProApiDatasetFileDetailParams,
GetProApiDatasetFileListParams,
GetProApiDatasetFilePreviewUrlParams
} from '../../core/dataset/apiDataset/proApi';
declare global { declare global {
var textCensorHandler: (params: { text: string }) => Promise<{ code: number; message?: string }>; var textCensorHandler: (params: { text: string }) => Promise<{ code: number; message?: string }>;
@@ -19,16 +13,4 @@ declare global {
var authOpenApiHandler: (data: AuthOpenApiLimitProps) => Promise<any>; var authOpenApiHandler: (data: AuthOpenApiLimitProps) => Promise<any>;
var createUsageHandler: (data: CreateUsageProps) => any; var createUsageHandler: (data: CreateUsageProps) => any;
var concatUsageHandler: (data: ConcatUsageProps) => any; var concatUsageHandler: (data: ConcatUsageProps) => any;
// API dataset
var getProApiDatasetFileList: (data: GetProApiDatasetFileListParams) => Promise<APIFileItem[]>;
var getProApiDatasetFileContent: (
data: GetProApiDatasetFileContentParams
) => Promise<ApiFileReadContentResponse>;
var getProApiDatasetFilePreviewUrl: (
data: GetProApiDatasetFilePreviewUrlParams
) => Promise<string>;
var getProApiDatasetFileDetail: (
data: GetProApiDatasetFileDetailParams
) => Promise<ApiDatasetDetailResponse>;
} }

View File

@@ -210,15 +210,15 @@ export const readFileContentFromMongo = async ({
tmbId, tmbId,
bucketName, bucketName,
fileId, fileId,
isQAImport = false, customPdfParse = false,
customPdfParse = false getFormatText
}: { }: {
teamId: string; teamId: string;
tmbId: string; tmbId: string;
bucketName: `${BucketNameEnum}`; bucketName: `${BucketNameEnum}`;
fileId: string; fileId: string;
isQAImport?: boolean;
customPdfParse?: boolean; customPdfParse?: boolean;
getFormatText?: boolean; // 数据类型都尽可能转化成 markdown 格式
}): Promise<{ }): Promise<{
rawText: string; rawText: string;
filename: string; filename: string;
@@ -254,8 +254,8 @@ export const readFileContentFromMongo = async ({
// Get raw text // Get raw text
const { rawText } = await readRawContentByFileBuffer({ const { rawText } = await readRawContentByFileBuffer({
customPdfParse, customPdfParse,
getFormatText,
extension, extension,
isQAImport,
teamId, teamId,
tmbId, tmbId,
buffer: fileBuffers, buffer: fileBuffers,

View File

@@ -16,6 +16,7 @@ export type readRawTextByLocalFileParams = {
path: string; path: string;
encoding: string; encoding: string;
customPdfParse?: boolean; customPdfParse?: boolean;
getFormatText?: boolean;
metadata?: Record<string, any>; metadata?: Record<string, any>;
}; };
export const readRawTextByLocalFile = async (params: readRawTextByLocalFileParams) => { export const readRawTextByLocalFile = async (params: readRawTextByLocalFileParams) => {
@@ -27,8 +28,8 @@ export const readRawTextByLocalFile = async (params: readRawTextByLocalFileParam
return readRawContentByFileBuffer({ return readRawContentByFileBuffer({
extension, extension,
isQAImport: false,
customPdfParse: params.customPdfParse, customPdfParse: params.customPdfParse,
getFormatText: params.getFormatText,
teamId: params.teamId, teamId: params.teamId,
tmbId: params.tmbId, tmbId: params.tmbId,
encoding: params.encoding, encoding: params.encoding,
@@ -46,7 +47,7 @@ export const readRawContentByFileBuffer = async ({
encoding, encoding,
metadata, metadata,
customPdfParse = false, customPdfParse = false,
isQAImport = false getFormatText = true
}: { }: {
teamId: string; teamId: string;
tmbId: string; tmbId: string;
@@ -57,8 +58,10 @@ export const readRawContentByFileBuffer = async ({
metadata?: Record<string, any>; metadata?: Record<string, any>;
customPdfParse?: boolean; customPdfParse?: boolean;
isQAImport: boolean; getFormatText?: boolean;
}): Promise<ReadFileResponse> => { }): Promise<{
rawText: string;
}> => {
const systemParse = () => const systemParse = () =>
runWorker<ReadFileResponse>(WorkerNameEnum.readFile, { runWorker<ReadFileResponse>(WorkerNameEnum.readFile, {
extension, extension,
@@ -149,7 +152,7 @@ export const readRawContentByFileBuffer = async ({
return await systemParse(); return await systemParse();
})(); })();
addLog.debug(`Parse file success, time: ${Date.now() - start}ms. Uploading file image.`); addLog.debug(`Parse file success, time: ${Date.now() - start}ms. `);
// markdown data format // markdown data format
if (imageList) { if (imageList) {
@@ -176,16 +179,7 @@ export const readRawContentByFileBuffer = async ({
}); });
} }
if (['csv', 'xlsx'].includes(extension)) { addLog.debug(`Upload file success, time: ${Date.now() - start}ms`);
// qa data
if (isQAImport) {
rawText = rawText || '';
} else {
rawText = formatText || rawText;
}
}
addLog.debug(`Upload file image success, time: ${Date.now() - start}ms`); return { rawText: getFormatText ? formatText || rawText : rawText };
return { rawText, formatText, imageList };
}; };

View File

@@ -1,5 +1,5 @@
import { jsonRes } from '../response'; import { jsonRes } from '../response';
import type { NextApiResponse } from 'next'; import type { NextApiRequest, NextApiResponse } from 'next';
import { withNextCors } from './cors'; import { withNextCors } from './cors';
import { type ApiRequestProps } from '../../type/next'; import { type ApiRequestProps } from '../../type/next';
import { addLog } from '../system/log'; import { addLog } from '../system/log';
@@ -9,14 +9,21 @@ export type NextApiHandler<T = any> = (
res: NextApiResponse<T> res: NextApiResponse<T>
) => unknown | Promise<unknown>; ) => unknown | Promise<unknown>;
export const NextEntry = ({ beforeCallback = [] }: { beforeCallback?: Promise<any>[] }) => { export const NextEntry = ({
beforeCallback = []
}: {
beforeCallback?: ((req: NextApiRequest, res: NextApiResponse) => Promise<any>)[];
}) => {
return (...args: NextApiHandler[]): NextApiHandler => { return (...args: NextApiHandler[]): NextApiHandler => {
return async function api(req: ApiRequestProps, res: NextApiResponse) { return async function api(req: ApiRequestProps, res: NextApiResponse) {
const start = Date.now(); const start = Date.now();
addLog.debug(`Request start ${req.url}`); addLog.debug(`Request start ${req.url}`);
try { try {
await Promise.all([withNextCors(req, res), ...beforeCallback]); await Promise.all([
withNextCors(req, res),
...beforeCallback.map((item) => item(req, res))
]);
let response = null; let response = null;
for await (const handler of args) { for await (const handler of args) {

View File

@@ -1,7 +1,10 @@
import { getGlobalRedisCacheConnection } from './index'; import { getGlobalRedisConnection } from './index';
import { addLog } from '../system/log'; import { addLog } from '../system/log';
import { retryFn } from '@fastgpt/global/common/system/utils'; import { retryFn } from '@fastgpt/global/common/system/utils';
const redisPrefix = 'cache:';
const getCacheKey = (key: string) => `${redisPrefix}${key}`;
export enum CacheKeyEnum { export enum CacheKeyEnum {
team_vector_count = 'team_vector_count' team_vector_count = 'team_vector_count'
} }
@@ -13,12 +16,12 @@ export const setRedisCache = async (
) => { ) => {
return await retryFn(async () => { return await retryFn(async () => {
try { try {
const redis = getGlobalRedisCacheConnection(); const redis = getGlobalRedisConnection();
if (expireSeconds) { if (expireSeconds) {
await redis.set(key, data, 'EX', expireSeconds); await redis.set(getCacheKey(key), data, 'EX', expireSeconds);
} else { } else {
await redis.set(key, data); await redis.set(getCacheKey(key), data);
} }
} catch (error) { } catch (error) {
addLog.error('Set cache error:', error); addLog.error('Set cache error:', error);
@@ -28,11 +31,11 @@ export const setRedisCache = async (
}; };
export const getRedisCache = async (key: string) => { export const getRedisCache = async (key: string) => {
const redis = getGlobalRedisCacheConnection(); const redis = getGlobalRedisConnection();
return await retryFn(() => redis.get(key)); return await retryFn(() => redis.get(getCacheKey(key)));
}; };
export const delRedisCache = async (key: string) => { export const delRedisCache = async (key: string) => {
const redis = getGlobalRedisCacheConnection(); const redis = getGlobalRedisConnection();
await retryFn(() => redis.del(key)); await retryFn(() => redis.del(getCacheKey(key)));
}; };

View File

@@ -27,17 +27,26 @@ export const newWorkerRedisConnection = () => {
return redis; return redis;
}; };
export const getGlobalRedisCacheConnection = () => { export const FASTGPT_REDIS_PREFIX = 'fastgpt:';
if (global.redisCache) return global.redisCache; export const getGlobalRedisConnection = () => {
if (global.redisClient) return global.redisClient;
global.redisCache = new Redis(REDIS_URL, { keyPrefix: 'fastgpt:cache:' }); global.redisClient = new Redis(REDIS_URL, { keyPrefix: FASTGPT_REDIS_PREFIX });
global.redisCache.on('connect', () => { global.redisClient.on('connect', () => {
addLog.info('Redis connected'); addLog.info('Redis connected');
}); });
global.redisCache.on('error', (error) => { global.redisClient.on('error', (error) => {
addLog.error('Redis connection error', error); addLog.error('Redis connection error', error);
}); });
return global.redisCache; return global.redisClient;
};
export const getAllKeysByPrefix = async (key: string) => {
const redis = getGlobalRedisConnection();
const keys = (await redis.keys(`${FASTGPT_REDIS_PREFIX}${key}:*`)).map((key) =>
key.replace(FASTGPT_REDIS_PREFIX, '')
);
return keys;
}; };

View File

@@ -1,5 +1,5 @@
import type Redis from 'ioredis'; import type Redis from 'ioredis';
declare global { declare global {
var redisCache: Redis | null; var redisClient: Redis | null;
} }

View File

@@ -42,7 +42,7 @@ export const cheerioToHtml = ({
} }
} }
}); });
selectDom.find('img').each((i, el) => { selectDom.find('img, video, source, audio, iframe').each((i, el) => {
const src = $(el).attr('src'); const src = $(el).attr('src');
if (src) { if (src) {
if (src.startsWith('//')) { if (src.startsWith('//')) {

View File

@@ -10,6 +10,7 @@ let jieba: Jieba | undefined;
})(); })();
const stopWords = new Set([ const stopWords = new Set([
'\n',
'--', '--',
'?', '?',
'“', '“',
@@ -1519,8 +1520,7 @@ const stopWords = new Set([
]); ]);
export async function jiebaSplit({ text }: { text: string }) { export async function jiebaSplit({ text }: { text: string }) {
text = text.replace(/[#*`_~>[\](){}|]/g, '').replace(/\S*https?\S*/gi, ''); text = text.replace(/[#*`_~>[\](){}|]|\S*https?\S*/g, '').trim();
const tokens = (await jieba!.cutAsync(text, true)) as string[]; const tokens = (await jieba!.cutAsync(text, true)) as string[];
return ( return (

View File

@@ -2,26 +2,44 @@ import { SystemConfigsTypeEnum } from '@fastgpt/global/common/system/config/cons
import { MongoSystemConfigs } from './schema'; import { MongoSystemConfigs } from './schema';
import { type FastGPTConfigFileType } from '@fastgpt/global/common/system/types'; import { type FastGPTConfigFileType } from '@fastgpt/global/common/system/types';
import { FastGPTProUrl } from '../constants'; import { FastGPTProUrl } from '../constants';
import { type LicenseDataType } from '@fastgpt/global/common/system/types';
export const getFastGPTConfigFromDB = async () => { export const getFastGPTConfigFromDB = async (): Promise<{
fastgptConfig: FastGPTConfigFileType;
licenseData?: LicenseDataType;
}> => {
if (!FastGPTProUrl) { if (!FastGPTProUrl) {
return { return {
config: {} as FastGPTConfigFileType fastgptConfig: {} as FastGPTConfigFileType
}; };
} }
const res = await MongoSystemConfigs.findOne({ const [fastgptConfig, licenseConfig] = await Promise.all([
type: SystemConfigsTypeEnum.fastgpt MongoSystemConfigs.findOne({
}).sort({ type: SystemConfigsTypeEnum.fastgpt
createTime: -1 }).sort({
}); createTime: -1
}),
MongoSystemConfigs.findOne({
type: SystemConfigsTypeEnum.license
}).sort({
createTime: -1
})
]);
const config = res?.value || {}; const config = fastgptConfig?.value || {};
const licenseData = licenseConfig?.value?.data as LicenseDataType | undefined;
const fastgptConfigTime = fastgptConfig?.createTime.getTime().toString();
const licenseConfigTime = licenseConfig?.createTime.getTime().toString();
// 利用配置文件的创建时间(更新时间)来做缓存,如果前端命中缓存,则不需要再返回配置文件 // 利用配置文件的创建时间(更新时间)来做缓存,如果前端命中缓存,则不需要再返回配置文件
global.systemInitBufferId = res ? res.createTime.getTime().toString() : undefined; global.systemInitBufferId = fastgptConfigTime
? `${fastgptConfigTime}-${licenseConfigTime}`
: undefined;
return { return {
config: config as FastGPTConfigFileType fastgptConfig: config as FastGPTConfigFileType,
licenseData
}; };
}; };

View File

@@ -57,14 +57,19 @@ export const addLog = {
level === LogLevelEnum.error && console.error(obj); level === LogLevelEnum.error && console.error(obj);
// store // store log
if (level >= STORE_LOG_LEVEL && connectionMongo.connection.readyState === 1) { if (level >= STORE_LOG_LEVEL && connectionMongo.connection.readyState === 1) {
// store log (async () => {
getMongoLog().create({ try {
text: msg, await getMongoLog().create({
level, text: msg,
metadata: obj level,
}); metadata: obj
});
} catch (error) {
console.error('store log error', error);
}
})();
} }
}, },
debug(msg: string, obj?: Record<string, any>) { debug(msg: string, obj?: Record<string, any>) {

View File

@@ -188,6 +188,7 @@ export class PgVectorCtrl {
const results: any = await PgClient.query( const results: any = await PgClient.query(
`BEGIN; `BEGIN;
SET LOCAL hnsw.ef_search = ${global.systemEnv?.hnswEfSearch || 100}; SET LOCAL hnsw.ef_search = ${global.systemEnv?.hnswEfSearch || 100};
SET LOCAL hnsw.max_scan_tuples = ${global.systemEnv?.hnswMaxScanTuples || 100000};
SET LOCAL hnsw.iterative_scan = relaxed_order; SET LOCAL hnsw.iterative_scan = relaxed_order;
WITH relaxed_results AS MATERIALIZED ( WITH relaxed_results AS MATERIALIZED (
select id, collection_id, vector <#> '[${vector}]' AS score select id, collection_id, vector <#> '[${vector}]' AS score
@@ -199,7 +200,7 @@ export class PgVectorCtrl {
) SELECT id, collection_id, score FROM relaxed_results ORDER BY score; ) SELECT id, collection_id, score FROM relaxed_results ORDER BY score;
COMMIT;` COMMIT;`
); );
const rows = results?.[3]?.rows as PgSearchRawType[]; const rows = results?.[results.length - 2]?.rows as PgSearchRawType[];
if (!Array.isArray(rows)) { if (!Array.isArray(rows)) {
return { return {

View File

@@ -78,7 +78,7 @@ export const createChatCompletion = async ({
} }
body.model = modelConstantsData.model; body.model = modelConstantsData.model;
const formatTimeout = timeout ? timeout : body.stream ? 60000 : 600000; const formatTimeout = timeout ? timeout : 600000;
const ai = getAIApi({ const ai = getAIApi({
userKey, userKey,
timeout: formatTimeout timeout: formatTimeout

View File

@@ -1,6 +1,54 @@
{ {
"provider": "Claude", "provider": "Claude",
"list": [ "list": [
{
"model": "claude-sonnet-4-20250514",
"name": "claude-sonnet-4-20250514",
"maxContext": 200000,
"maxResponse": 8000,
"quoteMaxToken": 100000,
"maxTemperature": 1,
"showTopP": true,
"showStopSign": true,
"vision": true,
"toolChoice": true,
"functionCall": false,
"defaultSystemChatPrompt": "",
"datasetProcess": true,
"usedInClassify": true,
"customCQPrompt": "",
"usedInExtractFields": true,
"usedInQueryExtension": true,
"customExtractPrompt": "",
"usedInToolCall": true,
"defaultConfig": {},
"fieldMap": {},
"type": "llm"
},
{
"model": "claude-opus-4-20250514",
"name": "claude-opus-4-20250514",
"maxContext": 200000,
"maxResponse": 4096,
"quoteMaxToken": 100000,
"maxTemperature": 1,
"showTopP": true,
"showStopSign": true,
"vision": true,
"toolChoice": true,
"functionCall": false,
"defaultSystemChatPrompt": "",
"datasetProcess": true,
"usedInClassify": true,
"customCQPrompt": "",
"usedInExtractFields": true,
"usedInQueryExtension": true,
"customExtractPrompt": "",
"usedInToolCall": true,
"defaultConfig": {},
"fieldMap": {},
"type": "llm"
},
{ {
"model": "claude-3-7-sonnet-20250219", "model": "claude-3-7-sonnet-20250219",
"name": "claude-3-7-sonnet-20250219", "name": "claude-3-7-sonnet-20250219",

View File

@@ -25,6 +25,30 @@
"showTopP": true, "showTopP": true,
"showStopSign": true "showStopSign": true
}, },
{
"model": "gemini-2.5-flash-preview-04-17",
"name": "gemini-2.5-flash-preview-04-17",
"maxContext": 1000000,
"maxResponse": 8000,
"quoteMaxToken": 60000,
"maxTemperature": 1,
"vision": true,
"toolChoice": true,
"functionCall": false,
"defaultSystemChatPrompt": "",
"datasetProcess": true,
"usedInClassify": true,
"customCQPrompt": "",
"usedInExtractFields": true,
"usedInQueryExtension": true,
"customExtractPrompt": "",
"usedInToolCall": true,
"defaultConfig": {},
"fieldMap": {},
"type": "llm",
"showTopP": true,
"showStopSign": true
},
{ {
"model": "gemini-2.0-flash", "model": "gemini-2.0-flash",
"name": "gemini-2.0-flash", "name": "gemini-2.0-flash",

View File

@@ -11,21 +11,24 @@ import type {
import { getLLMModel } from './model'; import { getLLMModel } from './model';
import { getLLMDefaultUsage } from '@fastgpt/global/core/ai/constants'; import { getLLMDefaultUsage } from '@fastgpt/global/core/ai/constants';
import { getNanoid } from '@fastgpt/global/common/string/tools'; import { getNanoid } from '@fastgpt/global/common/string/tools';
import json5 from 'json5';
/* /*
Count response max token Count response max token
*/ */
export const computedMaxToken = ({ export const computedMaxToken = ({
maxToken, maxToken,
model model,
min
}: { }: {
maxToken?: number; maxToken?: number;
model: LLMModelItemType; model: LLMModelItemType;
min?: number;
}) => { }) => {
if (maxToken === undefined) return; if (maxToken === undefined) return;
maxToken = Math.min(maxToken, model.maxResponse); maxToken = Math.min(maxToken, model.maxResponse);
return maxToken; return Math.max(maxToken, min || 0);
}; };
// FastGPT temperature range: [0,10], ai temperature:[0,2],{0,1]…… // FastGPT temperature range: [0,10], ai temperature:[0,2],{0,1]……
@@ -54,8 +57,6 @@ type InferCompletionsBody<T> = T extends { stream: true }
export const llmCompletionsBodyFormat = <T extends CompletionsBodyType>( export const llmCompletionsBodyFormat = <T extends CompletionsBodyType>(
body: T & { body: T & {
response_format?: any;
json_schema?: string;
stop?: string; stop?: string;
}, },
model: string | LLMModelItemType model: string | LLMModelItemType
@@ -65,8 +66,26 @@ export const llmCompletionsBodyFormat = <T extends CompletionsBodyType>(
return body as unknown as InferCompletionsBody<T>; return body as unknown as InferCompletionsBody<T>;
} }
const response_format = body.response_format; const response_format = (() => {
const json_schema = body.json_schema ?? undefined; if (!body.response_format?.type) return undefined;
if (body.response_format.type === 'json_schema') {
try {
return {
type: 'json_schema',
json_schema: json5.parse(body.response_format?.json_schema as unknown as string)
};
} catch (error) {
throw new Error('Json schema error');
}
}
if (body.response_format.type) {
return {
type: body.response_format.type
};
}
return undefined;
})();
const stop = body.stop ?? undefined; const stop = body.stop ?? undefined;
const requestBody: T = { const requestBody: T = {
@@ -80,12 +99,7 @@ export const llmCompletionsBodyFormat = <T extends CompletionsBodyType>(
}) })
: undefined, : undefined,
...modelData?.defaultConfig, ...modelData?.defaultConfig,
response_format: response_format response_format,
? {
type: response_format,
json_schema
}
: undefined,
stop: stop?.split('|') stop: stop?.split('|')
}; };
@@ -123,12 +137,14 @@ export const llmStreamResponseToAnswerText = async (
// Tool calls // Tool calls
if (responseChoice?.tool_calls?.length) { if (responseChoice?.tool_calls?.length) {
responseChoice.tool_calls.forEach((toolCall) => { responseChoice.tool_calls.forEach((toolCall, i) => {
const index = toolCall.index; const index = toolCall.index ?? i;
if (toolCall.id || callingTool) { // Call new tool
// 有 id代表新 call 工具 const hasNewTool = toolCall?.function?.name || callingTool;
if (toolCall.id) { if (hasNewTool) {
// 有 function name代表新 call 工具
if (toolCall?.function?.name) {
callingTool = { callingTool = {
name: toolCall.function?.name || '', name: toolCall.function?.name || '',
arguments: toolCall.function?.arguments || '' arguments: toolCall.function?.arguments || ''
@@ -164,7 +180,7 @@ export const llmStreamResponseToAnswerText = async (
} }
} }
return { return {
text: parseReasoningContent(answer)[1], text: removeDatasetCiteText(parseReasoningContent(answer)[1], false),
usage, usage,
toolCalls toolCalls
}; };
@@ -178,8 +194,9 @@ export const llmUnStreamResponseToAnswerText = async (
}> => { }> => {
const answer = response.choices?.[0]?.message?.content || ''; const answer = response.choices?.[0]?.message?.content || '';
const toolCalls = response.choices?.[0]?.message?.tool_calls; const toolCalls = response.choices?.[0]?.message?.tool_calls;
return { return {
text: answer, text: removeDatasetCiteText(parseReasoningContent(answer)[1], false),
usage: response.usage, usage: response.usage,
toolCalls toolCalls
}; };
@@ -209,7 +226,9 @@ export const parseReasoningContent = (text: string): [string, string] => {
}; };
export const removeDatasetCiteText = (text: string, retainDatasetCite: boolean) => { export const removeDatasetCiteText = (text: string, retainDatasetCite: boolean) => {
return retainDatasetCite ? text : text.replace(/\[([a-f0-9]{24})\]\(CITE\)/g, ''); return retainDatasetCite
? text.replace(/\[id\]\(CITE\)/g, '')
: text.replace(/\[([a-f0-9]{24})\](?:\([^\)]*\)?)?/g, '').replace(/\[id\]\(CITE\)/g, '');
}; };
// Parse llm stream part // Parse llm stream part
@@ -224,6 +243,12 @@ export const parseLLMStreamResponse = () => {
let citeBuffer = ''; let citeBuffer = '';
const maxCiteBufferLength = 32; // [Object](CITE)总长度为32 const maxCiteBufferLength = 32; // [Object](CITE)总长度为32
// Buffer
let buffer_finishReason: CompletionFinishReason = null;
let buffer_usage: CompletionUsage = getLLMDefaultUsage();
let buffer_reasoningContent = '';
let buffer_content = '';
/* /*
parseThinkTag - 只控制是否主动解析 <think></think>,如果接口已经解析了,则不再解析。 parseThinkTag - 只控制是否主动解析 <think></think>,如果接口已经解析了,则不再解析。
retainDatasetCite - retainDatasetCite -
@@ -241,6 +266,7 @@ export const parseLLMStreamResponse = () => {
}; };
finish_reason?: CompletionFinishReason; finish_reason?: CompletionFinishReason;
}[]; }[];
usage?: CompletionUsage;
}; };
parseThinkTag?: boolean; parseThinkTag?: boolean;
retainDatasetCite?: boolean; retainDatasetCite?: boolean;
@@ -250,72 +276,71 @@ export const parseLLMStreamResponse = () => {
responseContent: string; responseContent: string;
finishReason: CompletionFinishReason; finishReason: CompletionFinishReason;
} => { } => {
const finishReason = part.choices?.[0]?.finish_reason || null; const data = (() => {
const content = part.choices?.[0]?.delta?.content || ''; buffer_usage = part.usage || buffer_usage;
// @ts-ignore
const reasoningContent = part.choices?.[0]?.delta?.reasoning_content || '';
const isStreamEnd = !!finishReason;
// Parse think const finishReason = part.choices?.[0]?.finish_reason || null;
const { reasoningContent: parsedThinkReasoningContent, content: parsedThinkContent } = (() => { buffer_finishReason = finishReason || buffer_finishReason;
if (reasoningContent || !parseThinkTag) {
isInThinkTag = false;
return { reasoningContent, content };
}
if (!content) { const content = part.choices?.[0]?.delta?.content || '';
return { // @ts-ignore
reasoningContent: '', const reasoningContent = part.choices?.[0]?.delta?.reasoning_content || '';
content: '' const isStreamEnd = !!buffer_finishReason;
};
}
// 如果不在 think 标签中,或者有 reasoningContent(接口已解析),则返回 reasoningContent 和 content // Parse think
if (isInThinkTag === false) { const { reasoningContent: parsedThinkReasoningContent, content: parsedThinkContent } =
return { (() => {
reasoningContent: '', if (reasoningContent || !parseThinkTag) {
content isInThinkTag = false;
}; return { reasoningContent, content };
} }
// 检测是否为 think 标签开头的数据 // 如果不在 think 标签中,或者有 reasoningContent(接口已解析),则返回 reasoningContent 和 content
if (isInThinkTag === undefined) { if (isInThinkTag === false) {
// Parse content think and answer
startTagBuffer += content;
// 太少内容时候,暂时不解析
if (startTagBuffer.length < thinkStartChars.length) {
if (isStreamEnd) {
const tmpContent = startTagBuffer;
startTagBuffer = '';
return { return {
reasoningContent: '', reasoningContent: '',
content: tmpContent content
}; };
} }
return {
reasoningContent: '',
content: ''
};
}
if (startTagBuffer.startsWith(thinkStartChars)) { // 检测是否为 think 标签开头的数据
isInThinkTag = true; if (isInThinkTag === undefined) {
return { // Parse content think and answer
reasoningContent: startTagBuffer.slice(thinkStartChars.length), startTagBuffer += content;
content: '' // 太少内容时候,暂时不解析
}; if (startTagBuffer.length < thinkStartChars.length) {
} if (isStreamEnd) {
const tmpContent = startTagBuffer;
startTagBuffer = '';
return {
reasoningContent: '',
content: tmpContent
};
}
return {
reasoningContent: '',
content: ''
};
}
// 如果未命中 think 标签,则认为不在 think 标签中,返回 buffer 内容作为 content if (startTagBuffer.startsWith(thinkStartChars)) {
isInThinkTag = false; isInThinkTag = true;
return { return {
reasoningContent: '', reasoningContent: startTagBuffer.slice(thinkStartChars.length),
content: startTagBuffer content: ''
}; };
} }
// 确认是 think 标签内容,开始返回 think 内容,并实时检测 </think> // 如果未命中 think 标签,则认为不在 think 标签中,返回 buffer 内容作为 content
/* isInThinkTag = false;
return {
reasoningContent: '',
content: startTagBuffer
};
}
// 确认是 think 标签内容,开始返回 think 内容,并实时检测 </think>
/*
检测 </think> 方案。 检测 </think> 方案。
存储所有疑似 </think> 的内容,直到检测到完整的 </think> 标签或超出 </think> 长度。 存储所有疑似 </think> 的内容,直到检测到完整的 </think> 标签或超出 </think> 长度。
content 返回值包含以下几种情况: content 返回值包含以下几种情况:
@@ -326,124 +351,145 @@ export const parseLLMStreamResponse = () => {
</think>abc - 完全命中尾标签 </think>abc - 完全命中尾标签
k>abc - 命中一部分尾标签 k>abc - 命中一部分尾标签
*/ */
// endTagBuffer 专门用来记录疑似尾标签的内容 // endTagBuffer 专门用来记录疑似尾标签的内容
if (endTagBuffer) { if (endTagBuffer) {
endTagBuffer += content; endTagBuffer += content;
if (endTagBuffer.includes(thinkEndChars)) { if (endTagBuffer.includes(thinkEndChars)) {
isInThinkTag = false; isInThinkTag = false;
const answer = endTagBuffer.slice(thinkEndChars.length); const answer = endTagBuffer.slice(thinkEndChars.length);
return { return {
reasoningContent: '', reasoningContent: '',
content: answer content: answer
}; };
} else if (endTagBuffer.length >= thinkEndChars.length) { } else if (endTagBuffer.length >= thinkEndChars.length) {
// 缓存内容超出尾标签长度,且仍未命中 </think>,则认为本次猜测 </think> 失败,仍处于 think 阶段。 // 缓存内容超出尾标签长度,且仍未命中 </think>,则认为本次猜测 </think> 失败,仍处于 think 阶段。
const tmp = endTagBuffer; const tmp = endTagBuffer;
endTagBuffer = ''; endTagBuffer = '';
return { return {
reasoningContent: tmp, reasoningContent: tmp,
content: '' content: ''
}; };
} }
return {
reasoningContent: '',
content: ''
};
} else if (content.includes(thinkEndChars)) {
// 返回内容,完整命中</think>,直接结束
isInThinkTag = false;
const [think, answer] = content.split(thinkEndChars);
return {
reasoningContent: think,
content: answer
};
} else {
// 无 buffer且未命中 </think>,开始疑似 </think> 检测。
for (let i = 1; i < thinkEndChars.length; i++) {
const partialEndTag = thinkEndChars.slice(0, i);
// 命中一部分尾标签
if (content.endsWith(partialEndTag)) {
const think = content.slice(0, -partialEndTag.length);
endTagBuffer += partialEndTag;
return { return {
reasoningContent: think, reasoningContent: '',
content: '' content: ''
}; };
} else if (content.includes(thinkEndChars)) {
// 返回内容,完整命中</think>,直接结束
isInThinkTag = false;
const [think, answer] = content.split(thinkEndChars);
return {
reasoningContent: think,
content: answer
};
} else {
// 无 buffer且未命中 </think>,开始疑似 </think> 检测。
for (let i = 1; i < thinkEndChars.length; i++) {
const partialEndTag = thinkEndChars.slice(0, i);
// 命中一部分尾标签
if (content.endsWith(partialEndTag)) {
const think = content.slice(0, -partialEndTag.length);
endTagBuffer += partialEndTag;
return {
reasoningContent: think,
content: ''
};
}
}
} }
}
// 完全未命中尾标签,还是 think 阶段。
return {
reasoningContent: content,
content: ''
};
})();
// Parse datset cite
if (retainDatasetCite) {
return {
reasoningContent: parsedThinkReasoningContent,
content: parsedThinkContent,
responseContent: parsedThinkContent,
finishReason: buffer_finishReason
};
} }
// 完全未命中尾标签,还是 think 阶段。 // 缓存包含 [ 的字符串,直到超出 maxCiteBufferLength 再一次性返回
return { const parseCite = (text: string) => {
reasoningContent: content, // 结束时,返回所有剩余内容
content: '' if (isStreamEnd) {
}; const content = citeBuffer + text;
})(); return {
content: removeDatasetCiteText(content, false)
};
}
// 新内容包含 [,初始化缓冲数据
if (text.includes('[')) {
const index = text.indexOf('[');
const beforeContent = citeBuffer + text.slice(0, index);
citeBuffer = text.slice(index);
// beforeContent 可能是:普通字符串,带 [ 的字符串
return {
content: removeDatasetCiteText(beforeContent, false)
};
}
// 处于 Cite 缓冲区,判断是否满足条件
else if (citeBuffer) {
citeBuffer += text;
// 检查缓冲区长度是否达到完整Quote长度或已经流结束
if (citeBuffer.length >= maxCiteBufferLength) {
const content = removeDatasetCiteText(citeBuffer, false);
citeBuffer = '';
return {
content
};
} else {
// 暂时不返回内容
return { content: '' };
}
}
return {
content: text
};
};
const { content: pasedCiteContent } = parseCite(parsedThinkContent);
// Parse datset cite
if (retainDatasetCite) {
return { return {
reasoningContent: parsedThinkReasoningContent, reasoningContent: parsedThinkReasoningContent,
content: parsedThinkContent, content: parsedThinkContent,
responseContent: parsedThinkContent, responseContent: pasedCiteContent,
finishReason finishReason: buffer_finishReason
}; };
} })();
// 缓存包含 [ 的字符串,直到超出 maxCiteBufferLength 再一次性返回 buffer_reasoningContent += data.reasoningContent;
const parseCite = (text: string) => { buffer_content += data.content;
// 结束时,返回所有剩余内容
if (isStreamEnd) {
const content = citeBuffer + text;
return {
content: removeDatasetCiteText(content, false)
};
}
// 新内容包含 [,初始化缓冲数据 return data;
if (text.includes('[')) { };
const index = text.indexOf('[');
const beforeContent = citeBuffer + text.slice(0, index);
citeBuffer = text.slice(index);
// beforeContent 可能是:普通字符串,带 [ 的字符串
return {
content: removeDatasetCiteText(beforeContent, false)
};
}
// 处于 Cite 缓冲区,判断是否满足条件
else if (citeBuffer) {
citeBuffer += text;
// 检查缓冲区长度是否达到完整Quote长度或已经流结束
if (citeBuffer.length >= maxCiteBufferLength) {
const content = removeDatasetCiteText(citeBuffer, false);
citeBuffer = '';
return {
content
};
} else {
// 暂时不返回内容
return { content: '' };
}
}
return {
content: text
};
};
const { content: pasedCiteContent } = parseCite(parsedThinkContent);
const getResponseData = () => {
return { return {
reasoningContent: parsedThinkReasoningContent, finish_reason: buffer_finishReason,
content: parsedThinkContent, usage: buffer_usage,
responseContent: pasedCiteContent, reasoningContent: buffer_reasoningContent,
finishReason content: buffer_content
}; };
}; };
const updateFinishReason = (finishReason: CompletionFinishReason) => {
buffer_finishReason = finishReason;
};
return { return {
parsePart parsePart,
getResponseData,
updateFinishReason
}; };
}; };

View File

@@ -11,40 +11,6 @@ export const beforeUpdateAppFormat = <T extends AppSchema['modules'] | undefined
nodes: T; nodes: T;
isPlugin: boolean; isPlugin: boolean;
}) => { }) => {
if (nodes) {
// Check dataset maxTokens
if (isPlugin) {
let maxTokens = 16000;
nodes.forEach((item) => {
if (
item.flowNodeType === FlowNodeTypeEnum.chatNode ||
item.flowNodeType === FlowNodeTypeEnum.tools
) {
const model =
item.inputs.find((item) => item.key === NodeInputKeyEnum.aiModel)?.value || '';
const chatModel = getLLMModel(model);
const quoteMaxToken = chatModel.quoteMaxToken || 16000;
maxTokens = Math.max(maxTokens, quoteMaxToken);
}
});
nodes.forEach((item) => {
if (item.flowNodeType === FlowNodeTypeEnum.datasetSearchNode) {
item.inputs.forEach((input) => {
if (input.key === NodeInputKeyEnum.datasetMaxTokens) {
const val = input.value as number;
if (val > maxTokens) {
input.value = maxTokens;
}
}
});
}
});
}
}
return { return {
nodes nodes
}; };

View File

@@ -1,7 +1,7 @@
import { Client } from '@modelcontextprotocol/sdk/client/index.js'; import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js'; import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'; import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
import { type ToolType } from '@fastgpt/global/core/app/type'; import { type McpToolConfigType } from '@fastgpt/global/core/app/type';
import { addLog } from '../../common/system/log'; import { addLog } from '../../common/system/log';
import { retryFn } from '@fastgpt/global/common/system/utils'; import { retryFn } from '@fastgpt/global/common/system/utils';
@@ -41,7 +41,7 @@ export class MCPClient {
* Get available tools list * Get available tools list
* @returns List of tools * @returns List of tools
*/ */
public async getTools(): Promise<ToolType[]> { public async getTools(): Promise<McpToolConfigType[]> {
try { try {
const client = await this.getConnection(); const client = await this.getConnection();
const response = await client.listTools(); const response = await client.listTools();

View File

@@ -1,5 +1,5 @@
import { type FlowNodeTemplateType } from '@fastgpt/global/core/workflow/type/node.d'; import { type FlowNodeTemplateType } from '@fastgpt/global/core/workflow/type/node.d';
import { FlowNodeTypeEnum, defaultNodeVersion } from '@fastgpt/global/core/workflow/node/constant'; import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import { import {
appData2FlowNodeIO, appData2FlowNodeIO,
pluginData2FlowNodeIO, pluginData2FlowNodeIO,
@@ -14,10 +14,15 @@ import { cloneDeep } from 'lodash';
import { MongoApp } from '../schema'; import { MongoApp } from '../schema';
import { type SystemPluginTemplateItemType } from '@fastgpt/global/core/workflow/type'; import { type SystemPluginTemplateItemType } from '@fastgpt/global/core/workflow/type';
import { getSystemPluginTemplates } from '../../../../plugins/register'; import { getSystemPluginTemplates } from '../../../../plugins/register';
import { getAppLatestVersion, getAppVersionById } from '../version/controller'; import {
checkIsLatestVersion,
getAppLatestVersion,
getAppVersionById
} from '../version/controller';
import { type PluginRuntimeType } from '@fastgpt/global/core/plugin/type'; import { type PluginRuntimeType } from '@fastgpt/global/core/plugin/type';
import { MongoSystemPlugin } from './systemPluginSchema'; import { MongoSystemPlugin } from './systemPluginSchema';
import { PluginErrEnum } from '@fastgpt/global/common/error/code/plugin'; import { PluginErrEnum } from '@fastgpt/global/common/error/code/plugin';
import { Types } from 'mongoose';
/* /*
plugin id rule: plugin id rule:
@@ -90,20 +95,28 @@ const getSystemPluginTemplateById = async (
/* Format plugin to workflow preview node data */ /* Format plugin to workflow preview node data */
export async function getChildAppPreviewNode({ export async function getChildAppPreviewNode({
id appId,
versionId
}: { }: {
id: string; appId: string;
versionId?: string;
}): Promise<FlowNodeTemplateType> { }): Promise<FlowNodeTemplateType> {
const app: ChildAppType = await (async () => { const app: ChildAppType = await (async () => {
const { source, pluginId } = await splitCombinePluginId(id); const { source, pluginId } = await splitCombinePluginId(appId);
if (source === PluginSourceEnum.personal) { if (source === PluginSourceEnum.personal) {
const item = await MongoApp.findById(id).lean(); const item = await MongoApp.findById(appId).lean();
if (!item) return Promise.reject('plugin not found'); if (!item) return Promise.reject('plugin not found');
const version = await getAppLatestVersion(id, item); const version = await getAppVersionById({ appId, versionId, app: item });
if (!version.versionId) return Promise.reject('App version not found'); const isLatest =
version.versionId && Types.ObjectId.isValid(version.versionId)
? await checkIsLatestVersion({
appId,
versionId: version.versionId
})
: true;
return { return {
id: String(item._id), id: String(item._id),
@@ -118,7 +131,11 @@ export async function getChildAppPreviewNode({
chatConfig: version.chatConfig chatConfig: version.chatConfig
}, },
templateType: FlowNodeTemplateTypeEnum.teamApp, templateType: FlowNodeTemplateTypeEnum.teamApp,
version: version.versionId, version: version.versionId,
versionLabel: version?.versionName || '',
isLatestVersion: isLatest,
originCost: 0, originCost: 0,
currentCost: 0, currentCost: 0,
hasTokenFee: false, hasTokenFee: false,
@@ -175,7 +192,11 @@ export async function getChildAppPreviewNode({
userGuide: app.userGuide, userGuide: app.userGuide,
showStatus: app.showStatus, showStatus: app.showStatus,
isTool: true, isTool: true,
version: app.version, version: app.version,
versionLabel: app.versionLabel,
isLatestVersion: app.isLatestVersion,
sourceHandle: isToolSet sourceHandle: isToolSet
? getHandleConfig(false, false, false, false) ? getHandleConfig(false, false, false, false)
: getHandleConfig(true, true, true, true), : getHandleConfig(true, true, true, true),
@@ -224,7 +245,7 @@ export async function getChildAppRuntimeById(
templateType: FlowNodeTemplateTypeEnum.teamApp, templateType: FlowNodeTemplateTypeEnum.teamApp,
// 用不到 // 用不到
version: item?.pluginData?.nodeVersion || defaultNodeVersion, version: item?.pluginData?.nodeVersion,
originCost: 0, originCost: 0,
currentCost: 0, currentCost: 0,
hasTokenFee: false, hasTokenFee: false,

View File

@@ -119,6 +119,7 @@ const AppSchema = new Schema({
defaultPermission: Number defaultPermission: Number
}); });
AppSchema.index({ type: 1 });
AppSchema.index({ teamId: 1, updateTime: -1 }); AppSchema.index({ teamId: 1, updateTime: -1 });
AppSchema.index({ teamId: 1, type: 1 }); AppSchema.index({ teamId: 1, type: 1 });
AppSchema.index( AppSchema.index(

View File

@@ -1,8 +1,14 @@
import { MongoDataset } from '../dataset/schema'; import { MongoDataset } from '../dataset/schema';
import { getEmbeddingModel } from '../ai/model'; import { getEmbeddingModel } from '../ai/model';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant'; import {
AppNodeFlowNodeTypeMap,
FlowNodeTypeEnum
} from '@fastgpt/global/core/workflow/node/constant';
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants'; import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
import type { StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node'; import type { StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node';
import { MongoAppVersion } from './version/schema';
import { checkIsLatestVersion } from './version/controller';
import { Types } from '../../common/mongo';
export async function listAppDatasetDataByTeamIdAndDatasetIds({ export async function listAppDatasetDataByTeamIdAndDatasetIds({
teamId, teamId,
@@ -35,6 +41,46 @@ export async function rewriteAppWorkflowToDetail({
}) { }) {
const datasetIdSet = new Set<string>(); const datasetIdSet = new Set<string>();
// Add node(App Type) versionlabel and latest sign
const appNodes = nodes.filter((node) => AppNodeFlowNodeTypeMap[node.flowNodeType]);
const versionIds = appNodes
.filter((node) => node.version && Types.ObjectId.isValid(node.version))
.map((node) => node.version);
if (versionIds.length > 0) {
const versionDataList = await MongoAppVersion.find(
{
_id: { $in: versionIds }
},
'_id versionName appId time'
).lean();
const versionMap: Record<string, any> = {};
const isLatestChecks = await Promise.all(
versionDataList.map(async (version) => {
const isLatest = await checkIsLatestVersion({
appId: version.appId,
versionId: version._id
});
return { versionId: String(version._id), isLatest };
})
);
const isLatestMap = new Map(isLatestChecks.map((item) => [item.versionId, item.isLatest]));
versionDataList.forEach((version) => {
versionMap[String(version._id)] = version;
});
appNodes.forEach((node) => {
if (!node.version) return;
const versionData = versionMap[String(node.version)];
if (versionData) {
node.versionLabel = versionData.versionName;
node.isLatestVersion = isLatestMap.get(String(node.version)) || false;
}
});
}
// Get all dataset ids from nodes // Get all dataset ids from nodes
nodes.forEach((node) => { nodes.forEach((node) => {
if (node.flowNodeType !== FlowNodeTypeEnum.datasetSearchNode) return; if (node.flowNodeType !== FlowNodeTypeEnum.datasetSearchNode) return;

View File

@@ -15,6 +15,7 @@ export const getAppLatestVersion = async (appId: string, app?: AppSchema) => {
if (version) { if (version) {
return { return {
versionId: version._id, versionId: version._id,
versionName: version.versionName,
nodes: version.nodes, nodes: version.nodes,
edges: version.edges, edges: version.edges,
chatConfig: version.chatConfig || app?.chatConfig || {} chatConfig: version.chatConfig || app?.chatConfig || {}
@@ -22,6 +23,7 @@ export const getAppLatestVersion = async (appId: string, app?: AppSchema) => {
} }
return { return {
versionId: app?.pluginData?.nodeVersion, versionId: app?.pluginData?.nodeVersion,
versionName: app?.name,
nodes: app?.modules || [], nodes: app?.modules || [],
edges: app?.edges || [], edges: app?.edges || [],
chatConfig: app?.chatConfig || {} chatConfig: app?.chatConfig || {}
@@ -47,6 +49,7 @@ export const getAppVersionById = async ({
if (version) { if (version) {
return { return {
versionId: version._id, versionId: version._id,
versionName: version.versionName,
nodes: version.nodes, nodes: version.nodes,
edges: version.edges, edges: version.edges,
chatConfig: version.chatConfig || app?.chatConfig || {} chatConfig: version.chatConfig || app?.chatConfig || {}
@@ -57,3 +60,22 @@ export const getAppVersionById = async ({
// If the version does not exist, the latest version is returned // If the version does not exist, the latest version is returned
return getAppLatestVersion(appId, app); return getAppLatestVersion(appId, app);
}; };
export const checkIsLatestVersion = async ({
appId,
versionId
}: {
appId: string;
versionId: string;
}) => {
const version = await MongoAppVersion.findOne(
{
appId,
isPublish: true,
_id: { $gt: versionId }
},
'_id'
).lean();
return !version;
};

View File

@@ -61,6 +61,7 @@ const ChatItemSchema = new Schema({
type: Array, type: Array,
default: [] default: []
}, },
errorMsg: String,
userGoodFeedback: { userGoodFeedback: {
type: String type: String
}, },

View File

@@ -34,6 +34,10 @@ const ChatSchema = new Schema({
ref: AppCollectionName, ref: AppCollectionName,
required: true required: true
}, },
createTime: {
type: Date,
default: () => new Date()
},
updateTime: { updateTime: {
type: Date, type: Date,
default: () => new Date() default: () => new Date()

View File

@@ -32,6 +32,7 @@ type Props = {
content: [UserChatItemType & { dataId?: string }, AIChatItemType & { dataId?: string }]; content: [UserChatItemType & { dataId?: string }, AIChatItemType & { dataId?: string }];
metadata?: Record<string, any>; metadata?: Record<string, any>;
durationSeconds: number; //s durationSeconds: number; //s
errorMsg?: string;
}; };
export async function saveChat({ export async function saveChat({
@@ -50,6 +51,7 @@ export async function saveChat({
outLinkUid, outLinkUid,
content, content,
durationSeconds, durationSeconds,
errorMsg,
metadata = {} metadata = {}
}: Props) { }: Props) {
if (!chatId || chatId === 'NO_RECORD_HISTORIES') return; if (!chatId || chatId === 'NO_RECORD_HISTORIES') return;
@@ -104,7 +106,8 @@ export async function saveChat({
return { return {
...item, ...item,
[DispatchNodeResponseKeyEnum.nodeResponse]: nodeResponse, [DispatchNodeResponseKeyEnum.nodeResponse]: nodeResponse,
durationSeconds durationSeconds,
errorMsg
}; };
} }
return item; return item;

View File

@@ -65,8 +65,8 @@ export const filterGPTMessageByMaxContext = async ({
if (lastMessage.role === ChatCompletionRequestMessageRoleEnum.User) { if (lastMessage.role === ChatCompletionRequestMessageRoleEnum.User) {
const tokens = await countGptMessagesTokens([lastMessage, ...tmpChats]); const tokens = await countGptMessagesTokens([lastMessage, ...tmpChats]);
maxContext -= tokens; maxContext -= tokens;
// 该轮信息整体 tokens 超出范围,这段数据不要了 // 该轮信息整体 tokens 超出范围,这段数据不要了。但是至少保证一组。
if (maxContext < 0) { if (maxContext < 0 && chats.length > 0) {
break; break;
} }

View File

@@ -2,7 +2,9 @@ import type {
APIFileListResponse, APIFileListResponse,
ApiFileReadContentResponse, ApiFileReadContentResponse,
APIFileReadResponse, APIFileReadResponse,
APIFileServer ApiDatasetDetailResponse,
APIFileServer,
APIFileItem
} from '@fastgpt/global/core/dataset/apiDataset'; } from '@fastgpt/global/core/dataset/apiDataset';
import axios, { type Method } from 'axios'; import axios, { type Method } from 'axios';
import { addLog } from '../../../common/system/log'; import { addLog } from '../../../common/system/log';
@@ -89,7 +91,7 @@ export const useApiDatasetRequest = ({ apiServer }: { apiServer: APIFileServer }
`/v1/file/list`, `/v1/file/list`,
{ {
searchKey, searchKey,
parentId parentId: parentId || apiServer.basePath
}, },
'POST' 'POST'
); );
@@ -144,7 +146,8 @@ export const useApiDatasetRequest = ({ apiServer }: { apiServer: APIFileServer }
tmbId, tmbId,
url: previewUrl, url: previewUrl,
relatedId: apiFileId, relatedId: apiFileId,
customPdfParse customPdfParse,
getFormatText: true
}); });
return { return {
title, title,
@@ -164,9 +167,34 @@ export const useApiDatasetRequest = ({ apiServer }: { apiServer: APIFileServer }
return url; return url;
}; };
const getFileDetail = async ({
apiFileId
}: {
apiFileId: string;
}): Promise<ApiDatasetDetailResponse> => {
const fileData = await request<ApiDatasetDetailResponse>(
`/v1/file/detail`,
{
id: apiFileId
},
'GET'
);
if (fileData) {
return {
id: fileData.id,
name: fileData.name,
parentId: fileData.parentId === null ? '' : fileData.parentId
};
}
return Promise.reject('File not found');
};
return { return {
getFileContent, getFileContent,
listFiles, listFiles,
getFilePreviewUrl getFilePreviewUrl,
getFileDetail
}; };
}; };

View File

@@ -0,0 +1,27 @@
import type {
APIFileServer,
YuqueServer,
FeishuServer
} from '@fastgpt/global/core/dataset/apiDataset';
import { useApiDatasetRequest } from './api';
import { useYuqueDatasetRequest } from '../yuqueDataset/api';
import { useFeishuDatasetRequest } from '../feishuDataset/api';
export const getApiDatasetRequest = async (data: {
apiServer?: APIFileServer;
yuqueServer?: YuqueServer;
feishuServer?: FeishuServer;
}) => {
const { apiServer, yuqueServer, feishuServer } = data;
if (apiServer) {
return useApiDatasetRequest({ apiServer });
}
if (yuqueServer) {
return useYuqueDatasetRequest({ yuqueServer });
}
if (feishuServer) {
return useFeishuDatasetRequest({ feishuServer });
}
return Promise.reject('Can not find api dataset server');
};

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