Compare commits
11 Commits
v4.8.6-alp
...
v4.8.7-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b5c98a4f63 | ||
|
|
090c880860 | ||
|
|
dd2a9bdee5 | ||
|
|
8d60ef505f | ||
|
|
b14514c105 | ||
|
|
11ffaaf2c5 | ||
|
|
e2ae571d15 | ||
|
|
f548e24e7d | ||
|
|
5605f1a892 | ||
|
|
88d10451c9 | ||
|
|
8a7bd689ae |
1
.npmrc
1
.npmrc
@@ -1,2 +1,3 @@
|
||||
public-hoist-pattern[]=*tiktoken*
|
||||
public-hoist-pattern[]=*@zilliz/milvus2-sdk-node*
|
||||
registry=https://registry.npmjs.org/
|
||||
5
.vscode/extensions.json
vendored
5
.vscode/extensions.json
vendored
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"inlang.vs-code-extension"
|
||||
]
|
||||
}
|
||||
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@@ -2,7 +2,7 @@
|
||||
"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",
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 >}}
|
||||
|
||||
@@ -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. 修复 - 文档描述
|
||||
|
||||
27
docSite/content/zh-cn/docs/development/upgrading/487.md
Normal file
27
docSite/content/zh-cn/docs/development/upgrading/487.md
Normal file
@@ -0,0 +1,27 @@
|
||||
---
|
||||
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. 修复 - 简易模式无法变更全局变量
|
||||
@@ -78,7 +78,7 @@ weight: 404
|
||||
},
|
||||
{
|
||||
"nodeId": "u6IAOEssxoZT",
|
||||
"name": "工具调用(实验)",
|
||||
"name": "工具调用",
|
||||
"intro": "通过AI模型自动选择一个或多个功能块进行调用,也可以对插件进行调用。",
|
||||
"avatar": "/imgs/workflow/tool.svg",
|
||||
"flowNodeType": "tools",
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
1
packages/global/common/file/image/type.d.ts
vendored
1
packages/global/common/file/image/type.d.ts
vendored
@@ -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
|
||||
};
|
||||
};
|
||||
|
||||
@@ -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}}"""`
|
||||
}
|
||||
|
||||
10
packages/global/core/app/plugin/utils.ts
Normal file
10
packages/global/core/app/plugin/utils.ts
Normal 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);
|
||||
};
|
||||
1
packages/global/core/app/type.d.ts
vendored
1
packages/global/core/app/type.d.ts
vendored
@@ -21,6 +21,7 @@ export type AppSchema = {
|
||||
name: string;
|
||||
avatar: string;
|
||||
intro: string;
|
||||
|
||||
updateTime: Date;
|
||||
|
||||
modules: StoreNodeItemType[];
|
||||
|
||||
@@ -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 &&
|
||||
|
||||
3
packages/global/core/chat/type.d.ts
vendored
3
packages/global/core/chat/type.d.ts
vendored
@@ -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 --------- */
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -142,6 +142,9 @@ export type DispatchNodeResponseType = {
|
||||
|
||||
// code
|
||||
codeLog?: string;
|
||||
|
||||
// plugin
|
||||
pluginOutput?: Record<string, any>;
|
||||
};
|
||||
|
||||
export type DispatchNodeResultType<T> = {
|
||||
|
||||
@@ -123,6 +123,7 @@ export const checkNodeRunStatus = ({
|
||||
(item) => item.target === node.nodeId
|
||||
);
|
||||
|
||||
// Entry
|
||||
if (workflowEdges.length === 0) {
|
||||
return 'run';
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"extends":"../../tsconfig.json",
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": "."
|
||||
},
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -88,7 +88,7 @@
|
||||
"x": 1050.9890727421412,
|
||||
"y": -415.2085119990912
|
||||
},
|
||||
"version": "486",
|
||||
"version": "481",
|
||||
"inputs": [
|
||||
{
|
||||
"key": "system_addInputParam",
|
||||
|
||||
@@ -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
|
||||
);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,65 @@ 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);
|
||||
try {
|
||||
model.syncIndexes();
|
||||
} catch (error) {
|
||||
addLog.error('Create index error', error);
|
||||
}
|
||||
|
||||
return model;
|
||||
};
|
||||
|
||||
export const ReadPreference = connectionMongo.mongo.ReadPreference;
|
||||
|
||||
@@ -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
|
||||
);
|
||||
|
||||
@@ -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);
|
||||
|
||||
10
packages/service/common/system/log/constant.ts
Normal file
10
packages/service/common/system/log/constant.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
export enum LogLevelEnum {
|
||||
debug = 0,
|
||||
info = 1,
|
||||
warn = 2,
|
||||
error = 3
|
||||
}
|
||||
|
||||
export enum LogSignEnum {
|
||||
slowOperation = 'slowOperation'
|
||||
}
|
||||
29
packages/service/common/system/log/schema.ts
Normal file
29
packages/service/common/system/log/schema.ts
Normal 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);
|
||||
};
|
||||
9
packages/service/common/system/log/type.d.ts
vendored
Normal file
9
packages/service/common/system/log/type.d.ts
vendored
Normal 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>;
|
||||
};
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
);
|
||||
|
||||
@@ -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';
|
||||
@@ -90,7 +89,7 @@ try {
|
||||
get chat logs;
|
||||
close custom feedback;
|
||||
*/
|
||||
ChatItemSchema.index({ appId: 1, chatId: 1, dataId: 1 }, { background: true });
|
||||
ChatItemSchema.index({ appId: 1, chatId: 1, dataId: 1 }, { background: true, unique: true });
|
||||
// admin charts
|
||||
ChatItemSchema.index({ time: -1, obj: 1 }, { background: true });
|
||||
// timer, clear history
|
||||
@@ -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);
|
||||
|
||||
@@ -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';
|
||||
@@ -85,7 +85,7 @@ try {
|
||||
// get user history
|
||||
ChatSchema.index({ tmbId: 1, appId: 1, top: -1, updateTime: -1 }, { background: true });
|
||||
// delete by appid; clear history; init chat; update chat; auth chat; get chat;
|
||||
ChatSchema.index({ appId: 1, chatId: 1 }, { background: true });
|
||||
ChatSchema.index({ appId: 1, chatId: 1 }, { background: true, unique: true });
|
||||
|
||||
// get chat logs;
|
||||
ChatSchema.index({ teamId: 1, appId: 1, updateTime: -1 }, { background: true });
|
||||
@@ -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);
|
||||
|
||||
@@ -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[] => {
|
||||
|
||||
@@ -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
|
||||
);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
);
|
||||
|
||||
@@ -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
|
||||
};
|
||||
};
|
||||
|
||||
@@ -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
|
||||
);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 的数据
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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
|
||||
);
|
||||
|
||||
@@ -198,7 +198,7 @@ ${description ? `- ${description}` : ''}
|
||||
required: []
|
||||
}
|
||||
};
|
||||
console.log(properties);
|
||||
|
||||
return {
|
||||
filterMessages,
|
||||
agentFunction
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -81,7 +81,6 @@ const MultipleSelect = <T = any,>({
|
||||
borderRadius={'md'}
|
||||
border={'base'}
|
||||
userSelect={'none'}
|
||||
minH={'40px'}
|
||||
cursor={'pointer'}
|
||||
_active={{
|
||||
transform: 'none'
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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}</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ const LightRowTabs = <ValueType = string,>({
|
||||
borderRadius={'sm'}
|
||||
fontSize={sizeMap.fontSize}
|
||||
overflowX={'auto'}
|
||||
userSelect={'none'}
|
||||
{...props}
|
||||
>
|
||||
{list.map((item) => (
|
||||
|
||||
@@ -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'}
|
||||
|
||||
@@ -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~",
|
||||
|
||||
@@ -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",
|
||||
@@ -1068,7 +1070,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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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": "没找到插件",
|
||||
|
||||
@@ -108,8 +108,10 @@
|
||||
"Rename Success": "重命名成功",
|
||||
"Request Error": "请求异常",
|
||||
"Require Input": "必填",
|
||||
"Restart": "重新开始",
|
||||
"Role": "权限",
|
||||
"Root folder": "根目录",
|
||||
"Run": "运行",
|
||||
"Save": "保存",
|
||||
"Save Failed": "保存失败",
|
||||
"Save Success": "保存成功",
|
||||
@@ -1077,7 +1079,7 @@
|
||||
"Debug": "调试",
|
||||
"Debug Node": "Debug 模式",
|
||||
"Failed": "运行失败",
|
||||
"Not intro": "这个节点没有介绍~\\",
|
||||
"Not intro": "这个节点没有介绍~",
|
||||
"Run from here": "从这里开始运行",
|
||||
"Run result": "",
|
||||
"Running": "运行中",
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
"Tool input": "工具参数",
|
||||
"code": {
|
||||
"Reset template": "还原模板",
|
||||
"Reset template confirm": "确认还原代码模板?请注意保存当前代码。"
|
||||
"Reset template confirm": "确认还原代码模板?将会重置所有输入和输出至模板值,请注意保存当前代码。"
|
||||
},
|
||||
"ifelse": {
|
||||
"Input value": "输入值",
|
||||
|
||||
@@ -38,8 +38,8 @@
|
||||
"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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -314,7 +314,7 @@ const Input: ComponentStyleConfig = {
|
||||
}),
|
||||
md: defineStyle({
|
||||
field: {
|
||||
h: '40px',
|
||||
h: '34px',
|
||||
borderRadius: 'md'
|
||||
}
|
||||
})
|
||||
|
||||
4710
pnpm-lock.yaml
generated
4710
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -22,7 +22,7 @@ MONGODB_URI=mongodb://username:password@0.0.0.0:27017/fastgpt?authSource=admin
|
||||
# 向量库优先级: pg > milvus
|
||||
# PG 向量库连接参数
|
||||
PG_URL=postgresql://username:password@host:port/postgres
|
||||
# mivlus 向量库连接参数
|
||||
# milvus 向量库连接参数
|
||||
MILVUS_ADDRESS=https://in03-78bd7f60e6e2a7c.api.gcp-us-west1.zillizcloud.com
|
||||
MILVUS_TOKEN=133964348b00b4b4e4b51bef680a61350950385c8c64a3ec16b1ab92d3c67dcc4e0370fb9dd15791bcd6dadaf765e98a98735d0d
|
||||
|
||||
@@ -33,6 +33,6 @@ PRO_URL=
|
||||
# 首页路径
|
||||
HOME_URL=/
|
||||
# 日志等级: debug, info, warn, error
|
||||
LOG_LEVEL=info
|
||||
# Loki Log Path
|
||||
# LOKI_LOG_URL=
|
||||
LOG_LEVEL=debug
|
||||
STORE_LOG_LEVEL=warn
|
||||
# Loki Log Path
|
||||
@@ -1,11 +1,11 @@
|
||||
# --------- install dependence -----------
|
||||
FROM node:18.17-alpine AS mainDeps
|
||||
FROM node:20.14.0-alpine AS mainDeps
|
||||
WORKDIR /app
|
||||
|
||||
ARG proxy
|
||||
|
||||
RUN [ -z "$proxy" ] || sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories
|
||||
RUN apk add --no-cache libc6-compat && npm install -g pnpm@8.6.0
|
||||
RUN apk add --no-cache libc6-compat && npm install -g pnpm@9.4.0
|
||||
# if proxy exists, set proxy
|
||||
RUN [ -z "$proxy" ] || pnpm config set registry https://registry.npmmirror.com
|
||||
|
||||
@@ -19,7 +19,7 @@ RUN [ -f pnpm-lock.yaml ] || (echo "Lockfile not found." && exit 1)
|
||||
RUN pnpm i
|
||||
|
||||
# --------- builder -----------
|
||||
FROM node:18.17-alpine AS builder
|
||||
FROM node:20.14.0-alpine AS builder
|
||||
WORKDIR /app
|
||||
|
||||
ARG proxy
|
||||
@@ -33,13 +33,13 @@ COPY --from=mainDeps /app/projects/app/node_modules ./projects/app/node_modules
|
||||
|
||||
RUN [ -z "$proxy" ] || sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories
|
||||
|
||||
RUN apk add --no-cache libc6-compat && npm install -g pnpm@8.6.0
|
||||
RUN apk add --no-cache libc6-compat && npm install -g pnpm@9.4.0
|
||||
|
||||
ENV NODE_OPTIONS="--max-old-space-size=4096"
|
||||
RUN pnpm --filter=app build
|
||||
|
||||
# --------- runner -----------
|
||||
FROM node:18.17-alpine AS runner
|
||||
FROM node:20.14.0-alpine AS runner
|
||||
WORKDIR /app
|
||||
|
||||
ARG proxy
|
||||
|
||||
@@ -72,13 +72,13 @@
|
||||
"@types/jsonwebtoken": "^9.0.3",
|
||||
"@types/lodash": "^4.14.191",
|
||||
"@types/node": "^20.14.2",
|
||||
"@types/react": "18.3.0",
|
||||
"@types/react": "18.3.1",
|
||||
"@types/react-dom": "18.3.0",
|
||||
"@types/react-syntax-highlighter": "^15.5.6",
|
||||
"@types/request-ip": "^0.0.37",
|
||||
"eslint": "8.56.0",
|
||||
"eslint-config-next": "14.2.3",
|
||||
"nextjs-node-loader": "^1.1.5",
|
||||
"typescript": "4.9.5"
|
||||
"typescript": "^5.1.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,262 +0,0 @@
|
||||
import {
|
||||
Box,
|
||||
BoxProps,
|
||||
Card,
|
||||
Flex,
|
||||
useTheme,
|
||||
Accordion,
|
||||
AccordionItem,
|
||||
AccordionButton,
|
||||
AccordionPanel,
|
||||
AccordionIcon,
|
||||
Image
|
||||
} from '@chakra-ui/react';
|
||||
import React, { useMemo } from 'react';
|
||||
import ChatController, { type ChatControllerProps } from './ChatController';
|
||||
import ChatAvatar from './ChatAvatar';
|
||||
import { MessageCardStyle } from '../constants';
|
||||
import { formatChatValue2InputType } from '../utils';
|
||||
import Markdown, { CodeClassName } from '@/components/Markdown';
|
||||
import styles from '../index.module.scss';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import {
|
||||
ChatItemValueTypeEnum,
|
||||
ChatRoleEnum,
|
||||
ChatStatusEnum
|
||||
} from '@fastgpt/global/core/chat/constants';
|
||||
import FilesBlock from './FilesBox';
|
||||
import { ChatBoxContext } from '../Provider';
|
||||
import Avatar from '@/components/Avatar';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
|
||||
const colorMap = {
|
||||
[ChatStatusEnum.loading]: {
|
||||
bg: 'myGray.100',
|
||||
color: 'myGray.600'
|
||||
},
|
||||
[ChatStatusEnum.running]: {
|
||||
bg: 'green.50',
|
||||
color: 'green.700'
|
||||
},
|
||||
[ChatStatusEnum.finish]: {
|
||||
bg: 'green.50',
|
||||
color: 'green.700'
|
||||
}
|
||||
};
|
||||
|
||||
const ChatItem = ({
|
||||
type,
|
||||
avatar,
|
||||
statusBoxData,
|
||||
children,
|
||||
isLastChild,
|
||||
questionGuides = [],
|
||||
...chatControllerProps
|
||||
}: {
|
||||
type: ChatRoleEnum.Human | ChatRoleEnum.AI;
|
||||
avatar?: string;
|
||||
statusBoxData?: {
|
||||
status: `${ChatStatusEnum}`;
|
||||
name: string;
|
||||
};
|
||||
questionGuides?: string[];
|
||||
children?: React.ReactNode;
|
||||
} & ChatControllerProps) => {
|
||||
const styleMap: BoxProps =
|
||||
type === ChatRoleEnum.Human
|
||||
? {
|
||||
order: 0,
|
||||
borderRadius: '8px 0 8px 8px',
|
||||
justifyContent: 'flex-end',
|
||||
textAlign: 'right',
|
||||
bg: 'primary.100'
|
||||
}
|
||||
: {
|
||||
order: 1,
|
||||
borderRadius: '0 8px 8px 8px',
|
||||
justifyContent: 'flex-start',
|
||||
textAlign: 'left',
|
||||
bg: 'myGray.50'
|
||||
};
|
||||
|
||||
const isChatting = useContextSelector(ChatBoxContext, (v) => v.isChatting);
|
||||
const { chat } = chatControllerProps;
|
||||
|
||||
const ContentCard = useMemo(() => {
|
||||
if (type === 'Human') {
|
||||
const { text, files = [] } = formatChatValue2InputType(chat.value);
|
||||
|
||||
return (
|
||||
<>
|
||||
{files.length > 0 && <FilesBlock files={files} />}
|
||||
<Markdown source={text} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
/* AI */
|
||||
return (
|
||||
<Flex flexDirection={'column'} key={chat.dataId} gap={2}>
|
||||
{chat.value.map((value, i) => {
|
||||
const key = `${chat.dataId}-ai-${i}`;
|
||||
|
||||
if (value.text) {
|
||||
let source = (value.text?.content || '').trim();
|
||||
|
||||
if (!source && chat.value.length > 1) return null;
|
||||
|
||||
if (
|
||||
isLastChild &&
|
||||
!isChatting &&
|
||||
questionGuides.length > 0 &&
|
||||
i === chat.value.length - 1
|
||||
) {
|
||||
source = `${source}
|
||||
\`\`\`${CodeClassName.questionGuide}
|
||||
${JSON.stringify(questionGuides)}`;
|
||||
}
|
||||
|
||||
return (
|
||||
<Markdown
|
||||
key={key}
|
||||
source={source}
|
||||
showAnimation={isLastChild && isChatting && i === chat.value.length - 1}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (value.type === ChatItemValueTypeEnum.tool && value.tools) {
|
||||
return (
|
||||
<Box key={key}>
|
||||
{value.tools.map((tool) => {
|
||||
const toolParams = (() => {
|
||||
try {
|
||||
return JSON.stringify(JSON.parse(tool.params), null, 2);
|
||||
} catch (error) {
|
||||
return tool.params;
|
||||
}
|
||||
})();
|
||||
const toolResponse = (() => {
|
||||
try {
|
||||
return JSON.stringify(JSON.parse(tool.response), null, 2);
|
||||
} catch (error) {
|
||||
return tool.response;
|
||||
}
|
||||
})();
|
||||
|
||||
return (
|
||||
<Box key={tool.id}>
|
||||
<Accordion allowToggle>
|
||||
<AccordionItem borderTop={'none'} borderBottom={'none'}>
|
||||
<AccordionButton
|
||||
w={'auto'}
|
||||
bg={'white'}
|
||||
borderRadius={'md'}
|
||||
borderWidth={'1px'}
|
||||
borderColor={'myGray.200'}
|
||||
boxShadow={'1'}
|
||||
_hover={{
|
||||
bg: 'auto'
|
||||
}}
|
||||
>
|
||||
<Avatar src={tool.toolAvatar} w={'1rem'} mr={2} />
|
||||
<Box mr={1} fontSize={'sm'}>
|
||||
{tool.toolName}
|
||||
</Box>
|
||||
{isChatting && !tool.response && (
|
||||
<MyIcon name={'common/loading'} w={'14px'} />
|
||||
)}
|
||||
<AccordionIcon color={'myGray.600'} ml={5} />
|
||||
</AccordionButton>
|
||||
<AccordionPanel
|
||||
py={0}
|
||||
px={0}
|
||||
mt={0}
|
||||
borderRadius={'md'}
|
||||
overflow={'hidden'}
|
||||
maxH={'500px'}
|
||||
overflowY={'auto'}
|
||||
>
|
||||
{toolParams && toolParams !== '{}' && (
|
||||
<Markdown
|
||||
source={`~~~json#Input
|
||||
${toolParams}`}
|
||||
/>
|
||||
)}
|
||||
{toolResponse && (
|
||||
<Markdown
|
||||
source={`~~~json#Response
|
||||
${toolResponse}`}
|
||||
/>
|
||||
)}
|
||||
</AccordionPanel>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
})}
|
||||
</Flex>
|
||||
);
|
||||
}, [chat.dataId, chat.value, isChatting, isLastChild, questionGuides, type]);
|
||||
|
||||
const chatStatusMap = useMemo(() => {
|
||||
if (!statusBoxData?.status) return;
|
||||
return colorMap[statusBoxData.status];
|
||||
}, [statusBoxData?.status]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* control icon */}
|
||||
<Flex w={'100%'} alignItems={'center'} gap={2} justifyContent={styleMap.justifyContent}>
|
||||
{isChatting && type === ChatRoleEnum.AI && isLastChild ? null : (
|
||||
<Box order={styleMap.order} ml={styleMap.ml}>
|
||||
<ChatController {...chatControllerProps} isLastChild={isLastChild} />
|
||||
</Box>
|
||||
)}
|
||||
<ChatAvatar src={avatar} type={type} />
|
||||
|
||||
{!!chatStatusMap && statusBoxData && isLastChild && (
|
||||
<Flex
|
||||
alignItems={'center'}
|
||||
px={3}
|
||||
py={'1.5px'}
|
||||
borderRadius="md"
|
||||
bg={chatStatusMap.bg}
|
||||
fontSize={'sm'}
|
||||
>
|
||||
<Box
|
||||
className={styles.statusAnimation}
|
||||
bg={chatStatusMap.color}
|
||||
w="8px"
|
||||
h="8px"
|
||||
borderRadius={'50%'}
|
||||
mt={'1px'}
|
||||
/>
|
||||
<Box ml={2} color={'myGray.600'}>
|
||||
{statusBoxData.name}
|
||||
</Box>
|
||||
</Flex>
|
||||
)}
|
||||
</Flex>
|
||||
{/* content */}
|
||||
<Box mt={['6px', 2]} textAlign={styleMap.textAlign}>
|
||||
<Card
|
||||
className="markdown"
|
||||
{...MessageCardStyle}
|
||||
bg={styleMap.bg}
|
||||
borderRadius={styleMap.borderRadius}
|
||||
textAlign={'left'}
|
||||
>
|
||||
{ContentCard}
|
||||
{children}
|
||||
</Card>
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(ChatItem);
|
||||
@@ -1,325 +0,0 @@
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { Box, useTheme, Flex, Image, BoxProps } from '@chakra-ui/react';
|
||||
import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/type.d';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { moduleTemplatesFlat } from '@fastgpt/global/core/workflow/template/constants';
|
||||
|
||||
import LightRowTabs from '@fastgpt/web/components/common/Tabs/LightRowTabs';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import Markdown from '../../Markdown';
|
||||
import { QuoteList } from './QuoteModal';
|
||||
import { DatasetSearchModeMap } from '@fastgpt/global/core/dataset/constants';
|
||||
import { formatNumber } from '@fastgpt/global/common/math/tools';
|
||||
import { useI18n } from '@/web/context/I18n';
|
||||
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
||||
|
||||
function RowRender({
|
||||
children,
|
||||
mb,
|
||||
label,
|
||||
...props
|
||||
}: { children: React.ReactNode; label: string } & BoxProps) {
|
||||
return (
|
||||
<Box mb={3}>
|
||||
<Box fontSize={'sm'} mb={mb} flex={'0 0 90px'}>
|
||||
{label}:
|
||||
</Box>
|
||||
<Box borderRadius={'sm'} fontSize={['xs', 'sm']} bg={'myGray.50'} {...props}>
|
||||
{children}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
function Row({
|
||||
label,
|
||||
value,
|
||||
rawDom
|
||||
}: {
|
||||
label: string;
|
||||
value?: string | number | boolean | object;
|
||||
rawDom?: React.ReactNode;
|
||||
}) {
|
||||
const theme = useTheme();
|
||||
const val = value || rawDom;
|
||||
const isObject = typeof value === 'object';
|
||||
|
||||
const formatValue = useMemo(() => {
|
||||
if (isObject) {
|
||||
return `~~~json\n${JSON.stringify(value, null, 2)}`;
|
||||
}
|
||||
return `${value}`;
|
||||
}, [isObject, value]);
|
||||
|
||||
if (rawDom) {
|
||||
return (
|
||||
<RowRender label={label} mb={1}>
|
||||
{rawDom}
|
||||
</RowRender>
|
||||
);
|
||||
}
|
||||
|
||||
if (val === undefined || val === '' || val === 'undefined') return null;
|
||||
|
||||
return (
|
||||
<RowRender
|
||||
label={label}
|
||||
mb={isObject ? 0 : 1}
|
||||
{...(isObject
|
||||
? { transform: 'translateY(-3px)' }
|
||||
: value
|
||||
? { px: 3, py: 2, border: theme.borders.base }
|
||||
: {})}
|
||||
>
|
||||
<Markdown source={formatValue} />
|
||||
</RowRender>
|
||||
);
|
||||
}
|
||||
|
||||
const WholeResponseModal = ({
|
||||
response,
|
||||
showDetail,
|
||||
onClose
|
||||
}: {
|
||||
response: ChatHistoryItemResType[];
|
||||
showDetail: boolean;
|
||||
onClose: () => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<MyModal
|
||||
isCentered
|
||||
isOpen={true}
|
||||
onClose={onClose}
|
||||
h={['90vh', '80vh']}
|
||||
minW={['90vw', '600px']}
|
||||
iconSrc="/imgs/modal/wholeRecord.svg"
|
||||
title={
|
||||
<Flex alignItems={'center'}>
|
||||
{t('core.chat.response.Complete Response')}
|
||||
<QuestionTip ml={2} label={'从左往右,为各个模块的响应顺序'}></QuestionTip>
|
||||
</Flex>
|
||||
}
|
||||
>
|
||||
<Flex h={'100%'} flexDirection={'column'}>
|
||||
<ResponseBox response={response} showDetail={showDetail} />
|
||||
</Flex>
|
||||
</MyModal>
|
||||
);
|
||||
};
|
||||
|
||||
export default WholeResponseModal;
|
||||
|
||||
export const ResponseBox = React.memo(function ResponseBox({
|
||||
response,
|
||||
showDetail,
|
||||
hideTabs = false
|
||||
}: {
|
||||
response: ChatHistoryItemResType[];
|
||||
showDetail: boolean;
|
||||
hideTabs?: boolean;
|
||||
}) {
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
const { workflowT } = useI18n();
|
||||
|
||||
const list = useMemo(
|
||||
() =>
|
||||
response.map((item, i) => ({
|
||||
label: (
|
||||
<Flex alignItems={'center'} justifyContent={'center'} px={2}>
|
||||
<Image
|
||||
mr={2}
|
||||
src={
|
||||
item.moduleLogo ||
|
||||
moduleTemplatesFlat.find((template) => item.moduleType === template.flowNodeType)
|
||||
?.avatar
|
||||
}
|
||||
alt={''}
|
||||
w={['14px', '16px']}
|
||||
/>
|
||||
{t(item.moduleName)}
|
||||
</Flex>
|
||||
),
|
||||
value: `${i}`
|
||||
})),
|
||||
[response, t]
|
||||
);
|
||||
|
||||
const [currentTab, setCurrentTab] = useState(`0`);
|
||||
|
||||
const activeModule = useMemo(() => response[Number(currentTab)], [currentTab, response]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{!hideTabs && (
|
||||
<Box>
|
||||
<LightRowTabs list={list} value={currentTab} onChange={setCurrentTab} />
|
||||
</Box>
|
||||
)}
|
||||
<Box py={2} px={4} flex={'1 0 0'} overflow={'auto'}>
|
||||
<>
|
||||
<Row label={t('core.chat.response.module name')} value={t(activeModule.moduleName)} />
|
||||
{activeModule?.totalPoints !== undefined && (
|
||||
<Row
|
||||
label={t('support.wallet.usage.Total points')}
|
||||
value={formatNumber(activeModule.totalPoints)}
|
||||
/>
|
||||
)}
|
||||
<Row
|
||||
label={t('core.chat.response.module time')}
|
||||
value={`${activeModule?.runningTime || 0}s`}
|
||||
/>
|
||||
<Row label={t('core.chat.response.module model')} value={activeModule?.model} />
|
||||
<Row label={t('core.chat.response.module tokens')} value={`${activeModule?.tokens}`} />
|
||||
<Row
|
||||
label={t('core.chat.response.Tool call tokens')}
|
||||
value={`${activeModule?.toolCallTokens}`}
|
||||
/>
|
||||
|
||||
<Row label={t('core.chat.response.module query')} value={activeModule?.query} />
|
||||
<Row
|
||||
label={t('core.chat.response.context total length')}
|
||||
value={activeModule?.contextTotalLen}
|
||||
/>
|
||||
<Row label={workflowT('response.Error')} value={activeModule?.error} />
|
||||
</>
|
||||
|
||||
{/* ai chat */}
|
||||
<>
|
||||
<Row
|
||||
label={t('core.chat.response.module temperature')}
|
||||
value={activeModule?.temperature}
|
||||
/>
|
||||
<Row label={t('core.chat.response.module maxToken')} value={activeModule?.maxToken} />
|
||||
<Row
|
||||
label={t('core.chat.response.module historyPreview')}
|
||||
rawDom={
|
||||
activeModule.historyPreview ? (
|
||||
<Box px={3} py={2} border={theme.borders.base} borderRadius={'md'}>
|
||||
{activeModule.historyPreview?.map((item, i) => (
|
||||
<Box
|
||||
key={i}
|
||||
_notLast={{
|
||||
borderBottom: '1px solid',
|
||||
borderBottomColor: 'myWhite.700',
|
||||
mb: 2
|
||||
}}
|
||||
pb={2}
|
||||
>
|
||||
<Box fontWeight={'bold'}>{item.obj}</Box>
|
||||
<Box whiteSpace={'pre-wrap'}>{item.value}</Box>
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
) : (
|
||||
''
|
||||
)
|
||||
}
|
||||
/>
|
||||
</>
|
||||
|
||||
{/* dataset search */}
|
||||
<>
|
||||
{activeModule?.searchMode && (
|
||||
<Row
|
||||
label={t('core.dataset.search.search mode')}
|
||||
// @ts-ignore
|
||||
value={t(DatasetSearchModeMap[activeModule.searchMode]?.title)}
|
||||
/>
|
||||
)}
|
||||
<Row label={t('core.chat.response.module similarity')} value={activeModule?.similarity} />
|
||||
<Row label={t('core.chat.response.module limit')} value={activeModule?.limit} />
|
||||
<Row
|
||||
label={t('core.chat.response.search using reRank')}
|
||||
value={`${activeModule?.searchUsingReRank}`}
|
||||
/>
|
||||
<Row
|
||||
label={t('core.chat.response.Extension model')}
|
||||
value={activeModule?.extensionModel}
|
||||
/>
|
||||
<Row
|
||||
label={t('support.wallet.usage.Extension result')}
|
||||
value={`${activeModule?.extensionResult}`}
|
||||
/>
|
||||
{activeModule.quoteList && activeModule.quoteList.length > 0 && (
|
||||
<Row
|
||||
label={t('core.chat.response.module quoteList')}
|
||||
rawDom={<QuoteList showDetail={showDetail} rawSearch={activeModule.quoteList} />}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
||||
{/* classify question */}
|
||||
<>
|
||||
<Row label={t('core.chat.response.module cq result')} value={activeModule?.cqResult} />
|
||||
<Row
|
||||
label={t('core.chat.response.module cq')}
|
||||
value={(() => {
|
||||
if (!activeModule?.cqList) return '';
|
||||
return activeModule.cqList.map((item) => `* ${item.value}`).join('\n');
|
||||
})()}
|
||||
/>
|
||||
</>
|
||||
|
||||
{/* if-else */}
|
||||
<>
|
||||
<Row
|
||||
label={t('core.chat.response.module if else Result')}
|
||||
value={activeModule?.ifElseResult}
|
||||
/>
|
||||
</>
|
||||
|
||||
{/* extract */}
|
||||
<>
|
||||
<Row
|
||||
label={t('core.chat.response.module extract description')}
|
||||
value={activeModule?.extractDescription}
|
||||
/>
|
||||
<Row
|
||||
label={t('core.chat.response.module extract result')}
|
||||
value={activeModule?.extractResult}
|
||||
/>
|
||||
</>
|
||||
|
||||
{/* http */}
|
||||
<>
|
||||
<Row label={'Headers'} value={activeModule?.headers} />
|
||||
<Row label={'Params'} value={activeModule?.params} />
|
||||
<Row label={'Body'} value={activeModule?.body} />
|
||||
<Row
|
||||
label={t('core.chat.response.module http result')}
|
||||
value={activeModule?.httpResult}
|
||||
/>
|
||||
</>
|
||||
|
||||
{/* plugin */}
|
||||
<>
|
||||
<Row label={t('core.chat.response.plugin output')} value={activeModule?.pluginOutput} />
|
||||
{activeModule?.pluginDetail && activeModule?.pluginDetail.length > 0 && (
|
||||
<Row
|
||||
label={t('core.chat.response.Plugin response detail')}
|
||||
rawDom={<ResponseBox response={activeModule.pluginDetail} showDetail={showDetail} />}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
||||
{/* text output */}
|
||||
<Row label={t('core.chat.response.text output')} value={activeModule?.textOutput} />
|
||||
|
||||
{/* tool call */}
|
||||
{activeModule?.toolDetail && activeModule?.toolDetail.length > 0 && (
|
||||
<Row
|
||||
label={t('core.chat.response.Tool call response detail')}
|
||||
rawDom={<ResponseBox response={activeModule.toolDetail} showDetail={showDetail} />}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* code */}
|
||||
<Row label={workflowT('response.Custom outputs')} value={activeModule?.customOutputs} />
|
||||
<Row label={workflowT('response.Custom inputs')} value={activeModule?.customInputs} />
|
||||
<Row label={workflowT('response.Code log')} value={activeModule?.codeLog} />
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
});
|
||||
@@ -10,6 +10,7 @@ import { getUnreadCount } from '@/web/support/user/inform/api';
|
||||
import dynamic from 'next/dynamic';
|
||||
|
||||
import Auth from './auth';
|
||||
import { useSystem } from '@fastgpt/web/hooks/useSystem';
|
||||
const Navbar = dynamic(() => import('./navbar'));
|
||||
const NavbarPhone = dynamic(() => import('./navbarPhone'));
|
||||
const UpdateInviteModal = dynamic(() => import('@/components/support/user/team/UpdateInviteModal'));
|
||||
@@ -43,7 +44,8 @@ const phoneUnShowLayoutRoute: Record<string, boolean> = {
|
||||
const Layout = ({ children }: { children: JSX.Element }) => {
|
||||
const router = useRouter();
|
||||
const { Loading } = useLoading();
|
||||
const { loading, setScreenWidth, isPc, feConfigs, isNotSufficientModal } = useSystemStore();
|
||||
const { loading, feConfigs, isNotSufficientModal } = useSystemStore();
|
||||
const { isPc } = useSystem();
|
||||
const { userInfo } = useUserStore();
|
||||
|
||||
const isChatPage = useMemo(
|
||||
@@ -51,21 +53,6 @@ const Layout = ({ children }: { children: JSX.Element }) => {
|
||||
[router.pathname, router.query]
|
||||
);
|
||||
|
||||
// listen screen width
|
||||
useEffect(() => {
|
||||
const resize = throttle(() => {
|
||||
setScreenWidth(document.documentElement.clientWidth);
|
||||
}, 300);
|
||||
|
||||
window.addEventListener('resize', resize);
|
||||
|
||||
resize();
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('resize', resize);
|
||||
};
|
||||
}, [setScreenWidth]);
|
||||
|
||||
const { data, refetch: refetchUnRead } = useQuery(['getUnreadCount'], getUnreadCount, {
|
||||
enabled: !!userInfo && !!feConfigs.isPlus,
|
||||
refetchInterval: 10000
|
||||
|
||||
@@ -155,7 +155,7 @@ const SelectOneResource = ({
|
||||
return loading ? (
|
||||
<Loading fixed={false} />
|
||||
) : (
|
||||
<Box maxH={maxH} overflow={'auto'}>
|
||||
<Box maxH={maxH} h={'100%'} overflow={'auto'}>
|
||||
<Render list={concatRoot} />
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -32,20 +32,22 @@ export const useFolderDrag = ({
|
||||
e.preventDefault();
|
||||
setTargetId(undefined);
|
||||
},
|
||||
onDrop: async (e: DragEvent<HTMLDivElement>) => {
|
||||
e.preventDefault();
|
||||
setTrue();
|
||||
...(isFolder && {
|
||||
onDrop: async (e: DragEvent<HTMLDivElement>) => {
|
||||
e.preventDefault();
|
||||
setTrue();
|
||||
|
||||
try {
|
||||
if (targetId && dragId && targetId !== dragId) {
|
||||
await onDrop(dragId, targetId);
|
||||
}
|
||||
} catch (error) {}
|
||||
try {
|
||||
if (targetId && dragId && targetId !== dragId) {
|
||||
await onDrop(dragId, targetId);
|
||||
}
|
||||
} catch (error) {}
|
||||
|
||||
setTargetId(undefined);
|
||||
setDragId(undefined);
|
||||
setFalse();
|
||||
},
|
||||
setTargetId(undefined);
|
||||
setDragId(undefined);
|
||||
setFalse();
|
||||
}
|
||||
}),
|
||||
...(activeStyles &&
|
||||
targetId === dataId && {
|
||||
...activeStyles
|
||||
|
||||
@@ -18,6 +18,7 @@ import { ChatBoxContext } from '../Provider';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||
import { useSystem } from '@fastgpt/web/hooks/useSystem';
|
||||
|
||||
const InputGuideBox = dynamic(() => import('./InputGuideBox'));
|
||||
|
||||
@@ -53,7 +54,9 @@ const ChatInput = ({
|
||||
|
||||
const { isChatting, whisperConfig, autoTTSResponse, chatInputGuide, outLinkAuthData } =
|
||||
useContextSelector(ChatBoxContext, (v) => v);
|
||||
const { isPc, whisperModel } = useSystemStore();
|
||||
const { whisperModel } = useSystemStore();
|
||||
const { isPc } = useSystem();
|
||||
|
||||
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -15,38 +15,53 @@ import {
|
||||
defaultWhisperConfig
|
||||
} from '@fastgpt/global/core/app/constants';
|
||||
import { createContext } from 'use-context-selector';
|
||||
import { FieldValues, UseFormReturn } from 'react-hook-form';
|
||||
import { VariableInputEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
|
||||
export type ChatProviderProps = OutLinkChatAuthProps & {
|
||||
appAvatar?: string;
|
||||
|
||||
chatConfig?: AppChatConfigType;
|
||||
|
||||
type useChatStoreType = OutLinkChatAuthProps & {
|
||||
welcomeText: string;
|
||||
variableList: VariableItemType[];
|
||||
questionGuide: boolean;
|
||||
ttsConfig: AppTTSConfigType;
|
||||
whisperConfig: AppWhisperConfigType;
|
||||
autoTTSResponse: boolean;
|
||||
startSegmentedAudio: () => Promise<any>;
|
||||
splitText2Audio: (text: string, done?: boolean | undefined) => void;
|
||||
finishSegmentedAudio: () => void;
|
||||
audioLoading: boolean;
|
||||
audioPlaying: boolean;
|
||||
hasAudio: boolean;
|
||||
playAudioByText: ({
|
||||
text,
|
||||
buffer
|
||||
}: {
|
||||
text: string;
|
||||
buffer?: Uint8Array | undefined;
|
||||
}) => Promise<{
|
||||
buffer?: Uint8Array | undefined;
|
||||
}>;
|
||||
cancelAudio: () => void;
|
||||
audioPlayingChatId: string | undefined;
|
||||
setAudioPlayingChatId: React.Dispatch<React.SetStateAction<string | undefined>>;
|
||||
chatHistories: ChatSiteItemType[];
|
||||
setChatHistories: React.Dispatch<React.SetStateAction<ChatSiteItemType[]>>;
|
||||
isChatting: boolean;
|
||||
chatInputGuide: ChatInputGuideConfigType;
|
||||
outLinkAuthData: OutLinkChatAuthProps;
|
||||
variablesForm: UseFormReturn<FieldValues, any>;
|
||||
|
||||
// not chat test params
|
||||
chatId?: string;
|
||||
};
|
||||
|
||||
type useChatStoreType = OutLinkChatAuthProps &
|
||||
ChatProviderProps & {
|
||||
welcomeText: string;
|
||||
variableList: VariableItemType[];
|
||||
questionGuide: boolean;
|
||||
ttsConfig: AppTTSConfigType;
|
||||
whisperConfig: AppWhisperConfigType;
|
||||
autoTTSResponse: boolean;
|
||||
startSegmentedAudio: () => Promise<any>;
|
||||
splitText2Audio: (text: string, done?: boolean | undefined) => void;
|
||||
finishSegmentedAudio: () => void;
|
||||
audioLoading: boolean;
|
||||
audioPlaying: boolean;
|
||||
hasAudio: boolean;
|
||||
playAudioByText: ({
|
||||
text,
|
||||
buffer
|
||||
}: {
|
||||
text: string;
|
||||
buffer?: Uint8Array | undefined;
|
||||
}) => Promise<{
|
||||
buffer?: Uint8Array | undefined;
|
||||
}>;
|
||||
cancelAudio: () => void;
|
||||
audioPlayingChatId: string | undefined;
|
||||
setAudioPlayingChatId: React.Dispatch<React.SetStateAction<string | undefined>>;
|
||||
isChatting: boolean;
|
||||
chatInputGuide: ChatInputGuideConfigType;
|
||||
outLinkAuthData: OutLinkChatAuthProps;
|
||||
};
|
||||
|
||||
export const ChatBoxContext = createContext<useChatStoreType>({
|
||||
welcomeText: '',
|
||||
variableList: [],
|
||||
@@ -100,27 +115,27 @@ export const ChatBoxContext = createContext<useChatStoreType>({
|
||||
open: false,
|
||||
customUrl: ''
|
||||
},
|
||||
outLinkAuthData: {}
|
||||
outLinkAuthData: {},
|
||||
// @ts-ignore
|
||||
variablesForm: undefined
|
||||
});
|
||||
|
||||
export type ChatProviderProps = OutLinkChatAuthProps & {
|
||||
chatConfig?: AppChatConfigType;
|
||||
|
||||
// not chat test params
|
||||
chatId?: string;
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
const Provider = ({
|
||||
shareId,
|
||||
outLinkUid,
|
||||
teamId,
|
||||
teamToken,
|
||||
chatConfig = {},
|
||||
children
|
||||
}: ChatProviderProps) => {
|
||||
const [chatHistories, setChatHistories] = useState<ChatSiteItemType[]>([]);
|
||||
|
||||
chatHistories,
|
||||
setChatHistories,
|
||||
variablesForm,
|
||||
|
||||
chatConfig = {},
|
||||
children,
|
||||
...props
|
||||
}: ChatProviderProps & {
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
const {
|
||||
welcomeText = '',
|
||||
variables = [],
|
||||
@@ -167,12 +182,13 @@ const Provider = ({
|
||||
);
|
||||
|
||||
const value: useChatStoreType = {
|
||||
...props,
|
||||
shareId,
|
||||
outLinkUid,
|
||||
teamId,
|
||||
teamToken,
|
||||
welcomeText,
|
||||
variableList: variables,
|
||||
variableList: variables.filter((item) => item.type !== VariableInputEnum.custom),
|
||||
questionGuide,
|
||||
ttsConfig,
|
||||
whisperConfig,
|
||||
@@ -191,7 +207,8 @@ const Provider = ({
|
||||
setChatHistories,
|
||||
isChatting,
|
||||
chatInputGuide,
|
||||
outLinkAuthData
|
||||
outLinkAuthData,
|
||||
variablesForm
|
||||
};
|
||||
|
||||
return <ChatBoxContext.Provider value={value}>{children}</ChatBoxContext.Provider>;
|
||||
@@ -0,0 +1,158 @@
|
||||
import { Box, BoxProps, Card, Flex } from '@chakra-ui/react';
|
||||
import React, { useMemo } from 'react';
|
||||
import ChatController, { type ChatControllerProps } from './ChatController';
|
||||
import ChatAvatar from './ChatAvatar';
|
||||
import { MessageCardStyle } from '../constants';
|
||||
import { formatChatValue2InputType } from '../utils';
|
||||
import Markdown from '@/components/Markdown';
|
||||
import styles from '../index.module.scss';
|
||||
import { ChatRoleEnum, ChatStatusEnum } from '@fastgpt/global/core/chat/constants';
|
||||
import FilesBlock from './FilesBox';
|
||||
import { ChatBoxContext } from '../Provider';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import AIResponseBox from '../../../components/AIResponseBox';
|
||||
|
||||
const colorMap = {
|
||||
[ChatStatusEnum.loading]: {
|
||||
bg: 'myGray.100',
|
||||
color: 'myGray.600'
|
||||
},
|
||||
[ChatStatusEnum.running]: {
|
||||
bg: 'green.50',
|
||||
color: 'green.700'
|
||||
},
|
||||
[ChatStatusEnum.finish]: {
|
||||
bg: 'green.50',
|
||||
color: 'green.700'
|
||||
}
|
||||
};
|
||||
|
||||
const ChatItem = ({
|
||||
type,
|
||||
avatar,
|
||||
statusBoxData,
|
||||
children,
|
||||
isLastChild,
|
||||
questionGuides = [],
|
||||
...chatControllerProps
|
||||
}: {
|
||||
type: ChatRoleEnum.Human | ChatRoleEnum.AI;
|
||||
avatar?: string;
|
||||
statusBoxData?: {
|
||||
status: `${ChatStatusEnum}`;
|
||||
name: string;
|
||||
};
|
||||
questionGuides?: string[];
|
||||
children?: React.ReactNode;
|
||||
} & ChatControllerProps) => {
|
||||
const styleMap: BoxProps =
|
||||
type === ChatRoleEnum.Human
|
||||
? {
|
||||
order: 0,
|
||||
borderRadius: '8px 0 8px 8px',
|
||||
justifyContent: 'flex-end',
|
||||
textAlign: 'right',
|
||||
bg: 'primary.100'
|
||||
}
|
||||
: {
|
||||
order: 1,
|
||||
borderRadius: '0 8px 8px 8px',
|
||||
justifyContent: 'flex-start',
|
||||
textAlign: 'left',
|
||||
bg: 'myGray.50'
|
||||
};
|
||||
|
||||
const isChatting = useContextSelector(ChatBoxContext, (v) => v.isChatting);
|
||||
const { chat } = chatControllerProps;
|
||||
|
||||
const ContentCard = useMemo(() => {
|
||||
if (type === 'Human') {
|
||||
const { text, files = [] } = formatChatValue2InputType(chat.value);
|
||||
|
||||
return (
|
||||
<>
|
||||
{files.length > 0 && <FilesBlock files={files} />}
|
||||
<Markdown source={text} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
/* AI */
|
||||
return (
|
||||
<Flex flexDirection={'column'} key={chat.dataId} gap={2}>
|
||||
{chat.value.map((value, i) => {
|
||||
const key = `${chat.dataId}-ai-${i}`;
|
||||
|
||||
return (
|
||||
<AIResponseBox
|
||||
key={key}
|
||||
value={value}
|
||||
index={i}
|
||||
chat={chat}
|
||||
isLastChild={isLastChild}
|
||||
isChatting={isChatting}
|
||||
questionGuides={questionGuides}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Flex>
|
||||
);
|
||||
}, [chat, isChatting, isLastChild, questionGuides, type]);
|
||||
|
||||
const chatStatusMap = useMemo(() => {
|
||||
if (!statusBoxData?.status) return;
|
||||
return colorMap[statusBoxData.status];
|
||||
}, [statusBoxData?.status]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* control icon */}
|
||||
<Flex w={'100%'} alignItems={'center'} gap={2} justifyContent={styleMap.justifyContent}>
|
||||
{isChatting && type === ChatRoleEnum.AI && isLastChild ? null : (
|
||||
<Box order={styleMap.order} ml={styleMap.ml}>
|
||||
<ChatController {...chatControllerProps} isLastChild={isLastChild} />
|
||||
</Box>
|
||||
)}
|
||||
<ChatAvatar src={avatar} type={type} />
|
||||
|
||||
{!!chatStatusMap && statusBoxData && isLastChild && (
|
||||
<Flex
|
||||
alignItems={'center'}
|
||||
px={3}
|
||||
py={'1.5px'}
|
||||
borderRadius="md"
|
||||
bg={chatStatusMap.bg}
|
||||
fontSize={'sm'}
|
||||
>
|
||||
<Box
|
||||
className={styles.statusAnimation}
|
||||
bg={chatStatusMap.color}
|
||||
w="8px"
|
||||
h="8px"
|
||||
borderRadius={'50%'}
|
||||
mt={'1px'}
|
||||
/>
|
||||
<Box ml={2} color={'myGray.600'}>
|
||||
{statusBoxData.name}
|
||||
</Box>
|
||||
</Flex>
|
||||
)}
|
||||
</Flex>
|
||||
{/* content */}
|
||||
<Box mt={['6px', 2]} textAlign={styleMap.textAlign}>
|
||||
<Card
|
||||
className="markdown"
|
||||
{...MessageCardStyle}
|
||||
bg={styleMap.bg}
|
||||
borderRadius={styleMap.borderRadius}
|
||||
textAlign={'left'}
|
||||
>
|
||||
{ContentCard}
|
||||
{children}
|
||||
</Card>
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(ChatItem);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user