Compare commits
22 Commits
v4.9.6
...
v4.9.7-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0720bbe4da | ||
|
|
a669a60fe6 | ||
|
|
25dc8984be | ||
|
|
2a54be4d91 | ||
|
|
5c93545016 | ||
|
|
27614e9e8b | ||
|
|
2dd5cf6d1f | ||
|
|
6c61812e7a | ||
|
|
9f8b6dbc5f | ||
|
|
d8fe9806e6 | ||
|
|
9cd6d2e81f | ||
|
|
f789af51f5 | ||
|
|
4edd72b7e6 | ||
|
|
7e19628315 | ||
|
|
4ac2a2f43e | ||
|
|
61aa91b3aa | ||
|
|
d9a4a5f3e7 | ||
|
|
a18d34e40a | ||
|
|
b4aeaf10ae | ||
|
|
d71f4cee19 | ||
|
|
6ed06936a4 | ||
|
|
8d9125b0ee |
2
.github/workflows/docs-sync_imgs.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
||||
uses: BetaHuhn/repo-file-sync-action@v1.21.0
|
||||
with:
|
||||
GH_PAT: ${{ secrets.IMG_GH_PAT }}
|
||||
CONFIG_PATH: .github/sync_imgs.yml
|
||||
CONFIG_PATH: .github/doc-sync-image.yml
|
||||
ORIGINAL_MESSAGE: true
|
||||
SKIP_PR: true
|
||||
COMMIT_EACH_FILE: false
|
||||
|
||||
@@ -126,15 +126,26 @@ services:
|
||||
# fastgpt
|
||||
sandbox:
|
||||
container_name: sandbox
|
||||
image: ghcr.io/labring/fastgpt-sandbox:v4.9.5 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.5 # 阿里云
|
||||
image: ghcr.io/labring/fastgpt-sandbox:v4.9.6 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.6 # 阿里云
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
fastgpt-mcp-server:
|
||||
container_name: fastgpt-mcp-server
|
||||
image: ghcr.io/labring/fastgpt-mcp_server:v4.9.6 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.9.6 # 阿里云
|
||||
ports:
|
||||
- 3005:3000
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
environment:
|
||||
- FASTGPT_ENDPOINT=http://fastgpt:3000
|
||||
fastgpt:
|
||||
container_name: fastgpt
|
||||
image: ghcr.io/labring/fastgpt:v4.9.5 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.5 # 阿里云
|
||||
image: ghcr.io/labring/fastgpt:v4.9.6 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.6 # 阿里云
|
||||
ports:
|
||||
- 3000:3000
|
||||
networks:
|
||||
@@ -191,8 +202,8 @@ services:
|
||||
|
||||
# AI Proxy
|
||||
aiproxy:
|
||||
image: ghcr.io/labring/aiproxy:v0.1.3
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/labring/aiproxy:v0.1.3 # 阿里云
|
||||
image: ghcr.io/labring/aiproxy:v0.1.7
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/labring/aiproxy:v0.1.7 # 阿里云
|
||||
container_name: aiproxy
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
|
||||
@@ -87,19 +87,42 @@ services:
|
||||
|
||||
# 等待docker-entrypoint.sh脚本执行的MongoDB服务进程
|
||||
wait $$!
|
||||
redis:
|
||||
image: redis:7.2-alpine
|
||||
container_name: redis
|
||||
# ports:
|
||||
# - 6379:6379
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
command: |
|
||||
redis-server --requirepass mypassword --loglevel warning --maxclients 10000 --appendonly yes --save 60 10 --maxmemory 4gb --maxmemory-policy noeviction
|
||||
volumes:
|
||||
- ./redis/data:/data
|
||||
|
||||
# fastgpt
|
||||
sandbox:
|
||||
container_name: sandbox
|
||||
image: ghcr.io/labring/fastgpt-sandbox:v4.9.3 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.3 # 阿里云
|
||||
image: ghcr.io/labring/fastgpt-sandbox:v4.9.6 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.6 # 阿里云
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
fastgpt-mcp-server:
|
||||
container_name: fastgpt-mcp-server
|
||||
image: ghcr.io/labring/fastgpt-mcp_server:v4.9.6 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.9.6 # 阿里云
|
||||
ports:
|
||||
- 3005:3000
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
environment:
|
||||
- FASTGPT_ENDPOINT=http://fastgpt:3000
|
||||
fastgpt:
|
||||
container_name: fastgpt
|
||||
image: ghcr.io/labring/fastgpt:v4.9.3 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.3 # 阿里云
|
||||
image: ghcr.io/labring/fastgpt:v4.9.6 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.6 # 阿里云
|
||||
ports:
|
||||
- 3000:3000
|
||||
networks:
|
||||
@@ -118,14 +141,9 @@ services:
|
||||
# root 密码,用户名为: root。如果需要修改 root 密码,直接修改这个环境变量,并重启即可。
|
||||
- DEFAULT_ROOT_PSW=1234
|
||||
# # AI Proxy 的地址,如果配了该地址,优先使用
|
||||
# - AIPROXY_API_ENDPOINT=http://aiproxy:3000
|
||||
- AIPROXY_API_ENDPOINT=http://aiproxy:3000
|
||||
# # AI Proxy 的 Admin Token,与 AI Proxy 中的环境变量 ADMIN_KEY
|
||||
# - AIPROXY_API_TOKEN=aiproxy
|
||||
# 模型中转地址(如果用了 AI Proxy,下面 2 个就不需要了,旧版 OneAPI 用户,使用下面的变量)
|
||||
# openai 基本地址,可用作中转。
|
||||
# - OPENAI_BASE_URL=https://example.com
|
||||
# OpenAI API Key
|
||||
# - CHAT_API_KEY=sk-example
|
||||
- AIPROXY_API_TOKEN=aiproxy
|
||||
# 数据库最大连接数
|
||||
- DB_MAX_LINK=30
|
||||
# 登录凭证密钥
|
||||
@@ -138,6 +156,8 @@ services:
|
||||
- MONGODB_URI=mongodb://myusername:mypassword@mongo:27017/fastgpt?authSource=admin
|
||||
# OceanBase 向量库连接参数
|
||||
- OCEANBASE_URL=mysql://root%40tenantname:tenantpassword@ob:2881/test
|
||||
# Redis 连接参数
|
||||
- REDIS_URL=redis://default:mypassword@redis:6379
|
||||
# sandbox 地址
|
||||
- SANDBOX_URL=http://sandbox:3000
|
||||
# 日志等级: debug, info, warn, error
|
||||
@@ -151,13 +171,15 @@ services:
|
||||
- ALLOWED_ORIGINS=
|
||||
# 是否开启IP限制,默认不开启
|
||||
- USE_IP_LIMIT=false
|
||||
# 对话文件过期天数
|
||||
- CHAT_FILE_EXPIRE_TIME=7
|
||||
volumes:
|
||||
- ./config.json:/app/data/config.json
|
||||
|
||||
# AI Proxy
|
||||
aiproxy:
|
||||
image: ghcr.io/labring/aiproxy:v0.1.5
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/labring/aiproxy:v0.1.3 # 阿里云
|
||||
image: ghcr.io/labring/aiproxy:v0.1.7
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/labring/aiproxy:v0.1.7 # 阿里云
|
||||
container_name: aiproxy
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
|
||||
@@ -85,15 +85,26 @@ services:
|
||||
# fastgpt
|
||||
sandbox:
|
||||
container_name: sandbox
|
||||
image: ghcr.io/labring/fastgpt-sandbox:v4.9.5 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.5 # 阿里云
|
||||
image: ghcr.io/labring/fastgpt-sandbox:v4.9.6 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.6 # 阿里云
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
fastgpt-mcp-server:
|
||||
container_name: fastgpt-mcp-server
|
||||
image: ghcr.io/labring/fastgpt-mcp_server:v4.9.6 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.9.6 # 阿里云
|
||||
ports:
|
||||
- 3005:3000
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
environment:
|
||||
- FASTGPT_ENDPOINT=http://fastgpt:3000
|
||||
fastgpt:
|
||||
container_name: fastgpt
|
||||
image: ghcr.io/labring/fastgpt:v4.9.5 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.5 # 阿里云
|
||||
image: ghcr.io/labring/fastgpt:v4.9.6 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.6 # 阿里云
|
||||
ports:
|
||||
- 3000:3000
|
||||
networks:
|
||||
@@ -112,9 +123,6 @@ services:
|
||||
- AIPROXY_API_ENDPOINT=http://aiproxy:3000
|
||||
# AI Proxy 的 Admin Token,与 AI Proxy 中的环境变量 ADMIN_KEY
|
||||
- AIPROXY_API_TOKEN=aiproxy
|
||||
# 模型中转地址(如果用了 AI Proxy,下面 2 个就不需要了,旧版 OneAPI 用户,使用下面的变量)
|
||||
# - OPENAI_BASE_URL=http://oneapi:3000/v1
|
||||
# - CHAT_API_KEY=sk-fastgpt
|
||||
# 数据库最大连接数
|
||||
- DB_MAX_LINK=30
|
||||
# 登录凭证密钥
|
||||
@@ -149,8 +157,8 @@ services:
|
||||
|
||||
# AI Proxy
|
||||
aiproxy:
|
||||
image: ghcr.io/labring/aiproxy:v0.1.5
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/labring/aiproxy:v0.1.5 # 阿里云
|
||||
image: ghcr.io/labring/aiproxy:v0.1.7
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/labring/aiproxy:v0.1.7 # 阿里云
|
||||
container_name: aiproxy
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
|
||||
@@ -66,15 +66,26 @@ services:
|
||||
|
||||
sandbox:
|
||||
container_name: sandbox
|
||||
image: ghcr.io/labring/fastgpt-sandbox:v4.9.5 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.5 # 阿里云
|
||||
image: ghcr.io/labring/fastgpt-sandbox:v4.9.6 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.6 # 阿里云
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
fastgpt-mcp-server:
|
||||
container_name: fastgpt-mcp-server
|
||||
image: ghcr.io/labring/fastgpt-mcp_server:v4.9.6 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.9.6 # 阿里云
|
||||
ports:
|
||||
- 3005:3000
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
environment:
|
||||
- FASTGPT_ENDPOINT=http://fastgpt:3000
|
||||
fastgpt:
|
||||
container_name: fastgpt
|
||||
image: ghcr.io/labring/fastgpt:v4.9.5 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.5 # 阿里云
|
||||
image: ghcr.io/labring/fastgpt:v4.9.6 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.6 # 阿里云
|
||||
ports:
|
||||
- 3000:3000
|
||||
networks:
|
||||
@@ -130,8 +141,8 @@ services:
|
||||
|
||||
# AI Proxy
|
||||
aiproxy:
|
||||
image: ghcr.io/labring/aiproxy:v0.1.3
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/labring/aiproxy:v0.1.3 # 阿里云
|
||||
image: ghcr.io/labring/aiproxy:v0.1.7
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/labring/aiproxy:v0.1.7 # 阿里云
|
||||
container_name: aiproxy
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
|
||||
@@ -9,4 +9,6 @@ FROM fholzer/nginx-brotli:latest
|
||||
|
||||
LABEL org.opencontainers.image.source https://github.com/labring/FastGPT
|
||||
|
||||
COPY --from=builder /app/hugo/public /usr/share/nginx/html
|
||||
COPY --from=builder /app/hugo/public /usr/share/nginx/html
|
||||
|
||||
COPY ./docSite/nginx.conf /etc/nginx/conf.d/default.conf
|
||||
|
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 105 KiB |
BIN
docSite/assets/imgs/webSync2.jpg
Normal file
|
After Width: | Height: | Size: 143 KiB |
|
Before Width: | Height: | Size: 43 KiB |
BIN
docSite/assets/imgs/webSync3.jpg
Normal file
|
After Width: | Height: | Size: 137 KiB |
|
Before Width: | Height: | Size: 64 KiB |
BIN
docSite/assets/imgs/webSync4.jpg
Normal file
|
After Width: | Height: | Size: 250 KiB |
|
Before Width: | Height: | Size: 50 KiB |
BIN
docSite/assets/imgs/webSync5-1.jpg
Normal file
|
After Width: | Height: | Size: 150 KiB |
|
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 160 KiB |
BIN
docSite/assets/imgs/webSync6.jpg
Normal file
|
After Width: | Height: | Size: 330 KiB |
|
Before Width: | Height: | Size: 161 KiB |
@@ -187,7 +187,11 @@ curl -o docker-compose.yml https://raw.githubusercontent.com/labring/FastGPT/mai
|
||||
{{< /tab >}}
|
||||
{{< /tabs >}}
|
||||
|
||||
### 3. 启动容器
|
||||
### 3. 修改 config.json 配置文件
|
||||
|
||||
修改`config.json`文件中的`mcpServerProxyEndpoint`值,设置成`mcp server`的公网可访问地址,yml 文件中默认给出了映射到 3005 端口,如通过 IP 访问,则可能是:`120.172.2.10:3005`。
|
||||
|
||||
### 4. 启动容器
|
||||
|
||||
在 docker-compose.yml 同级目录下执行。请确保`docker-compose`版本最好在2.17以上,否则可能无法执行自动化命令。
|
||||
|
||||
@@ -196,7 +200,7 @@ curl -o docker-compose.yml https://raw.githubusercontent.com/labring/FastGPT/mai
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
### 4. 访问 FastGPT
|
||||
### 5. 访问 FastGPT
|
||||
|
||||
目前可以通过 `ip:3000` 直接访问(注意开放防火墙)。登录用户名为 `root`,密码为`docker-compose.yml`环境变量里设置的 `DEFAULT_ROOT_PSW`。
|
||||
|
||||
@@ -204,7 +208,7 @@ docker-compose up -d
|
||||
|
||||
首次运行,会自动初始化 root 用户,密码为 `1234`(与环境变量中的`DEFAULT_ROOT_PSW`一致),日志可能会提示一次`MongoServerError: Unable to read from a snapshot due to pending collection catalog changes;`可忽略。
|
||||
|
||||
### 5. 配置模型
|
||||
### 6. 配置模型
|
||||
|
||||
- 首次登录FastGPT后,系统会提示未配置`语言模型`和`索引模型`,并自动跳转模型配置页面。系统必须至少有这两类模型才能正常使用。
|
||||
- 如果系统未正常跳转,可以在`账号-模型提供商`页面,进行模型配置。[点击查看相关教程](/docs/development/modelconfig/ai-proxy)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: 'V4.9.6(进行中)'
|
||||
title: 'V4.9.6'
|
||||
description: 'FastGPT V4.9.6 更新说明'
|
||||
icon: 'upgrade'
|
||||
draft: false
|
||||
@@ -7,11 +7,6 @@ toc: true
|
||||
weight: 794
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 🚀 新增内容
|
||||
|
||||
1. 以 MCP 方式对外提供应用调用。
|
||||
@@ -35,4 +30,56 @@ weight: 794
|
||||
|
||||
1. 修复子工作流包含交互节点时,未成功恢复子工作流所有数据。
|
||||
2. completion v1 接口,未接受 interactive 参数,导致 API 调用失败。
|
||||
3. 连续工具调用,上下文截断异常
|
||||
3. 连续工具调用,上下文截断异常
|
||||
|
||||
## 升级指南
|
||||
### 1. 做好数据备份
|
||||
|
||||
### 2. 部署 MCP server 服务
|
||||
|
||||
#### Docker 部署
|
||||
|
||||
在`docker-compose.yml`文件中,加入`fastgpt-mcp-server`服务:
|
||||
|
||||
```yml
|
||||
fastgpt-mcp-server:
|
||||
container_name: fastgpt-mcp-server
|
||||
image: ghcr.io/labring/fastgpt-mcp_server:v4.9.6
|
||||
ports:
|
||||
- 3005:3000
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
environment:
|
||||
- FASTGPT_ENDPOINT=http://fastgpt:3000
|
||||
```
|
||||
|
||||
#### Sealos 部署
|
||||
|
||||
直接在`应用管理`中,增加一个`fastgpt-mcp-server`应用,镜像为`ghcr.io/labring/fastgpt-mcp_server:v4.9.6`,并设置环境变量`FASTGPT_ENDPOINT=fastgpt 的访问地址`。
|
||||
|
||||
### 3. 修改 FastGPT 容器环境变量
|
||||
|
||||
#### 开源版
|
||||
|
||||
修改`config.json`配置文件,增加: `"feconfigs.mcpServerProxyEndpoint": "fastgpt-mcp-server 的访问地址"`, 末尾不要携带/,例如:
|
||||
```json
|
||||
{
|
||||
"feConfigs": {
|
||||
"lafEnv": "https://laf.dev",
|
||||
"mcpServerProxyEndpoint": "https://mcp.fastgpt.cn"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 商业版
|
||||
|
||||
在 Admin 后台,`系统配置-基础配置-系统参数`中的`MCP 转发服务地址`中,设置`fastgpt-mcp-server`的公网访问地址。
|
||||
|
||||
### 4. 更新镜像 tag
|
||||
|
||||
- 更新 FastGPT 镜像 tag: v4.9.6
|
||||
- 更新 FastGPT 商业版镜像 tag: v4.9.6
|
||||
- 更新 Sandbox 镜像 tag: v4.9.6
|
||||
- 增加 FastGPT mcp server 镜像 tag: v4.9.6
|
||||
- AIProxy 无需更新
|
||||
|
||||
40
docSite/content/zh-cn/docs/development/upgrading/497.md
Normal file
@@ -0,0 +1,40 @@
|
||||
---
|
||||
title: 'V4.9.7(进行中)'
|
||||
description: 'FastGPT V4.9.7 更新说明'
|
||||
icon: 'upgrade'
|
||||
draft: false
|
||||
toc: true
|
||||
weight: 793
|
||||
---
|
||||
|
||||
## 🚀 新增内容
|
||||
|
||||
1. MCP 工具支持 HTTP Streamable 协议。
|
||||
2. MCP server 支持编辑工具名,适配部分客户端不支持中文名问题。
|
||||
3. 工作流右键可自动对齐节点。
|
||||
4. 支持生产环境自定义`config.json`路径。
|
||||
5. API 调用,支持传递一个特殊 chatId(`NO_RECORD_HISTORIES`),使得系统不会进行历史记录存储。
|
||||
6. 支持 Rerank 模型按量计费。
|
||||
7. 套餐兑换码功能
|
||||
8. 支付宝支付
|
||||
|
||||
## ⚙️ 优化
|
||||
|
||||
1. Doc2x 文档解析,增加报错信息捕获,增加超时时长。
|
||||
2. 调整 PG vector 查询语句,强制使用向量索引。
|
||||
3. 对话时间统计,准确返回工作流整体运行时间。
|
||||
4. 从 ai_proxy 获取音频解析时长。
|
||||
|
||||
## 🐛 修复
|
||||
|
||||
1. 文件上传分块大小限制,避免超出 MongoDB 限制。
|
||||
2. 使用记录仪表盘,无法获取指定成员的使用统计。
|
||||
3. 仪表盘接口,因未考虑时区问题,统计异常。
|
||||
4. LLM 模型测试接口,无法测试未启用的 LLM。同时修复,模型测试接口会把模型自定义请求地址去除问题。
|
||||
5. Copy app 权限问题。
|
||||
6. 导出对话记录,限制单条对话记录消息上限 1000 组,避免导出失败。
|
||||
7. 工作流变量下一段文本仍是工作流变量,不触发渲染。
|
||||
8. 调试知识库检索模块,提示无权操作知识库。
|
||||
9. 文本内容提取节点,默认值赋值逻辑。
|
||||
10. 分享链接中,会强制返回嵌套应用中的引用内容。
|
||||
|
||||
@@ -74,7 +74,7 @@ env:
|
||||
{{< table "table-hover table-striped-columns" >}}
|
||||
| <div style="text-align:center">企业微信</div> | <div style="text-align:center">钉钉</div> | <div style="text-align:center">飞书</div> |
|
||||
|-----------|-----------------|--------------|
|
||||
|  |  |  |
|
||||
|  |  |  |
|
||||
{{< /table >}}
|
||||
|
||||
#### 3. 开启成员同步(可选)
|
||||
@@ -210,7 +210,7 @@ fastgpt-sso:
|
||||
1. 企业的 CorpID
|
||||
|
||||
a. 使用管理员账号登陆企业微信管理后台 `https://work.weixin.qq.com/wework_admin/loginpage_wx`
|
||||
|
||||
|
||||
b. 点击 【我的企业】 页面,查看企业的 **企业ID**
|
||||
|
||||

