Compare commits

..

15 Commits

Author SHA1 Message Date
Archer
36f8755d09 lock (#2063)
* lock

* perf: init data

* perf: vision model url

* fix: chat index
2024-07-17 00:16:57 +08:00
jingyang
fc96bb99cc feat: optimize i18n implementation for better localization (#2062)
* feat: optimize i18n implementation for better localization

* delete i18n-ally-custom-framework.yml

* update common key
2024-07-16 17:56:27 +08:00
papapatrick
1e4ffc2481 docs: 完善本地开发教程 (#2061) 2024-07-16 17:38:11 +08:00
Archer
ee7496467b 4.8.7 test (#2058)
* fix: query extension id;App page change type invalid

* prettier
2024-07-16 16:55:12 +08:00
Archer
b5c98a4f63 Plugin runtime (#2050)
* feat: plugin run (#1950)

* feat: plugin run

* fix

* ui

* fix

* change user input type

* fix

* fix

* temp

* split out plugin chat

* perf: chatbox

* perf: chatbox

* fix: plugin runtime (#2032)

* fix: plugin runtime

* fix

* fix build

* fix build

* perf: chat send prompt

* perf: chat log ux

* perf: chatbox context and share page plugin runtime

* perf: plugin run time config

* fix: ts

* feat: doc

* perf: isPc check

* perf: variable input render

* feat: app search

* fix: response box height

* fix: phone ui

* perf: lock

* perf: plugin route

* fix: chat (#2049)

---------

Co-authored-by: heheer <71265218+newfish-cmyk@users.noreply.github.com>
2024-07-15 22:50:48 +08:00
Zong
090c880860 feat(image): metadata support image mime type (#2026) 2024-07-14 23:25:16 +08:00
Archer
dd2a9bdee5 Update 486.md 2024-07-11 14:43:34 +08:00
Archer
8d60ef505f Update 486.md 2024-07-11 14:41:48 +08:00
Archer
b14514c105 4.8.6 sandbox (#2020)
* fix: openapi doc

* update doc
2024-07-11 14:36:27 +08:00
Archer
11ffaaf2c5 4.8.6 sandbox (#2017)
* fix: openapi doc

* update doc
2024-07-11 10:34:59 +08:00
Archer
e2ae571d15 4.8.6 fix (#1970)
* fix: full text search match query

* perf: mongo schema import, Avoid duplicate import

* feat: mongo log store

* doc

* fix: sandbox outputs

* perf: desc color

* fix: node init

* perf code

* perf: chat header
2024-07-10 15:52:39 +08:00
Zong
f548e24e7d fix(env): typo template (#1990) 2024-07-08 15:05:23 +08:00
Archer
5605f1a892 4.8.6 fix (#1963)
* feat: log store

* fix: full text search match query

* perf: mongo schema import, Avoid duplicate import
2024-07-05 17:37:42 +08:00
Archer
88d10451c9 perf: collection created response (#1947)
* perf: collection created response

* update openapi doc

* remove default collection

* perf: chat ui

* fix: system prompt concat

* perf: published check

* perf: update app
2024-07-05 10:27:38 +08:00
Zong
8a7bd689ae fix(prompt): typo extra content (#1942)
* fix(prompt): typo extra content

* refactor(prompt): AIChat template
2024-07-04 19:08:26 +08:00
364 changed files with 7103 additions and 6416 deletions

1
.npmrc
View File

@@ -1,2 +1,3 @@
public-hoist-pattern[]=*tiktoken*
public-hoist-pattern[]=*@zilliz/milvus2-sdk-node*
registry=https://registry.npmjs.org/

View File

@@ -1,5 +0,0 @@
{
"recommendations": [
"inlang.vs-code-extension"
]
}

View File

@@ -1,46 +0,0 @@
# .vscode/i18n-ally-custom-framework.yml
# An array of strings which contain Language Ids defined by VS Code
# You can check available language ids here: https://code.visualstudio.com/docs/languages/identifiers
languageIds:
- javascript
- typescript
- javascriptreact
- typescriptreact
# An array of RegExes to find the key usage. **The key should be captured in the first match group**.
# You should unescape RegEx strings in order to fit in the YAML file
# To help with this, you can use https://www.freeformatter.com/json-escape.html
usageMatchRegex:
# The following example shows how to detect `t("your.i18n.keys")`
# the `{key}` will be placed by a proper keypath matching regex,
# you can ignore it and use your own matching rules as well
- "[^\\w\\d]t\\(['\"`]({key})['\"`]"
- "[^\\w\\d]commonT\\(['\"`]({key})['\"`]"
# 支持 appT("your.i18n.keys")
- "[^\\w\\d]appT\\(['\"`]({key})['\"`]"
# 支持 datasetT("your.i18n.keys")
- "[^\\w\\d]datasetT\\(['\"`]({key})['\"`]"
- "[^\\w\\d]fileT\\(['\"`]({key})['\"`]"
- "[^\\w\\d]publishT\\(['\"`]({key})['\"`]"
- "[^\\w\\d]workflowT\\(['\"`]({key})['\"`]"
- "[^\\w\\d]userT\\(['\"`]({key})['\"`]"
- "[^\\w\\d]chatT\\(['\"`]({key})['\"`]"
# A RegEx to set a custom scope range. This scope will be used as a prefix when detecting keys
# and works like how the i18next framework identifies the namespace scope from the
# useTranslation() hook.
# You should unescape RegEx strings in order to fit in the YAML file
# To help with this, you can use https://www.freeformatter.com/json-escape.html
scopeRangeRegex: "useTranslation\\(\\s*\\[?\\s*['\"`](.*?)['\"`]"
# An array of strings containing refactor templates.
# The "$1" will be replaced by the keypath specified.
# Optional: uncomment the following two lines to use
# refactorTemplates:
# - i18n.get("$1")
# If set to true, only enables this custom framework (will disable all built-in frameworks)
monopoly: true

11
.vscode/settings.json vendored
View File

@@ -2,16 +2,23 @@
"editor.formatOnSave": true,
"editor.mouseWheelZoom": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"prettier.prettierPath": "../node_modules/prettier",
"prettier.prettierPath": "node_modules/prettier",
"typescript.tsdk": "node_modules/typescript/lib",
"i18n-ally.localesPaths": [
"packages/web/i18n",
],
"i18n-ally.enabledParsers": ["json", "yaml", "js", "ts"],
"i18n-ally.enabledParsers": [
"json",
"yaml",
"js",
"ts"
],
"i18n-ally.keystyle": "nested",
"i18n-ally.sortKeys": true,
"i18n-ally.keepFulfilled": false,
"i18n-ally.sourceLanguage": "zh", // 根据此语言文件翻译其他语言文件的变量和内容
"i18n-ally.displayLanguage": "zh", // 显示语言
"i18n-ally.namespace": true,
"i18n-ally.pathMatcher": "{locale}/{namespaces}.json",
"i18n-ally.extract.targetPickingStrategy": "most-similar-by-key"
}

View File

@@ -11,7 +11,6 @@ weight: 707
![](/imgs/sealos-fastgpt.webp)
{{% alert icon="🤖" context="success" %}}
- MongoDB用于存储除了向量外的各类数据
@@ -105,13 +104,11 @@ brew install orbstack
{{< /tab >}}
{{< /tabs >}}
## 开始部署
### 1. 下载 docker-compose.yml
非 Linux 环境或无法访问外网环境,可手动创建一个目录,并下载配置文件和对应版本的`docker-compose.yml`
非 Linux 环境或无法访问外网环境,可手动创建一个目录,并下载配置文件和对应版本的`docker-compose.yml`在这个文件夹中依据下载的配置文件运行docker若作为本地开发使用推荐`docker-compose-pgvector`版本,并且自行拉取并运行`sandbox``fastgpt`并在docker配置文件中注释掉`sandbox``fastgpt`的部分
- [config.json](https://github.com/labring/FastGPT/blob/main/projects/app/data/config.json)
- [docker-compose.yml](https://github.com/labring/FastGPT/blob/main/files/docker) (注意,不同向量库版本的文件不一样)
@@ -271,7 +268,6 @@ rs.status()
默认是写了OneAPi的连接地址和密钥可以通过修改`docker-compose.yml`fastgpt容器的环境变量实现。
`OPENAI_BASE_URL`API 接口的地址,需要加/v1
`CHAT_API_KEY`API 接口的凭证)。
@@ -315,8 +311,7 @@ docker-compose up -d
1. `docker exec -it fastgpt sh` 进入 FastGPT 容器。
2. 直接输入`env`命令查看所有环境变量。
### 为什么无法连接`本地模型`镜像。
### 为什么无法连接`本地模型`镜像
`docker-compose.yml`中使用了桥接的模式建立了`fastgpt`网络如想通过0.0.0.0或镜像名访问其它镜像,需将其它镜像也加入到网络中。
@@ -368,8 +363,8 @@ mongo连接失败查看mongo的运行状态**对应日志**。
由于服务初始化错误,系统重启导致。
* 90%是由于配置文件写不对,导致 JSON 解析报错
* 剩下的基本是因为向量数据库连不上
- 90%是由于配置文件写不对,导致 JSON 解析报错
- 剩下的基本是因为向量数据库连不上
### 如何修改密码

View File

@@ -7,8 +7,7 @@ toc: true
weight: 705
---
本文档介绍了如何设置开发环境以构建和测试 [FastGPT](https://fastgpt.in)。
本文档介绍了如何设置开发环境以构建和测试 [FastGPT](https://fastgpt.in)
## 前置依赖项
@@ -16,13 +15,14 @@ weight: 705
- [Git](http://git-scm.com/)
- [Docker](https://www.docker.com/)(构建镜像)
- [Node.js v18.17 / v20.x](http://nodejs.org)
- [Node.js v18.17 / v20.x](http://nodejs.org)版本尽量一样可以使用nvm管理node版本
- [pnpm](https://pnpm.io/) 版本 8.6.0 (目前官方的开发环境)
- make命令: 根据不同平台,百度安装 (官方是GNU Make 4.3)
## 开始本地开发
{{% alert context="success" %}}
1. 用户默认的时区为 `Asia/Shanghai`,非 linux 环境时候,获取系统时间会异常,本地开发时候,可以将用户的时区调整成 UTC+0
2. 建议先服务器装好**数据库**,再进行本地开发。
{{% /alert %}}
@@ -47,9 +47,10 @@ git clone git@github.com:<github_username>/FastGPT.git
### 3. 安装数据库
第一次开发,需要先部署数据库,建议本地开发可以随便找一台 2C2G 的轻量小数据库实践。数据库部署教程:[Docker 快速部署](/docs/development/docker/)。部署完了,可以本地访问其数据库。
第一次开发,需要先部署数据库,建议本地开发可以随便找一台 2C2G 的轻量小数据库实践或者新建文件夹并配置相关文件用以运行docker。数据库部署教程:[Docker 快速部署](/docs/development/docker/)。部署完了,可以本地访问其数据库。
{{% alert context="warning" %}}
Mongo 数据库需要注意,需要注意在连接地址中增加 `directConnection=true` 参数,才能连接上副本集的数据库。
{{% /alert %}}
### 4. 初始配置
@@ -57,7 +58,7 @@ Mongo 数据库需要注意,需要注意在连接地址中增加 `directConnec
**1. 环境变量**
复制`.env.template`文件,在同级目录下生成一个`.env.local` 文件,修改`.env.local` 里内容才是有效的变量。变量说明见 .env.template
复制`.env.template`文件,在同级目录下生成一个`.env.local` 文件,修改`.env.local` 里内容才是有效的变量。变量说明见 .env.template,主要需要修改`API_KEY`和数据库的地址与端口以及数据库账号的用户名和密码具体配置需要和docker配置文件相同其中用户名和密码如需修改需要修改docker配置文件、数据库和`.env.local`文件,不能只改一处。
**2. config 配置文件**
@@ -73,7 +74,7 @@ Mongo 数据库需要注意,需要注意在连接地址中增加 `directConnec
### 5. 运行
可参考项目根目录下的 `dev.md`
可参考项目根目录下的 `dev.md`,第一次编译运行可能会有点慢,需要点耐心哦
```bash
# 给自动化脚本代码执行权限(非 linux 系统, 可以手动执行里面的 postinstall.sh 文件内容)
@@ -114,7 +115,6 @@ make build name=app image=registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8
如果遇到问题,比如合并冲突或不知道如何打开拉取请求,请查看 GitHub 的[拉取请求教程](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests),了解如何解决合并冲突和其他问题。一旦您的 PR 被合并,您将自豪地被列为[贡献者表](https://github.com/labring/FastGPT/graphs/contributors)中的一员。
## QA
### 本地数据库无法连接
@@ -130,6 +130,7 @@ make build name=app image=registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8
FastGPT 在`pnpm i`后会执行`postinstall`脚本,用于自动生成`ChakraUI``Type`。如果没有权限,可以先执行`chmod -R +x ./scripts/`,再执行`pnpm i`
仍不可行的话,可以手动执行`./scripts/postinstall.sh`里的内容。
*如果是Windows下的话可以使用git bash给`postinstall`脚本添加执行权限并执行sh脚本*
### TypeError: Cannot read properties of null (reading 'useMemo' )
@@ -141,6 +142,9 @@ FastGPT 在`pnpm i`后会执行`postinstall`脚本,用于自动生成`ChakraUI
4. `cd projects/app`
5. `pnpm dev`
### Error response from daemon: error while creating mount source path 'XXX': mkdir XXX: file exists
这个错误可能是之前停止容器时有文件残留导致的首先需要确认相关镜像都全部关闭然后手动删除相关文件或者重启docker即可
## 加入社区
@@ -155,6 +159,7 @@ FastGPT 在`pnpm i`后会执行`postinstall`脚本,用于自动生成`ChakraUI
FastGPT 使用了 nextjs 的 page route 作为框架。为了区分好前后端代码,在目录分配上会分成 global, service, web 3个自目录分别对应着 `前后端共用``后端专用``前端专用`的代码。
### monorepo
FastGPT 采用 pnpm workspace 方式构建 monorepo 项目,主要分为两个部分:
- projects/app - FastGPT 主项目
@@ -173,6 +178,7 @@ support - 支撑功能(用户体系,计费,鉴权等)
common - 基础功能(日志管理,文件读写等)
{{% details title="代码结构说明" closed="true" %}}
```
.
├── .github // github 相关配置
@@ -200,4 +206,5 @@ common - 基础功能(日志管理,文件读写等)
├── README_ja.md
├── dev.md
```
{{% /details %}}

View File

@@ -7,18 +7,22 @@ toc: true
weight: 852
---
## 发起对话
{{% alert icon="🤖 " context="success" %}}
该接口的 API Key 需使用`应用特定的 key`,否则会报错。
有些包调用时,`BaseUrl`需要添加`v1`路径有些不需要如果出现404情况可补充`v1`重试。
{{% /alert %}}
## 发起对话(简易应用和工作流)
**对话接口兼容`GPT`的接口!如果你的项目使用的是标准的`GPT`官方接口,可以直接通过修改`BaseUrl`和 `Authorization`来访问 FastGpt 应用**
对话接口兼容`GPT`的接口!如果你的项目使用的是标准的`GPT`官方接口,可以直接通过修改`BaseUrl``Authorization`来访问 FastGpt 应用,不过需要注意下面几个规则:
## 请求
{{% alert icon="🤖 " context="success" %}}
* 传入的`model``temperature`等参数字段均无效,这些字段由编排决定。
* 不会返回实际消耗`Token`值,如果需要,可以设置`detail=true`,并手动计算 `responseData` 里的`tokens`值。
{{% /alert %}}
### 请求
{{< tabs tabTotal="2" >}}
{{< tab tabName="请求示例" >}}
@@ -67,7 +71,7 @@ curl --location --request POST 'https://api.fastgpt.in/api/v1/chat/completions'
{{< /tab >}}
{{< /tabs >}}
## 响应
### 响应
{{< tabs tabTotal="5" >}}
{{< tab tabName="detail=false,stream=false 响应" >}}
@@ -245,7 +249,7 @@ data: [{"moduleName":"知识库搜索","moduleType":"datasetSearchNode","running
{{< /markdownify >}}
{{< /tab >}}
{{< tab tabName="detail=true,stream=true 时,event值" >}}
{{< tab tabName="event值" >}}
{{< markdownify >}}
event取值
@@ -265,6 +269,192 @@ event取值
{{< /tabs >}}
## 请求插件
插件的接口与对话接口一致,仅请求参数略有区别,有以下规定:
* 调用插件类型的应用时,接口默认为`detail`模式。
* 无需传入 `chatId`,因为插件只能运行一轮。
* 无需传入`messages`
* 通过传递`variables`来代表插件的输入。
* 通过获取`pluginData`来获取插件输出。
### 请求示例
```bash
curl --location --request POST 'http://localhost:3000/api/v1/chat/completions' \
--header 'Authorization: Bearer test-xxxxx' \
--header 'Content-Type: application/json' \
--data-raw '{
"stream": false,
"chatId": "test",
"variables": {
"query":"你好" # 我的插件输入有一个参数,变量名叫 query
}
}'
```
### 响应示例
{{< tabs tabTotal="3" >}}
{{< tab tabName="detail=true,stream=false 响应" >}}
{{< markdownify >}}
* 插件的输出可以通过查找`responseData`中, `moduleType=pluginOutput`的元素,其`pluginOutput`是插件的输出。
* 流输出,仍可以通过`choices`进行获取。
```json
{
"responseData": [
{
"nodeId": "fdDgXQ6SYn8v",
"moduleName": "AI 对话",
"moduleType": "chatNode",
"totalPoints": 0.685,
"model": "FastAI-3.5",
"tokens": 685,
"query": "你好",
"maxToken": 2000,
"historyPreview": [
{
"obj": "Human",
"value": "你好"
},
{
"obj": "AI",
"value": "你好!有什么可以帮助你的吗?欢迎向我提问。"
}
],
"contextTotalLen": 14,
"runningTime": 1.73
},
{
"nodeId": "pluginOutput",
"moduleName": "自定义插件输出",
"moduleType": "pluginOutput",
"totalPoints": 0,
"pluginOutput": {
"result": "你好!有什么可以帮助你的吗?欢迎向我提问。"
},
"runningTime": 0
}
],
"newVariables": {
"query": "你好"
},
"id": "safsafsa",
"model": "",
"usage": {
"prompt_tokens": 1,
"completion_tokens": 1,
"total_tokens": 1
},
"choices": [
{
"message": {
"role": "assistant",
"content": "你好!有什么可以帮助你的吗?欢迎向我提问。"
},
"finish_reason": "stop",
"index": 0
}
]
}
```
{{< /markdownify >}}
{{< /tab >}}
{{< tab tabName="detail=true,stream=true 响应" >}}
{{< markdownify >}}
* 插件的输出可以通过获取`event=flowResponses`中的字符串,并将其反序列化后得到一个数组。同样的,查找 `moduleType=pluginOutput`的元素,其`pluginOutput`是插件的输出。
* 流输出,仍和对话接口一样获取。
```bash
event: flowNodeStatus
data: {"status":"running","name":"AI 对话"}
event: answer
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":""},"index":0,"finish_reason":null}]}
event: answer
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":"你"},"index":0,"finish_reason":null}]}
event: answer
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":"好"},"index":0,"finish_reason":null}]}
event: answer
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":""},"index":0,"finish_reason":null}]}
event: answer
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":"有"},"index":0,"finish_reason":null}]}
event: answer
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":"什"},"index":0,"finish_reason":null}]}
event: answer
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":"么"},"index":0,"finish_reason":null}]}
event: answer
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":"可以"},"index":0,"finish_reason":null}]}
event: answer
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":"帮"},"index":0,"finish_reason":null}]}
event: answer
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":"助"},"index":0,"finish_reason":null}]}
event: answer
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":"你"},"index":0,"finish_reason":null}]}
event: answer
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":"的"},"index":0,"finish_reason":null}]}
event: answer
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":"吗"},"index":0,"finish_reason":null}]}
event: answer
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":""},"index":0,"finish_reason":null}]}
event: answer
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":""},"index":0,"finish_reason":null}]}
event: answer
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{},"index":0,"finish_reason":"stop"}]}
event: answer
data: [DONE]
event: flowResponses
data: [{"nodeId":"fdDgXQ6SYn8v","moduleName":"AI 对话","moduleType":"chatNode","totalPoints":0.033,"model":"FastAI-3.5","tokens":33,"query":"你好","maxToken":2000,"historyPreview":[{"obj":"Human","value":"你好"},{"obj":"AI","value":"你好!有什么可以帮助你的吗?"}],"contextTotalLen":2,"runningTime":1.42},{"nodeId":"pluginOutput","moduleName":"自定义插件输出","moduleType":"pluginOutput","totalPoints":0,"pluginOutput":{"result":"你好!有什么可以帮助你的吗?"},"runningTime":0}]
```
{{< /markdownify >}}
{{< /tab >}}
{{< tab tabName="输出获取" >}}
{{< markdownify >}}
event取值
- answer: 返回给客户端的文本(最终会算作回答)
- fastAnswer: 指定回复返回给客户端的文本(最终会算作回答)
- toolCall: 执行工具
- toolParams: 工具参数
- toolResponse: 工具返回
- flowNodeStatus: 运行到的节点状态
- flowResponses: 节点完整响应
- updateVariables: 更新变量
- error: 报错
{{< /markdownify >}}
{{< /tab >}}
{{< /tabs >}}
## 使用案例
- [接入 NextWeb/ChatGPT web 等应用](/docs/use-cases/openapi)

View File

@@ -13,7 +13,7 @@ weight: 853
## 创建训练订单(4.6.9地址发生改动)
## 创建训练订单
{{< tabs tabTotal="2" >}}
{{< tab tabName="请求示例" >}}
@@ -26,6 +26,7 @@ curl --location --request POST 'https://api.fastgpt.in/api/support/wallet/usage/
--header 'Authorization: Bearer {{apikey}}' \
--header 'Content-Type: application/json' \
--data-raw '{
"datasetId": "知识库 ID",
"name": "可选,自定义订单名称,例如:文档训练-fastgpt.docx"
}'
```
@@ -127,8 +128,12 @@ curl --location --request POST 'http://localhost:3000/api/core/dataset/create' \
{{< markdownify >}}
```bash
curl --location --request GET 'http://localhost:3000/api/core/dataset/list?parentId=' \
--header 'Authorization: Bearer {{authorization}}' \
curl --location --request POST 'http://localhost:3000/api/core/dataset/list?parentId=' \
--header 'Authorization: Bearer xxxx' \
--header 'Content-Type: application/json' \
--data-raw '{
"parentId":""
}'
```
{{< /markdownify >}}
@@ -138,7 +143,7 @@ curl --location --request GET 'http://localhost:3000/api/core/dataset/list?paren
{{< markdownify >}}
{{% alert icon=" " context="success" %}}
- parentId - 父级ID不传或为空,代表获取根目录下的知识库
- parentId - 父级ID传空字符串或者null,代表获取根目录下的知识库
{{% /alert %}}
{{< /markdownify >}}

View File

@@ -1,5 +1,5 @@
---
title: 'V4.8.6(进行中)'
title: 'V4.8.6(需要初始化)'
description: 'FastGPT V4.8.6 更新说明'
icon: 'upgrade'
draft: false
@@ -13,9 +13,9 @@ weight: 818
### 2. 修改镜像
- fastgpt 镜像 tag 修改成 v4.8.6-alpha
- fastgpt-sandbox 镜像 tag 修改成 v4.8.6-alpha
- 商业版镜像 tag 修改成 v4.8.6-alpha
- fastgpt 镜像 tag 修改成 v4.8.6
- fastgpt-sandbox 镜像 tag 修改成 v4.8.6
- 商业版镜像 tag 修改成 v4.8.6
### 3. 执行初始化
@@ -33,10 +33,19 @@ curl --location --request POST 'https://{{host}}/api/admin/initv486' \
## V4.8.6 更新说明
1. 新增 - 知识库支持单个集合禁用功能
2. 新增 - 文件夹权限继承
3. 新增 - 网页抓取和数学计算器系统插件
4. 新增 - 移动文本加工和自定义反馈到基础节点中
5. 优化 - Read file 默认选中从节点,实现 MongoDB 读写分离,减轻主节点压力
6. 修复 - 工作流中团队插件加载异常
7. 修复 - 知识库集合目录导航失效
1. 新增 - 应用权限继承
2. 新增 - 知识库支持单个集合禁用功能
3. 新增 - 系统插件模式变更,新增链接读取和数学计算器插件,正式版会更新如何自定义系统插件
4. 新增 - 代码沙盒运行参数
5. 新增 - AI对话时隐藏头部的功能主要是适配移动端
6. 优化 - 文件读取Mongo 默认使用从节点,减轻主节点压力
7. 优化 - 提示词模板
8. 优化 - Mongo model 重复加载
9. 修复 - 创建链接集合未返回 id
10. 修复 - 文档接口说明
11. 修复 - api system 提示合并
12. 修复 - 团队插件目录内的内容无法加载
13. 修复 - 知识库集合目录面包屑无法加载
14. 修复 - Markdown 导出对话异常
15. 修复 - 提示模板结束标签错误
16. 修复 - 文档描述

View File

@@ -0,0 +1,29 @@
---
title: 'V4.8.7(进行中)'
description: 'FastGPT V4.8.7 更新说明'
icon: 'upgrade'
draft: false
toc: true
weight: 817
---
## 升级指南
### 1. 做好数据库备份
### 2. 修改镜像
- fastgpt 镜像 tag 修改成 v4.8.7-alpha
- 商业版镜像 tag 修改成 v4.8.7-alpha
-------
## V4.8.7 更新说明
1. 新增 - 插件支持独立运行,发布和日志查看
2. 新增 - 应用搜索
3. 优化 - 对话框代码
4. 优化 - 升级 Dockerfile node 和 pnpm 版本
5. 优化 - local 域名部署,也可以正常使用 vision 模式
6. 修复 - 简易模式无法变更全局变量
7. 修复 - gpt4o 无法同时使用工具和图片

View File

@@ -78,7 +78,7 @@ weight: 404
},
{
"nodeId": "u6IAOEssxoZT",
"name": "工具调用(实验)",
"name": "工具调用",
"intro": "通过AI模型自动选择一个或多个功能块进行调用也可以对插件进行调用。",
"avatar": "/imgs/workflow/tool.svg",
"flowNodeType": "tools",

View File

@@ -121,8 +121,8 @@ services:
restart: always
fastgpt:
container_name: fastgpt
image: ghcr.io/labring/fastgpt:v4.8.5 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.5 # 阿里云
image: ghcr.io/labring/fastgpt:v4.8.6 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.6 # 阿里云
ports:
- 3000:3000
networks:
@@ -156,6 +156,7 @@ services:
- SANDBOX_URL=http://sandbox:3000
# 日志等级: debug, info, warn, error
- LOG_LEVEL=info
- STORE_LOG_LEVEL=warn
volumes:
- ./config.json:/app/data/config.json

View File

@@ -79,8 +79,8 @@ services:
restart: always
fastgpt:
container_name: fastgpt
image: ghcr.io/labring/fastgpt:v4.8.5 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.5 # 阿里云
image: ghcr.io/labring/fastgpt:v4.8.6 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.6 # 阿里云
ports:
- 3000:3000
networks:
@@ -113,6 +113,7 @@ services:
- SANDBOX_URL=http://sandbox:3000
# 日志等级: debug, info, warn, error
- LOG_LEVEL=info
- STORE_LOG_LEVEL=warn
volumes:
- ./config.json:/app/data/config.json

View File

@@ -60,8 +60,8 @@ services:
restart: always
fastgpt:
container_name: fastgpt
image: ghcr.io/labring/fastgpt:v4.8.5 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.5 # 阿里云
image: ghcr.io/labring/fastgpt:v4.8.6 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.6 # 阿里云
ports:
- 3000:3000
networks:
@@ -94,6 +94,7 @@ services:
- SANDBOX_URL=http://sandbox:3000
# 日志等级: debug, info, warn, error
- LOG_LEVEL=info
- STORE_LOG_LEVEL=warn
volumes:
- ./config.json:/app/data/config.json

View File

@@ -14,11 +14,11 @@
"devDependencies": {
"@chakra-ui/cli": "^2.4.1",
"husky": "^8.0.3",
"i18next": "23.10.0",
"lint-staged": "^13.3.0",
"next-i18next": "15.2.0",
"i18next": "23.11.5",
"next-i18next": "15.3.0",
"react-i18next": "14.1.2",
"prettier": "3.2.4",
"react-i18next": "13.5.0",
"zhlint": "^0.7.4"
},
"lint-staged": {
@@ -26,7 +26,7 @@
"./docSite/**/**/*.md": "npm run format-doc"
},
"engines": {
"node": ">=18.0.0",
"pnpm": ">=8.6.0"
"node": ">=18.16.0",
"pnpm": ">=9.0.0"
}
}
}

View File

@@ -9,6 +9,7 @@ export type MongoImageSchemaType = {
type: `${MongoImageTypeEnum}`;
metadata?: {
mime?: string; // image mime type.
relatedId?: string; // This id is associated with a set of images
};
};

View File

@@ -70,9 +70,3 @@ export type SystemEnvType = {
oneapiUrl?: string;
chatApiKey?: string;
};
// declare global {
// var feConfigs: FastGPTFeConfigsType;
// var systemEnv: SystemEnvType;
// var systemInitd: boolean;
// }

View File

@@ -100,7 +100,7 @@ export const Prompt_QuotePromptList: PromptTemplateItem[] = [
<QA>
{{quote}}
</QA>}
</QA>
思考流程:
1. 判断问题是否与 <QA></QA> 标记中的内容有关。
@@ -109,7 +109,12 @@ export const Prompt_QuotePromptList: PromptTemplateItem[] = [
4. 如果有相同的问题,直接输出对应答案。
5. 如果只有相近的问题,请把相近的问题和答案一起输出。
最后,避免提及你是从 QA 获取的知识,只需要回复答案。
回答要求:
- 如果没有相关的问答对,你需要澄清。
- 回答的内容应尽可能与 <QA></QA> 标记中的内容一致。
- 避免提及你是从 QA 获取的知识,只需要回复答案。
- 使用 Markdown 语法优化回答格式。
- 使用与问题相同的语言回答。
问题:"""{{question}}"""`
}

View File

@@ -0,0 +1,10 @@
import { StoreNodeItemType } from '../../workflow/type/node';
import { FlowNodeInputItemType } from '../../workflow/type/io';
import { FlowNodeTypeEnum } from '../../workflow/node/constant';
export const getPluginInputsFromStoreNodes = (nodes: StoreNodeItemType[]) => {
return nodes.find((node) => node.flowNodeType === FlowNodeTypeEnum.pluginInput)?.inputs || [];
};
export const getPluginRunContent = (e: { pluginInputs: FlowNodeInputItemType[] }) => {
return JSON.stringify(e);
};

View File

@@ -21,6 +21,7 @@ export type AppSchema = {
name: string;
avatar: string;
intro: string;
updateTime: Date;
modules: StoreNodeItemType[];

View File

@@ -56,7 +56,10 @@ export const chats2GPTMessages = ({
text: item.text?.content || ''
};
}
if (item.type === 'file' && item.file?.type === ChatFileTypeEnum.image) {
if (
item.type === ChatItemValueTypeEnum.file &&
item.file?.type === ChatFileTypeEnum.image
) {
return {
type: 'image_url',
image_url: {
@@ -64,7 +67,6 @@ export const chats2GPTMessages = ({
}
};
}
return;
})
.filter(Boolean) as ChatCompletionContentPart[];
@@ -166,7 +168,7 @@ export const GPTMessages2Chats = (
} else if (item.type === 'image_url') {
value.push({
//@ts-ignore
type: 'file',
type: ChatItemValueTypeEnum.file,
file: {
type: ChatFileTypeEnum.image,
name: '',
@@ -175,7 +177,6 @@ export const GPTMessages2Chats = (
});
}
});
// @ts-ignore
}
} else if (
obj === ChatRoleEnum.AI &&

View File

@@ -56,7 +56,4 @@ export enum ChatStatusEnum {
finish = 'finish'
}
export const IMG_BLOCK_KEY = 'img-block';
export const FILE_BLOCK_KEY = 'file-block';
export const MARKDOWN_QUOTE_SIGN = 'QUOTE SIGN';

View File

@@ -13,8 +13,8 @@ import { DispatchNodeResponseKeyEnum } from '../workflow/runtime/constants';
import { AppChatConfigType, AppSchema, VariableItemType } from '../app/type';
import type { AppSchema as AppType } from '@fastgpt/global/core/app/type.d';
import { DatasetSearchModeEnum } from '../dataset/constants';
import { ChatBoxInputType } from '../../../../projects/app/src/components/ChatBox/type';
import { DispatchNodeResponseType } from '../workflow/runtime/type.d';
import { ChatBoxInputType } from '../../../../projects/app/src/components/core/chat/ChatContainer/ChatBox/type';
export type ChatSchema = {
_id: string;
@@ -115,6 +115,7 @@ export type ChatSiteItemType = (UserChatItemType | SystemChatItemType | AIChatIt
status: `${ChatStatusEnum}`;
moduleName?: string;
ttsBuffer?: Uint8Array;
responseData?: ChatHistoryItemResType[];
} & ChatBoxInputType;
/* --------- team chat --------- */

View File

@@ -3,6 +3,17 @@ import { FlowNodeTypeEnum } from '../workflow/node/constant';
import { ChatItemValueTypeEnum, ChatRoleEnum } from './constants';
import { ChatHistoryItemResType, ChatItemType, UserChatItemValueItemType } from './type.d';
// Concat 2 -> 1, and sort by role
export const concatHistories = (histories1: ChatItemType[], histories2: ChatItemType[]) => {
const newHistories = [...histories1, ...histories2];
return newHistories.sort((a, b) => {
if (a.obj === ChatRoleEnum.System) {
return -1;
}
return 1;
});
};
export const getChatTitleFromChatMessage = (message?: ChatItemType, defaultValue = '新对话') => {
// @ts-ignore
const textMsg = message?.value.find((item) => item.type === ChatItemValueTypeEnum.text);
@@ -54,11 +65,12 @@ export const filterPublicNodeResponseData = ({
}: {
flowResponses?: ChatHistoryItemResType[];
}) => {
const filedList = ['quoteList', 'moduleType'];
const filedList = ['quoteList', 'moduleType', 'pluginOutput'];
const filterModuleTypeList: any[] = [
FlowNodeTypeEnum.pluginModule,
FlowNodeTypeEnum.datasetSearchNode,
FlowNodeTypeEnum.tools
FlowNodeTypeEnum.tools,
FlowNodeTypeEnum.pluginOutput
];
return flowResponses
@@ -78,14 +90,22 @@ export const filterPublicNodeResponseData = ({
});
};
export const removeEmptyUserInput = (input: UserChatItemValueItemType[]) => {
return input.filter((item) => {
if (item.type === ChatItemValueTypeEnum.text && !item.text?.content?.trim()) {
return false;
}
if (item.type === ChatItemValueTypeEnum.file && !item.file?.url) {
return false;
}
return true;
});
export const removeEmptyUserInput = (input?: UserChatItemValueItemType[]) => {
return (
input?.filter((item) => {
if (item.type === ChatItemValueTypeEnum.text && !item.text?.content?.trim()) {
return false;
}
if (item.type === ChatItemValueTypeEnum.file && !item.file?.url) {
return false;
}
return true;
}) || []
);
};
export const getPluginOutputsFromChatResponses = (responses: ChatHistoryItemResType[]) => {
const outputs =
responses.find((item) => item.moduleType === FlowNodeTypeEnum.pluginOutput)?.pluginOutput ?? {};
return outputs;
};

View File

@@ -142,6 +142,9 @@ export type DispatchNodeResponseType = {
// code
codeLog?: string;
// plugin
pluginOutput?: Record<string, any>;
};
export type DispatchNodeResultType<T> = {

View File

@@ -123,6 +123,7 @@ export const checkNodeRunStatus = ({
(item) => item.target === node.nodeId
);
// Entry
if (workflowEdges.length === 0) {
return 'run';
}

View File

@@ -8,5 +8,10 @@ export const Output_Template_AddOutput: FlowNodeOutputItemType = {
key: NodeOutputKeyEnum.addOutputParam,
type: FlowNodeOutputTypeEnum.dynamic,
valueType: WorkflowIOValueTypeEnum.dynamic,
label: ''
label: '',
customFieldConfig: {
selectValueTypeList: Object.values(WorkflowIOValueTypeEnum),
showDescription: false,
showDefaultValue: false
}
};

View File

@@ -82,12 +82,7 @@ export const HttpNode468: FlowNodeTemplateType = {
],
outputs: [
{
...Output_Template_AddOutput,
customFieldConfig: {
selectValueTypeList: Object.values(WorkflowIOValueTypeEnum),
showDescription: false,
showDefaultValue: true
}
...Output_Template_AddOutput
},
{
id: NodeOutputKeyEnum.error,

View File

@@ -19,7 +19,7 @@ import { LLMModelTypeEnum } from '../../../ai/constants';
import { getHandleConfig } from '../utils';
export const AiQueryExtension: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.chatNode,
id: FlowNodeTypeEnum.queryExtension,
templateType: FlowNodeTemplateTypeEnum.other,
flowNodeType: FlowNodeTypeEnum.queryExtension,
sourceHandle: getHandleConfig(true, true, true, true),

View File

@@ -36,6 +36,32 @@ export const CodeNode: FlowNodeTemplateType = {
showDefaultValue: true
}
},
{
renderTypeList: [FlowNodeInputTypeEnum.reference],
valueType: WorkflowIOValueTypeEnum.string,
canEdit: true,
key: 'data1',
label: 'data1',
customInputConfig: {
selectValueTypeList: Object.values(WorkflowIOValueTypeEnum),
showDescription: false,
showDefaultValue: true
},
required: true
},
{
renderTypeList: [FlowNodeInputTypeEnum.reference],
valueType: WorkflowIOValueTypeEnum.string,
canEdit: true,
key: 'data2',
label: 'data2',
customInputConfig: {
selectValueTypeList: Object.values(WorkflowIOValueTypeEnum),
showDescription: false,
showDefaultValue: true
},
required: true
},
{
key: NodeInputKeyEnum.codeType,
renderTypeList: [FlowNodeInputTypeEnum.hidden],
@@ -52,7 +78,7 @@ export const CodeNode: FlowNodeTemplateType = {
outputs: [
{
...Output_Template_AddOutput,
description: '将代码中 return 的对象作为输出,传递给后续的节点'
description: '将代码中 return 的对象作为输出,传递给后续的节点。变量名需要对应 return 的 key'
},
{
id: NodeOutputKeyEnum.rawResponse,

View File

@@ -27,7 +27,7 @@ export const ToolModule: FlowNodeTemplateType = {
sourceHandle: getHandleConfig(true, true, false, true),
targetHandle: getHandleConfig(true, true, false, true),
avatar: '/imgs/workflow/tool.svg',
name: '工具调用(实验)',
name: '工具调用',
intro: '通过AI模型自动选择一个或多个功能块进行调用也可以对插件进行调用。',
showStatus: true,
version: '481',

View File

@@ -22,6 +22,7 @@ import {
defaultWhisperConfig
} from '../app/constants';
import { IfElseResultEnum } from './template/system/ifElse/constant';
import { RuntimeNodeItemType } from './runtime/type';
export const getHandleId = (nodeId: string, type: 'source' | 'target', key: string) => {
return `${nodeId}-${type}-${key}`;
@@ -190,3 +191,38 @@ export const isReferenceValue = (value: any): boolean => {
export const getElseIFLabel = (i: number) => {
return i === 0 ? IfElseResultEnum.IF : `${IfElseResultEnum.ELSE_IF} ${i}`;
};
// add value to plugin input node when run plugin
export const updatePluginInputByVariables = (
nodes: RuntimeNodeItemType[],
variables: Record<string, any>
) => {
return nodes.map((node) =>
node.flowNodeType === FlowNodeTypeEnum.pluginInput
? {
...node,
inputs: node.inputs.map((input) => {
const parseValue = (() => {
try {
if (
input.valueType === WorkflowIOValueTypeEnum.string ||
input.valueType === WorkflowIOValueTypeEnum.number ||
input.valueType === WorkflowIOValueTypeEnum.boolean
)
return variables[input.key];
return JSON.parse(variables[input.key]);
} catch (e) {
return variables[input.key];
}
})();
return {
...input,
value: parseValue ?? input.value
};
})
}
: node
);
};

View File

@@ -10,13 +10,13 @@
"js-yaml": "^4.1.0",
"jschardet": "3.1.1",
"nanoid": "^4.0.1",
"next": "14.2.3",
"next": "14.2.5",
"openai": "4.28.0",
"openapi-types": "^12.1.3",
"timezones-list": "^3.0.2"
},
"devDependencies": {
"@types/js-yaml": "^4.0.9",
"@types/node": "^20.14.2"
"@types/node": "20.14.0"
}
}

View File

@@ -1,5 +1,5 @@
{
"extends":"../../tsconfig.json",
"extends": "../../tsconfig.json",
"compilerOptions": {
"baseUrl": "."
},

View File

@@ -7,6 +7,6 @@
"devDependencies": {
"@fastgpt/global": "workspace:*",
"@fastgpt/service": "workspace:*",
"@types/node": "^20.14.2"
"@types/node": "20.14.0"
}
}

View File

@@ -1,5 +1,4 @@
import { SystemPluginResponseType } from '../../type';
import { urlsFetch } from '../../../service/common/string/cheerio';
import { urlsFetch } from '@fastgpt/service/common/string/cheerio';
type Props = {
url: string;

View File

@@ -88,7 +88,7 @@
"x": 1050.9890727421412,
"y": -415.2085119990912
},
"version": "486",
"version": "481",
"inputs": [
{
"key": "system_addInputParam",

View File

@@ -63,6 +63,7 @@ const instance = axios.create({
'Cache-Control': 'no-cache'
}
});
export const serverRequestBaseUrl = `http://${SERVICE_LOCAL_HOST}`;
/* 请求拦截 */
instance.interceptors.request.use(requestStart, (err) => Promise.reject(err));
@@ -79,7 +80,7 @@ export function request(url: string, data: any, config: ConfigType, method: Meth
return instance
.request({
baseURL: `http://${SERVICE_LOCAL_HOST}`,
baseURL: serverRequestBaseUrl,
url,
method,
data: ['POST', 'PUT'].includes(method) ? data : null,

View File

@@ -1,4 +1,4 @@
import { connectionMongo, type Model } from '../../mongo';
import { connectionMongo, getMongoModel, type Model } from '../../mongo';
const { Schema, model, models } = connectionMongo;
import { RawTextBufferSchemaType } from './type';
@@ -28,6 +28,7 @@ try {
console.log(error);
}
export const MongoRawTextBuffer: Model<RawTextBufferSchemaType> =
models[collectionName] || model(collectionName, RawTextBufferSchema);
MongoRawTextBuffer.syncIndexes();
export const MongoRawTextBuffer = getMongoModel<RawTextBufferSchemaType>(
collectionName,
RawTextBufferSchema
);

View File

@@ -1,4 +1,4 @@
import { connectionMongo, type Model } from '../../../common/mongo';
import { connectionMongo, getMongoModel, type Model } from '../../../common/mongo';
const { Schema, model, models } = connectionMongo;
import { TTSBufferSchemaType } from './type.d';
@@ -31,6 +31,4 @@ try {
console.log(error);
}
export const MongoTTSBuffer: Model<TTSBufferSchemaType> =
models[collectionName] || model(collectionName, TTSBufferSchema);
MongoTTSBuffer.syncIndexes();
export const MongoTTSBuffer = getMongoModel<TTSBufferSchemaType>(collectionName, TTSBufferSchema);

View File

@@ -1,4 +1,4 @@
import { connectionMongo, type Model } from '../../mongo';
import { connectionMongo, getMongoModel, type Model } from '../../mongo';
const { Schema, model, models } = connectionMongo;
const FileSchema = new Schema({});
@@ -10,6 +10,4 @@ try {
console.log(error);
}
export const MongoFileSchema = models['dataset.files'] || model('dataset.files', FileSchema);
MongoFileSchema.syncIndexes();
export const MongoFileSchema = getMongoModel('dataset.files', FileSchema);

View File

@@ -8,6 +8,7 @@ export function getMongoImgUrl(id: string) {
}
export const maxImgSize = 1024 * 1024 * 12;
const base64MimeRegex = /data:image\/([^\)]+);base64/;
export async function uploadMongoImg({
type,
base64Img,
@@ -22,7 +23,8 @@ export async function uploadMongoImg({
return Promise.reject('Image too large');
}
const base64Data = base64Img.split(',')[1];
const [base64Mime, base64Data] = base64Img.split(',')
const mime = `image/${base64Mime.match(base64MimeRegex)?.[1] ?? 'jpeg'}`
const binary = Buffer.from(base64Data, 'base64');
const { _id } = await MongoImage.create({
@@ -30,7 +32,7 @@ export async function uploadMongoImg({
teamId,
binary,
expiredTime,
metadata,
metadata: Object.assign({ mime }, metadata),
shareId
});
@@ -42,7 +44,7 @@ export async function readMongoImg({ id }: { id: string }) {
if (!data) {
return Promise.reject('Image not found');
}
return data?.binary;
return data;
}
export async function delImgByRelatedId({

View File

@@ -1,5 +1,5 @@
import { TeamCollectionName } from '@fastgpt/global/support/user/team/constant';
import { connectionMongo, type Model } from '../../mongo';
import { connectionMongo, getMongoModel, type Model } from '../../mongo';
import { MongoImageSchemaType } from '@fastgpt/global/common/file/image/type.d';
import { mongoImageTypeMap } from '@fastgpt/global/common/file/image/constants';
const { Schema, model, models } = connectionMongo;
@@ -41,7 +41,4 @@ try {
console.log(error);
}
export const MongoImage: Model<MongoImageSchemaType> =
models['image'] || model('image', ImageSchema);
MongoImage.syncIndexes();
export const MongoImage = getMongoModel<MongoImageSchemaType>('image', ImageSchema);

View File

@@ -1,4 +1,5 @@
import mongoose from 'mongoose';
import { addLog } from '../../common/system/log';
import mongoose, { Model } from 'mongoose';
export default mongoose;
export * from 'mongoose';
@@ -11,4 +12,68 @@ export const connectionMongo = (() => {
return global.mongodb;
})();
export const ReadPreference = mongoose.mongo.ReadPreference;
const addCommonMiddleware = (schema: mongoose.Schema) => {
const operations = [
/^find/,
'save',
'create',
/^update/,
/^delete/,
'aggregate',
'count',
'countDocuments',
'estimatedDocumentCount',
'distinct',
'insertMany'
];
operations.forEach((op: any) => {
schema.pre(op, function (this: any, next) {
this._startTime = Date.now();
this._query = this.getQuery ? this.getQuery() : null;
next();
});
schema.post(op, function (this: any, result: any, next) {
if (this._startTime) {
const duration = Date.now() - this._startTime;
const warnLogData = {
query: this._query,
op,
duration
};
if (duration > 1000) {
addLog.warn(`Slow operation ${duration}ms`, warnLogData);
} else if (duration > 300) {
addLog.error(`Slow operation ${duration}ms`, warnLogData);
}
}
next();
});
});
return schema;
};
export const getMongoModel = <T>(name: string, schema: mongoose.Schema) => {
if (connectionMongo.models[name]) return connectionMongo.models[name] as Model<T>;
console.log('Load model======', name);
addCommonMiddleware(schema);
const model = connectionMongo.model<T>(name, schema);
if (process.env.SYNC_INDEX !== '0') {
try {
model.syncIndexes({ background: true });
} catch (error) {
addLog.error('Create index error', error);
}
}
return model;
};
export const ReadPreference = connectionMongo.mongo.ReadPreference;

View File

@@ -1,3 +1,4 @@
import { exit } from 'process';
import { addLog } from '../system/log';
import { connectionMongo } from './index';
import type { Mongoose } from 'mongoose';
@@ -56,9 +57,13 @@ export async function connectMongo({
}
try {
afterHook && (await afterHook());
if (!global.systemInited) {
global.systemInited = true;
afterHook && (await afterHook());
}
} catch (error) {
addLog.error('mongo connect after hook error', error);
addLog.error('Mongo connect after hook error', error);
exit(1);
}
return connectionMongo;

View File

@@ -1,5 +1,5 @@
import { SystemConfigsType } from '@fastgpt/global/common/system/config/type';
import { connectionMongo, type Model } from '../../../common/mongo';
import { connectionMongo, getMongoModel, type Model } from '../../../common/mongo';
import { SystemConfigsTypeMap } from '@fastgpt/global/common/system/config/constants';
const { Schema, model, models } = connectionMongo;
@@ -27,6 +27,7 @@ try {
console.log(error);
}
export const MongoSystemConfigs: Model<SystemConfigsType> =
models[collectionName] || model(collectionName, systemConfigSchema);
MongoSystemConfigs.syncIndexes();
export const MongoSystemConfigs = getMongoModel<SystemConfigsType>(
collectionName,
systemConfigSchema
);

View File

@@ -1,13 +1,9 @@
import dayjs from 'dayjs';
import chalk from 'chalk';
import { isProduction } from './constants';
import { LogLevelEnum } from './log/constant';
import { connectionMongo } from '../mongo/index';
import { getMongoLog } from './log/schema';
enum LogLevelEnum {
debug = 0,
info = 1,
warn = 2,
error = 3
}
const logMap = {
[LogLevelEnum.debug]: {
levelLog: chalk.green('[Debug]')
@@ -23,23 +19,26 @@ const logMap = {
}
};
const envLogLevelMap: Record<string, number> = {
debug: 0,
info: 1,
warn: 2,
error: 3
debug: LogLevelEnum.debug,
info: LogLevelEnum.info,
warn: LogLevelEnum.warn,
error: LogLevelEnum.error
};
const logLevel = (() => {
if (!isProduction) return LogLevelEnum.debug;
const envLogLevel = (process.env.LOG_LEVEL || 'info').toLocaleLowerCase();
if (!envLogLevel || envLogLevelMap[envLogLevel] === undefined) return LogLevelEnum.info;
return envLogLevelMap[envLogLevel];
const { LOG_LEVEL, STORE_LOG_LEVEL } = (() => {
const LOG_LEVEL = (process.env.LOG_LEVEL || 'info').toLocaleLowerCase();
const STORE_LOG_LEVEL = (process.env.STORE_LOG_LEVEL || '').toLocaleLowerCase();
return {
LOG_LEVEL: envLogLevelMap[LOG_LEVEL] || LogLevelEnum.info,
STORE_LOG_LEVEL: envLogLevelMap[STORE_LOG_LEVEL] ?? 99
};
})();
/* add logger */
export const addLog = {
log(level: LogLevelEnum, msg: string, obj: Record<string, any> = {}) {
if (level < logLevel) return;
if (level < LOG_LEVEL) return;
const stringifyObj = JSON.stringify(obj);
const isEmpty = Object.keys(obj).length === 0;
@@ -52,35 +51,15 @@ export const addLog = {
level === LogLevelEnum.error && console.error(obj);
const lokiUrl = process.env.LOKI_LOG_URL as string;
if (!lokiUrl) return;
try {
fetch(lokiUrl, {
method: 'POST',
headers: {
'Content-type': 'application/json'
},
body: JSON.stringify({
streams: [
{
stream: {
level
},
values: [
[
`${Date.now() * 1000000}`,
JSON.stringify({
message: msg,
...obj
})
]
]
}
]
})
// store
if (level >= STORE_LOG_LEVEL && connectionMongo.connection.readyState === 1) {
// store log
getMongoLog().create({
text: msg,
level,
metadata: obj
});
} catch (error) {}
}
},
debug(msg: string, obj?: Record<string, any>) {
this.log(LogLevelEnum.debug, msg, obj);

View File

@@ -0,0 +1,10 @@
export enum LogLevelEnum {
debug = 0,
info = 1,
warn = 2,
error = 3
}
export enum LogSignEnum {
slowOperation = 'slowOperation'
}

View File

@@ -0,0 +1,29 @@
import { getMongoModel, Schema } from '../../../common/mongo';
import { SystemLogType } from './type';
import { LogLevelEnum } from './constant';
export const LogCollectionName = 'system_logs';
export const getMongoLog = () => {
const SystemLogSchema = new Schema({
text: {
type: String,
required: true
},
level: {
type: String,
required: true,
enum: Object.values(LogLevelEnum)
},
time: {
type: Date,
default: () => new Date()
},
metadata: Object
});
SystemLogSchema.index({ time: 1 }, { expires: '15d' });
SystemLogSchema.index({ level: 1 });
return getMongoModel<SystemLogType>(LogCollectionName, SystemLogSchema);
};

View File

@@ -0,0 +1,9 @@
import { LogLevelEnum, LogSignEnum } from './constant';
export type SystemLogType = {
_id: string;
text: string;
level: LogLevelEnum;
time: Date;
metadata?: Record<string, any>;
};

View File

@@ -1,4 +1,4 @@
import { connectionMongo, type Model } from '../../mongo';
import { connectionMongo, getMongoModel, type Model } from '../../mongo';
import { timerIdMap } from './constants';
const { Schema, model, models } = connectionMongo;
import { TimerLockSchemaType } from './type.d';
@@ -24,6 +24,4 @@ try {
console.log(error);
}
export const MongoTimerLock: Model<TimerLockSchemaType> =
models[collectionName] || model(collectionName, TimerLockSchema);
MongoTimerLock.syncIndexes();
export const MongoTimerLock = getMongoModel<TimerLockSchemaType>(collectionName, TimerLockSchema);

View File

@@ -1,6 +1,5 @@
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { connectionMongo, type Model } from '../../common/mongo';
const { Schema, model, models } = connectionMongo;
import { Schema, getMongoModel } from '../../common/mongo';
import type { AppSchema as AppType } from '@fastgpt/global/core/app/type.d';
import {
TeamCollectionName,
@@ -21,6 +20,7 @@ export const chatConfigType = {
chatInputGuide: Object
};
// schema
const AppSchema = new Schema({
parentId: {
type: Schema.Types.ObjectId,
@@ -58,6 +58,7 @@ const AppSchema = new Schema({
type: String,
default: ''
},
updateTime: {
type: Date,
default: () => new Date()
@@ -112,15 +113,8 @@ const AppSchema = new Schema({
...getPermissionSchema(AppDefaultPermissionVal)
});
try {
AppSchema.index({ updateTime: -1 });
AppSchema.index({ teamId: 1, type: 1 });
AppSchema.index({ scheduledTriggerConfig: 1, intervalNextTime: -1 });
} catch (error) {
console.log(error);
}
AppSchema.index({ teamId: 1, updateTime: -1 });
AppSchema.index({ teamId: 1, type: 1 });
AppSchema.index({ scheduledTriggerConfig: 1, intervalNextTime: -1 });
export const MongoApp: Model<AppType> =
models[AppCollectionName] || model(AppCollectionName, AppSchema);
MongoApp.syncIndexes();
export const MongoApp = getMongoModel<AppType>(AppCollectionName, AppSchema);

View File

@@ -1,4 +1,4 @@
import { connectionMongo, type Model } from '../../../common/mongo';
import { connectionMongo, getMongoModel, type Model } from '../../../common/mongo';
const { Schema, model, models } = connectionMongo;
import { AppVersionSchemaType } from '@fastgpt/global/core/app/version';
import { chatConfigType } from '../schema';
@@ -34,7 +34,7 @@ try {
console.log(error);
}
export const MongoAppVersion: Model<AppVersionSchemaType> =
models[AppVersionCollectionName] || model(AppVersionCollectionName, AppVersionSchema);
MongoAppVersion.syncIndexes();
export const MongoAppVersion = getMongoModel<AppVersionSchemaType>(
AppVersionCollectionName,
AppVersionSchema
);

View File

@@ -1,4 +1,4 @@
import { connectionMongo, type Model } from '../../common/mongo';
import { connectionMongo, getMongoModel, type Model } from '../../common/mongo';
const { Schema, model, models } = connectionMongo;
import { ChatItemSchema as ChatItemType } from '@fastgpt/global/core/chat/type';
import { ChatRoleMap } from '@fastgpt/global/core/chat/constants';
@@ -9,7 +9,6 @@ import {
} from '@fastgpt/global/support/user/team/constant';
import { AppCollectionName } from '../app/schema';
import { userCollectionName } from '../../support/user/schema';
import { NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
export const ChatItemCollectionName = 'chatitems';
@@ -99,7 +98,4 @@ try {
console.log(error);
}
export const MongoChatItem: Model<ChatItemType> =
models[ChatItemCollectionName] || model(ChatItemCollectionName, ChatItemSchema);
MongoChatItem.syncIndexes();
export const MongoChatItem = getMongoModel<ChatItemType>(ChatItemCollectionName, ChatItemSchema);

View File

@@ -1,4 +1,4 @@
import { connectionMongo, type Model } from '../../common/mongo';
import { connectionMongo, getMongoModel, type Model } from '../../common/mongo';
const { Schema, model, models } = connectionMongo;
import { ChatSchema as ChatType } from '@fastgpt/global/core/chat/type.d';
import { ChatSourceMap } from '@fastgpt/global/core/chat/constants';
@@ -98,6 +98,4 @@ try {
console.log(error);
}
export const MongoChat: Model<ChatType> =
models[chatCollectionName] || model(chatCollectionName, ChatSchema);
MongoChat.syncIndexes();
export const MongoChat = getMongoModel<ChatType>(chatCollectionName, ChatSchema);

View File

@@ -13,24 +13,24 @@ export async function getChatItems({
chatId?: string;
limit?: number;
field: string;
}): Promise<{ history: ChatItemType[] }> {
}): Promise<{ histories: ChatItemType[] }> {
if (!chatId) {
return { history: [] };
return { histories: [] };
}
const history = await MongoChatItem.find({ appId, chatId }, field)
const histories = await MongoChatItem.find({ appId, chatId }, field)
.sort({ _id: -1 })
.limit(limit)
.lean();
history.reverse();
histories.reverse();
history.forEach((item) => {
histories.forEach((item) => {
// @ts-ignore
item.value = adaptStringValue(item.value);
});
return { history };
return { histories };
}
/* 临时适配旧的对话记录 */
export const adaptStringValue = (value: any): ChatItemValueItemType[] => {

View File

@@ -1,5 +1,5 @@
import { AppCollectionName } from '../../app/schema';
import { connectionMongo, type Model } from '../../../common/mongo';
import { connectionMongo, getMongoModel, type Model } from '../../../common/mongo';
const { Schema, model, models } = connectionMongo;
import type { ChatInputGuideSchemaType } from '@fastgpt/global/core/chat/inputGuide/type.d';
@@ -23,7 +23,7 @@ try {
console.log(error);
}
export const MongoChatInputGuide: Model<ChatInputGuideSchemaType> =
models[ChatInputGuideCollectionName] || model(ChatInputGuideCollectionName, ChatInputGuideSchema);
MongoChatInputGuide.syncIndexes();
export const MongoChatInputGuide = getMongoModel<ChatInputGuideSchemaType>(
ChatInputGuideCollectionName,
ChatInputGuideSchema
);

View File

@@ -1,4 +1,3 @@
import { IMG_BLOCK_KEY } from '@fastgpt/global/core/chat/constants';
import { countGptMessagesTokens } from '../../common/string/tiktoken/index';
import type {
ChatCompletionContentPart,
@@ -7,6 +6,8 @@ import type {
import axios from 'axios';
import { ChatCompletionRequestMessageRoleEnum } from '@fastgpt/global/core/ai/constants';
import { guessBase64ImageType } from '../../common/file/utils';
import { serverRequestBaseUrl } from '../../common/api/serverRequest';
import { cloneDeep } from 'lodash';
/* slice chat context by tokens */
const filterEmptyMessages = (messages: ChatCompletionMessageParam[]) => {
@@ -120,137 +121,64 @@ export const formatGPTMessagesInRequestBefore = (messages: ChatCompletionMessage
.filter(Boolean) as ChatCompletionMessageParam[];
};
/**
string to vision model. Follow the markdown code block rule for interception:
@rule:
```img-block
{src:""}
{src:""}
```
```file-block
{name:"",src:""},
{name:"",src:""}
```
@example:
Whats in this image?
```img-block
{src:"https://1.png"}
```
@return
[
{ type: 'text', text: 'Whats in this image?' },
{
type: 'image_url',
image_url: {
url: 'https://1.png'
}
}
]
*/
export async function formatStr2ChatContent(str: string) {
const content: ChatCompletionContentPart[] = [];
let lastIndex = 0;
const regex = new RegExp(`\`\`\`(${IMG_BLOCK_KEY})\\n([\\s\\S]*?)\`\`\``, 'g');
const imgKey: 'image_url' = 'image_url';
let match;
while ((match = regex.exec(str)) !== null) {
// add previous text
if (match.index > lastIndex) {
const text = str.substring(lastIndex, match.index).trim();
if (text) {
content.push({ type: 'text', text });
}
}
const blockType = match[1].trim();
if (blockType === IMG_BLOCK_KEY) {
const blockContentLines = match[2].trim().split('\n');
const jsonLines = blockContentLines.map((item) => {
try {
return JSON.parse(item) as { src: string };
} catch (error) {
return { src: '' };
}
});
for (const item of jsonLines) {
if (!item.src) throw new Error("image block's content error");
}
content.push(
...jsonLines.map((item) => ({
type: imgKey,
image_url: {
url: item.src
}
}))
);
}
lastIndex = regex.lastIndex;
}
// add remaining text
if (lastIndex < str.length) {
const remainingText = str.substring(lastIndex).trim();
if (remainingText) {
content.push({ type: 'text', text: remainingText });
}
}
// Continuous text type content, if type=text, merge them
for (let i = 0; i < content.length - 1; i++) {
const currentContent = content[i];
const nextContent = content[i + 1];
if (currentContent.type === 'text' && nextContent.type === 'text') {
currentContent.text += nextContent.text;
content.splice(i + 1, 1);
i--;
}
}
if (content.length === 1 && content[0].type === 'text') {
return content[0].text;
}
if (!content) return null;
// load img to base64
for await (const item of content) {
if (item.type === imgKey && item[imgKey]?.url) {
const response = await axios.get(item[imgKey].url, {
responseType: 'arraybuffer'
});
const base64 = Buffer.from(response.data).toString('base64');
item[imgKey].url = `data:${response.headers['content-type']};base64,${base64}`;
}
}
return content ? content : null;
}
/* Load user chat content.
Img: to base 64
*/
export const loadChatImgToBase64 = async (content: string | ChatCompletionContentPart[]) => {
if (typeof content === 'string') {
return content;
}
return Promise.all(
content.map(async (item) => {
if (item.type === 'text') return item;
// load image
const response = await axios.get(item.image_url.url, {
responseType: 'arraybuffer'
});
const base64 = Buffer.from(response.data).toString('base64');
let imageType = response.headers['content-type'];
if (imageType === undefined) {
imageType = guessBase64ImageType(base64);
if (!item.image_url.url) return item;
/*
1. From db: Get it from db
2. From web: Not update
*/
if (item.image_url.url.startsWith('/')) {
const response = await axios.get(item.image_url.url, {
baseURL: serverRequestBaseUrl,
responseType: 'arraybuffer'
});
const base64 = Buffer.from(response.data).toString('base64');
let imageType = response.headers['content-type'];
if (imageType === undefined) {
imageType = guessBase64ImageType(base64);
}
return {
...item,
image_url: {
...item.image_url,
url: `data:${imageType};base64,${base64}`
}
};
}
item.image_url.url = `data:${imageType};base64,${base64}`;
return item;
})
);
};
export const loadRequestMessages = async (messages: ChatCompletionMessageParam[]) => {
if (messages.length === 0) {
return Promise.reject('core.chat.error.Messages empty');
}
const loadMessages = await Promise.all(
messages.map(async (item) => {
if (item.role === ChatCompletionRequestMessageRoleEnum.User) {
return {
...item,
content: await loadChatImgToBase64(item.content)
};
} else {
return item;
}
})
);
return loadMessages;
};

View File

@@ -75,54 +75,9 @@ export async function createOneCollection({
{ session }
);
// create default collection
if (type === DatasetCollectionTypeEnum.folder) {
await createDefaultCollection({
datasetId,
parentId: collection._id,
teamId,
tmbId,
session
});
}
return collection;
}
// create default collection
export function createDefaultCollection({
name = '手动录入',
datasetId,
parentId,
teamId,
tmbId,
session
}: {
name?: '手动录入' | '手动标注';
datasetId: string;
parentId?: string;
teamId: string;
tmbId: string;
session?: ClientSession;
}) {
return MongoDatasetCollection.create(
[
{
name,
teamId,
tmbId,
datasetId,
parentId,
type: DatasetCollectionTypeEnum.virtual,
trainingType: TrainingModeEnum.chunk,
chunkSize: 0,
updateTime: new Date('2099')
}
],
{ session }
);
}
/* delete collection related images/files */
export const delCollectionRelatedSource = async ({
collections,

View File

@@ -1,4 +1,4 @@
import { connectionMongo, type Model } from '../../../common/mongo';
import { connectionMongo, getMongoModel, type Model } from '../../../common/mongo';
const { Schema, model, models } = connectionMongo;
import { DatasetCollectionSchemaType } from '@fastgpt/global/core/dataset/type.d';
import { TrainingTypeMap, DatasetCollectionTypeMap } from '@fastgpt/global/core/dataset/constants';
@@ -94,9 +94,6 @@ const DatasetCollectionSchema = new Schema({
}
});
export const MongoDatasetCollection: Model<DatasetCollectionSchemaType> =
models[DatasetColCollectionName] || model(DatasetColCollectionName, DatasetCollectionSchema);
try {
// auth file
DatasetCollectionSchema.index({ teamId: 1, fileId: 1 });
@@ -111,8 +108,11 @@ try {
// get forbid
// DatasetCollectionSchema.index({ teamId: 1, datasetId: 1, forbid: 1 });
MongoDatasetCollection.syncIndexes({ background: true });
} catch (error) {
console.log(error);
}
export const MongoDatasetCollection = getMongoModel<DatasetCollectionSchemaType>(
DatasetColCollectionName,
DatasetCollectionSchema
);

View File

@@ -10,6 +10,7 @@ import {
} from '@fastgpt/global/core/dataset/constants';
import { hashStr } from '@fastgpt/global/common/string/tools';
import { ClientSession } from '../../../common/mongo';
import { PushDatasetDataResponse } from '@fastgpt/global/core/dataset/api';
/**
* get all collection by top collectionId
@@ -138,7 +139,7 @@ export const reloadCollectionChunks = async ({
billId?: string;
rawText?: string;
session: ClientSession;
}) => {
}): Promise<PushDatasetDataResponse> => {
const {
title,
rawText: newRawText,
@@ -149,7 +150,10 @@ export const reloadCollectionChunks = async ({
newRawText: rawText
});
if (isSameRawText) return;
if (isSameRawText)
return {
insertLen: 0
};
// split data
const { chunks } = splitText2Chunks({
@@ -164,7 +168,7 @@ export const reloadCollectionChunks = async ({
return Promise.reject('Training model error');
})();
await MongoDatasetTraining.insertMany(
const result = await MongoDatasetTraining.insertMany(
chunks.map((item, i) => ({
teamId: col.teamId,
tmbId,
@@ -191,4 +195,8 @@ export const reloadCollectionChunks = async ({
},
{ session }
);
return {
insertLen: result.length
};
};

View File

@@ -1,4 +1,4 @@
import { connectionMongo, type Model } from '../../../common/mongo';
import { connectionMongo, getMongoModel, type Model } from '../../../common/mongo';
const { Schema, model, models } = connectionMongo;
import { DatasetDataSchemaType } from '@fastgpt/global/core/dataset/type.d';
import {
@@ -77,27 +77,23 @@ const DatasetDataSchema = new Schema({
rebuilding: Boolean
});
export const MongoDatasetData: Model<DatasetDataSchemaType> =
models[DatasetDataCollectionName] || model(DatasetDataCollectionName, DatasetDataSchema);
// list collection and count data; list data; delete collection(relate data)
DatasetDataSchema.index({
teamId: 1,
datasetId: 1,
collectionId: 1,
chunkIndex: 1,
updateTime: -1
});
// full text index
DatasetDataSchema.index({ teamId: 1, datasetId: 1, fullTextToken: 'text' });
// Recall vectors after data matching
DatasetDataSchema.index({ teamId: 1, datasetId: 1, collectionId: 1, 'indexes.dataId': 1 });
DatasetDataSchema.index({ updateTime: 1 });
// rebuild data
DatasetDataSchema.index({ rebuilding: 1, teamId: 1, datasetId: 1 });
try {
// list collection and count data; list data; delete collection(relate data)
DatasetDataSchema.index({
teamId: 1,
datasetId: 1,
collectionId: 1,
chunkIndex: 1,
updateTime: -1
});
// full text index
DatasetDataSchema.index({ teamId: 1, datasetId: 1, fullTextToken: 'text' });
// Recall vectors after data matching
DatasetDataSchema.index({ teamId: 1, datasetId: 1, collectionId: 1, 'indexes.dataId': 1 });
DatasetDataSchema.index({ updateTime: 1 });
// rebuild data
DatasetDataSchema.index({ rebuilding: 1, teamId: 1, datasetId: 1 });
MongoDatasetData.syncIndexes({ background: true });
} catch (error) {
console.log(error);
}
export const MongoDatasetData = getMongoModel<DatasetDataSchemaType>(
DatasetDataCollectionName,
DatasetDataSchema
);

View File

@@ -1,4 +1,4 @@
import { connectionMongo, type Model } from '../../common/mongo';
import { connectionMongo, getMongoModel, type Model } from '../../common/mongo';
const { Schema, model, models } = connectionMongo;
import { DatasetSchemaType } from '@fastgpt/global/core/dataset/type.d';
import {
@@ -11,7 +11,6 @@ import {
TeamCollectionName,
TeamMemberCollectionName
} from '@fastgpt/global/support/user/team/constant';
import { PermissionTypeEnum, PermissionTypeMap } from '@fastgpt/global/support/permission/constant';
import { DatasetDefaultPermissionVal } from '@fastgpt/global/support/permission/dataset/constant';
export const DatasetCollectionName = 'datasets';
@@ -99,6 +98,4 @@ try {
console.log(error);
}
export const MongoDataset: Model<DatasetSchemaType> =
models[DatasetCollectionName] || model(DatasetCollectionName, DatasetSchema);
MongoDataset.syncIndexes();
export const MongoDataset = getMongoModel<DatasetSchemaType>(DatasetCollectionName, DatasetSchema);

View File

@@ -212,7 +212,7 @@ export async function searchDatasetData(props: SearchDatasetDataProps) {
{
$match: {
$expr: { $eq: ['$_id', '$$collectionId'] },
forbid: { $eq: false } // 直接在lookup阶段过滤
forbid: { $eq: true } // 匹配被禁用的数据
}
},
{
@@ -226,7 +226,7 @@ export async function searchDatasetData(props: SearchDatasetDataProps) {
},
{
$match: {
collection: { $ne: [] }
collection: { $eq: [] } // 没有 forbid=true 的数据
}
},
{

View File

@@ -1,5 +1,5 @@
/* 模型的知识库 */
import { connectionMongo, type Model } from '../../../common/mongo';
import { connectionMongo, getMongoModel, type Model } from '../../../common/mongo';
const { Schema, model, models } = connectionMongo;
import { DatasetTrainingSchemaType } from '@fastgpt/global/core/dataset/type';
import { TrainingTypeMap } from '@fastgpt/global/core/dataset/constants';
@@ -103,7 +103,7 @@ try {
console.log(error);
}
export const MongoDatasetTraining: Model<DatasetTrainingSchemaType> =
models[DatasetTrainingCollectionName] || model(DatasetTrainingCollectionName, TrainingDataSchema);
MongoDatasetTraining.syncIndexes();
export const MongoDatasetTraining = getMongoModel<DatasetTrainingSchemaType>(
DatasetTrainingCollectionName,
TrainingDataSchema
);

View File

@@ -198,7 +198,7 @@ ${description ? `- ${description}` : ''}
required: []
}
};
console.log(properties);
return {
filterMessages,
agentFunction

View File

@@ -1,6 +1,6 @@
import { LLMModelItemType } from '@fastgpt/global/core/ai/model.d';
import { getAIApi } from '../../../../ai/config';
import { filterGPTMessageByMaxTokens } from '../../../../chat/utils';
import { filterGPTMessageByMaxTokens, loadRequestMessages } from '../../../../chat/utils';
import {
ChatCompletion,
StreamChatType,
@@ -88,6 +88,7 @@ export const runToolWithFunctionCall = async (
}
return item;
});
const requestMessages = await loadRequestMessages(formativeMessages);
/* Run llm */
const ai = getAIApi({
@@ -99,7 +100,7 @@ export const runToolWithFunctionCall = async (
model: toolModel.model,
temperature: 0,
stream,
messages: formativeMessages,
messages: requestMessages,
functions,
function_call: 'auto'
},

View File

@@ -12,6 +12,7 @@ import { ChatItemType } from '@fastgpt/global/core/chat/type';
import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
import {
GPTMessages2Chats,
chatValue2RuntimePrompt,
chats2GPTMessages,
getSystemPrompt,
runtimePrompt2ChatsValue
@@ -29,10 +30,11 @@ type Response = DispatchNodeResultType<{
export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<Response> => {
const {
node: { nodeId, name, outputs },
node: { nodeId, name },
runtimeNodes,
runtimeEdges,
histories,
query,
params: { model, systemPrompt, userChatInput, history = 6 }
} = props;
@@ -65,7 +67,7 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
obj: ChatRoleEnum.Human,
value: runtimePrompt2ChatsValue({
text: userChatInput,
files: []
files: chatValue2RuntimePrompt(query).files
})
}
];

View File

@@ -1,6 +1,6 @@
import { LLMModelItemType } from '@fastgpt/global/core/ai/model.d';
import { getAIApi } from '../../../../ai/config';
import { filterGPTMessageByMaxTokens } from '../../../../chat/utils';
import { filterGPTMessageByMaxTokens, loadRequestMessages } from '../../../../chat/utils';
import {
ChatCompletion,
StreamChatType,
@@ -87,6 +87,8 @@ export const runToolWithPromptCall = async (
messages,
maxTokens: toolModel.maxContext - 500 // filter token. not response maxToken
});
const requestMessages = await loadRequestMessages(filterMessages);
// console.log(JSON.stringify(filterMessages, null, 2));
/* Run llm */
const ai = getAIApi({
@@ -98,7 +100,7 @@ export const runToolWithPromptCall = async (
model: toolModel.model,
temperature: 0,
stream,
messages: filterMessages
messages: requestMessages
},
{
headers: {

View File

@@ -1,6 +1,6 @@
import { LLMModelItemType } from '@fastgpt/global/core/ai/model.d';
import { getAIApi } from '../../../../ai/config';
import { filterGPTMessageByMaxTokens } from '../../../../chat/utils';
import { filterGPTMessageByMaxTokens, loadRequestMessages } from '../../../../chat/utils';
import {
ChatCompletion,
ChatCompletionMessageToolCall,
@@ -99,6 +99,8 @@ export const runToolWithToolChoice = async (
}
return item;
});
const requestMessages = await loadRequestMessages(formativeMessages);
// console.log(
// JSON.stringify(
// {
@@ -106,7 +108,7 @@ export const runToolWithToolChoice = async (
// model: toolModel.model,
// temperature: 0,
// stream,
// messages: formativeMessages,
// messages: requestMessages,
// tools,
// tool_choice: 'auto'
// },
@@ -124,7 +126,7 @@ export const runToolWithToolChoice = async (
model: toolModel.model,
temperature: 0,
stream,
messages: formativeMessages,
messages: requestMessages,
tools,
tool_choice: 'auto'
},

View File

@@ -2,7 +2,7 @@ import type { NextApiResponse } from 'next';
import {
filterGPTMessageByMaxTokens,
formatGPTMessagesInRequestBefore,
loadChatImgToBase64
loadRequestMessages
} from '../../../chat/utils';
import type { ChatItemType, UserChatItemValueItemType } from '@fastgpt/global/core/chat/type.d';
import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
@@ -151,22 +151,7 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
...formatGPTMessagesInRequestBefore(filterMessages)
] as ChatCompletionMessageParam[];
if (concatMessages.length === 0) {
return Promise.reject('core.chat.error.Messages empty');
}
const loadMessages = await Promise.all(
concatMessages.map(async (item) => {
if (item.role === ChatCompletionRequestMessageRoleEnum.User) {
return {
...item,
content: await loadChatImgToBase64(item.content)
};
} else {
return item;
}
})
);
const requestMessages = await loadRequestMessages(concatMessages);
const requestBody = {
...modelConstantsData?.defaultConfig,
@@ -174,7 +159,7 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
temperature,
max_tokens,
stream,
messages: loadMessages
messages: requestMessages
};
const response = await ai.chat.completions.create(requestBody, {
headers: {

View File

@@ -1,12 +1,11 @@
import { getErrText } from '@fastgpt/global/common/error/utils';
import { replaceSensitiveText } from '@fastgpt/global/common/string/tools';
import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
import type { ChatItemType } from '@fastgpt/global/core/chat/type.d';
import {
WorkflowIOValueTypeEnum,
NodeOutputKeyEnum
} from '@fastgpt/global/core/workflow/constants';
import { RuntimeEdgeItemType } from '@fastgpt/global/core/workflow/runtime/type';
import { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io';
export const filterToolNodeIdByEdges = ({
nodeId,
@@ -45,10 +44,16 @@ export const filterToolNodeIdByEdges = ({
export const getHistories = (history?: ChatItemType[] | number, histories: ChatItemType[] = []) => {
if (!history) return [];
if (typeof history === 'number') return histories.slice(-(history * 2));
if (Array.isArray(history)) return history;
return [];
const systemHistories = histories.filter((item) => item.obj === ChatRoleEnum.System);
const filterHistories = (() => {
if (typeof history === 'number') return histories.slice(-(history * 2));
if (Array.isArray(history)) return history;
return [];
})();
return [...systemHistories, ...filterHistories];
};
/* value type format */

View File

@@ -25,7 +25,7 @@
"mammoth": "^1.6.0",
"mongoose": "^7.0.2",
"multer": "1.4.5-lts.1",
"next": "14.2.3",
"next": "14.2.5",
"nextjs-cors": "^2.2.0",
"node-cron": "^3.0.3",
"node-xlsx": "^0.23.0",

View File

@@ -1,4 +1,4 @@
import { connectionMongo, type Model } from '../../../common/mongo';
import { connectionMongo, getMongoModel, type Model } from '../../../common/mongo';
const { Schema, model, models } = connectionMongo;
import { PromotionRecordSchema as PromotionRecordType } from '@fastgpt/global/support/activity/type.d';
@@ -29,6 +29,7 @@ const PromotionRecordSchema = new Schema({
}
});
export const MongoPromotionRecord: Model<PromotionRecordType> =
models['promotionRecord'] || model('promotionRecord', PromotionRecordSchema);
MongoPromotionRecord.syncIndexes();
export const MongoPromotionRecord = getMongoModel<PromotionRecordType>(
'promotionRecord',
PromotionRecordSchema
);

View File

@@ -1,4 +1,4 @@
import { connectionMongo, type Model } from '../../common/mongo';
import { connectionMongo, getMongoModel, type Model } from '../../common/mongo';
const { Schema, model, models } = connectionMongo;
import type { OpenApiSchema } from '@fastgpt/global/support/openapi/type';
import {
@@ -64,6 +64,4 @@ try {
console.log(error);
}
export const MongoOpenApi: Model<OpenApiSchema> =
models['openapi'] || model('openapi', OpenApiSchema);
MongoOpenApi.syncIndexes();
export const MongoOpenApi = getMongoModel<OpenApiSchema>('openapi', OpenApiSchema);

View File

@@ -1,4 +1,4 @@
import { connectionMongo, type Model } from '../../common/mongo';
import { connectionMongo, getMongoModel, type Model } from '../../common/mongo';
const { Schema, model, models } = connectionMongo;
import { OutLinkSchema as SchemaType } from '@fastgpt/global/support/outLink/type';
import {
@@ -90,7 +90,4 @@ try {
console.log(error);
}
export const MongoOutLink: Model<SchemaType> =
models['outlinks'] || model('outlinks', OutLinkSchema);
MongoOutLink.syncIndexes();
export const MongoOutLink = getMongoModel<SchemaType>('outlinks', OutLinkSchema);

View File

@@ -2,7 +2,7 @@ import {
TeamCollectionName,
TeamMemberCollectionName
} from '@fastgpt/global/support/user/team/constant';
import { Model, connectionMongo } from '../../common/mongo';
import { Model, connectionMongo, getMongoModel } from '../../common/mongo';
import type { ResourcePermissionType } from '@fastgpt/global/support/permission/type';
import { PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant';
const { Schema, model, models } = connectionMongo;
@@ -54,8 +54,7 @@ try {
console.log(error);
}
export const MongoResourcePermission: Model<ResourcePermissionType> =
models[ResourcePermissionCollectionName] ||
model(ResourcePermissionCollectionName, ResourcePermissionSchema);
MongoResourcePermission.syncIndexes();
export const MongoResourcePermission = getMongoModel<ResourcePermissionType>(
ResourcePermissionCollectionName,
ResourcePermissionSchema
);

View File

@@ -1,4 +1,4 @@
import { connectionMongo, type Model } from '../../common/mongo';
import { connectionMongo, getMongoModel, type Model } from '../../common/mongo';
const { Schema, model, models } = connectionMongo;
import { hashStr } from '@fastgpt/global/common/string/tools';
import type { UserModelSchema } from '@fastgpt/global/support/user/type';
@@ -74,6 +74,4 @@ try {
console.log(error);
}
export const MongoUser: Model<UserModelSchema> =
models[userCollectionName] || model(userCollectionName, UserSchema);
MongoUser.syncIndexes();
export const MongoUser = getMongoModel<UserModelSchema>(userCollectionName, UserSchema);

View File

@@ -1,4 +1,4 @@
import { connectionMongo, type Model } from '../../../common/mongo';
import { connectionMongo, getMongoModel, type Model } from '../../../common/mongo';
const { Schema, model, models } = connectionMongo;
import { TeamMemberSchema as TeamMemberType } from '@fastgpt/global/support/user/team/type.d';
import { userCollectionName } from '../../user/schema';
@@ -49,5 +49,7 @@ try {
console.log(error);
}
export const MongoTeamMember: Model<TeamMemberType> =
models[TeamMemberCollectionName] || model(TeamMemberCollectionName, TeamMemberSchema);
export const MongoTeamMember = getMongoModel<TeamMemberType>(
TeamMemberCollectionName,
TeamMemberSchema
);

View File

@@ -1,4 +1,4 @@
import { connectionMongo, type Model } from '../../../common/mongo';
import { connectionMongo, getMongoModel, type Model } from '../../../common/mongo';
const { Schema, model, models } = connectionMongo;
import { TeamSchema as TeamType } from '@fastgpt/global/support/user/team/type.d';
import { userCollectionName } from '../../user/schema';
@@ -61,5 +61,4 @@ try {
console.log(error);
}
export const MongoTeam: Model<TeamType> =
models[TeamCollectionName] || model(TeamCollectionName, TeamSchema);
export const MongoTeam = getMongoModel<TeamType>(TeamCollectionName, TeamSchema);

View File

@@ -1,4 +1,4 @@
import { connectionMongo, type Model } from '../../../common/mongo';
import { connectionMongo, getMongoModel, type Model } from '../../../common/mongo';
const { Schema, model, models } = connectionMongo;
import { TeamTagSchema as TeamTagsSchemaType } from '@fastgpt/global/support/user/team/type.d';
import {
@@ -32,5 +32,7 @@ try {
console.log(error);
}
export const MongoTeamTags: Model<TeamTagsSchemaType> =
models[TeamTagsCollectionName] || model(TeamTagsCollectionName, TeamTagSchema);
export const MongoTeamTags = getMongoModel<TeamTagsSchemaType>(
TeamTagsCollectionName,
TeamTagSchema
);

View File

@@ -3,7 +3,7 @@
1. type=standard: There will only be 1, and each team will have one
2. type=extraDatasetSize/extraPoints: Can buy multiple
*/
import { connectionMongo, type Model } from '../../../common/mongo';
import { connectionMongo, getMongoModel, type Model } from '../../../common/mongo';
const { Schema, model, models } = connectionMongo;
import { TeamCollectionName } from '@fastgpt/global/support/user/team/constant';
import {
@@ -93,5 +93,4 @@ try {
console.log(error);
}
export const MongoTeamSub: Model<TeamSubSchema> =
models[subCollectionName] || model(subCollectionName, SubSchema);
export const MongoTeamSub = getMongoModel<TeamSubSchema>(subCollectionName, SubSchema);

View File

@@ -1,4 +1,4 @@
import { connectionMongo, type Model } from '../../../common/mongo';
import { connectionMongo, getMongoModel, type Model } from '../../../common/mongo';
const { Schema, model, models } = connectionMongo;
import { UsageSchemaType } from '@fastgpt/global/support/wallet/usage/type';
import { UsageSourceMap } from '@fastgpt/global/support/wallet/usage/constants';
@@ -70,6 +70,4 @@ try {
console.log(error);
}
export const MongoUsage: Model<UsageSchemaType> =
models[UsageCollectionName] || model(UsageCollectionName, UsageSchema);
MongoUsage.syncIndexes();
export const MongoUsage = getMongoModel<UsageSchemaType>(UsageCollectionName, UsageSchema);

View File

@@ -25,4 +25,6 @@ declare global {
worker: Worker;
callbackMap: Record<string, (e: number) => void>;
}[];
var systemInited: boolean;
}

View File

@@ -8,6 +8,7 @@ import {
DragStart,
DropResult
} from 'react-beautiful-dnd';
export * from 'react-beautiful-dnd';
type Props<T = any> = {
onDragEndCb: (result: T[]) => void;
@@ -57,5 +58,3 @@ function DndDrag<T>({ children, renderClone, onDragEndCb, dataList }: Props<T>)
}
export default DndDrag;
export * from 'react-beautiful-dnd';

View File

@@ -6,15 +6,15 @@ const CloseIcon = (props: FlexProps) => {
return (
<Flex
cursor={'pointer'}
w={'22px'}
h={'22px'}
w={'1.5rem'}
h={'1.5rem'}
alignItems={'center'}
justifyContent={'center'}
borderRadius={'50%'}
_hover={{ bg: 'myGray.200' }}
{...props}
>
<MyIcon name={'common/closeLight'} w={'12px'} color={'myGray.500'} />
<MyIcon name={'common/closeLight'} w={'80%'} h={'80%'} color={'myGray.500'} />
</Flex>
);
};

View File

@@ -7,11 +7,11 @@ import {
ModalCloseButton,
ModalContentProps,
Box,
Image,
useMediaQuery
Image
} from '@chakra-ui/react';
import MyIcon from '../Icon';
import MyBox from '../MyBox';
import { useSystem } from '../../../hooks/useSystem';
export interface MyModalProps extends ModalContentProps {
iconSrc?: string;
@@ -34,7 +34,7 @@ const MyModal = ({
maxW = ['90vw', '600px'],
...props
}: MyModalProps) => {
const [isPc] = useMediaQuery('(min-width: 900px)');
const isPc = useSystem();
return (
<Modal

View File

@@ -1,5 +1,4 @@
import React, { useMemo } from 'react';
import MyPopover from './index';
import { useTranslation } from 'next-i18next';
import MyIcon from '../Icon';
import { useRequest2 } from '../../../hooks/useRequest';

View File

@@ -81,7 +81,6 @@ const MultipleSelect = <T = any,>({
borderRadius={'md'}
border={'base'}
userSelect={'none'}
minH={'40px'}
cursor={'pointer'}
_active={{
transform: 'none'

View File

@@ -1,4 +1,11 @@
import React, { useRef, forwardRef, useMemo } from 'react';
import React, {
useRef,
forwardRef,
useMemo,
useEffect,
useImperativeHandle,
ForwardedRef
} from 'react';
import {
Menu,
MenuList,
@@ -28,17 +35,21 @@ export type SelectProps<T = any> = ButtonProps & {
onchange?: (val: T) => void;
};
const MySelect = <T = any,>({
placeholder,
value,
width = '100%',
list = [],
onchange,
isLoading = false,
...props
}: SelectProps<T>) => {
const ref = useRef<HTMLButtonElement>(null);
const { Loading } = useLoading();
const MySelect = <T = any,>(
{
placeholder,
value,
width = '100%',
list = [],
onchange,
isLoading = false,
...props
}: SelectProps<T>,
ref: ForwardedRef<{
focus: () => void;
}>
) => {
const ButtonRef = useRef<HTMLButtonElement>(null);
const menuItemStyles: MenuItemProps = {
borderRadius: 'sm',
py: 2,
@@ -54,6 +65,12 @@ const MySelect = <T = any,>({
const { isOpen, onOpen, onClose } = useDisclosure();
const selectItem = useMemo(() => list.find((item) => item.value === value), [list, value]);
useImperativeHandle(ref, () => ({
focus() {
onOpen();
}
}));
return (
<Box
css={css({
@@ -72,7 +89,7 @@ const MySelect = <T = any,>({
>
<MenuButton
as={Button}
ref={ref}
ref={ButtonRef}
width={width}
px={3}
rightIcon={<ChevronDownIcon />}
@@ -98,7 +115,7 @@ const MySelect = <T = any,>({
<MenuList
className={props.className}
minW={(() => {
const w = ref.current?.clientWidth;
const w = ButtonRef.current?.clientWidth;
if (w) {
return `${w}px !important`;
}
@@ -152,4 +169,6 @@ const MySelect = <T = any,>({
);
};
export default MySelect;
export default forwardRef(MySelect) as <T>(
props: SelectProps<T> & { ref?: React.Ref<HTMLSelectElement> }
) => JSX.Element;

View File

@@ -1,14 +1,13 @@
import React from 'react';
import { Box, Tooltip, TooltipProps, css, useMediaQuery } from '@chakra-ui/react';
import { Box, Tooltip, TooltipProps } from '@chakra-ui/react';
import { useSystem } from '../../../hooks/useSystem';
interface Props extends TooltipProps {
forceShow?: boolean;
}
interface Props extends TooltipProps {}
const MyTooltip = ({ children, forceShow = false, shouldWrapChildren = true, ...props }: Props) => {
const [isPc] = useMediaQuery('(min-width: 900px)');
const MyTooltip = ({ children, shouldWrapChildren = true, ...props }: Props) => {
const { isPc } = useSystem();
return isPc || forceShow ? (
return (
<Tooltip
className="chakra-tooltip"
bg={'white'}
@@ -27,8 +26,6 @@ const MyTooltip = ({ children, forceShow = false, shouldWrapChildren = true, ...
>
{children}
</Tooltip>
) : (
<>{children}</>
);
};

View File

@@ -51,6 +51,7 @@ const LightRowTabs = <ValueType = string,>({
borderRadius={'sm'}
fontSize={sizeMap.fontSize}
overflowX={'auto'}
userSelect={'none'}
{...props}
>
{list.map((item) => (

View File

@@ -23,6 +23,8 @@ type Props = Omit<BoxProps, 'resize' | 'onChange'> & {
variables?: EditorVariablePickerType[];
defaultHeight?: number;
placeholder?: string;
isDisabled?: boolean;
isInvalid?: boolean;
};
const options = {
@@ -55,6 +57,8 @@ const JSONEditor = ({
variables = [],
placeholder,
defaultHeight = 100,
isDisabled = false,
isInvalid = false,
...props
}: Props) => {
const { toast } = useToast();
@@ -209,9 +213,9 @@ const JSONEditor = ({
return (
<Box
borderWidth={'1px'}
borderWidth={isInvalid ? '2px' : '1px'}
borderRadius={'md'}
borderColor={'myGray.200'}
borderColor={isInvalid ? 'red.500' : 'myGray.200'}
py={2}
height={height}
position={'relative'}

View File

@@ -26,6 +26,7 @@
"Export Configs": "Export Configs",
"Feedback Count": "User Feedback",
"Go to chat": "To chat",
"Go to run": "Run",
"Import Configs": "Import Configs",
"Import Configs Failed": "Failed to import configs, please ensure configs are valid!",
"Input Field Settings": "Input Field Settings",
@@ -39,8 +40,12 @@
"My Apps": "My Apps",
"Output Field Settings": "Output Field Settings",
"Paste Config": "Paste Config",
"Plugin dispatch": "Plugins",
"Plugin dispatch tip": "It is up to the model to decide which plug-ins to add additional capabilities to. If the plug-in is selected, the knowledge base call is also treated as a special plug-in.",
"Publish channel": "Publish channel",
"Publish success": "Publish success",
"Run": "Run",
"Search app": "Search app",
"Setting app": "Settings",
"Setting plugin": "Setting plugin",
"To Chat": "Go to Chat",
@@ -57,7 +62,7 @@
},
"module": {
"Combine Modules": "Combine Modules",
"Confirm Sync": "Using the latest template will overwrite the existing one and may result in the loss of some previous configuration information. Please confirm.",
"Confirm Sync": "The template will be updated to the latest template configuration. Fields that do not exist in the template will be deleted (including all custom fields). You are advised to make a copy of the node and then update the original node version.",
"Custom Title Tip": "This title will be displayed during the conversation",
"My Modules": "My Modules",
"No Modules": "No plugins yet~",

View File

@@ -107,8 +107,10 @@
"Rename Success": "Rename Success",
"Request Error": "Request Error",
"Require Input": "Required Input",
"Restart": "Restart",
"Role": "Role",
"Root folder": "Root folder",
"Run": "Run",
"Save": "Save",
"Save Failed": "Save Failed",
"Save Success": "Save Success",
@@ -595,8 +597,7 @@
"success": "Start syncing"
}
},
"training": {
}
"training": {}
},
"data": {
"Auxiliary Data": "Auxiliary data",
@@ -1068,7 +1069,7 @@
"Debug": "Debug",
"Debug Node": "Debug mode",
"Failed": "Execution failed",
"Not intro": "This node has no introduction~\\",
"Not intro": "This node has no introduction~",
"Run from here": "Run from here",
"Run result": "Run result",
"Running": "Running",
@@ -1371,7 +1372,7 @@
"Terms": "Terms of service",
"Username": "Username",
"Wechat": "Login with Wechat",
"Wx qr login": "Wechat QR code login"
"wx_qr_login": "Wechat QR code login"
},
"team": {
"Dataset usage": "Knowledge base capacity",
@@ -1636,4 +1637,4 @@
}
}
}
}
}

View File

@@ -16,7 +16,7 @@
"Tool input": "Tool",
"code": {
"Reset template": "Reset template",
"Reset template confirm": "Are you sure to restore the code template? Be careful to save the current code."
"Reset template confirm": "Are you sure to restore the code template? All input and output to template values will be reset, please be careful to save the current code."
},
"ifelse": {
"Input value": "Input",

View File

@@ -25,6 +25,7 @@
"Export Configs": "导出配置",
"Feedback Count": "用户反馈",
"Go to chat": "去对话",
"Go to run": "去运行",
"Import Configs": "导入配置",
"Import Configs Failed": "导入配置失败,请确保配置正常!",
"Input Field Settings": "输入字段编辑",
@@ -38,8 +39,12 @@
"My Apps": "我的应用",
"Output Field Settings": "输出字段编辑",
"Paste Config": "粘贴配置",
"Plugin dispatch": "插件调用",
"Plugin dispatch tip": "给模型附加额外的能力,具体调用哪些插件,将由模型自主决定。\n若选择了插件知识库调用将自动作为一个特殊的插件。",
"Publish channel": "发布渠道",
"Publish success": "发布成功",
"Run": "运行",
"Search app": "搜索应用",
"Setting app": "应用配置",
"Setting plugin": "插件配置",
"To Chat": "前去对话",
@@ -56,7 +61,7 @@
},
"module": {
"Combine Modules": "组合模块",
"Confirm Sync": "将会使用最新模板进行覆盖,可能会丢失一些旧的配置信息,请确认",
"Confirm Sync": "将会更新至最新模板配置,不存在模板中的字段将会被删除(包括所有自定义字段),建议您先复制一份节点,再更新原来节点的版本。",
"Custom Title Tip": "该标题名字会展示在对话过程中",
"My Modules": "",
"No Modules": "没找到插件",

View File

@@ -108,8 +108,10 @@
"Rename Success": "重命名成功",
"Request Error": "请求异常",
"Require Input": "必填",
"Restart": "重新开始",
"Role": "权限",
"Root folder": "根目录",
"Run": "运行",
"Save": "保存",
"Save Failed": "保存失败",
"Save Success": "保存成功",
@@ -598,8 +600,7 @@
"success": "开始同步"
}
},
"training": {
}
"training": {}
},
"data": {
"Auxiliary Data": "辅助数据",
@@ -1077,7 +1078,7 @@
"Debug": "调试",
"Debug Node": "Debug 模式",
"Failed": "运行失败",
"Not intro": "这个节点没有介绍~\\",
"Not intro": "这个节点没有介绍~",
"Run from here": "从这里开始运行",
"Run result": "",
"Running": "运行中",
@@ -1380,7 +1381,7 @@
"Terms": "服务协议",
"Username": "用户名",
"Wechat": "微信登录",
"Wx qr login": "微信扫码登录"
"wx_qr_login": "微信扫码登录"
},
"team": {
"Dataset usage": "知识库容量",
@@ -1645,4 +1646,4 @@
}
}
}
}
}

View File

@@ -16,7 +16,7 @@
"Tool input": "工具参数",
"code": {
"Reset template": "还原模板",
"Reset template confirm": "确认还原代码模板?请注意保存当前代码。"
"Reset template confirm": "确认还原代码模板?将会重置所有输入和输出至模板值,请注意保存当前代码。"
},
"ifelse": {
"Input value": "输入值",

View File

@@ -21,25 +21,25 @@
"ahooks": "^3.7.11",
"date-fns": "2.30.0",
"dayjs": "^1.11.7",
"i18next": "23.10.0",
"lexical": "0.12.6",
"lodash": "^4.17.21",
"next-i18next": "15.2.0",
"i18next": "23.11.5",
"next-i18next": "15.3.0",
"react-i18next": "14.1.2",
"papaparse": "^5.4.1",
"react": "18.3.1",
"react-beautiful-dnd": "^13.1.1",
"react-day-picker": "^8.7.1",
"react-dom": "18.3.1",
"react-hook-form": "7.43.1",
"react-i18next": "13.5.0",
"react-photo-view": "^1.2.6",
"use-context-selector": "^1.4.4"
},
"devDependencies": {
"@types/lodash": "^4.14.191",
"@types/papaparse": "^5.3.7",
"@types/react": "18.3.0",
"@types/react-beautiful-dnd": "^13.1.8",
"@types/react": "18.3.1",
"@types/react-beautiful-dnd": "^13.1.1",
"@types/react-dom": "18.3.0"
}
}
}

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