|
||||
@@ -296,52 +296,81 @@ fastgpt-sso:
|
||||
|
||||
### 标准 OAuth2.0
|
||||
|
||||
我们提供一套 RFC 6749 中鉴权码模式的 OAuth2.0 接入支持。
|
||||
参考:
|
||||
- [RFC 6749](https://datatracker.ietf.org/doc/html/rfc6749) 文档。
|
||||
- [阮一峰的网络日志](https://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html)
|
||||
|
||||
#### 参数需求
|
||||
|
||||
##### 三个地址
|
||||
我们提供一套标准的 OAuth2.0 接入流程。需要三个地址:
|
||||
|
||||
1. 登陆鉴权地址(登陆后将 code 传入 redirect_uri)
|
||||
- 需要将地址完整写好,除了 redirect_uri 以外(会自动补全)
|
||||
2. 获取 access_token 的地址,请求为 GET 方法,参数 code
|
||||
1. 登陆鉴权地址(用户点击 SSO 按钮后将携带参数直接跳转到该地址), 例如:`http://example.com/oauth/authorize`
|
||||
```bash
|
||||
curl -X GET\
|
||||
"http://example.com/oauth/authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz&redirect_uri=https%3A%2F%2Ffastgpt.cn%2Flogin%2Fprovider"
|
||||
```
|
||||
用户输入账号密码后,会跳转到 redirect_uri 中,并携带 code 参数:
|
||||
`https://fastgpt.cn/login/provider?code=4/P7qD2qAz4&state=xyz`
|
||||
2. 获取 access_token 的地址,获取到 code 后,通过*服务器请求*该地址获取 access_token 例如:`http://example.com/oauth/access_token`
|
||||
```bash
|
||||
curl -X POST\
|
||||
-H "Content-Type: application/x-www-form-urlencoded"\
|
||||
"http://example.com/oauth/access_token?grant_type=authorization_code&client_id=s6BhdRkqt3&client_secret=xxx&code=4/P7qD2qAz4&redirect_uri=https%3A%2F%2Ffastgpt.cn%2Flogin%2Fprovider"
|
||||
```
|
||||
注意:Content-Type 必须是 application/x-www-form-urlencoded, 而不是 application/json
|
||||
3. 获取用户信息的地址,需要传入 access_token 例如:`http://example.com/oauth/user_info`
|
||||
```bash
|
||||
curl -X GET\
|
||||
-H "Authorization: Bearer 4/P7qD2qAz4"\
|
||||
"http://example.com/oauth/user_info"
|
||||
```
|
||||
注意: access_token 作为 Authorization 头部传入, 格式为 Bearer xxxx
|
||||
|
||||
```bash
|
||||
http://example.com/oauth/access_token?code=xxxx
|
||||
```
|
||||
##### 参数配置
|
||||
- CLIENT_ID: 必须
|
||||
- CLIENT_SECRET: 非必须,如果没有可以不配置
|
||||
- SCOPE: 非必须,如果没有可以不配置
|
||||
|
||||
3. 获取用户信息的地址
|
||||
|
||||
```bash
|
||||
http://example.com/oauth/user_info
|
||||
|
||||
```
|
||||
> redirect_uri 参数会根据运行环境自动补全
|
||||
>
|
||||
> 其他固定参数如 grant_type, response_type 等会自动补全
|
||||
|
||||
#### 配置示例
|
||||
|
||||
```bash
|
||||
fastgpt-sso:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sso-service:v4.9.0
|
||||
container_name: fastgpt-sso
|
||||
restart: always
|
||||
networks:
|
||||
- fastgpt
|
||||
environment:
|
||||
# OAuth2.0
|
||||
- AUTH_TOKEN=xxxxx
|
||||
- SSO_PROVIDER=oauth2
|
||||
# OAuth2 重定向地址
|
||||
- OAUTH2_AUTHORIZE_URL=
|
||||
# OAuth2 获取 AccessToken 地址
|
||||
- OAUTH2_TOKEN_URL=
|
||||
# OAuth2 获取用户信息地址
|
||||
- OAUTH2_USER_INFO_URL=
|
||||
# OAuth2 用户名字段映射(必填)
|
||||
- OAUTH2_USERNAME_MAP=
|
||||
# OAuth2 头像字段映射(选填)
|
||||
- OAUTH2_AVATAR_MAP=
|
||||
# OAuth2 成员名字段映射(选填)
|
||||
- OAUTH2_MEMBER_NAME_MAP=
|
||||
# OAuth2 联系方式字段映射(选填)
|
||||
- OAUTH2_CONTACT_MAP=
|
||||
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sso-service:v4.9.0
|
||||
container_name: fastgpt-sso
|
||||
restart: always
|
||||
networks:
|
||||
- fastgpt
|
||||
environment:
|
||||
# OAuth2.0
|
||||
# === 请求地址 ===
|
||||
# 1. OAuth2 登陆鉴权地址 (必填)
|
||||
- OAUTH2_AUTHORIZE_URL=
|
||||
# 2. OAuth2 获取 AccessToken 地址 (必填)
|
||||
- OAUTH2_TOKEN_URL=
|
||||
# 3. OAuth2 获取用户信息地址 (必填)
|
||||
- OAUTH2_USER_INFO_URL=
|
||||
# === 参数 ===
|
||||
# 1. client_id (必填)
|
||||
- OAUTH2_CLIENT_ID=
|
||||
# 2. client_secret (选填,如果没有则不传)
|
||||
- OAUTH2_CLIENT_SECRET=
|
||||
# 3. scope (选填)
|
||||
- OAUTH2_SCOPE=
|
||||
# === 字段映射 ===
|
||||
# OAuth2 用户名字段映射(必填)
|
||||
- OAUTH2_USERNAME_MAP=
|
||||
# OAuth2 头像字段映射(选填)
|
||||
- OAUTH2_AVATAR_MAP=
|
||||
# OAuth2 成员名字段映射(选填)
|
||||
- OAUTH2_MEMBER_NAME_MAP=
|
||||
# OAuth2 联系方式字段映射(选填)
|
||||
- OAUTH2_CONTACT_MAP=
|
||||
```
|
||||
|
||||
## 标准接口文档
|
||||
@@ -473,7 +502,7 @@ curl -X GET "https://example.com/org/list" \
|
||||
type OrgListResponseType = {
|
||||
message?: string; // 报错信息
|
||||
success: boolean;
|
||||
orgList: {
|
||||
orgList: {
|
||||
id: string; // 部门的唯一 id
|
||||
name: string; // 名字
|
||||
parentId: string; // parentId,如果为根部门,传空字符串。
|
||||
@@ -531,7 +560,7 @@ type UserListResponseListType = {
|
||||
message?: string; // 报错信息
|
||||
success: boolean;
|
||||
userList: {
|
||||
username: string; // 唯一 id username 必须与 SSO 接口返回的用户 username 相同。并且必须携带一个前缀,例如: sync-aaaaa,和 sso 接口返回的前缀一致
|
||||
username: string; // 唯一 id username 必须与 SSO 接口返回的用户 username 相同。并且必须携带一个前缀,例如: sync-aaaaa,和 sso 接口返回的前缀一致
|
||||
memberName?: string; // 名字,作为 tmbname
|
||||
avatar?: string;
|
||||
contact?: string; // email or phone number
|
||||
@@ -574,8 +603,8 @@ curl示例
|
||||
|
||||
## 如何对接非标准系统
|
||||
|
||||
1. 客户自己开发:按 fastgpt 提供的标准接口进行开发,并将部署后的服务地址填入 fastgpt-pro
|
||||
可以参考该模版库:[fastgpt-sso-template](https://github.com/labring/fastgpt-sso-template) 进行开发
|
||||
2. 由 fastgpt 团队定制开发:
|
||||
a. 提供系统的 SSO 文档、获取成员和组织的文档、以及外网测试地址。
|
||||
1. 客户自己开发:按 fastgpt 提供的标准接口进行开发,并将部署后的服务地址填入 fastgpt-pro
|
||||
可以参考该模版库:[fastgpt-sso-template](https://github.com/labring/fastgpt-sso-template) 进行开发
|
||||
2. 由 fastgpt 团队定制开发:
|
||||
a. 提供系统的 SSO 文档、获取成员和组织的文档、以及外网测试地址。
|
||||
b. 在 fastgpt-sso-service 中,增加对应的 provider 和环境变量,并编写代码来对接。
|
||||
|
||||
@@ -62,7 +62,7 @@ FastGPT MCP Server 功能允许你选择`多个`在 FastGPT 上构建好的应
|
||||
|
||||
## 私有化部署 MCP server 问题
|
||||
|
||||
私有化部署版本的 FastGPT,需要升级到`v4.9.6-alpha`及以上版本才可使用 MCP server 功能。
|
||||
私有化部署版本的 FastGPT,需要升级到`v4.9.6`及以上版本才可使用 MCP server 功能。
|
||||
|
||||
### 修改 docker-compose.yml 文件
|
||||
|
||||
@@ -100,4 +100,4 @@ fastgpt-mcp-server:
|
||||
```bash
|
||||
docker-compose down
|
||||
docker-compose up -d
|
||||
```
|
||||
```
|
||||
|
||||
@@ -25,24 +25,26 @@ curl https://doc.tryfastgpt.ai/docs/intro/
|
||||
|
||||
### 1. 新建知识库,选择 Web 站点同步
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
### 2. 点击配置站点信息
|
||||
|
||||

|
||||

|
||||
|
||||
### 3. 填写网址和选择器
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
好了, 现在点击开始同步,静等系统自动抓取网站信息即可。
|
||||
|
||||
|
||||
## 创建应用,绑定知识库
|
||||
|
||||

|
||||

|
||||
|
||||
## 选择器如何使用
|
||||
|
||||
@@ -77,4 +79,4 @@ curl https://doc.tryfastgpt.ai/docs/intro/
|
||||
|
||||
另一组是`.docs-content div[data-prismjs-copy]`,含义是`docs-content` 类下包含`data-prismjs-copy`属性的`div`元素。
|
||||
|
||||
把两组选择器用逗号隔开即可:`.docs-content .mb-0.d-flex, .docs-content div[data-prismjs-copy]`
|
||||
把两组选择器用逗号隔开即可:`.docs-content .mb-0.d-flex, .docs-content div[data-prismjs-copy]`
|
||||
|
||||
51
docSite/nginx.conf
Normal file
@@ -0,0 +1,51 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
|
||||
#charset koi8-r;
|
||||
#access_log /var/log/nginx/host.access.log main;
|
||||
|
||||
# 设置UTF-8编码
|
||||
charset utf-8;
|
||||
|
||||
location / {
|
||||
root /usr/share/nginx/html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
|
||||
location ~\.txt$ {
|
||||
root /usr/share/nginx/html;
|
||||
charset utf-8;
|
||||
}
|
||||
#error_page 404 /404.html;
|
||||
|
||||
# redirect server error pages to the static page /50x.html
|
||||
#
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
|
||||
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
|
||||
#
|
||||
#location ~ \.php$ {
|
||||
# proxy_pass http://127.0.0.1;
|
||||
#}
|
||||
|
||||
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
|
||||
#
|
||||
#location ~ \.php$ {
|
||||
# root html;
|
||||
# fastcgi_pass 127.0.0.1:9000;
|
||||
# fastcgi_index index.php;
|
||||
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
|
||||
# include fastcgi_params;
|
||||
#}
|
||||
|
||||
# deny access to .htaccess files, if Apache's document root
|
||||
# concurs with nginx's one
|
||||
#
|
||||
#location ~ /\.ht {
|
||||
# deny all;
|
||||
#}
|
||||
}
|
||||
22
env.d.ts
vendored
@@ -23,16 +23,18 @@ declare global {
|
||||
FE_DOMAIN: string;
|
||||
FILE_DOMAIN: string;
|
||||
NEXT_PUBLIC_BASE_URL: string;
|
||||
LOG_LEVEL: string;
|
||||
STORE_LOG_LEVEL: string;
|
||||
USE_IP_LIMIT: string;
|
||||
WORKFLOW_MAX_RUN_TIMES: string;
|
||||
WORKFLOW_MAX_LOOP_TIMES: string;
|
||||
CHECK_INTERNAL_IP: string;
|
||||
CHAT_LOG_URL: string;
|
||||
CHAT_LOG_INTERVAL: string;
|
||||
CHAT_LOG_SOURCE_ID_PREFIX: string;
|
||||
ALLOWED_ORIGINS: string;
|
||||
LOG_LEVEL?: string;
|
||||
STORE_LOG_LEVEL?: string;
|
||||
USE_IP_LIMIT?: string;
|
||||
WORKFLOW_MAX_RUN_TIMES?: string;
|
||||
WORKFLOW_MAX_LOOP_TIMES?: string;
|
||||
CHECK_INTERNAL_IP?: string;
|
||||
CHAT_LOG_URL?: string;
|
||||
CHAT_LOG_INTERVAL?: string;
|
||||
CHAT_LOG_SOURCE_ID_PREFIX?: string;
|
||||
ALLOWED_ORIGINS?: string;
|
||||
SHOW_COUPON?: string;
|
||||
CONFIG_JSON_PATH?: string;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,13 @@ export const getErrText = (err: any, def = ''): any => {
|
||||
const msg: string =
|
||||
typeof err === 'string'
|
||||
? err
|
||||
: err?.response?.data?.message || err?.response?.message || err?.message || def;
|
||||
: err?.response?.data?.message ||
|
||||
err?.response?.message ||
|
||||
err?.message ||
|
||||
err?.response?.data?.msg ||
|
||||
err?.response?.msg ||
|
||||
err?.msg ||
|
||||
def;
|
||||
// msg && console.log('error =>', msg);
|
||||
return replaceSensitiveText(msg);
|
||||
};
|
||||
|
||||
@@ -60,6 +60,7 @@ export type FastGPTFeConfigsType = {
|
||||
show_team_chat?: boolean;
|
||||
show_compliance_copywriting?: boolean;
|
||||
show_aiproxy?: boolean;
|
||||
show_coupon?: boolean;
|
||||
concatMd?: string;
|
||||
|
||||
concatMd?: string;
|
||||
@@ -106,6 +107,12 @@ export type FastGPTFeConfigsType = {
|
||||
lafEnv?: string;
|
||||
navbarItems?: NavbarItemType[];
|
||||
externalProviderWorkflowVariables?: ExternalProviderWorkflowVarType[];
|
||||
|
||||
payConfig?: {
|
||||
wx?: boolean;
|
||||
alipay?: boolean;
|
||||
bank?: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
export type SystemEnvType = {
|
||||
|
||||
@@ -30,7 +30,7 @@ export const getTimezoneOffset = (timeZone: string): number => {
|
||||
*
|
||||
* Generated by Trelent
|
||||
*/
|
||||
export const timezoneList = () => {
|
||||
export const getTimeZoneList = () => {
|
||||
const result = timezones
|
||||
.map((timezone) => {
|
||||
try {
|
||||
@@ -71,6 +71,23 @@ export const timezoneList = () => {
|
||||
time: number;
|
||||
}[];
|
||||
};
|
||||
export const timeZoneList = getTimeZoneList();
|
||||
|
||||
export const getMongoTimezoneCode = (timeString: string) => {
|
||||
if (!timeString.includes(':')) {
|
||||
return '+00:00';
|
||||
}
|
||||
|
||||
if (timeString.includes('+')) {
|
||||
const timezoneMatch = timeString.split('+');
|
||||
return `+${timezoneMatch[1]}`;
|
||||
} else if (timeString.includes('-')) {
|
||||
const timezoneMatch = timeString.split('-');
|
||||
return `-${timezoneMatch[1]}`;
|
||||
} else {
|
||||
return '+00:00';
|
||||
}
|
||||
};
|
||||
|
||||
export const getSystemTime = (timeZone: string) => {
|
||||
const timezoneDiff = getTimezoneOffset(timeZone);
|
||||
|
||||
@@ -7,6 +7,13 @@ export const Prompt_QuoteTemplateList: PromptTemplateItem[] = [
|
||||
title: i18nT('app:template.standard_template'),
|
||||
desc: i18nT('app:template.standard_template_des'),
|
||||
value: {
|
||||
['4.9.7']: `{
|
||||
"sourceIndex": "{{sourceIndex}}",
|
||||
"id": "{{id}}",
|
||||
"sourceName": "{{source}}",
|
||||
"content": "{{q}}\n{{a}}"
|
||||
}
|
||||
`,
|
||||
['4.9.2']: `{
|
||||
"sourceName": "{{source}}",
|
||||
"updateTime": "{{updateTime}}",
|
||||
@@ -31,6 +38,13 @@ export const Prompt_QuoteTemplateList: PromptTemplateItem[] = [
|
||||
title: i18nT('app:template.standard_strict'),
|
||||
desc: i18nT('app:template.standard_strict_des'),
|
||||
value: {
|
||||
['4.9.7']: `{
|
||||
"sourceIndex": "{{sourceIndex}}",
|
||||
"id": "{{id}}",
|
||||
"sourceName": "{{source}}",
|
||||
"content": "{{q}}\n{{a}}"
|
||||
}
|
||||
`,
|
||||
['4.9.2']: `{
|
||||
"sourceName": "{{source}}",
|
||||
"updateTime": "{{updateTime}}",
|
||||
@@ -64,6 +78,21 @@ export const Prompt_userQuotePromptList: PromptTemplateItem[] = [
|
||||
title: i18nT('app:template.standard_template'),
|
||||
desc: '',
|
||||
value: {
|
||||
['4.9.7']: `使用 <Reference></Reference> 标记中的内容作为本次对话的参考:
|
||||
|
||||
<Reference>
|
||||
{{quote}}
|
||||
</Reference>
|
||||
|
||||
回答要求:
|
||||
- 如果你不清楚答案,你需要澄清。
|
||||
- 避免提及你是从 <Reference></Reference> 获取的知识。
|
||||
- 保持答案与 <Reference></Reference> 中描述的一致。
|
||||
- 使用 Markdown 语法优化回答格式。
|
||||
- 使用与问题相同的语言回答。
|
||||
- 使用 [id](QUOTE{{sourceIndex}}) 格式来引用<Reference></Reference>中的知识,其中 QUOTE 是固定常量, id 和 sourceIndex 分别为引文中的值。
|
||||
- 在每段结尾自然地整合引用。例如: "FastGPT 是一个基于大语言模型(LLM)的知识库问答系统[67e517e74767063e882d6861](QUOTE1)。"
|
||||
- 每段至少包含一个引用,也可根据内容需要加入多个引用,按顺序排列。`,
|
||||
['4.9.2']: `使用 <Reference></Reference> 标记中的内容作为本次对话的参考:
|
||||
|
||||
<Reference>
|
||||
@@ -103,6 +132,27 @@ export const Prompt_userQuotePromptList: PromptTemplateItem[] = [
|
||||
title: i18nT('app:template.standard_strict'),
|
||||
desc: '',
|
||||
value: {
|
||||
['4.9.7']: `忘记你已有的知识,仅使用 <Reference></Reference> 标记中的内容作为本次对话的参考:
|
||||
|
||||
<Reference>
|
||||
{{quote}}
|
||||
</Reference>
|
||||
|
||||
思考流程:
|
||||
1. 判断问题是否与 <Reference></Reference> 标记中的内容有关。
|
||||
2. 如果有关,你按下面的要求回答。
|
||||
3. 如果无关,你直接拒绝回答本次问题。
|
||||
|
||||
回答要求:
|
||||
- 避免提及你是从 <Reference></Reference> 获取的知识。
|
||||
- 保持答案与 <Reference></Reference> 中描述的一致。
|
||||
- 使用 Markdown 语法优化回答格式。
|
||||
- 使用与问题相同的语言回答。
|
||||
- 使用 [id](QUOTE{{sourceIndex}}) 格式来引用<Reference></Reference>中的知识,其中 QUOTE 是固定常量, id 和 sourceIndex 分别为引文中的值。
|
||||
- 在每段结尾自然地整合引用。例如: "FastGPT 是一个基于大语言模型(LLM)的知识库问答系统[67e517e74767063e882d6861](QUOTE1)。"
|
||||
- 每段至少包含一个引用,也可根据内容需要加入多个引用,按顺序排列。
|
||||
|
||||
问题:"""{{question}}"""`,
|
||||
['4.9.2']: `忘记你已有的知识,仅使用 <Reference></Reference> 标记中的内容作为本次对话的参考:
|
||||
|
||||
<Reference>
|
||||
@@ -157,6 +207,21 @@ export const Prompt_systemQuotePromptList: PromptTemplateItem[] = [
|
||||
title: i18nT('app:template.standard_template'),
|
||||
desc: '',
|
||||
value: {
|
||||
['4.9.7']: `使用 <Reference></Reference> 标记中的内容作为本次对话的参考:
|
||||
|
||||
<Reference>
|
||||
{{quote}}
|
||||
</Reference>
|
||||
|
||||
回答要求:
|
||||
- 如果你不清楚答案,你需要澄清。
|
||||
- 避免提及你是从 <Reference></Reference> 获取的知识。
|
||||
- 保持答案与 <Reference></Reference> 中描述的一致。
|
||||
- 使用 Markdown 语法优化回答格式。
|
||||
- 使用与问题相同的语言回答。
|
||||
- 使用 [id](QUOTE{{sourceIndex}}) 格式来引用<Reference></Reference>中的知识,其中 QUOTE 是固定常量, id 和 sourceIndex 分别为引文中的值。
|
||||
- 在每段结尾自然地整合引用。例如: "FastGPT 是一个基于大语言模型(LLM)的知识库问答系统[67e517e74767063e882d6861](QUOTE1)。"
|
||||
- 每段至少包含一个引用,也可根据内容需要加入多个引用,按顺序排列。`,
|
||||
['4.9.2']: `使用 <Reference></Reference> 标记中的内容作为本次对话的参考:
|
||||
|
||||
<Reference>
|
||||
@@ -192,6 +257,27 @@ export const Prompt_systemQuotePromptList: PromptTemplateItem[] = [
|
||||
title: i18nT('app:template.standard_strict'),
|
||||
desc: '',
|
||||
value: {
|
||||
['4.9.7']: `忘记你已有的知识,仅使用 <Reference></Reference> 标记中的内容作为本次对话的参考:
|
||||
|
||||
<Reference>
|
||||
{{quote}}
|
||||
</Reference>
|
||||
|
||||
思考流程:
|
||||
1. 判断问题是否与 <Reference></Reference> 标记中的内容有关。
|
||||
2. 如果有关,你按下面的要求回答。
|
||||
3. 如果无关,你直接拒绝回答本次问题。
|
||||
|
||||
回答要求:
|
||||
- 避免提及你是从 <Reference></Reference> 获取的知识。
|
||||
- 保持答案与 <Reference></Reference> 中描述的一致。
|
||||
- 使用 Markdown 语法优化回答格式。
|
||||
- 使用与问题相同的语言回答。
|
||||
- 使用 [id](QUOTE{{sourceIndex}}) 格式来引用<Reference></Reference>中的知识,其中 QUOTE 是固定常量, id 和 sourceIndex 分别为引文中的值。
|
||||
- 在每段结尾自然地整合引用。例如: "FastGPT 是一个基于大语言模型(LLM)的知识库问答系统[67e517e74767063e882d6861](QUOTE1)。"
|
||||
- 每段至少包含一个引用,也可根据内容需要加入多个引用,按顺序排列。
|
||||
|
||||
问题:"""{{question}}"""`,
|
||||
['4.9.2']: `忘记你已有的知识,仅使用 <Reference></Reference> 标记中的内容作为本次对话的参考:
|
||||
|
||||
<Reference>
|
||||
@@ -250,10 +336,10 @@ export const getQuotePrompt = (version?: string, role: 'user' | 'system' = 'user
|
||||
export const getDocumentQuotePrompt = (version: string) => {
|
||||
const promptMap = {
|
||||
['4.9.2']: `将 <FilesContent></FilesContent> 中的内容作为本次对话的参考:
|
||||
<FilesContent>
|
||||
{{quote}}
|
||||
</FilesContent>
|
||||
`
|
||||
<FilesContent>
|
||||
{{quote}}
|
||||
</FilesContent>
|
||||
`
|
||||
};
|
||||
|
||||
return getPromptByVersion(version, promptMap);
|
||||
|
||||
@@ -26,7 +26,7 @@ export const getDefaultAppForm = (): AppSimpleEditFormType => {
|
||||
similarity: 0.4,
|
||||
limit: 3000,
|
||||
searchMode: DatasetSearchModeEnum.embedding,
|
||||
usingReRank: false,
|
||||
usingReRank: true,
|
||||
rerankModel: '',
|
||||
rerankWeight: 0.5,
|
||||
datasetSearchUsingExtensionQuery: true,
|
||||
|
||||
3
packages/global/core/chat/type.d.ts
vendored
@@ -110,6 +110,7 @@ export type ChatItemSchema = (UserChatItemType | SystemChatItemType | AIChatItem
|
||||
tmbId: string;
|
||||
appId: string;
|
||||
time: Date;
|
||||
durationSeconds?: number;
|
||||
};
|
||||
|
||||
export type AdminFbkType = {
|
||||
@@ -122,7 +123,6 @@ export type AdminFbkType = {
|
||||
|
||||
/* --------- chat item ---------- */
|
||||
export type ResponseTagItemType = {
|
||||
totalRunningTime?: number;
|
||||
totalQuoteList?: SearchDataResponseItemType[];
|
||||
llmModuleAccount?: number;
|
||||
historyPreviewLength?: number;
|
||||
@@ -141,6 +141,7 @@ export type ChatSiteItemType = (UserChatItemType | SystemChatItemType | AIChatIt
|
||||
ttsBuffer?: Uint8Array;
|
||||
responseData?: ChatHistoryItemResType[];
|
||||
time?: Date;
|
||||
durationSeconds?: number;
|
||||
} & ChatBoxInputType &
|
||||
ResponseTagItemType;
|
||||
|
||||
|
||||
@@ -77,13 +77,6 @@ export const getHistoryPreview = (
|
||||
});
|
||||
};
|
||||
|
||||
export const filterModuleTypeList: any[] = [
|
||||
FlowNodeTypeEnum.pluginModule,
|
||||
FlowNodeTypeEnum.datasetSearchNode,
|
||||
FlowNodeTypeEnum.tools,
|
||||
FlowNodeTypeEnum.pluginOutput
|
||||
];
|
||||
|
||||
export const filterPublicNodeResponseData = ({
|
||||
flowResponses = [],
|
||||
responseDetail = false
|
||||
@@ -91,12 +84,19 @@ export const filterPublicNodeResponseData = ({
|
||||
flowResponses?: ChatHistoryItemResType[];
|
||||
responseDetail?: boolean;
|
||||
}) => {
|
||||
const publicNodeMap: Record<string, any> = {
|
||||
[FlowNodeTypeEnum.pluginModule]: true,
|
||||
[FlowNodeTypeEnum.datasetSearchNode]: true,
|
||||
[FlowNodeTypeEnum.tools]: true,
|
||||
[FlowNodeTypeEnum.pluginOutput]: true
|
||||
};
|
||||
|
||||
const filedList = responseDetail
|
||||
? ['quoteList', 'moduleType', 'pluginOutput', 'runningTime']
|
||||
: ['moduleType', 'pluginOutput', 'runningTime'];
|
||||
|
||||
return flowResponses
|
||||
.filter((item) => filterModuleTypeList.includes(item.moduleType))
|
||||
.filter((item) => publicNodeMap[item.moduleType])
|
||||
.map((item) => {
|
||||
const obj: DispatchNodeResponseType = {};
|
||||
for (let key in item) {
|
||||
|
||||
@@ -2,6 +2,7 @@ import { FlowNodeInputTypeEnum } from '../node/constant';
|
||||
|
||||
export enum SseResponseEventEnum {
|
||||
error = 'error',
|
||||
workflowDuration = 'workflowDuration', // workflow duration
|
||||
answer = 'answer', // animation stream
|
||||
fastAnswer = 'fastAnswer', // direct answer text, not animation
|
||||
flowNodeStatus = 'flowNodeStatus', // update node status
|
||||
|
||||
@@ -24,6 +24,7 @@ import { AiChatQuoteRoleType } from '../template/system/aiChat/type';
|
||||
import { LafAccountType, OpenaiAccountType } from '../../../support/user/team/type';
|
||||
import { CompletionFinishReason } from '../../ai/type';
|
||||
import { WorkflowInteractiveResponseType } from '../template/system/interactive/type';
|
||||
import { SearchDataResponseItemType } from '../../dataset/type';
|
||||
export type ExternalProviderType = {
|
||||
openaiAccount?: OpenaiAccountType;
|
||||
externalWorkflowVariables?: Record<string, string>;
|
||||
@@ -62,6 +63,8 @@ export type ChatDispatchProps = {
|
||||
workflowStreamResponse?: WorkflowResponseType;
|
||||
workflowDispatchDeep?: number;
|
||||
version?: 'v1' | 'v2';
|
||||
|
||||
responseAllData?: boolean;
|
||||
responseDetail?: boolean;
|
||||
};
|
||||
|
||||
@@ -136,12 +139,15 @@ export type DispatchNodeResponseType = {
|
||||
finishReason?: CompletionFinishReason;
|
||||
|
||||
// dataset search
|
||||
embeddingModel?: string;
|
||||
embeddingTokens?: number;
|
||||
similarity?: number;
|
||||
limit?: number;
|
||||
searchMode?: `${DatasetSearchModeEnum}`;
|
||||
embeddingWeight?: number;
|
||||
rerankModel?: string;
|
||||
rerankWeight?: number;
|
||||
reRankInputTokens?: number;
|
||||
searchUsingReRank?: boolean;
|
||||
queryExtensionResult?: {
|
||||
model: string;
|
||||
|
||||
@@ -55,7 +55,7 @@ export const AiChatModule: FlowNodeTemplateType = {
|
||||
showStatus: true,
|
||||
isTool: true,
|
||||
courseUrl: '/docs/guide/workbench/workflow/ai_chat/',
|
||||
version: '4.9.0',
|
||||
version: '4.9.7',
|
||||
inputs: [
|
||||
Input_Template_SettingAiModel,
|
||||
// --- settings modal
|
||||
|
||||
@@ -3,19 +3,19 @@
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"@apidevtools/swagger-parser": "^10.1.0",
|
||||
"@bany/curl-to-json": "^1.2.8",
|
||||
"axios": "^1.8.2",
|
||||
"cron-parser": "^4.9.0",
|
||||
"dayjs": "^1.11.7",
|
||||
"encoding": "^0.1.13",
|
||||
"js-yaml": "^4.1.0",
|
||||
"jschardet": "3.1.1",
|
||||
"json5": "^2.2.3",
|
||||
"nanoid": "^5.1.3",
|
||||
"next": "14.2.26",
|
||||
"openai": "4.61.0",
|
||||
"openapi-types": "^12.1.3",
|
||||
"json5": "^2.2.3",
|
||||
"timezones-list": "^3.0.2",
|
||||
"@bany/curl-to-json": "^1.2.8"
|
||||
"timezones-list": "^3.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
|
||||
1
packages/global/support/mcp/type.d.ts
vendored
@@ -9,6 +9,7 @@ export type McpKeyType = {
|
||||
|
||||
export type McpAppType = {
|
||||
appId: string;
|
||||
appName?: string;
|
||||
toolName: string;
|
||||
description: string;
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@ export enum OperationLogEventEnum {
|
||||
JOIN_TEAM = 'JOIN_TEAM',
|
||||
CHANGE_MEMBER_NAME = 'CHANGE_MEMBER_NAME',
|
||||
KICK_OUT_TEAM = 'KICK_OUT_TEAM',
|
||||
RECOVER_TEAM_MEMBER = 'RECOVER_TEAM_MEMBER',
|
||||
CREATE_DEPARTMENT = 'CREATE_DEPARTMENT',
|
||||
CHANGE_DEPARTMENT = 'CHANGE_DEPARTMENT',
|
||||
DELETE_DEPARTMENT = 'DELETE_DEPARTMENT',
|
||||
|
||||
20
packages/global/support/wallet/bill/api.d.ts
vendored
@@ -1,5 +1,11 @@
|
||||
import { StandardSubLevelEnum, SubModeEnum } from '../sub/constants';
|
||||
import { BillTypeEnum } from './constants';
|
||||
import { BillTypeEnum, DrawBillQRItem } from './constants';
|
||||
|
||||
export type CreateOrderResponse = {
|
||||
qrCode?: string;
|
||||
iframeCode?: string;
|
||||
markdown?: string;
|
||||
};
|
||||
|
||||
export type CreateStandPlanBill = {
|
||||
type: BillTypeEnum.standSubPlan;
|
||||
@@ -22,6 +28,16 @@ export type CreateBillProps =
|
||||
|
||||
export type CreateBillResponse = {
|
||||
billId: string;
|
||||
codeUrl: string;
|
||||
readPrice: number;
|
||||
payment: BillPayWayEnum;
|
||||
} & CreateOrderResponse;
|
||||
|
||||
export type UpdatePaymentProps = {
|
||||
billId: string;
|
||||
payWay: BillPayWayEnum;
|
||||
};
|
||||
|
||||
export type CheckPayResultResponse = {
|
||||
status: BillStatusEnum;
|
||||
description?: string;
|
||||
};
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { i18nT } from '../../../../web/i18n/utils';
|
||||
|
||||
export enum BillTypeEnum {
|
||||
balance = 'balance',
|
||||
standSubPlan = 'standSubPlan',
|
||||
@@ -6,16 +8,16 @@ export enum BillTypeEnum {
|
||||
}
|
||||
export const billTypeMap = {
|
||||
[BillTypeEnum.balance]: {
|
||||
label: 'support.wallet.subscription.type.balance'
|
||||
label: i18nT('common:support.wallet.subscription.type.balance')
|
||||
},
|
||||
[BillTypeEnum.standSubPlan]: {
|
||||
label: 'support.wallet.subscription.type.standard'
|
||||
label: i18nT('common:support.wallet.subscription.type.standard')
|
||||
},
|
||||
[BillTypeEnum.extraDatasetSub]: {
|
||||
label: 'support.wallet.subscription.type.extraDatasetSize'
|
||||
label: i18nT('common:support.wallet.subscription.type.extraDatasetSize')
|
||||
},
|
||||
[BillTypeEnum.extraPoints]: {
|
||||
label: 'support.wallet.subscription.type.extraPoints'
|
||||
label: i18nT('common:support.wallet.subscription.type.extraPoints')
|
||||
}
|
||||
};
|
||||
|
||||
@@ -27,32 +29,46 @@ export enum BillStatusEnum {
|
||||
}
|
||||
export const billStatusMap = {
|
||||
[BillStatusEnum.SUCCESS]: {
|
||||
label: 'support.wallet.bill.status.success'
|
||||
label: i18nT('common:support.wallet.bill.status.success')
|
||||
},
|
||||
[BillStatusEnum.REFUND]: {
|
||||
label: 'support.wallet.bill.status.refund'
|
||||
label: i18nT('common:support.wallet.bill.status.refund')
|
||||
},
|
||||
[BillStatusEnum.NOTPAY]: {
|
||||
label: 'support.wallet.bill.status.notpay'
|
||||
label: i18nT('common:support.wallet.bill.status.notpay')
|
||||
},
|
||||
[BillStatusEnum.CLOSED]: {
|
||||
label: 'support.wallet.bill.status.closed'
|
||||
label: i18nT('common:support.wallet.bill.status.closed')
|
||||
}
|
||||
};
|
||||
|
||||
export enum BillPayWayEnum {
|
||||
balance = 'balance',
|
||||
wx = 'wx'
|
||||
wx = 'wx',
|
||||
alipay = 'alipay',
|
||||
bank = 'bank',
|
||||
coupon = 'coupon'
|
||||
}
|
||||
|
||||
export const billPayWayMap = {
|
||||
[BillPayWayEnum.balance]: {
|
||||
label: 'support.wallet.bill.payWay.balance'
|
||||
label: i18nT('common:support.wallet.bill.payWay.balance')
|
||||
},
|
||||
[BillPayWayEnum.wx]: {
|
||||
label: 'support.wallet.bill.payWay.wx'
|
||||
label: i18nT('common:support.wallet.bill.payWay.wx')
|
||||
},
|
||||
[BillPayWayEnum.alipay]: {
|
||||
label: i18nT('common:support.wallet.bill.payWay.alipay')
|
||||
},
|
||||
[BillPayWayEnum.bank]: {
|
||||
label: i18nT('common:support.wallet.bill.payWay.bank')
|
||||
},
|
||||
[BillPayWayEnum.coupon]: {
|
||||
label: i18nT('account_bill:payway_coupon')
|
||||
}
|
||||
};
|
||||
|
||||
export const SUB_DATASET_SIZE_RATE = 1000;
|
||||
export const SUB_EXTRA_POINT_RATE = 1000;
|
||||
export const MAX_WX_PAY_AMOUNT = 6000;
|
||||
export const QR_CODE_SIZE = 210;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { StandardSubLevelEnum, SubModeEnum, SubTypeEnum } from '../sub/constants';
|
||||
import { BillPayWayEnum, BillTypeEnum } from './constants';
|
||||
import { BillPayWayEnum, BillStatusEnum, BillTypeEnum } from './constants';
|
||||
import { TeamInvoiceHeaderType } from '../../user/team/type';
|
||||
|
||||
export type BillSchemaType = {
|
||||
_id: string;
|
||||
userId: string;
|
||||
@@ -8,7 +9,7 @@ export type BillSchemaType = {
|
||||
tmbId: string;
|
||||
createTime: Date;
|
||||
orderId: string;
|
||||
status: 'SUCCESS' | 'REFUND' | 'NOTPAY' | 'CLOSED';
|
||||
status: `${BillStatusEnum}`;
|
||||
type: BillTypeEnum;
|
||||
price: number;
|
||||
hasInvoice: boolean;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { i18nT } from '../../../../web/i18n/utils';
|
||||
import { BillTypeEnum } from '../bill/constants';
|
||||
|
||||
export enum SubTypeEnum {
|
||||
standard = 'standard',
|
||||
@@ -9,15 +10,18 @@ export enum SubTypeEnum {
|
||||
export const subTypeMap = {
|
||||
[SubTypeEnum.standard]: {
|
||||
label: 'support.wallet.subscription.type.standard',
|
||||
icon: 'support/account/plans'
|
||||
icon: 'support/account/plans',
|
||||
orderType: BillTypeEnum.standSubPlan
|
||||
},
|
||||
[SubTypeEnum.extraDatasetSize]: {
|
||||
label: 'support.wallet.subscription.type.extraDatasetSize',
|
||||
icon: 'core/dataset/datasetLight'
|
||||
icon: 'core/dataset/datasetLight',
|
||||
orderType: BillTypeEnum.extraDatasetSub
|
||||
},
|
||||
[SubTypeEnum.extraPoints]: {
|
||||
label: 'support.wallet.subscription.type.extraPoints',
|
||||
icon: 'core/chat/chatLight'
|
||||
icon: 'core/chat/chatLight',
|
||||
orderType: BillTypeEnum.extraPoints
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
16
packages/global/support/wallet/sub/coupon/type.d.ts
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
import { SubTypeEnum, StandardSubLevelEnum } from '../constants';
|
||||
|
||||
export type TeamCouponSub = {
|
||||
type: `${SubTypeEnum}`; // Sub type
|
||||
durationDay: number; // Duration day
|
||||
level?: `${StandardSubLevelEnum}`; // Standard sub level
|
||||
extraDatasetSize?: number; // Extra dataset size
|
||||
totalPoints?: number; // Total points(Extrapoints or Standard sub)
|
||||
};
|
||||
|
||||
export type TeamCouponSchema = {
|
||||
key: string;
|
||||
subscriptions: TeamCouponSub[];
|
||||
redeemedAt?: Date;
|
||||
expiredAt?: Date;
|
||||
};
|
||||
2
packages/global/support/wallet/sub/type.d.ts
vendored
@@ -15,7 +15,7 @@ export type TeamStandardSubPlanItemType = {
|
||||
permissionCustomApiKey: boolean;
|
||||
permissionCustomCopyright: boolean; // feature
|
||||
permissionWebsiteSync: boolean;
|
||||
permissionReRank: boolean;
|
||||
permissionTeamOperationLog: boolean;
|
||||
};
|
||||
|
||||
export type StandSubPlanLevelMapType = Record<
|
||||
|
||||
@@ -7,8 +7,8 @@ export type CreateTrainingUsageProps = {
|
||||
};
|
||||
|
||||
export type GetUsageProps = {
|
||||
dateStart: Date;
|
||||
dateEnd: Date;
|
||||
dateStart: string;
|
||||
dateEnd: string;
|
||||
sources?: UsageSourceEnum[];
|
||||
teamMemberIds?: string[];
|
||||
projectName?: string;
|
||||
|
||||
@@ -65,9 +65,10 @@ export async function uploadFile({
|
||||
const bucket = getGridBucket(bucketName);
|
||||
|
||||
const fileSize = stats.size;
|
||||
// 单块大小:尽可能大,但不超过 14MB,不小于512KB
|
||||
const chunkSizeBytes = (() => {
|
||||
// 计算理想块大小:文件大小 ÷ 目标块数(10)
|
||||
const idealChunkSize = Math.ceil(fileSize / 10);
|
||||
// 计算理想块大小:文件大小 ÷ 目标块数(10)。 并且每个块需要小于 14MB
|
||||
const idealChunkSize = Math.min(Math.ceil(fileSize / 10), 14 * 1024 * 1024);
|
||||
|
||||
// 确保块大小至少为512KB
|
||||
const minChunkSize = 512 * 1024; // 512KB
|
||||
|
||||
@@ -2,16 +2,13 @@ import { uploadMongoImg } from '../image/controller';
|
||||
import FormData from 'form-data';
|
||||
import { WorkerNameEnum, runWorker } from '../../../worker/utils';
|
||||
import fs from 'fs';
|
||||
import type { ImageType, ReadFileResponse } from '../../../worker/readFile/type';
|
||||
import type { ReadFileResponse } from '../../../worker/readFile/type';
|
||||
import axios from 'axios';
|
||||
import { addLog } from '../../system/log';
|
||||
import { batchRun } from '@fastgpt/global/common/system/utils';
|
||||
import { htmlTable2Md, matchMdImg } from '@fastgpt/global/common/string/markdown';
|
||||
import { matchMdImg } from '@fastgpt/global/common/string/markdown';
|
||||
import { createPdfParseUsage } from '../../../support/wallet/usage/controller';
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
import { delay } from '@fastgpt/global/common/system/utils';
|
||||
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||
import { getImageBase64 } from '../image/utils';
|
||||
import { useDoc2xServer } from '../../../thirdProvider/doc2x';
|
||||
|
||||
export type readRawTextByLocalFileParams = {
|
||||
teamId: string;
|
||||
@@ -114,169 +111,12 @@ export const readRawContentByFileBuffer = async ({
|
||||
imageList
|
||||
};
|
||||
};
|
||||
// Doc2x api
|
||||
const parsePdfFromDoc2x = async (): Promise<ReadFileResponse> => {
|
||||
const doc2xKey = global.systemEnv.customPdfParse?.doc2xKey;
|
||||
if (!doc2xKey) return systemParse();
|
||||
|
||||
const parseTextImage = async (text: string) => {
|
||||
// Extract image links and convert to base64
|
||||
const imageList: { id: string; url: string }[] = [];
|
||||
let processedText = text.replace(/!\[.*?\]\((http[^)]+)\)/g, (match, url) => {
|
||||
const id = `IMAGE_${getNanoid()}_IMAGE`;
|
||||
imageList.push({
|
||||
id,
|
||||
url
|
||||
});
|
||||
return ``;
|
||||
});
|
||||
|
||||
// Get base64 from image url
|
||||
let resultImageList: ImageType[] = [];
|
||||
await batchRun(
|
||||
imageList,
|
||||
async (item) => {
|
||||
try {
|
||||
const { base64, mime } = await getImageBase64(item.url);
|
||||
resultImageList.push({
|
||||
uuid: item.id,
|
||||
mime,
|
||||
base64
|
||||
});
|
||||
} catch (error) {
|
||||
processedText = processedText.replace(item.id, item.url);
|
||||
addLog.warn(`Failed to get image from ${item.url}: ${getErrText(error)}`);
|
||||
}
|
||||
},
|
||||
5
|
||||
);
|
||||
|
||||
return {
|
||||
text: processedText,
|
||||
imageList: resultImageList
|
||||
};
|
||||
};
|
||||
|
||||
let startTime = Date.now();
|
||||
|
||||
// 1. Get pre-upload URL first
|
||||
const { data: preupload_data } = await axios
|
||||
.post<{ code: string; data: { uid: string; url: string } }>(
|
||||
'https://v2.doc2x.noedgeai.com/api/v2/parse/preupload',
|
||||
null,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${doc2xKey}`
|
||||
}
|
||||
}
|
||||
)
|
||||
.catch((error) => {
|
||||
return Promise.reject(
|
||||
`[Pre-upload Error] Failed to get pre-upload URL: ${getErrText(error)}`
|
||||
);
|
||||
});
|
||||
if (preupload_data?.code !== 'success') {
|
||||
return Promise.reject(`Failed to get pre-upload URL: ${JSON.stringify(preupload_data)}`);
|
||||
}
|
||||
|
||||
const upload_url = preupload_data.data.url;
|
||||
const uid = preupload_data.data.uid;
|
||||
|
||||
// 2. Upload file to pre-signed URL with binary stream
|
||||
const blob = new Blob([buffer], { type: 'application/pdf' });
|
||||
const response = await axios
|
||||
.put(upload_url, blob, {
|
||||
headers: {
|
||||
'Content-Type': 'application/pdf'
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
return Promise.reject(`[Upload Error] Failed to upload file: ${getErrText(error)}`);
|
||||
});
|
||||
if (response.status !== 200) {
|
||||
return Promise.reject(`Upload failed with status ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
await delay(5000);
|
||||
addLog.debug(`Uploaded file to Doc2x, uid: ${uid}`);
|
||||
// 3. Get the result by uid
|
||||
const checkResult = async (retry = 30) => {
|
||||
if (retry <= 0) {
|
||||
return Promise.reject(
|
||||
`[Parse Timeout Error] Failed to get result (uid: ${uid}): Process timeout`
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const { data: result_data } = await axios
|
||||
.get<{
|
||||
code: string;
|
||||
data: {
|
||||
progress: number;
|
||||
status: 'processing' | 'failed' | 'success';
|
||||
result: {
|
||||
pages: {
|
||||
md: string;
|
||||
}[];
|
||||
};
|
||||
};
|
||||
}>(`https://v2.doc2x.noedgeai.com/api/v2/parse/status?uid=${uid}`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${doc2xKey}`
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
return Promise.reject(
|
||||
`[Parse Status Error] Failed to get parse status: ${getErrText(error)}`
|
||||
);
|
||||
});
|
||||
|
||||
// Error
|
||||
if (!['ok', 'success'].includes(result_data.code)) {
|
||||
return Promise.reject(
|
||||
`Failed to get result (uid: ${uid}): ${JSON.stringify(result_data)}`
|
||||
);
|
||||
}
|
||||
|
||||
// Process
|
||||
if (['ready', 'processing'].includes(result_data.data.status)) {
|
||||
addLog.debug(`Waiting for the result, uid: ${uid}`);
|
||||
await delay(5000);
|
||||
return checkResult(retry - 1);
|
||||
}
|
||||
|
||||
// Finifsh
|
||||
if (result_data.data.status === 'success') {
|
||||
const result = result_data.data.result.pages
|
||||
.map((page) => page.md)
|
||||
.join('')
|
||||
// Do some post-processing
|
||||
.replace(/\\[\(\)]/g, '$')
|
||||
.replace(/\\[\[\]]/g, '$$')
|
||||
.replace(/<img\s+src="([^"]+)"(?:\s*\?[^>]*)?(?:\s*\/>|>)/g, '')
|
||||
.replace(/<!-- Media -->/g, '')
|
||||
.replace(/<!-- Footnote -->/g, '')
|
||||
.replace(/\$(.+?)\s+\\tag\{(.+?)\}\$/g, '$$$1 \\qquad \\qquad ($2)$$')
|
||||
.replace(/\\text\{([^}]*?)(\b\w+)_(\w+\b)([^}]*?)\}/g, '\\text{$1$2\\_$3$4}');
|
||||
|
||||
const { text, imageList } = await parseTextImage(htmlTable2Md(result));
|
||||
|
||||
return {
|
||||
pages: result_data.data.result.pages.length,
|
||||
text,
|
||||
imageList
|
||||
};
|
||||
}
|
||||
return checkResult(retry - 1);
|
||||
} catch (error) {
|
||||
if (retry > 1) {
|
||||
await delay(100);
|
||||
return checkResult(retry - 1);
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
};
|
||||
|
||||
const { pages, text, imageList } = await checkResult();
|
||||
const { pages, text, imageList } = await useDoc2xServer({ apiKey: doc2xKey }).parsePDF(buffer);
|
||||
|
||||
createPdfParseUsage({
|
||||
teamId,
|
||||
@@ -284,7 +124,6 @@ export const readRawContentByFileBuffer = async ({
|
||||
pages
|
||||
});
|
||||
|
||||
addLog.info(`Doc2x parse success, time: ${Date.now() - startTime}ms`);
|
||||
return {
|
||||
rawText: text,
|
||||
formatText: text,
|
||||
|
||||
@@ -4,6 +4,12 @@ import { LogLevelEnum } from './log/constant';
|
||||
import { connectionMongo } from '../mongo/index';
|
||||
import { getMongoLog } from './log/schema';
|
||||
|
||||
export enum EventTypeEnum {
|
||||
outLinkBot = '[Outlink bot]',
|
||||
feishuBot = '[Feishu bot]',
|
||||
wxOffiaccount = '[Offiaccount bot]'
|
||||
}
|
||||
|
||||
const logMap = {
|
||||
[LogLevelEnum.debug]: {
|
||||
levelLog: chalk.green('[Debug]')
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { ClientSession } from '../../mongo';
|
||||
import { MongoTimerLock } from './schema';
|
||||
import { addMinutes } from 'date-fns';
|
||||
|
||||
@@ -6,16 +7,23 @@ import { addMinutes } from 'date-fns';
|
||||
*/
|
||||
export const checkTimerLock = async ({
|
||||
timerId,
|
||||
lockMinuted
|
||||
lockMinuted,
|
||||
session
|
||||
}: {
|
||||
timerId: string;
|
||||
lockMinuted: number;
|
||||
session?: ClientSession;
|
||||
}) => {
|
||||
try {
|
||||
await MongoTimerLock.create({
|
||||
timerId,
|
||||
expiredTime: addMinutes(new Date(), lockMinuted)
|
||||
});
|
||||
await MongoTimerLock.create(
|
||||
[
|
||||
{
|
||||
timerId,
|
||||
expiredTime: addMinutes(new Date(), lockMinuted)
|
||||
}
|
||||
],
|
||||
{ session, ordered: true }
|
||||
);
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/* vector crud */
|
||||
import { PgVectorCtrl } from './pg/class';
|
||||
import { ObVectorCtrl } from './oceanbase/class';
|
||||
import { PgVectorCtrl } from './pg';
|
||||
import { ObVectorCtrl } from './oceanbase';
|
||||
import { getVectorsByText } from '../../core/ai/embedding';
|
||||
import { DelDatasetVectorCtrlProps, InsertVectorProps } from './controller.d';
|
||||
import { EmbeddingModelItemType } from '@fastgpt/global/core/ai/model.d';
|
||||
import { MILVUS_ADDRESS, PG_ADDRESS, OCEANBASE_ADDRESS } from './constants';
|
||||
import { MilvusCtrl } from './milvus/class';
|
||||
import { MilvusCtrl } from './milvus';
|
||||
import { setRedisCache, getRedisCache, delRedisCache, CacheKeyEnum } from '../redis/cache';
|
||||
import { throttle } from 'lodash';
|
||||
import { retryFn } from '@fastgpt/global/common/system/utils';
|
||||
@@ -12,7 +12,7 @@ import type {
|
||||
InsertVectorControllerProps
|
||||
} from '../controller.d';
|
||||
import { delay } from '@fastgpt/global/common/system/utils';
|
||||
import { addLog } from '../../../common/system/log';
|
||||
import { addLog } from '../../system/log';
|
||||
import { customNanoid } from '@fastgpt/global/common/string/tools';
|
||||
|
||||
export class MilvusCtrl {
|
||||
@@ -1,8 +1,8 @@
|
||||
/* oceanbase vector crud */
|
||||
import { DatasetVectorTableName } from '../constants';
|
||||
import { delay } from '@fastgpt/global/common/system/utils';
|
||||
import { ObClient } from './index';
|
||||
import { RowDataPacket, ResultSetHeader } from 'mysql2/promise';
|
||||
import { ObClient } from './controller';
|
||||
import { RowDataPacket } from 'mysql2/promise';
|
||||
import {
|
||||
DelDatasetVectorCtrlProps,
|
||||
EmbeddingRecallCtrlProps,
|
||||
@@ -1,9 +1,9 @@
|
||||
/* pg vector crud */
|
||||
import { DatasetVectorTableName } from '../constants';
|
||||
import { delay } from '@fastgpt/global/common/system/utils';
|
||||
import { PgClient, connectPg } from './index';
|
||||
import { PgClient, connectPg } from './controller';
|
||||
import { PgSearchRawType } from '@fastgpt/global/core/dataset/api';
|
||||
import {
|
||||
import type {
|
||||
DelDatasetVectorCtrlProps,
|
||||
EmbeddingRecallCtrlProps,
|
||||
EmbeddingRecallResponse,
|
||||
@@ -192,8 +192,7 @@ export class PgVectorCtrl {
|
||||
WITH relaxed_results AS MATERIALIZED (
|
||||
select id, collection_id, vector <#> '[${vector}]' AS score
|
||||
from ${DatasetVectorTableName}
|
||||
where team_id='${teamId}'
|
||||
AND dataset_id IN (${datasetIds.map((id) => `'${String(id)}'`).join(',')})
|
||||
where dataset_id IN (${datasetIds.map((id) => `'${String(id)}'`).join(',')})
|
||||
${filterCollectionIdSql}
|
||||
${forbidCollectionSql}
|
||||
order by score limit ${limit}
|
||||
@@ -202,6 +201,12 @@ export class PgVectorCtrl {
|
||||
);
|
||||
const rows = results?.[3]?.rows as PgSearchRawType[];
|
||||
|
||||
if (!Array.isArray(rows)) {
|
||||
return {
|
||||
results: []
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
results: rows.map((item) => ({
|
||||
id: String(item.id),
|
||||
@@ -2,7 +2,6 @@ import fs from 'fs';
|
||||
import { getAxiosConfig } from '../config';
|
||||
import axios from 'axios';
|
||||
import FormData from 'form-data';
|
||||
import { getSTTModel } from '../model';
|
||||
import { STTModelType } from '@fastgpt/global/core/ai/model.d';
|
||||
|
||||
export const aiTranscriptions = async ({
|
||||
@@ -24,7 +23,7 @@ export const aiTranscriptions = async ({
|
||||
|
||||
const aiAxiosConfig = getAxiosConfig();
|
||||
|
||||
const { data: result } = await axios<{ text: string }>({
|
||||
const { data: result } = await axios<{ text: string; usage?: { total_tokens: number } }>({
|
||||
method: 'post',
|
||||
...(modelData.requestUrl
|
||||
? { url: modelData.requestUrl }
|
||||
|
||||
@@ -10,6 +10,7 @@ import { addLog } from '../../common/system/log';
|
||||
import { i18nT } from '../../../web/i18n/utils';
|
||||
import { OpenaiAccountType } from '@fastgpt/global/support/user/team/type';
|
||||
import { getLLMModel } from './model';
|
||||
import { LLMModelItemType } from '@fastgpt/global/core/ai/model.d';
|
||||
|
||||
const aiProxyBaseUrl = process.env.AIPROXY_API_ENDPOINT
|
||||
? `${process.env.AIPROXY_API_ENDPOINT}/v1`
|
||||
@@ -44,11 +45,13 @@ export const getAxiosConfig = (props?: { userKey?: OpenaiAccountType }) => {
|
||||
};
|
||||
|
||||
export const createChatCompletion = async ({
|
||||
modelData,
|
||||
body,
|
||||
userKey,
|
||||
timeout,
|
||||
options
|
||||
}: {
|
||||
modelData?: LLMModelItemType;
|
||||
body: ChatCompletionCreateParamsNonStreaming | ChatCompletionCreateParamsStreaming;
|
||||
userKey?: OpenaiAccountType;
|
||||
timeout?: number;
|
||||
@@ -68,7 +71,12 @@ export const createChatCompletion = async ({
|
||||
)
|
||||
> => {
|
||||
try {
|
||||
const modelConstantsData = getLLMModel(body.model);
|
||||
// Rewrite model
|
||||
const modelConstantsData = modelData || getLLMModel(body.model);
|
||||
if (!modelConstantsData) {
|
||||
return Promise.reject(`${body.model} not found`);
|
||||
}
|
||||
body.model = modelConstantsData.model;
|
||||
|
||||
const formatTimeout = timeout ? timeout : body.stream ? 60000 : 600000;
|
||||
const ai = getAIApi({
|
||||
|
||||
@@ -23,23 +23,23 @@ import {
|
||||
} from '../../../common/system/config/controller';
|
||||
import { delay } from '@fastgpt/global/common/system/utils';
|
||||
|
||||
const getModelConfigBaseUrl = () => {
|
||||
const currentFileUrl = new URL(import.meta.url);
|
||||
const filePath = decodeURIComponent(
|
||||
process.platform === 'win32'
|
||||
? currentFileUrl.pathname.substring(1) // Remove leading slash on Windows
|
||||
: currentFileUrl.pathname
|
||||
);
|
||||
const modelsPath = path.join(path.dirname(filePath), 'provider');
|
||||
return modelsPath;
|
||||
};
|
||||
|
||||
/*
|
||||
TODO: 分优先级读取:
|
||||
1. 有外部挂载目录,则读取外部的
|
||||
2. 没有外部挂载目录,则读取本地的。然后试图拉取云端的进行覆盖。
|
||||
*/
|
||||
export const loadSystemModels = async (init = false) => {
|
||||
const getProviderList = () => {
|
||||
const currentFileUrl = new URL(import.meta.url);
|
||||
const filePath = decodeURIComponent(
|
||||
process.platform === 'win32'
|
||||
? currentFileUrl.pathname.substring(1) // Remove leading slash on Windows
|
||||
: currentFileUrl.pathname
|
||||
);
|
||||
const modelsPath = path.join(path.dirname(filePath), 'provider');
|
||||
|
||||
return fs.readdirSync(modelsPath) as string[];
|
||||
};
|
||||
const pushModel = (model: SystemModelItemType) => {
|
||||
global.systemModelList.push(model);
|
||||
|
||||
@@ -100,9 +100,10 @@ export const loadSystemModels = async (init = false) => {
|
||||
|
||||
try {
|
||||
const dbModels = await MongoSystemModel.find({}).lean();
|
||||
const providerList = getProviderList();
|
||||
|
||||
// System model
|
||||
// Load system model from local
|
||||
const modelsPath = getModelConfigBaseUrl();
|
||||
const providerList = fs.readdirSync(modelsPath) as string[];
|
||||
await Promise.all(
|
||||
providerList.map(async (name) => {
|
||||
const fileContent = (await import(`./provider/${name}`))?.default as {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { cloneDeep } from 'lodash';
|
||||
import { SystemModelItemType } from './type';
|
||||
|
||||
export const getDefaultLLMModel = () => global?.systemDefaultModel.llm!;
|
||||
@@ -53,5 +54,5 @@ export const findAIModel = (model: string): SystemModelItemType | undefined => {
|
||||
);
|
||||
};
|
||||
export const findModelFromAlldata = (model: string) => {
|
||||
return global.systemModelList.find((item) => item.model === model);
|
||||
return cloneDeep(global.systemModelList.find((item) => item.model === model));
|
||||
};
|
||||
|
||||
@@ -3,6 +3,7 @@ import { POST } from '../../../common/api/serverRequest';
|
||||
import { getDefaultRerankModel } from '../model';
|
||||
import { getAxiosConfig } from '../config';
|
||||
import { RerankModelItemType } from '@fastgpt/global/core/ai/model.d';
|
||||
import { countPromptTokens } from '../../../common/string/tiktoken';
|
||||
|
||||
type PostReRankResponse = {
|
||||
id: string;
|
||||
@@ -10,8 +11,17 @@ type PostReRankResponse = {
|
||||
index: number;
|
||||
relevance_score: number;
|
||||
}[];
|
||||
meta?: {
|
||||
tokens: {
|
||||
input_tokens: number;
|
||||
output_tokens: number;
|
||||
};
|
||||
};
|
||||
};
|
||||
type ReRankCallResult = {
|
||||
results: { id: string; score?: number }[];
|
||||
inputTokens: number;
|
||||
};
|
||||
type ReRankCallResult = { id: string; score?: number }[];
|
||||
|
||||
export function reRankRecall({
|
||||
model = getDefaultRerankModel(),
|
||||
@@ -28,18 +38,22 @@ export function reRankRecall({
|
||||
return Promise.reject('no rerank model');
|
||||
}
|
||||
if (documents.length === 0) {
|
||||
return Promise.resolve([]);
|
||||
return Promise.resolve({
|
||||
results: [],
|
||||
inputTokens: 0
|
||||
});
|
||||
}
|
||||
|
||||
const { baseUrl, authorization } = getAxiosConfig();
|
||||
|
||||
let start = Date.now();
|
||||
const documentsTextArray = documents.map((doc) => doc.text);
|
||||
return POST<PostReRankResponse>(
|
||||
model.requestUrl ? model.requestUrl : `${baseUrl}/rerank`,
|
||||
{
|
||||
model: model.model,
|
||||
query,
|
||||
documents: documents.map((doc) => doc.text)
|
||||
documents: documentsTextArray
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
@@ -49,17 +63,22 @@ export function reRankRecall({
|
||||
timeout: 30000
|
||||
}
|
||||
)
|
||||
.then((data) => {
|
||||
.then(async (data) => {
|
||||
addLog.info('ReRank finish:', { time: Date.now() - start });
|
||||
|
||||
if (!data?.results || data?.results?.length === 0) {
|
||||
addLog.error('ReRank error, empty result', data);
|
||||
}
|
||||
|
||||
return data?.results?.map((item) => ({
|
||||
id: documents[item.index].id,
|
||||
score: item.relevance_score
|
||||
}));
|
||||
return {
|
||||
results: data?.results?.map((item) => ({
|
||||
id: documents[item.index].id,
|
||||
score: item.relevance_score
|
||||
})),
|
||||
inputTokens:
|
||||
data?.meta?.tokens?.input_tokens ||
|
||||
(await countPromptTokens(documentsTextArray.join('\n') + query, ''))
|
||||
};
|
||||
})
|
||||
.catch((err) => {
|
||||
addLog.error('rerank error', err);
|
||||
|
||||
94
packages/service/core/app/mcp.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
||||
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
|
||||
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
||||
import { ToolType } from '@fastgpt/global/core/app/type';
|
||||
import { addLog } from '../../common/system/log';
|
||||
import { retryFn } from '@fastgpt/global/common/system/utils';
|
||||
|
||||
export class MCPClient {
|
||||
private client: Client;
|
||||
private url: string;
|
||||
|
||||
constructor(config: { url: string }) {
|
||||
this.url = config.url;
|
||||
this.client = new Client({
|
||||
name: 'FastGPT-MCP-client',
|
||||
version: '1.0.0'
|
||||
});
|
||||
}
|
||||
|
||||
private async getConnection(): Promise<Client> {
|
||||
try {
|
||||
const transport = new StreamableHTTPClientTransport(new URL(this.url));
|
||||
await this.client.connect(transport);
|
||||
return this.client;
|
||||
} catch (error) {
|
||||
await this.client.connect(new SSEClientTransport(new URL(this.url)));
|
||||
return this.client;
|
||||
}
|
||||
}
|
||||
|
||||
// 内部方法:关闭连接
|
||||
private async closeConnection() {
|
||||
try {
|
||||
await retryFn(() => this.client.close(), 3);
|
||||
} catch (error) {
|
||||
addLog.error('[MCP Client] Failed to close connection:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get available tools list
|
||||
* @returns List of tools
|
||||
*/
|
||||
public async getTools(): Promise<ToolType[]> {
|
||||
try {
|
||||
const client = await this.getConnection();
|
||||
const response = await client.listTools();
|
||||
|
||||
if (!Array.isArray(response.tools)) {
|
||||
return Promise.reject('[MCP Client] Get tools response is not an array');
|
||||
}
|
||||
|
||||
const tools = response.tools.map((tool) => ({
|
||||
name: tool.name,
|
||||
description: tool.description || '',
|
||||
inputSchema: tool.inputSchema || {
|
||||
type: 'object',
|
||||
properties: {}
|
||||
}
|
||||
}));
|
||||
|
||||
// @ts-ignore
|
||||
return tools;
|
||||
} catch (error) {
|
||||
addLog.error('[MCP Client] Failed to get tools:', error);
|
||||
return Promise.reject(error);
|
||||
} finally {
|
||||
await this.closeConnection();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call tool
|
||||
* @param toolName Tool name
|
||||
* @param params Parameters
|
||||
* @returns Tool execution result
|
||||
*/
|
||||
public async toolCall(toolName: string, params: Record<string, any>): Promise<any> {
|
||||
try {
|
||||
const client = await this.getConnection();
|
||||
addLog.debug(`[MCP Client] Call tool: ${toolName}`, params);
|
||||
|
||||
return await client.callTool({
|
||||
name: toolName,
|
||||
arguments: params
|
||||
});
|
||||
} catch (error) {
|
||||
addLog.error(`[MCP Client] Failed to call tool ${toolName}:`, error);
|
||||
return Promise.reject(error);
|
||||
} finally {
|
||||
await this.closeConnection();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -82,7 +82,8 @@ const ChatItemSchema = new Schema({
|
||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
||||
type: Array,
|
||||
default: []
|
||||
}
|
||||
},
|
||||
durationSeconds: Number
|
||||
});
|
||||
|
||||
try {
|
||||
|
||||
@@ -34,6 +34,7 @@ type Props = {
|
||||
outLinkUid?: string;
|
||||
content: [UserChatItemType & { dataId?: string }, AIChatItemType & { dataId?: string }];
|
||||
metadata?: Record<string, any>;
|
||||
durationSeconds: number; //s
|
||||
};
|
||||
|
||||
export async function saveChat({
|
||||
@@ -51,8 +52,11 @@ export async function saveChat({
|
||||
shareId,
|
||||
outLinkUid,
|
||||
content,
|
||||
durationSeconds,
|
||||
metadata = {}
|
||||
}: Props) {
|
||||
if (!chatId || chatId === 'NO_RECORD_HISTORIES') return;
|
||||
|
||||
try {
|
||||
const chat = await MongoChat.findOne(
|
||||
{
|
||||
@@ -78,34 +82,33 @@ export async function saveChat({
|
||||
// Format save chat content: Remove quote q/a
|
||||
const processedContent = content.map((item) => {
|
||||
if (item.obj === ChatRoleEnum.AI) {
|
||||
const nodeResponse = item[DispatchNodeResponseKeyEnum.nodeResponse];
|
||||
const nodeResponse = item[DispatchNodeResponseKeyEnum.nodeResponse]?.map((responseItem) => {
|
||||
if (
|
||||
responseItem.moduleType === FlowNodeTypeEnum.datasetSearchNode &&
|
||||
responseItem.quoteList
|
||||
) {
|
||||
return {
|
||||
...responseItem,
|
||||
quoteList: responseItem.quoteList.map((quote: any) => ({
|
||||
id: quote.id,
|
||||
chunkIndex: quote.chunkIndex,
|
||||
datasetId: quote.datasetId,
|
||||
collectionId: quote.collectionId,
|
||||
sourceId: quote.sourceId,
|
||||
sourceName: quote.sourceName,
|
||||
score: quote.score,
|
||||
tokens: quote.tokens
|
||||
}))
|
||||
};
|
||||
}
|
||||
return responseItem;
|
||||
});
|
||||
|
||||
if (nodeResponse) {
|
||||
return {
|
||||
...item,
|
||||
[DispatchNodeResponseKeyEnum.nodeResponse]: nodeResponse.map((responseItem) => {
|
||||
if (
|
||||
responseItem.moduleType === FlowNodeTypeEnum.datasetSearchNode &&
|
||||
responseItem.quoteList
|
||||
) {
|
||||
return {
|
||||
...responseItem,
|
||||
quoteList: responseItem.quoteList.map((quote: any) => ({
|
||||
id: quote.id,
|
||||
chunkIndex: quote.chunkIndex,
|
||||
datasetId: quote.datasetId,
|
||||
collectionId: quote.collectionId,
|
||||
sourceId: quote.sourceId,
|
||||
sourceName: quote.sourceName,
|
||||
score: quote.score,
|
||||
tokens: quote.tokens
|
||||
}))
|
||||
};
|
||||
}
|
||||
return responseItem;
|
||||
})
|
||||
};
|
||||
}
|
||||
return {
|
||||
...item,
|
||||
[DispatchNodeResponseKeyEnum.nodeResponse]: nodeResponse,
|
||||
durationSeconds
|
||||
};
|
||||
}
|
||||
return item;
|
||||
});
|
||||
@@ -175,13 +178,15 @@ export const updateInteractiveChat = async ({
|
||||
appId,
|
||||
userInteractiveVal,
|
||||
aiResponse,
|
||||
newVariables
|
||||
newVariables,
|
||||
durationSeconds
|
||||
}: {
|
||||
chatId: string;
|
||||
appId: string;
|
||||
userInteractiveVal: string;
|
||||
aiResponse: AIChatItemType & { dataId?: string };
|
||||
newVariables?: Record<string, any>;
|
||||
durationSeconds: number;
|
||||
}) => {
|
||||
if (!chatId) return;
|
||||
|
||||
@@ -246,6 +251,10 @@ export const updateInteractiveChat = async ({
|
||||
chatItem.value = chatItem.value ? [...chatItem.value, ...aiResponse.value] : aiResponse.value;
|
||||
}
|
||||
|
||||
chatItem.durationSeconds = chatItem.durationSeconds
|
||||
? +(chatItem.durationSeconds + durationSeconds).toFixed(2)
|
||||
: durationSeconds;
|
||||
|
||||
await mongoSessionRun(async (session) => {
|
||||
await chatItem.save({ session });
|
||||
await MongoChat.updateOne(
|
||||
|
||||
@@ -9,7 +9,7 @@ import { DatasetCollectionSchemaType, DatasetSchemaType } from '@fastgpt/global/
|
||||
import { MongoDatasetTraining } from '../training/schema';
|
||||
import { MongoDatasetData } from '../data/schema';
|
||||
import { delImgByRelatedId } from '../../../common/file/image/controller';
|
||||
import { deleteDatasetDataVector } from '../../../common/vectorStore/controller';
|
||||
import { deleteDatasetDataVector } from '../../../common/vectorDB/controller';
|
||||
import { delFileByFileIdList } from '../../../common/file/gridfs/controller';
|
||||
import { BucketNameEnum } from '@fastgpt/global/common/file/constants';
|
||||
import { ClientSession } from '../../../common/mongo';
|
||||
|
||||
@@ -5,7 +5,7 @@ import { delCollectionRelatedSource } from './collection/controller';
|
||||
import { ClientSession } from '../../common/mongo';
|
||||
import { MongoDatasetTraining } from './training/schema';
|
||||
import { MongoDatasetData } from './data/schema';
|
||||
import { deleteDatasetDataVector } from '../../common/vectorStore/controller';
|
||||
import { deleteDatasetDataVector } from '../../common/vectorDB/controller';
|
||||
import { MongoDatasetDataText } from './data/dataTextSchema';
|
||||
import { DatasetErrEnum } from '@fastgpt/global/common/error/code/dataset';
|
||||
import { retryFn } from '@fastgpt/global/common/system/utils';
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
DatasetSearchModeMap,
|
||||
SearchScoreTypeEnum
|
||||
} from '@fastgpt/global/core/dataset/constants';
|
||||
import { recallFromVectorStore } from '../../../common/vectorStore/controller';
|
||||
import { recallFromVectorStore } from '../../../common/vectorDB/controller';
|
||||
import { getVectorsByText } from '../../ai/embedding';
|
||||
import { getEmbeddingModel, getDefaultRerankModel, getLLMModel } from '../../ai/model';
|
||||
import { MongoDatasetData } from '../data/schema';
|
||||
@@ -62,7 +62,8 @@ export type SearchDatasetDataProps = {
|
||||
|
||||
export type SearchDatasetDataResponse = {
|
||||
searchRes: SearchDataResponseItemType[];
|
||||
tokens: number;
|
||||
embeddingTokens: number;
|
||||
reRankInputTokens: number;
|
||||
searchMode: `${DatasetSearchModeEnum}`;
|
||||
limit: number;
|
||||
similarity: number;
|
||||
@@ -86,8 +87,11 @@ export const datasetDataReRank = async ({
|
||||
rerankModel?: RerankModelItemType;
|
||||
data: SearchDataResponseItemType[];
|
||||
query: string;
|
||||
}): Promise<SearchDataResponseItemType[]> => {
|
||||
const results = await reRankRecall({
|
||||
}): Promise<{
|
||||
results: SearchDataResponseItemType[];
|
||||
inputTokens: number;
|
||||
}> => {
|
||||
const { results, inputTokens } = await reRankRecall({
|
||||
model: rerankModel,
|
||||
query,
|
||||
documents: data.map((item) => ({
|
||||
@@ -114,7 +118,10 @@ export const datasetDataReRank = async ({
|
||||
})
|
||||
.filter(Boolean) as SearchDataResponseItemType[];
|
||||
|
||||
return mergeResult;
|
||||
return {
|
||||
results: mergeResult,
|
||||
inputTokens
|
||||
};
|
||||
};
|
||||
export const filterDatasetDataByMaxTokens = async (
|
||||
data: SearchDataResponseItemType[],
|
||||
@@ -694,14 +701,23 @@ export async function searchDatasetData(
|
||||
const { embeddingLimit, fullTextLimit } = countRecallLimit();
|
||||
|
||||
// recall
|
||||
const { embeddingRecallResults, fullTextRecallResults, tokens } = await multiQueryRecall({
|
||||
const {
|
||||
embeddingRecallResults,
|
||||
fullTextRecallResults,
|
||||
tokens: embeddingTokens
|
||||
} = await multiQueryRecall({
|
||||
embeddingLimit,
|
||||
fullTextLimit
|
||||
});
|
||||
|
||||
// ReRank results
|
||||
const reRankResults = await (async () => {
|
||||
if (!usingReRank) return [];
|
||||
const { results: reRankResults, inputTokens: reRankInputTokens } = await (async () => {
|
||||
if (!usingReRank) {
|
||||
return {
|
||||
results: [],
|
||||
inputTokens: 0
|
||||
};
|
||||
}
|
||||
|
||||
set = new Set<string>(embeddingRecallResults.map((item) => item.id));
|
||||
const concatRecallResults = embeddingRecallResults.concat(
|
||||
@@ -725,7 +741,10 @@ export async function searchDatasetData(
|
||||
});
|
||||
} catch (error) {
|
||||
usingReRank = false;
|
||||
return [];
|
||||
return {
|
||||
results: [],
|
||||
inputTokens: 0
|
||||
};
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -790,7 +809,8 @@ export async function searchDatasetData(
|
||||
|
||||
return {
|
||||
searchRes: filterMaxTokensResult,
|
||||
tokens,
|
||||
embeddingTokens,
|
||||
reRankInputTokens,
|
||||
searchMode,
|
||||
limit: maxTokens,
|
||||
similarity,
|
||||
|
||||
@@ -103,7 +103,7 @@ export async function dispatchContentExtract(props: Props): Promise<Response> {
|
||||
|
||||
// auto fill required fields
|
||||
extractKeys.forEach((item) => {
|
||||
if (item.required && !arg[item.key]) {
|
||||
if (item.required && arg[item.key] === undefined) {
|
||||
arg[item.key] = item.defaultValue || '';
|
||||
}
|
||||
});
|
||||
|
||||
@@ -338,7 +338,17 @@ async function filterDatasetQuote({
|
||||
model: LLMModelItemType;
|
||||
quoteTemplate: string;
|
||||
}) {
|
||||
function getValue(item: SearchDataResponseItemType, index: number) {
|
||||
function getValue({
|
||||
item,
|
||||
index,
|
||||
sourceList
|
||||
}: {
|
||||
item: SearchDataResponseItemType;
|
||||
index: number;
|
||||
sourceList: { sourceName: string; sourceId: string; sourceIndex: number }[];
|
||||
}) {
|
||||
const source = sourceList.find((source) => source.sourceId === item.sourceId);
|
||||
|
||||
return replaceVariable(quoteTemplate, {
|
||||
id: item.id,
|
||||
q: item.q,
|
||||
@@ -346,6 +356,7 @@ async function filterDatasetQuote({
|
||||
updateTime: formatTime2YMDHM(item.updateTime),
|
||||
source: item.sourceName,
|
||||
sourceId: String(item.sourceId || ''),
|
||||
sourceIndex: source?.sourceIndex || 1,
|
||||
index: index + 1
|
||||
});
|
||||
}
|
||||
@@ -353,9 +364,24 @@ async function filterDatasetQuote({
|
||||
// slice filterSearch
|
||||
const filterQuoteQA = await filterSearchResultsByMaxChars(quoteQA, model.quoteMaxToken);
|
||||
|
||||
const sourceList = Object.values(
|
||||
filterQuoteQA.reduce((acc: Record<string, SearchDataResponseItemType[]>, cur) => {
|
||||
if (!acc[cur.collectionId]) {
|
||||
acc[cur.collectionId] = [cur];
|
||||
}
|
||||
return acc;
|
||||
}, {})
|
||||
)
|
||||
.flat()
|
||||
.map((item, index) => ({
|
||||
sourceName: item.sourceName || '',
|
||||
sourceId: item.sourceId || '',
|
||||
sourceIndex: index + 1
|
||||
}));
|
||||
|
||||
const datasetQuoteText =
|
||||
filterQuoteQA.length > 0
|
||||
? `${filterQuoteQA.map((item, index) => getValue(item, index).trim()).join('\n------\n')}`
|
||||
? `${filterQuoteQA.map((item, index) => getValue({ item, index, sourceList }).trim()).join('\n------\n')}`
|
||||
: '';
|
||||
|
||||
return {
|
||||
|
||||
@@ -12,7 +12,6 @@ import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workfl
|
||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||
import { DatasetSearchModeEnum } from '@fastgpt/global/core/dataset/constants';
|
||||
import { ChatNodeUsageType } from '@fastgpt/global/support/wallet/bill/type';
|
||||
import { checkTeamReRankPermission } from '../../../../support/permission/teamLimit';
|
||||
import { MongoDataset } from '../../../dataset/schema';
|
||||
import { i18nT } from '../../../../../web/i18n/utils';
|
||||
import { filterDatasetsByTmbId } from '../../../dataset/utils';
|
||||
@@ -119,6 +118,8 @@ export async function dispatchDatasetSearch(
|
||||
const vectorModel = getEmbeddingModel(
|
||||
(await MongoDataset.findById(datasets[0].datasetId, 'vectorModel').lean())?.vectorModel
|
||||
);
|
||||
// Get Rerank Model
|
||||
const rerankModelData = getRerankModel(rerankModel);
|
||||
|
||||
// start search
|
||||
const searchData = {
|
||||
@@ -132,14 +133,15 @@ export async function dispatchDatasetSearch(
|
||||
datasetIds,
|
||||
searchMode,
|
||||
embeddingWeight,
|
||||
usingReRank: usingReRank && (await checkTeamReRankPermission(teamId)),
|
||||
rerankModel: getRerankModel(rerankModel),
|
||||
usingReRank,
|
||||
rerankModel: rerankModelData,
|
||||
rerankWeight,
|
||||
collectionFilterMatch
|
||||
};
|
||||
const {
|
||||
searchRes,
|
||||
tokens,
|
||||
embeddingTokens,
|
||||
reRankInputTokens,
|
||||
usingSimilarityFilter,
|
||||
usingReRank: searchUsingReRank,
|
||||
queryExtensionResult,
|
||||
@@ -164,17 +166,29 @@ export async function dispatchDatasetSearch(
|
||||
const { totalPoints: embeddingTotalPoints, modelName: embeddingModelName } =
|
||||
formatModelChars2Points({
|
||||
model: vectorModel.model,
|
||||
inputTokens: tokens,
|
||||
inputTokens: embeddingTokens,
|
||||
modelType: ModelTypeEnum.embedding
|
||||
});
|
||||
nodeDispatchUsages.push({
|
||||
totalPoints: embeddingTotalPoints,
|
||||
moduleName: node.name,
|
||||
model: embeddingModelName,
|
||||
inputTokens: tokens
|
||||
inputTokens: embeddingTokens
|
||||
});
|
||||
// Rerank
|
||||
const { totalPoints: reRankTotalPoints, modelName: reRankModelName } = formatModelChars2Points({
|
||||
model: rerankModelData.model,
|
||||
inputTokens: reRankInputTokens,
|
||||
modelType: ModelTypeEnum.rerank
|
||||
});
|
||||
nodeDispatchUsages.push({
|
||||
totalPoints: reRankTotalPoints,
|
||||
moduleName: node.name,
|
||||
model: reRankModelName,
|
||||
inputTokens: reRankInputTokens
|
||||
});
|
||||
// Query extension
|
||||
const { totalPoints: queryExtensionTotalPoints } = (() => {
|
||||
(() => {
|
||||
if (queryExtensionResult) {
|
||||
const { totalPoints, modelName } = formatModelChars2Points({
|
||||
model: queryExtensionResult.model,
|
||||
@@ -198,7 +212,7 @@ export async function dispatchDatasetSearch(
|
||||
};
|
||||
})();
|
||||
// Deep search
|
||||
const { totalPoints: deepSearchTotalPoints } = (() => {
|
||||
(() => {
|
||||
if (deepSearchResult) {
|
||||
const { totalPoints, modelName } = formatModelChars2Points({
|
||||
model: deepSearchResult.model,
|
||||
@@ -221,20 +235,26 @@ export async function dispatchDatasetSearch(
|
||||
totalPoints: 0
|
||||
};
|
||||
})();
|
||||
const totalPoints = embeddingTotalPoints + queryExtensionTotalPoints + deepSearchTotalPoints;
|
||||
|
||||
const totalPoints = nodeDispatchUsages.reduce((acc, item) => acc + item.totalPoints, 0);
|
||||
|
||||
const responseData: DispatchNodeResponseType & { totalPoints: number } = {
|
||||
totalPoints,
|
||||
query: userChatInput,
|
||||
model: vectorModel.model,
|
||||
inputTokens: tokens,
|
||||
embeddingModel: vectorModel.name,
|
||||
embeddingTokens,
|
||||
similarity: usingSimilarityFilter ? similarity : undefined,
|
||||
limit,
|
||||
searchMode,
|
||||
embeddingWeight: searchMode === DatasetSearchModeEnum.mixedRecall ? embeddingWeight : undefined,
|
||||
rerankModel: usingReRank ? getRerankModel(rerankModel)?.name : undefined,
|
||||
rerankWeight: usingReRank ? rerankWeight : undefined,
|
||||
searchUsingReRank: searchUsingReRank,
|
||||
// Rerank
|
||||
...(searchUsingReRank && {
|
||||
rerankModel: rerankModelData?.name,
|
||||
rerankWeight: rerankWeight,
|
||||
reRankInputTokens
|
||||
}),
|
||||
searchUsingReRank,
|
||||
// Results
|
||||
quoteList: searchRes,
|
||||
queryExtensionResult,
|
||||
deepSearchResult
|
||||
|
||||
@@ -74,7 +74,7 @@ import { dispatchLoopStart } from './loop/runLoopStart';
|
||||
import { dispatchFormInput } from './interactive/formInput';
|
||||
import { dispatchToolParams } from './agent/runTool/toolParams';
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
import { filterModuleTypeList } from '@fastgpt/global/core/chat/utils';
|
||||
import { filterPublicNodeResponseData } from '@fastgpt/global/core/chat/utils';
|
||||
import { dispatchRunTool } from './plugin/runTool';
|
||||
|
||||
const callbackMap: Record<FlowNodeTypeEnum, Function> = {
|
||||
@@ -137,8 +137,10 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
||||
stream = false,
|
||||
version = 'v1',
|
||||
responseDetail = true,
|
||||
responseAllData = true,
|
||||
...props
|
||||
} = data;
|
||||
const startTime = Date.now();
|
||||
|
||||
rewriteRuntimeWorkFlow(runtimeNodes, runtimeEdges);
|
||||
|
||||
@@ -162,16 +164,24 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
||||
[DispatchNodeResponseKeyEnum.runTimes]: 1,
|
||||
[DispatchNodeResponseKeyEnum.assistantResponses]: [],
|
||||
[DispatchNodeResponseKeyEnum.toolResponses]: null,
|
||||
newVariables: removeSystemVariable(variables, externalProvider.externalWorkflowVariables)
|
||||
newVariables: removeSystemVariable(variables, externalProvider.externalWorkflowVariables),
|
||||
durationSeconds: 0
|
||||
};
|
||||
}
|
||||
|
||||
let workflowRunTimes = 0;
|
||||
|
||||
// set sse response headers
|
||||
// Init
|
||||
if (isRootRuntime) {
|
||||
// set sse response headers
|
||||
res?.setHeader('Connection', 'keep-alive'); // Set keepalive for long connection
|
||||
if (stream && res) {
|
||||
res.on('close', () => res.end());
|
||||
res.on('error', () => {
|
||||
addLog.error('Request error');
|
||||
res.end();
|
||||
});
|
||||
|
||||
res.setHeader('Content-Type', 'text/event-stream;charset=utf-8');
|
||||
res.setHeader('Access-Control-Allow-Origin', '*');
|
||||
res.setHeader('X-Accel-Buffering', 'no');
|
||||
@@ -191,13 +201,14 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
||||
};
|
||||
sendStreamTimerSign();
|
||||
}
|
||||
}
|
||||
|
||||
variables = {
|
||||
...getSystemVariable(data),
|
||||
...externalProvider.externalWorkflowVariables,
|
||||
...variables
|
||||
};
|
||||
// Add system variables
|
||||
variables = {
|
||||
...getSystemVariable(data),
|
||||
...externalProvider.externalWorkflowVariables,
|
||||
...variables
|
||||
};
|
||||
}
|
||||
|
||||
let chatResponses: ChatHistoryItemResType[] = []; // response request and save to database
|
||||
let chatAssistantResponse: AIChatItemValueItemType[] = []; // The value will be returned to the user
|
||||
@@ -640,16 +651,15 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
||||
})();
|
||||
|
||||
// Response node response
|
||||
if (
|
||||
version === 'v2' &&
|
||||
!props.isToolCall &&
|
||||
isRootRuntime &&
|
||||
formatResponseData &&
|
||||
!(responseDetail === false && filterModuleTypeList.includes(formatResponseData.moduleType))
|
||||
) {
|
||||
if (version === 'v2' && !props.isToolCall && isRootRuntime && formatResponseData) {
|
||||
props.workflowStreamResponse?.({
|
||||
event: SseResponseEventEnum.flowNodeResponse,
|
||||
data: formatResponseData
|
||||
data: responseAllData
|
||||
? formatResponseData
|
||||
: filterPublicNodeResponseData({
|
||||
flowResponses: [formatResponseData],
|
||||
responseDetail
|
||||
})[0]
|
||||
});
|
||||
}
|
||||
|
||||
@@ -737,6 +747,15 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
||||
}
|
||||
})();
|
||||
|
||||
const durationSeconds = +((Date.now() - startTime) / 1000).toFixed(2);
|
||||
|
||||
if (isRootRuntime && stream) {
|
||||
props.workflowStreamResponse?.({
|
||||
event: SseResponseEventEnum.workflowDuration,
|
||||
data: { durationSeconds }
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
flowResponses: chatResponses,
|
||||
flowUsages: chatNodeUsages,
|
||||
@@ -750,7 +769,8 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
||||
[DispatchNodeResponseKeyEnum.assistantResponses]:
|
||||
mergeAssistantResponseAnswerText(chatAssistantResponse),
|
||||
[DispatchNodeResponseKeyEnum.toolResponses]: toolRunResponse,
|
||||
newVariables: removeSystemVariable(variables, externalProvider.externalWorkflowVariables)
|
||||
newVariables: removeSystemVariable(variables, externalProvider.externalWorkflowVariables),
|
||||
durationSeconds
|
||||
};
|
||||
} catch (error) {
|
||||
return Promise.reject(error);
|
||||
|
||||
@@ -2,10 +2,10 @@ import {
|
||||
DispatchNodeResultType,
|
||||
ModuleDispatchProps
|
||||
} from '@fastgpt/global/core/workflow/runtime/type';
|
||||
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
|
||||
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||
import { NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import { MCPClient } from '../../../app/mcp';
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
|
||||
type RunToolProps = ModuleDispatchProps<{
|
||||
toolData: {
|
||||
@@ -15,7 +15,7 @@ type RunToolProps = ModuleDispatchProps<{
|
||||
}>;
|
||||
|
||||
type RunToolResponse = DispatchNodeResultType<{
|
||||
[NodeOutputKeyEnum.rawResponse]: any;
|
||||
[NodeOutputKeyEnum.rawResponse]?: any;
|
||||
}>;
|
||||
|
||||
export const dispatchRunTool = async (props: RunToolProps): Promise<RunToolResponse> => {
|
||||
@@ -27,34 +27,26 @@ export const dispatchRunTool = async (props: RunToolProps): Promise<RunToolRespo
|
||||
const { toolData, ...restParams } = params;
|
||||
const { name: toolName, url } = toolData;
|
||||
|
||||
const client = new Client({
|
||||
name: 'FastGPT-MCP-client',
|
||||
version: '1.0.0'
|
||||
});
|
||||
const mcpClient = new MCPClient({ url });
|
||||
|
||||
const result = await (async () => {
|
||||
try {
|
||||
const transport = new SSEClientTransport(new URL(url));
|
||||
await client.connect(transport);
|
||||
try {
|
||||
const result = await mcpClient.toolCall(toolName, restParams);
|
||||
|
||||
return await client.callTool({
|
||||
name: toolName,
|
||||
arguments: restParams
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error running MCP tool:', error);
|
||||
return Promise.reject(error);
|
||||
} finally {
|
||||
await client.close();
|
||||
}
|
||||
})();
|
||||
|
||||
return {
|
||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
||||
toolRes: result,
|
||||
moduleLogo: avatar
|
||||
},
|
||||
[DispatchNodeResponseKeyEnum.toolResponses]: result,
|
||||
[NodeOutputKeyEnum.rawResponse]: result
|
||||
};
|
||||
return {
|
||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
||||
toolRes: result,
|
||||
moduleLogo: avatar
|
||||
},
|
||||
[DispatchNodeResponseKeyEnum.toolResponses]: result,
|
||||
[NodeOutputKeyEnum.rawResponse]: result
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
||||
moduleLogo: avatar,
|
||||
error: getErrText(error)
|
||||
},
|
||||
[DispatchNodeResponseKeyEnum.toolResponses]: getErrText(error)
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -26,6 +26,7 @@ export type DispatchFlowResponse = {
|
||||
[DispatchNodeResponseKeyEnum.assistantResponses]: AIChatItemValueItemType[];
|
||||
[DispatchNodeResponseKeyEnum.runTimes]: number;
|
||||
newVariables: Record<string, string>;
|
||||
durationSeconds: number;
|
||||
};
|
||||
|
||||
export type WorkflowResponseType = ({
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"@fastgpt/global": "workspace:*",
|
||||
"@modelcontextprotocol/sdk": "^1.9.0",
|
||||
"@modelcontextprotocol/sdk": "^1.10.0",
|
||||
"@node-rs/jieba": "2.0.1",
|
||||
"@xmldom/xmldom": "^0.8.10",
|
||||
"@zilliz/milvus2-sdk-node": "2.4.2",
|
||||
|
||||
@@ -38,11 +38,14 @@ const McpKeySchema = new Schema({
|
||||
ref: AppCollectionName,
|
||||
required: true
|
||||
},
|
||||
appName: String,
|
||||
toolName: {
|
||||
type: String
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
description: {
|
||||
type: String
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
@@ -14,7 +14,6 @@ export function addOperationLog<T extends OperationLogEventEnum>({
|
||||
event: T;
|
||||
params?: TemplateParamsMap[T];
|
||||
}) {
|
||||
console.log('Insert log');
|
||||
retryFn(() =>
|
||||
MongoOperationLog.create({
|
||||
tmbId: tmbId,
|
||||
|
||||
@@ -1,85 +1,74 @@
|
||||
import { OperationLogEventEnum } from '@fastgpt/global/support/operationLog/constants';
|
||||
import { i18nT } from '../../../web/i18n/utils';
|
||||
|
||||
export const operationLogI18nMap = {
|
||||
export const operationLogMap = {
|
||||
[OperationLogEventEnum.LOGIN]: {
|
||||
content: i18nT('account_team:log_login'),
|
||||
typeLabel: i18nT('account_team:login')
|
||||
typeLabel: i18nT('account_team:login'),
|
||||
params: {} as { name?: string }
|
||||
},
|
||||
[OperationLogEventEnum.CREATE_INVITATION_LINK]: {
|
||||
content: i18nT('account_team:log_create_invitation_link'),
|
||||
typeLabel: i18nT('account_team:create_invitation_link')
|
||||
typeLabel: i18nT('account_team:create_invitation_link'),
|
||||
params: {} as { name?: string; link: string }
|
||||
},
|
||||
[OperationLogEventEnum.JOIN_TEAM]: {
|
||||
content: i18nT('account_team:log_join_team'),
|
||||
typeLabel: i18nT('account_team:join_team')
|
||||
typeLabel: i18nT('account_team:join_team'),
|
||||
params: {} as { name?: string; link: string }
|
||||
},
|
||||
[OperationLogEventEnum.CHANGE_MEMBER_NAME]: {
|
||||
content: i18nT('account_team:log_change_member_name'),
|
||||
typeLabel: i18nT('account_team:change_member_name')
|
||||
typeLabel: i18nT('account_team:change_member_name'),
|
||||
params: {} as { name?: string; memberName: string; newName: string }
|
||||
},
|
||||
[OperationLogEventEnum.KICK_OUT_TEAM]: {
|
||||
content: i18nT('account_team:log_kick_out_team'),
|
||||
typeLabel: i18nT('account_team:kick_out_team')
|
||||
typeLabel: i18nT('account_team:kick_out_team'),
|
||||
params: {} as { name?: string; memberName: string }
|
||||
},
|
||||
[OperationLogEventEnum.RECOVER_TEAM_MEMBER]: {
|
||||
content: i18nT('account_team:log_recover_team_member'),
|
||||
typeLabel: i18nT('account_team:recover_team_member'),
|
||||
params: {} as { name?: string; memberName: string }
|
||||
},
|
||||
[OperationLogEventEnum.CREATE_DEPARTMENT]: {
|
||||
content: i18nT('account_team:log_create_department'),
|
||||
typeLabel: i18nT('account_team:create_department')
|
||||
typeLabel: i18nT('account_team:create_department'),
|
||||
params: {} as { name?: string; departmentName: string }
|
||||
},
|
||||
[OperationLogEventEnum.CHANGE_DEPARTMENT]: {
|
||||
content: i18nT('account_team:log_change_department'),
|
||||
typeLabel: i18nT('account_team:change_department_name')
|
||||
typeLabel: i18nT('account_team:change_department_name'),
|
||||
params: {} as { name?: string; departmentName: string }
|
||||
},
|
||||
[OperationLogEventEnum.DELETE_DEPARTMENT]: {
|
||||
content: i18nT('account_team:log_delete_department'),
|
||||
typeLabel: i18nT('account_team:delete_department')
|
||||
typeLabel: i18nT('account_team:delete_department'),
|
||||
params: {} as { name?: string; departmentName: string }
|
||||
},
|
||||
[OperationLogEventEnum.RELOCATE_DEPARTMENT]: {
|
||||
content: i18nT('account_team:log_relocate_department'),
|
||||
typeLabel: i18nT('account_team:relocate_department')
|
||||
typeLabel: i18nT('account_team:relocate_department'),
|
||||
params: {} as { name?: string; departmentName: string }
|
||||
},
|
||||
[OperationLogEventEnum.CREATE_GROUP]: {
|
||||
content: i18nT('account_team:log_create_group'),
|
||||
typeLabel: i18nT('account_team:create_group')
|
||||
typeLabel: i18nT('account_team:create_group'),
|
||||
params: {} as { name?: string; groupName: string }
|
||||
},
|
||||
[OperationLogEventEnum.DELETE_GROUP]: {
|
||||
content: i18nT('account_team:log_delete_group'),
|
||||
typeLabel: i18nT('account_team:delete_group')
|
||||
typeLabel: i18nT('account_team:delete_group'),
|
||||
params: {} as { name?: string; groupName: string }
|
||||
},
|
||||
[OperationLogEventEnum.ASSIGN_PERMISSION]: {
|
||||
content: i18nT('account_team:log_assign_permission'),
|
||||
typeLabel: i18nT('account_team:assign_permission')
|
||||
typeLabel: i18nT('account_team:assign_permission'),
|
||||
params: {} as { name?: string; objectName: string; permission: string }
|
||||
}
|
||||
} as const;
|
||||
|
||||
export type TemplateParamsMap = {
|
||||
[OperationLogEventEnum.LOGIN]: { name?: string };
|
||||
[OperationLogEventEnum.CREATE_INVITATION_LINK]: { name?: string; link: string };
|
||||
[OperationLogEventEnum.JOIN_TEAM]: { name?: string; link: string };
|
||||
[OperationLogEventEnum.CHANGE_MEMBER_NAME]: {
|
||||
name?: string;
|
||||
memberName: string;
|
||||
newName: string;
|
||||
};
|
||||
[OperationLogEventEnum.KICK_OUT_TEAM]: {
|
||||
name?: string;
|
||||
memberName: string;
|
||||
};
|
||||
[OperationLogEventEnum.CREATE_DEPARTMENT]: { name?: string; departmentName: string };
|
||||
[OperationLogEventEnum.CHANGE_DEPARTMENT]: {
|
||||
name?: string;
|
||||
departmentName: string;
|
||||
};
|
||||
[OperationLogEventEnum.DELETE_DEPARTMENT]: { name?: string; departmentName: string };
|
||||
[OperationLogEventEnum.RELOCATE_DEPARTMENT]: {
|
||||
name?: string;
|
||||
departmentName: string;
|
||||
};
|
||||
[OperationLogEventEnum.CREATE_GROUP]: { name?: string; groupName: string };
|
||||
[OperationLogEventEnum.DELETE_GROUP]: { name?: string; groupName: string };
|
||||
[OperationLogEventEnum.ASSIGN_PERMISSION]: {
|
||||
name?: string;
|
||||
objectName: string;
|
||||
permission: string;
|
||||
};
|
||||
[K in OperationLogEventEnum]: (typeof operationLogMap)[K]['params'];
|
||||
};
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { mongoSessionRun } from '../../common/mongo/sessionRun';
|
||||
import { MongoResourcePermission } from './schema';
|
||||
import type { ClientSession, Model } from 'mongoose';
|
||||
import type { ParentIdType } from '@fastgpt/global/common/parentFolder/type';
|
||||
import type { RequireOnlyOne } from '@fastgpt/global/common/type/utils';
|
||||
import type { PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant';
|
||||
import type { PermissionValueType } from '@fastgpt/global/support/permission/type';
|
||||
import type { ClientSession, Model } from 'mongoose';
|
||||
import { mongoSessionRun } from '../../common/mongo/sessionRun';
|
||||
import { getResourceClbsAndGroups } from './controller';
|
||||
import type { RequireOnlyOne } from '@fastgpt/global/common/type/utils';
|
||||
import type { ParentIdType } from '@fastgpt/global/common/parentFolder/type';
|
||||
import { MongoResourcePermission } from './schema';
|
||||
|
||||
export type SyncChildrenPermissionResourceType = {
|
||||
_id: string;
|
||||
@@ -193,6 +193,7 @@ export async function syncCollaborators({
|
||||
resourceType: resourceType,
|
||||
tmbId: item.tmbId,
|
||||
groupId: item.groupId,
|
||||
orgId: item.orgId,
|
||||
permission: item.permission
|
||||
})),
|
||||
{
|
||||
|
||||
@@ -74,14 +74,3 @@ export const checkTeamAppLimit = async (teamId: string, amount = 1) => {
|
||||
return Promise.reject(TeamErrEnum.appAmountNotEnough);
|
||||
}
|
||||
};
|
||||
|
||||
export const checkTeamReRankPermission = async (teamId: string) => {
|
||||
const { standardConstants } = await getTeamStandPlan({
|
||||
teamId
|
||||
});
|
||||
|
||||
if (standardConstants && !standardConstants?.permissionReRank) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
33
packages/service/support/wallet/coupon/schema.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { addDays } from 'date-fns';
|
||||
import { connectionMongo, getMongoModel } from '../../../common/mongo';
|
||||
const { Schema } = connectionMongo;
|
||||
import type { TeamCouponSchema } from '@fastgpt/global/support/wallet/sub/coupon/type';
|
||||
|
||||
export const couponCollectionName = 'team_sub_coupons';
|
||||
|
||||
const CouponSchema = new Schema({
|
||||
key: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
subscriptions: {
|
||||
type: [Object],
|
||||
required: true
|
||||
},
|
||||
redeemedAt: {
|
||||
type: Date,
|
||||
default: undefined
|
||||
},
|
||||
expiredAt: {
|
||||
type: Date,
|
||||
default: () => addDays(new Date(), 7)
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
CouponSchema.index({ key: 1 }, { unique: true });
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
export const MongoTeamCoupon = getMongoModel<TeamCouponSchema>(couponCollectionName, CouponSchema);
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
user sub plan
|
||||
1. type=standard: There will only be 1, and each team will have one
|
||||
2. type=extraDatasetSize/extraPoints: Can buy multiple
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
} from '@fastgpt/global/support/wallet/sub/constants';
|
||||
import { MongoTeamSub } from './schema';
|
||||
import { FeTeamPlanStatusType, TeamSubSchema } from '@fastgpt/global/support/wallet/sub/type.d';
|
||||
import { getVectorCountByTeamId } from '../../../common/vectorStore/controller';
|
||||
import { getVectorCountByTeamId } from '../../../common/vectorDB/controller';
|
||||
import dayjs from 'dayjs';
|
||||
import { ClientSession } from '../../../common/mongo';
|
||||
import { addMonths } from 'date-fns';
|
||||
|
||||
@@ -22,7 +22,7 @@ export const formatModelChars2Points = ({
|
||||
};
|
||||
}
|
||||
|
||||
const isIOPriceType = typeof modelData.inputPrice === 'number';
|
||||
const isIOPriceType = typeof modelData.inputPrice === 'number' && modelData.inputPrice > 0;
|
||||
|
||||
const totalPoints = isIOPriceType
|
||||
? (modelData.inputPrice || 0) * (inputTokens / multiple) +
|
||||
|
||||
224
packages/service/thirdProvider/doc2x/index.ts
Normal file
@@ -0,0 +1,224 @@
|
||||
import { batchRun, delay } from '@fastgpt/global/common/system/utils';
|
||||
import { addLog } from '../../common/system/log';
|
||||
import { htmlTable2Md } from '@fastgpt/global/common/string/markdown';
|
||||
import axios, { Method } from 'axios';
|
||||
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
import { ImageType } from '../../worker/readFile/type';
|
||||
import { getImageBase64 } from '../../common/file/image/utils';
|
||||
|
||||
type ApiResponseDataType<T = any> = {
|
||||
code: string;
|
||||
msg?: string;
|
||||
data: T;
|
||||
};
|
||||
|
||||
export const useDoc2xServer = ({ apiKey }: { apiKey: string }) => {
|
||||
// Init request
|
||||
const instance = axios.create({
|
||||
baseURL: 'https://v2.doc2x.noedgeai.com/api',
|
||||
timeout: 60000,
|
||||
headers: {
|
||||
Authorization: `Bearer ${apiKey}`
|
||||
}
|
||||
});
|
||||
// Response check
|
||||
const checkRes = (data: ApiResponseDataType) => {
|
||||
if (data === undefined) {
|
||||
addLog.info('[Doc2x] Server data is empty');
|
||||
return Promise.reject('服务器异常');
|
||||
}
|
||||
return data;
|
||||
};
|
||||
const responseError = (err: any) => {
|
||||
if (!err) {
|
||||
return Promise.reject({ message: '[Doc2x] Unknown error' });
|
||||
}
|
||||
if (typeof err === 'string') {
|
||||
return Promise.reject({ message: `[Doc2x] ${err}` });
|
||||
}
|
||||
if (typeof err.message === 'string') {
|
||||
return Promise.reject({ message: `[Doc2x] ${err.message}` });
|
||||
}
|
||||
if (typeof err.data === 'string') {
|
||||
return Promise.reject({ message: `[Doc2x] ${err.data}` });
|
||||
}
|
||||
if (err?.response?.data) {
|
||||
return Promise.reject({ message: `[Doc2x] ${getErrText(err?.response?.data)}` });
|
||||
}
|
||||
|
||||
addLog.error('[Doc2x] Unknown error', err);
|
||||
return Promise.reject({ message: `[Doc2x] ${getErrText(err)}` });
|
||||
};
|
||||
const request = <T>(url: string, data: any, method: Method): Promise<ApiResponseDataType<T>> => {
|
||||
// Remove empty data
|
||||
for (const key in data) {
|
||||
if (data[key] === undefined) {
|
||||
delete data[key];
|
||||
}
|
||||
}
|
||||
|
||||
return instance
|
||||
.request({
|
||||
url,
|
||||
method,
|
||||
data: ['POST', 'PUT'].includes(method) ? data : undefined,
|
||||
params: !['POST', 'PUT'].includes(method) ? data : undefined
|
||||
})
|
||||
.then((res) => checkRes(res.data))
|
||||
.catch((err) => responseError(err));
|
||||
};
|
||||
|
||||
const parsePDF = async (fileBuffer: Buffer) => {
|
||||
addLog.debug('[Doc2x] PDF parse start');
|
||||
const startTime = Date.now();
|
||||
|
||||
// 1. Get pre-upload URL first
|
||||
const {
|
||||
code,
|
||||
msg,
|
||||
data: preupload_data
|
||||
} = await request<{ uid: string; url: string }>('/v2/parse/preupload', null, 'POST');
|
||||
if (!['ok', 'success'].includes(code)) {
|
||||
return Promise.reject(`[Doc2x] Failed to get pre-upload URL: ${msg}`);
|
||||
}
|
||||
const upload_url = preupload_data.url;
|
||||
const uid = preupload_data.uid;
|
||||
|
||||
// 2. Upload file to pre-signed URL with binary stream
|
||||
const blob = new Blob([fileBuffer], { type: 'application/pdf' });
|
||||
const response = await axios
|
||||
.put(upload_url, blob, {
|
||||
headers: {
|
||||
'Content-Type': 'application/pdf'
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
return Promise.reject(`[Doc2x] Failed to upload file: ${getErrText(error)}`);
|
||||
});
|
||||
if (response.status !== 200) {
|
||||
return Promise.reject(
|
||||
`[Doc2x] Upload failed with status ${response.status}: ${response.statusText}`
|
||||
);
|
||||
}
|
||||
addLog.debug(`[Doc2x] Uploaded file success, uid: ${uid}`);
|
||||
|
||||
await delay(5000);
|
||||
|
||||
// 3. Get the result by uid
|
||||
const checkResult = async () => {
|
||||
// 10 minutes
|
||||
let retry = 120;
|
||||
|
||||
while (retry > 0) {
|
||||
try {
|
||||
const {
|
||||
code,
|
||||
data: result_data,
|
||||
msg
|
||||
} = await request<{
|
||||
progress: number;
|
||||
status: 'processing' | 'failed' | 'success';
|
||||
result: {
|
||||
pages: {
|
||||
md: string;
|
||||
}[];
|
||||
};
|
||||
}>(`/v2/parse/status?uid=${uid}`, null, 'GET');
|
||||
|
||||
// Error
|
||||
if (!['ok', 'success'].includes(code)) {
|
||||
return Promise.reject(`[Doc2x] Failed to get result (uid: ${uid}): ${msg}`);
|
||||
}
|
||||
|
||||
// Process
|
||||
if (['ready', 'processing'].includes(result_data.status)) {
|
||||
addLog.debug(`[Doc2x] Waiting for the result, uid: ${uid}`);
|
||||
await delay(5000);
|
||||
}
|
||||
|
||||
// Finifsh
|
||||
if (result_data.status === 'success') {
|
||||
return {
|
||||
text: result_data.result.pages
|
||||
.map((page) => page.md)
|
||||
.join('')
|
||||
.replace(/\\[\(\)]/g, '$')
|
||||
.replace(/\\[\[\]]/g, '$$')
|
||||
.replace(/<img\s+src="([^"]+)"(?:\s*\?[^>]*)?(?:\s*\/>|>)/g, '')
|
||||
.replace(/<!-- Media -->/g, '')
|
||||
.replace(/<!-- Footnote -->/g, '')
|
||||
.replace(/\$(.+?)\s+\\tag\{(.+?)\}\$/g, '$$$1 \\qquad \\qquad ($2)$$')
|
||||
.replace(/\\text\{([^}]*?)(\b\w+)_(\w+\b)([^}]*?)\}/g, '\\text{$1$2\\_$3$4}'),
|
||||
pages: result_data.result.pages.length
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
// Just network error
|
||||
addLog.warn(`[Doc2x] Get result error`, { error });
|
||||
await delay(500);
|
||||
}
|
||||
|
||||
retry--;
|
||||
}
|
||||
return Promise.reject(`[Doc2x] Failed to get result (uid: ${uid}): Process timeout`);
|
||||
};
|
||||
|
||||
const { text, pages } = await checkResult();
|
||||
|
||||
//  => 
|
||||
const parseTextImage = async (text: string) => {
|
||||
// Extract image links and convert to base64
|
||||
const imageList: { id: string; url: string }[] = [];
|
||||
let processedText = text.replace(/!\[.*?\]\((http[^)]+)\)/g, (match, url) => {
|
||||
const id = `IMAGE_${getNanoid()}_IMAGE`;
|
||||
imageList.push({
|
||||
id,
|
||||
url
|
||||
});
|
||||
return ``;
|
||||
});
|
||||
|
||||
// Get base64 from image url
|
||||
let resultImageList: ImageType[] = [];
|
||||
await batchRun(
|
||||
imageList,
|
||||
async (item) => {
|
||||
try {
|
||||
const { base64, mime } = await getImageBase64(item.url);
|
||||
resultImageList.push({
|
||||
uuid: item.id,
|
||||
mime,
|
||||
base64
|
||||
});
|
||||
} catch (error) {
|
||||
processedText = processedText.replace(item.id, item.url);
|
||||
addLog.warn(`[Doc2x] Failed to get image from ${item.url}: ${getErrText(error)}`);
|
||||
}
|
||||
},
|
||||
5
|
||||
);
|
||||
|
||||
return {
|
||||
text: processedText,
|
||||
imageList: resultImageList
|
||||
};
|
||||
};
|
||||
const { text: formatText, imageList } = await parseTextImage(htmlTable2Md(text));
|
||||
|
||||
addLog.debug(`[Doc2x] PDF parse finished`, {
|
||||
time: `${Math.round((Date.now() - startTime) / 1000)}s`,
|
||||
pages
|
||||
});
|
||||
|
||||
return {
|
||||
pages,
|
||||
text: formatText,
|
||||
imageList
|
||||
};
|
||||
};
|
||||
|
||||
return {
|
||||
parsePDF
|
||||
};
|
||||
};
|
||||
@@ -1,6 +1,7 @@
|
||||
// @ts-nocheck
|
||||
|
||||
export const iconPaths = {
|
||||
alignLeft: () => import('./icons/alignLeft.svg'),
|
||||
book: () => import('./icons/book.svg'),
|
||||
change: () => import('./icons/change.svg'),
|
||||
chatSend: () => import('./icons/chatSend.svg'),
|
||||
@@ -17,6 +18,7 @@ export const iconPaths = {
|
||||
'common/addLight': () => import('./icons/common/addLight.svg'),
|
||||
'common/addUser': () => import('./icons/common/addUser.svg'),
|
||||
'common/administrator': () => import('./icons/common/administrator.svg'),
|
||||
'common/alipay': () => import('./icons/common/alipay.svg'),
|
||||
'common/app': () => import('./icons/common/app.svg'),
|
||||
'common/arrowLeft': () => import('./icons/common/arrowLeft.svg'),
|
||||
'common/arrowRight': () => import('./icons/common/arrowRight.svg'),
|
||||
@@ -105,15 +107,16 @@ export const iconPaths = {
|
||||
'common/tickFill': () => import('./icons/common/tickFill.svg'),
|
||||
'common/toolkit': () => import('./icons/common/toolkit.svg'),
|
||||
'common/trash': () => import('./icons/common/trash.svg'),
|
||||
'common/upRightArrowLight': () => import('./icons/common/upRightArrowLight.svg'),
|
||||
'common/uploadFileFill': () => import('./icons/common/uploadFileFill.svg'),
|
||||
'common/upperRight': () => import('./icons/common/upperRight.svg'),
|
||||
'common/upRightArrowLight': () => import('./icons/common/upRightArrowLight.svg'),
|
||||
'common/userInfo': () => import('./icons/common/userInfo.svg'),
|
||||
'common/variable': () => import('./icons/common/variable.svg'),
|
||||
'common/viewLight': () => import('./icons/common/viewLight.svg'),
|
||||
'common/voiceLight': () => import('./icons/common/voiceLight.svg'),
|
||||
'common/wallet': () => import('./icons/common/wallet.svg'),
|
||||
'common/warn': () => import('./icons/common/warn.svg'),
|
||||
'common/wechat': () => import('./icons/common/wechat.svg'),
|
||||
'common/wechatFill': () => import('./icons/common/wechatFill.svg'),
|
||||
'common/wecom': () => import('./icons/common/wecom.svg'),
|
||||
configmap: () => import('./icons/configmap.svg'),
|
||||
@@ -143,8 +146,6 @@ export const iconPaths = {
|
||||
'core/app/simpleMode/tts': () => import('./icons/core/app/simpleMode/tts.svg'),
|
||||
'core/app/simpleMode/variable': () => import('./icons/core/app/simpleMode/variable.svg'),
|
||||
'core/app/simpleMode/whisper': () => import('./icons/core/app/simpleMode/whisper.svg'),
|
||||
'core/app/templates/TranslateRobot': () =>
|
||||
import('./icons/core/app/templates/TranslateRobot.svg'),
|
||||
'core/app/templates/animalLife': () => import('./icons/core/app/templates/animalLife.svg'),
|
||||
'core/app/templates/chinese': () => import('./icons/core/app/templates/chinese.svg'),
|
||||
'core/app/templates/divination': () => import('./icons/core/app/templates/divination.svg'),
|
||||
@@ -154,6 +155,8 @@ export const iconPaths = {
|
||||
'core/app/templates/plugin-dalle': () => import('./icons/core/app/templates/plugin-dalle.svg'),
|
||||
'core/app/templates/plugin-feishu': () => import('./icons/core/app/templates/plugin-feishu.svg'),
|
||||
'core/app/templates/stock': () => import('./icons/core/app/templates/stock.svg'),
|
||||
'core/app/templates/TranslateRobot': () =>
|
||||
import('./icons/core/app/templates/TranslateRobot.svg'),
|
||||
'core/app/toolCall': () => import('./icons/core/app/toolCall.svg'),
|
||||
'core/app/ttsFill': () => import('./icons/core/app/ttsFill.svg'),
|
||||
'core/app/type/httpPlugin': () => import('./icons/core/app/type/httpPlugin.svg'),
|
||||
@@ -172,7 +175,6 @@ export const iconPaths = {
|
||||
'core/app/variable/input': () => import('./icons/core/app/variable/input.svg'),
|
||||
'core/app/variable/select': () => import('./icons/core/app/variable/select.svg'),
|
||||
'core/app/variable/textarea': () => import('./icons/core/app/variable/textarea.svg'),
|
||||
'core/chat/QGFill': () => import('./icons/core/chat/QGFill.svg'),
|
||||
'core/chat/backText': () => import('./icons/core/chat/backText.svg'),
|
||||
'core/chat/cancelSpeak': () => import('./icons/core/chat/cancelSpeak.svg'),
|
||||
'core/chat/chatFill': () => import('./icons/core/chat/chatFill.svg'),
|
||||
@@ -189,6 +191,7 @@ export const iconPaths = {
|
||||
'core/chat/fileSelect': () => import('./icons/core/chat/fileSelect.svg'),
|
||||
'core/chat/finishSpeak': () => import('./icons/core/chat/finishSpeak.svg'),
|
||||
'core/chat/imgSelect': () => import('./icons/core/chat/imgSelect.svg'),
|
||||
'core/chat/QGFill': () => import('./icons/core/chat/QGFill.svg'),
|
||||
'core/chat/quoteFill': () => import('./icons/core/chat/quoteFill.svg'),
|
||||
'core/chat/quoteSign': () => import('./icons/core/chat/quoteSign.svg'),
|
||||
'core/chat/recordFill': () => import('./icons/core/chat/recordFill.svg'),
|
||||
@@ -274,13 +277,12 @@ export const iconPaths = {
|
||||
'core/workflow/redo': () => import('./icons/core/workflow/redo.svg'),
|
||||
'core/workflow/revertVersion': () => import('./icons/core/workflow/revertVersion.svg'),
|
||||
'core/workflow/runError': () => import('./icons/core/workflow/runError.svg'),
|
||||
'core/workflow/running': () => import('./icons/core/workflow/running.svg'),
|
||||
'core/workflow/runSkip': () => import('./icons/core/workflow/runSkip.svg'),
|
||||
'core/workflow/runSuccess': () => import('./icons/core/workflow/runSuccess.svg'),
|
||||
'core/workflow/running': () => import('./icons/core/workflow/running.svg'),
|
||||
'core/workflow/template/BI': () => import('./icons/core/workflow/template/BI.svg'),
|
||||
'core/workflow/template/FileRead': () => import('./icons/core/workflow/template/FileRead.svg'),
|
||||
'core/workflow/template/aiChat': () => import('./icons/core/workflow/template/aiChat.svg'),
|
||||
'core/workflow/template/baseChart': () => import('./icons/core/workflow/template/baseChart.svg'),
|
||||
'core/workflow/template/BI': () => import('./icons/core/workflow/template/BI.svg'),
|
||||
'core/workflow/template/bing': () => import('./icons/core/workflow/template/bing.svg'),
|
||||
'core/workflow/template/codeRun': () => import('./icons/core/workflow/template/codeRun.svg'),
|
||||
'core/workflow/template/customFeedback': () =>
|
||||
@@ -296,6 +298,7 @@ export const iconPaths = {
|
||||
'core/workflow/template/extractJson': () =>
|
||||
import('./icons/core/workflow/template/extractJson.svg'),
|
||||
'core/workflow/template/fetchUrl': () => import('./icons/core/workflow/template/fetchUrl.svg'),
|
||||
'core/workflow/template/FileRead': () => import('./icons/core/workflow/template/FileRead.svg'),
|
||||
'core/workflow/template/formInput': () => import('./icons/core/workflow/template/formInput.svg'),
|
||||
'core/workflow/template/getTime': () => import('./icons/core/workflow/template/getTime.svg'),
|
||||
'core/workflow/template/google': () => import('./icons/core/workflow/template/google.svg'),
|
||||
@@ -325,12 +328,12 @@ export const iconPaths = {
|
||||
'core/workflow/template/textConcat': () =>
|
||||
import('./icons/core/workflow/template/textConcat.svg'),
|
||||
'core/workflow/template/toolCall': () => import('./icons/core/workflow/template/toolCall.svg'),
|
||||
'core/workflow/template/toolParams': () =>
|
||||
import('./icons/core/workflow/template/toolParams.svg'),
|
||||
'core/workflow/template/toolkitActive': () =>
|
||||
import('./icons/core/workflow/template/toolkitActive.svg'),
|
||||
'core/workflow/template/toolkitInactive': () =>
|
||||
import('./icons/core/workflow/template/toolkitInactive.svg'),
|
||||
'core/workflow/template/toolParams': () =>
|
||||
import('./icons/core/workflow/template/toolParams.svg'),
|
||||
'core/workflow/template/userSelect': () =>
|
||||
import('./icons/core/workflow/template/userSelect.svg'),
|
||||
'core/workflow/template/variable': () => import('./icons/core/workflow/template/variable.svg'),
|
||||
@@ -388,10 +391,10 @@ export const iconPaths = {
|
||||
'modal/selectSource': () => import('./icons/modal/selectSource.svg'),
|
||||
'modal/setting': () => import('./icons/modal/setting.svg'),
|
||||
'modal/teamPlans': () => import('./icons/modal/teamPlans.svg'),
|
||||
'model/BAAI': () => import('./icons/model/BAAI.svg'),
|
||||
'model/alicloud': () => import('./icons/model/alicloud.svg'),
|
||||
'model/aws': () => import('./icons/model/aws.svg'),
|
||||
'model/azure': () => import('./icons/model/azure.svg'),
|
||||
'model/BAAI': () => import('./icons/model/BAAI.svg'),
|
||||
'model/baichuan': () => import('./icons/model/baichuan.svg'),
|
||||
'model/chatglm': () => import('./icons/model/chatglm.svg'),
|
||||
'model/claude': () => import('./icons/model/claude.svg'),
|
||||
@@ -439,6 +442,7 @@ export const iconPaths = {
|
||||
save: () => import('./icons/save.svg'),
|
||||
sliderTag: () => import('./icons/sliderTag.svg'),
|
||||
stop: () => import('./icons/stop.svg'),
|
||||
'support/account/coupon': () => import('./icons/support/account/coupon.svg'),
|
||||
'support/account/laf': () => import('./icons/support/account/laf.svg'),
|
||||
'support/account/loginoutLight': () => import('./icons/support/account/loginoutLight.svg'),
|
||||
'support/account/plans': () => import('./icons/support/account/plans.svg'),
|
||||
|
||||
1
packages/web/components/common/Icon/icons/alignLeft.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg t="1745324194466" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2622" ><path d="M96 128h832v96H96zM96 576h832v96H96zM96 352h576v96H96zM96 800h576v96H96z" p-id="2623"></path></svg>
|
||||
|
After Width: | Height: | Size: 230 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="18" height="19" viewBox="0 0 18 19" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Frame">
|
||||
<path id="Vector" d="M10.1717 12.3797C10.1717 12.3797 9 13.3168 8.57419 13.5626C8.24535 13.7523 7.89703 13.906 7.53525 14.0211C7.2673 14.1102 6.99132 14.1732 6.71119 14.2089C6.4755 14.2348 6.28031 14.2511 6.12563 14.259C6.10832 14.2668 6.08948 14.2707 6.0705 14.2702H5.89331C5.50724 14.2725 5.12255 14.2239 4.74919 14.1257C4.41511 14.0406 4.0989 13.8966 3.81544 13.7004C3.5536 13.5164 3.33796 13.2742 3.18544 12.9928C3.03019 12.7087 2.95312 12.3791 2.95312 12.0034C2.96044 11.6794 3.04875 11.3846 3.2175 11.1191C3.38896 10.8519 3.61285 10.6223 3.87562 10.4441C4.15006 10.2573 4.45255 10.1154 4.77169 10.0239C5.09256 9.92927 5.4269 9.88841 5.76112 9.90299C6.08512 9.91761 6.39844 9.95586 6.7005 10.0183C6.9936 10.0788 7.28331 10.1546 7.56844 10.2456C7.84462 10.3339 8.10619 10.4295 8.35313 10.5324C8.60063 10.6365 8.83406 10.7355 9.05512 10.8317C9.24461 10.9108 9.43631 10.9845 9.63 11.0527C9.84375 10.7799 10.0238 10.5127 10.1717 10.2512C10.4021 9.84644 10.5982 9.42308 10.7578 8.98555C10.8096 8.83818 10.8422 8.73524 10.8574 8.67618H6.05925V8.14518H8.325V6.91893H5.17444V6.38793H8.32556V5.3923C8.32556 5.32649 8.36212 5.27305 8.43581 5.23255C8.5095 5.19205 8.59444 5.16449 8.69006 5.1493C8.80088 5.12736 8.92631 5.11611 9.06581 5.11611H9.91687V6.38736H13.1563V6.91836H9.91687V8.14518H12.4909L12.4926 8.13393V8.14518H12.4909C12.4391 8.47368 12.3514 8.82018 12.2276 9.18468C12.1014 9.55657 11.948 9.91867 11.7686 10.2681C11.5428 10.7094 11.2743 11.1277 10.9671 11.5168C10.9671 11.5168 13.5039 12.7312 16.1826 13.1582C16.6269 12.1721 16.875 11.0769 16.875 9.92493C16.875 5.57568 13.3492 2.04993 9 2.04993C4.65075 2.04993 1.125 5.57568 1.125 9.92493C1.125 14.2736 4.65075 17.7999 9 17.7999C11.6978 17.7999 14.0782 16.4432 15.498 14.3749C14.0557 14.0604 12.5235 13.4242 10.1711 12.3797H10.1717ZM3.9375 11.8431C3.90769 13.1914 5.44275 13.2915 5.67956 13.2988C6.46256 13.3224 7.09425 13.0288 7.58644 12.7386C8.07806 12.4483 8.88075 11.6979 8.91113 11.6726C8.94037 11.6467 8.97019 11.6192 8.99944 11.5899C8.78456 11.4718 8.57362 11.3638 8.36662 11.2642C8.15962 11.1652 7.10663 10.6033 6.17287 10.5077C4.40325 10.3254 3.9465 11.4336 3.9375 11.8431Z" fill="#1777FF"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
@@ -0,0 +1,8 @@
|
||||
<svg width="19" height="19" viewBox="0 0 19 19" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="icon/solid/wechat">
|
||||
<g id="Union">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.1409 7.49005C12.8586 7.45189 12.5687 7.429 12.2711 7.429C9.02813 7.429 6.39563 9.6342 6.40326 12.3354C6.40326 12.6559 6.44141 12.9611 6.51008 13.2587C6.00648 13.19 5.53339 13.0603 5.08319 12.9L2.97719 13.8081L3.74787 12.2591C2.38202 11.3587 1.50452 9.97757 1.50452 8.41333C1.50452 5.70452 4.13702 3.50696 7.37995 3.50696C10.2414 3.50696 12.6221 5.22381 13.1409 7.49005ZM9.61367 5.76791C9.5248 5.7311 9.42954 5.71215 9.33335 5.71215C9.13907 5.71215 8.95275 5.78933 8.81538 5.92671C8.678 6.06408 8.60082 6.2504 8.60082 6.44468C8.60082 6.63895 8.678 6.82527 8.81538 6.96265C8.95275 7.10002 9.13907 7.1772 9.33335 7.1772C9.42954 7.1772 9.5248 7.15825 9.61367 7.12144C9.70255 7.08463 9.7833 7.03067 9.85132 6.96265C9.91934 6.89463 9.9733 6.81387 10.0101 6.725C10.0469 6.63613 10.0659 6.54087 10.0659 6.44468C10.0659 6.34848 10.0469 6.25323 10.0101 6.16435C9.9733 6.07548 9.91934 5.99473 9.85132 5.92671C9.7833 5.85868 9.70255 5.80473 9.61367 5.76791ZM4.90096 6.97028C5.03833 7.10765 5.22465 7.18483 5.41893 7.18483C5.61321 7.18483 5.79953 7.10765 5.9369 6.97028C6.07428 6.8329 6.15145 6.64658 6.15145 6.45231C6.15145 6.25803 6.07428 6.07171 5.9369 5.93434C5.79953 5.79696 5.61321 5.71979 5.41893 5.71979C5.22465 5.71979 5.03833 5.79696 4.90096 5.93434C4.76359 6.07171 4.68641 6.25803 4.68641 6.45231C4.68641 6.64658 4.76359 6.8329 4.90096 6.97028Z" fill="#1AAD1A"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.271 8.41331C14.9722 8.41331 17.1621 10.1683 17.1621 12.3354C17.1621 13.5944 16.4143 14.6932 15.2697 15.4104L16.1854 17.2417L13.5453 16.1048C13.1409 16.1964 12.7135 16.2574 12.271 16.2574C9.56981 16.2574 7.37987 14.5024 7.37987 12.3354C7.37987 10.1683 9.56981 8.41331 12.271 8.41331ZM10.3913 11.9677C10.5118 12.0482 10.6534 12.0912 10.7983 12.0912C11.2027 12.0912 11.5385 11.7631 11.5308 11.3587C11.5308 11.2138 11.4879 11.0722 11.4074 10.9517C11.3269 10.8312 11.2125 10.7373 11.0786 10.6819C10.9448 10.6265 10.7975 10.6119 10.6554 10.6402C10.5133 10.6685 10.3828 10.7382 10.2803 10.8407C10.1779 10.9431 10.1081 11.0737 10.0799 11.2158C10.0516 11.3578 10.0661 11.5051 10.1215 11.639C10.177 11.7728 10.2709 11.8872 10.3913 11.9677ZM13.2181 11.8766C13.3554 12.014 13.5417 12.0912 13.736 12.0912C13.9303 12.0912 14.1166 12.014 14.254 11.8766C14.3914 11.7393 14.4685 11.5529 14.4685 11.3587C14.4685 11.1644 14.3914 10.9781 14.254 10.8407C14.1166 10.7033 13.9303 10.6261 13.736 10.6261C13.5417 10.6261 13.3554 10.7033 13.2181 10.8407C13.0807 10.9781 13.0035 11.1644 13.0035 11.3587C13.0035 11.5529 13.0807 11.7393 13.2181 11.8766Z" fill="#1AAD1A"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
@@ -0,0 +1,9 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="icon/line/coupon_codes">
|
||||
<g id="Union">
|
||||
<path d="M7.33334 6.66667C6.96515 6.66667 6.66668 6.96514 6.66668 7.33333V8.5C6.66668 8.86819 6.96515 9.16667 7.33334 9.16667H7.66668C8.03487 9.16667 8.33334 8.86819 8.33334 8.5V7.33333C8.33334 6.96514 8.03487 6.66667 7.66668 6.66667H7.33334Z" fill="#3370FF"/>
|
||||
<path d="M6.66668 11.5C6.66668 11.1318 6.96515 10.8333 7.33334 10.8333H7.66668C8.03487 10.8333 8.33334 11.1318 8.33334 11.5V12.6667C8.33334 13.0349 8.03487 13.3333 7.66668 13.3333H7.33334C6.96515 13.3333 6.66668 13.0349 6.66668 12.6667V11.5Z" fill="#3370FF"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.50001 17.5C1.57954 17.5 0.833344 16.7538 0.833344 15.8333V13.8664C0.833344 13.5644 0.833344 13.4134 0.85697 13.2895C0.877583 13.1815 0.879995 13.1518 0.888963 13.1242C0.897931 13.0966 0.913455 13.0711 0.960293 12.9716C1.01398 12.8576 1.35054 12.3945 2.02366 11.4682C2.32326 11.056 2.50001 10.5486 2.50001 10C2.50001 9.4514 2.32331 8.94407 2.02371 8.53183C1.35057 7.60558 1.01398 7.14243 0.960293 7.02837C0.913455 6.92886 0.897931 6.9034 0.888963 6.8758C0.879995 6.84821 0.877583 6.81849 0.85697 6.71045C0.833344 6.58662 0.833344 6.43562 0.833344 6.13361V4.16667C0.833344 3.24619 1.57954 2.5 2.50001 2.5H17.5C18.4205 2.5 19.1667 3.24619 19.1667 4.16667V6.13361C19.1667 6.43562 19.1667 6.58662 19.143 6.71045C19.1224 6.81849 19.12 6.84821 19.1111 6.8758C19.1021 6.9034 19.0866 6.92886 19.0397 7.02837C18.986 7.14243 18.6495 7.60557 17.9763 8.53183C17.6767 8.94407 17.5 9.4514 17.5 10C17.5 10.5825 17.6992 11.1184 18.0332 11.5434C18.6624 12.3441 18.9769 12.7444 19.0333 12.8564C19.0796 12.9485 19.0965 12.9764 19.1069 13.0065C19.1174 13.0367 19.1213 13.0691 19.1418 13.1702C19.1667 13.293 19.1667 13.4527 19.1667 13.7721V15.8333C19.1667 16.7538 18.4205 17.5 17.5 17.5H2.50001ZM2.52704 6.25159C2.50001 6.1194 2.50001 5.94661 2.50001 5.60102V5.5C2.50001 5.03329 2.50001 4.79993 2.59084 4.62167C2.67073 4.46487 2.79822 4.33739 2.95502 4.25749C3.13328 4.16667 3.36663 4.16667 3.83334 4.16667H6.66668V4.33333C6.66668 4.70152 6.96515 5 7.33334 5H7.66668C8.03487 5 8.33334 4.70152 8.33334 4.33333V4.16667H16.1667C16.6334 4.16667 16.8667 4.16667 17.045 4.25749C17.2018 4.33739 17.3293 4.46487 17.4092 4.62167C17.5 4.79993 17.5 5.03329 17.5 5.5V5.60102C17.5 5.94661 17.5 6.1194 17.473 6.25159C17.4399 6.41345 17.43 6.44182 17.3551 6.58907C17.2939 6.70932 17.1085 6.94215 16.7379 7.40781C16.172 8.11871 15.8333 9.01919 15.8333 10C15.8333 11.0048 16.1887 11.9253 16.7798 12.644C17.1251 13.0639 17.2977 13.2738 17.3577 13.3886C17.4295 13.5258 17.4417 13.5599 17.4735 13.7115C17.5 13.8383 17.5 14.003 17.5 14.3324V14.5C17.5 14.9667 17.5 15.2001 17.4092 15.3783C17.3293 15.5351 17.2018 15.6626 17.045 15.7425C16.8667 15.8333 16.6334 15.8333 16.1667 15.8333H8.33334V15.6667C8.33334 15.2985 8.03487 15 7.66668 15H7.33334C6.96515 15 6.66668 15.2985 6.66668 15.6667V15.8333H3.83334C3.36663 15.8333 3.13328 15.8333 2.95502 15.7425C2.79822 15.6626 2.67073 15.5351 2.59084 15.3783C2.50001 15.2001 2.50001 14.9667 2.50001 14.5V14.399C2.50001 14.0534 2.50001 13.8806 2.52704 13.7484C2.56014 13.5865 2.57005 13.5582 2.64496 13.4109C2.70614 13.2907 2.89148 13.0578 3.26216 12.5922C3.82806 11.8813 4.16668 10.9808 4.16668 10C4.16668 9.01919 3.82806 8.11871 3.26216 7.40781C2.89148 6.94215 2.70614 6.70932 2.64496 6.58907C2.57005 6.44182 2.56014 6.41345 2.52704 6.25159Z" fill="#3370FF"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.4 KiB |
@@ -26,9 +26,9 @@ const MyNumberInput = (props: Props) => {
|
||||
<NumberInput
|
||||
{...restProps}
|
||||
onBlur={(e) => {
|
||||
const numE = Number(e.target.value);
|
||||
const numE = e.target.value === '' ? '' : Number(e.target.value);
|
||||
if (onBlur) {
|
||||
if (isNaN(numE)) {
|
||||
if (numE === '') {
|
||||
// @ts-ignore
|
||||
onBlur('');
|
||||
} else {
|
||||
@@ -46,9 +46,9 @@ const MyNumberInput = (props: Props) => {
|
||||
}
|
||||
}}
|
||||
onChange={(e) => {
|
||||
const numE = Number(e);
|
||||
const numE = e === '' ? '' : Number(e);
|
||||
if (onChange) {
|
||||
if (isNaN(numE)) {
|
||||
if (numE === '') {
|
||||
// @ts-ignore
|
||||
onChange('');
|
||||
} else {
|
||||
@@ -62,6 +62,7 @@ const MyNumberInput = (props: Props) => {
|
||||
value: numE
|
||||
}
|
||||
};
|
||||
|
||||
register(name).onChange(event);
|
||||
}
|
||||
}}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React, { useRef } from 'react';
|
||||
import { timezoneList } from '@fastgpt/global/common/time/timezone';
|
||||
import { getTimeZoneList } from '@fastgpt/global/common/time/timezone';
|
||||
import { Select } from '@chakra-ui/react';
|
||||
|
||||
const TimezoneSelect = ({ value, onChange }: { value?: string; onChange: (e: string) => void }) => {
|
||||
const timezones = useRef(timezoneList());
|
||||
const timezones = useRef(getTimeZoneList());
|
||||
|
||||
return (
|
||||
<Select
|
||||
|
||||
@@ -166,20 +166,14 @@ const MySelect = <T = any,>(
|
||||
const isSelecting = loading || isLoading;
|
||||
|
||||
return (
|
||||
<Box
|
||||
css={css({
|
||||
'& div': {
|
||||
width: 'auto !important'
|
||||
}
|
||||
})}
|
||||
>
|
||||
<Box>
|
||||
<Menu
|
||||
autoSelect={false}
|
||||
isOpen={isOpen && !isSelecting}
|
||||
onOpen={onOpen}
|
||||
onClose={onClose}
|
||||
strategy={'fixed'}
|
||||
matchWidth
|
||||
// matchWidth
|
||||
>
|
||||
<MenuButton
|
||||
as={Button}
|
||||
@@ -191,6 +185,9 @@ const MySelect = <T = any,>(
|
||||
size={'md'}
|
||||
fontSize={'sm'}
|
||||
textAlign={'left'}
|
||||
h={'auto'}
|
||||
whiteSpace={'pre-wrap'}
|
||||
wordBreak={'break-word'}
|
||||
_active={{
|
||||
transform: 'none'
|
||||
}}
|
||||
@@ -239,7 +236,7 @@ const MySelect = <T = any,>(
|
||||
<MenuList
|
||||
ref={MenuListRef}
|
||||
className={props.className}
|
||||
minW={(() => {
|
||||
w={(() => {
|
||||
const w = ButtonRef.current?.clientWidth;
|
||||
if (w) {
|
||||
return `${w}px !important`;
|
||||
@@ -248,7 +245,6 @@ const MySelect = <T = any,>(
|
||||
? width.map((item) => `${item} !important`)
|
||||
: `${width} !important`;
|
||||
})()}
|
||||
w={'auto'}
|
||||
px={'6px'}
|
||||
py={'6px'}
|
||||
border={'1px solid #fff'}
|
||||
|
||||
@@ -102,12 +102,6 @@ export function registerLexicalTextEntity<T extends TextNode | VariableLabelNode
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const nextMatch = getMatch(nextText);
|
||||
|
||||
if (nextMatch !== null && nextMatch.start === 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (match === null) {
|
||||
|
||||
@@ -65,7 +65,6 @@ export function useLinkedScroll<
|
||||
let scroolSign = useRef(false);
|
||||
const { runAsync: loadInitData } = useRequest2(
|
||||
async ({ scrollWhenFinish, refresh } = { scrollWhenFinish: true, refresh: false }) => {
|
||||
console.log('loadInitData', params);
|
||||
if (!currentData || isLoading) return;
|
||||
|
||||
const item = dataList.find((item) => item._id === currentData.id);
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
"confirm": "confirm",
|
||||
"contact_phone": "Contact phone number",
|
||||
"contact_phone_void": "Contact phone number format error",
|
||||
"day": "sky",
|
||||
"default_header": "Default header",
|
||||
"detail": "Details",
|
||||
"email_address": "Email address",
|
||||
@@ -22,6 +23,7 @@
|
||||
"invoice_detail": "Invoice details",
|
||||
"invoice_sending_info": "The invoice will be sent to your mailbox within 3-7 working days, please be patient.",
|
||||
"mm": "mm",
|
||||
"month": "moon",
|
||||
"need_special_invoice": "Do you need a special ticket?",
|
||||
"no": "no",
|
||||
"no_invoice_record": "No bill record~",
|
||||
@@ -30,14 +32,16 @@
|
||||
"order_type": "Order type",
|
||||
"organization_name": "Organization name",
|
||||
"payment_method": "Payment method",
|
||||
"payway_coupon": "Redeem code",
|
||||
"save": "save",
|
||||
"save_failed": "Save exception",
|
||||
"save_success": "Saved successfully",
|
||||
"status": "state",
|
||||
"sub_mode_custom": "Customize",
|
||||
"submit_failed": "Submission failed",
|
||||
"submit_success": "Submission successful",
|
||||
"submitted": "Submitted",
|
||||
"subscription_mode_month": "by month",
|
||||
"subscription_mode_month": "Duration",
|
||||
"subscription_package": "Subscription package",
|
||||
"subscription_period": "Subscription cycle",
|
||||
"support_wallet_amount": "Amount",
|
||||
|
||||
@@ -74,5 +74,6 @@
|
||||
"user_team_team_name": "Team",
|
||||
"verification_code": "Verification code",
|
||||
"you_can_convert": "you can redeem",
|
||||
"yuan": "Yuan"
|
||||
"yuan": "Yuan",
|
||||
"redeem_coupon": "Redeem coupon"
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
"log_status": "Status",
|
||||
"mapping": "Model Mapping",
|
||||
"mapping_tip": "A valid Json is required. \nThe model can be mapped when sending a request to the actual address. \nFor example:\n{\n \n \"gpt-4o\": \"gpt-4o-test\"\n\n}\n\nWhen FastGPT requests the gpt-4o model, the gpt-4o-test model is sent to the actual address, instead of gpt-4o.",
|
||||
"maxToken_tip": "The model max_tokens parameter, if left blank, means that the model does not support it.",
|
||||
"maxToken_tip": "Model max_tokens parameter",
|
||||
"max_temperature_tip": "If the model temperature parameter is not filled in, it means that the model does not support the temperature parameter.",
|
||||
"model": "Model",
|
||||
"model_name": "Model name",
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
"log_join_team": "【{{name}}】Join the team through the invitation link 【{{link}}】",
|
||||
"log_kick_out_team": "{{name}} removed member {{memberName}}",
|
||||
"log_login": "【{{name}}】Logined in the system",
|
||||
"log_recover_team_member": "【{{name}}】Restored member【{{memberName}}】",
|
||||
"log_relocate_department": "【{{name}}】Displayed department【{{departmentName}}】",
|
||||
"log_time": "Operation time",
|
||||
"log_type": "Operation Type",
|
||||
@@ -83,6 +84,7 @@
|
||||
"permission_datasetCreate_Tip": "Can create knowledge bases in the root directory (creation permissions in folders are controlled by the folder)",
|
||||
"permission_manage": "Admin",
|
||||
"permission_manage_tip": "Can manage members, create groups, manage all groups, and assign permissions to groups and members",
|
||||
"recover_team_member": "Member Recovery",
|
||||
"relocate_department": "Department Mobile",
|
||||
"remark": "remark",
|
||||
"remove_tip": "Confirm to remove {{username}} from the team?",
|
||||
|
||||