Compare commits

...

14 Commits

Author SHA1 Message Date
Archer
fa92ef86b9 fix: system title (#3522) 2025-01-03 15:30:46 +08:00
Archer
f39ea04178 feat: new provider (#3513) 2025-01-02 15:38:06 +08:00
Archer
bd4893ced9 更新 README.md (#3511) 2025-01-02 10:31:25 +08:00
Archer
b75e807f24 fix: tool choice run same tool will error (#3502) 2024-12-31 10:58:52 +08:00
Archer
b2fdefdc0c update config.json (#3497) 2024-12-30 13:53:12 +08:00
Archer
896fec4b82 update doc (#3496) 2024-12-30 13:51:05 +08:00
Archer
50bf7f9a3b V4.8.17 feature (#3493)
* split tokens into input and output (#3477)

* split tokens into input and output

* query extension & tool call & question guide

* fix

* perf: input and output tokens

* perf: tool call if else

* perf: remove code

* fix: extract usage count

* fix: qa usage count

---------

Co-authored-by: heheer <heheer@sealos.io>
2024-12-30 10:13:25 +08:00
Archer
da2831b948 update doc (#3488) 2024-12-27 20:21:08 +08:00
Archer
b520988c64 V4.8.17 feature (#3485)
* feat: add third party account config (#3443)

* temp

* editor workflow variable style

* add team to dispatch

* i18n

* delete console

* change openai account position

* fix

* fix

* fix

* fix

* fix

* 4.8.17 test (#3461)

* perf: external provider config

* perf: ui

* feat: add template config (#3434)

* change template position

* template config

* delete console

* delete

* fix

* fix

* perf: Mongo visutal field (#3464)

* remve invalid code

* perf: team member visutal code

* perf: virtual search; perf: search test data

* fix: ts

* fix: image response headers

* perf: template code

* perf: auth layout;perf: auto save (#3472)

* perf: auth layout

* perf: auto save

* perf: auto save

* fix: template guide display & http input support external variables (#3475)

* fix: template guide display

* http editor support external workflow variables

* perf: auto save;fix: ifelse checker line break; (#3478)

* perf: auto save

* perf: auto save

* fix: ifelse checker line break

* perf: doc

* perf: doc

* fix: update var type error

* 4.8.17 test (#3479)

* perf: auto save

* perf: auto save

* perf: template code

* 4.8.17 test (#3480)

* perf: auto save

* perf: auto save

* perf: model price model

* feat: add react memo

* perf: model provider filter

* fix: ts (#3481)

* perf: auto save

* perf: auto save

* fix: ts

* simple app tool select (#3473)

* workflow plugin userguide & simple tool ui

* simple tool filter

* reuse component

* change component to hook

* fix

* perf: too selector modal (#3484)

* perf: auto save

* perf: auto save

* perf: markdown render

* perf: too selector

* fix: app version require tmbId

* perf: templates refresh

* perf: templates refresh

* hide auto save error tip

* perf: toolkit guide

---------

Co-authored-by: heheer <heheer@sealos.io>
2024-12-27 20:05:12 +08:00
Archer
a209856d48 Fix config (#3476)
* feat: SiliconCloud doc

* feat: SiliconCloud doc

* perf: silicon cloud doc

* perf: silicon cloud doc
2024-12-26 21:36:09 +08:00
f7942655a2 docs: fix typo (#3471) 2024-12-26 11:21:36 +08:00
Archer
108e1b92ef perf: model provider show; perf: get init data buffer (#3459)
* pr code

* perf: model table show

* perf: model provider show

* perf: get init data buffer

* perf: get init data buffer

* perf: icon
2024-12-24 15:12:07 +08:00
Archer
f646ef8595 feat: model select support close params;perf: dataset params slider;update doc (#3453)
* feat: model select support close params

* perf: dataset params slider

* update doc

* update doc

* add delete log

* perf: ai config overflow

* test

* test

* test

* delete collection tags

* delete collection tags
2024-12-23 23:47:33 +08:00
Archer
a7f25994d5 update doc (#3452) 2024-12-23 12:46:35 +08:00
327 changed files with 4903 additions and 2392 deletions

View File

@@ -58,9 +58,9 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b
- [x] 多库复用,混用
- [x] chunk 记录修改和删除
- [x] 支持手动输入直接分段QA 拆分导入
- [x] 支持 txtmdhtmlpdfdocxpptxcsvxlsx (有需要更多可 PR file loader)
- [x] 支持 url 读取、CSV 批量导入
- [x] 支持 txtmdhtmlpdfdocxpptxcsvxlsx (有需要更多可 PR file loader),支持 url 读取、CSV 批量导入
- [x] 混合检索 & 重排
- [x] API 知识库
- [ ] 自定义文件读取服务
- [ ] 自定义分块服务
@@ -69,7 +69,7 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b
- [x] 对话时反馈引用并可修改与删除
- [x] 完整上下文呈现
- [x] 完整模块中间值呈现
- [x] 高级编排 DeBug 模式
- [ ] 高级编排 DeBug 模式
`4` OpenAPI 接口
- [x] completions 接口 (chat 模式对齐 GPT 接口)
@@ -104,7 +104,7 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b
* [快速开始本地开发](https://doc.tryfastgpt.ai/docs/development/intro/)
* [部署 FastGPT](https://doc.tryfastgpt.ai/docs/development/sealos/)
* [系统配置文件说明](https://doc.tryfastgpt.ai/docs/development/configuration/)
* [多模型配置](https://doc.tryfastgpt.ai/docs/development/one-api/)
* [多模型配置方案](https://doc.tryfastgpt.ai/docs/development/modelconfig/one-api/)
* [版本更新/升级介绍](https://doc.tryfastgpt.ai/docs/development/upgrading/)
* [OpenAPI API 文档](https://doc.tryfastgpt.ai/docs/development/openapi/)
* [知识库结构详解](https://doc.tryfastgpt.ai/docs/guide/knowledge_base/rag/)
@@ -127,7 +127,6 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b
我们正在寻找志同道合的小伙伴,加速 FastGPT 的发展。你可以通过 [FastGPT 2025 招聘](https://fael3z0zfze.feishu.cn/wiki/P7FOwEmPziVcaYkvVaacnVX1nvg)了解 FastGPT 的招聘信息。
## 💪 相关项目
- [Laf3 分钟快速接入三方应用](https://github.com/labring/laf)
@@ -139,19 +138,21 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b
<img src="https://img.shields.io/badge/-返回顶部-7d09f1.svg" alt="#" align="right">
</a>
## 👀 其他
- [保姆级 FastGPT 教程](https://www.bilibili.com/video/BV1n34y1A7Bo/?spm_id_from=333.999.0.0)
- [接入飞书](https://www.bilibili.com/video/BV1Su4y1r7R3/?spm_id_from=333.999.0.0)
- [接入企微](https://www.bilibili.com/video/BV1Tp4y1n72T/?spm_id_from=333.999.0.0)
## 🌿 第三方生态
- [COW 个人微信/企微机器人](https://doc.tryfastgpt.ai/docs/use-cases/external-integration/onwechat/)
- [SiliconCloud (硅基流动) —— 开源模型在线体验平台](https://cloud.siliconflow.cn/i/TR9Ym0c4)
<a href="#readme">
<img src="https://img.shields.io/badge/-返回顶部-7d09f1.svg" alt="#" align="right">
</a>
## 🌿 第三方生态
## 👀 其他
- [COW 个人微信/企微机器人](https://doc.tryfastgpt.ai/docs/use-cases/external-integration/onwechat/)
- [保姆级 FastGPT 教程](https://www.bilibili.com/video/BV1n34y1A7Bo/?spm_id_from=333.999.0.0)
- [接入飞书](https://www.bilibili.com/video/BV1Su4y1r7R3/?spm_id_from=333.999.0.0)
- [接入企微](https://www.bilibili.com/video/BV1Tp4y1n72T/?spm_id_from=333.999.0.0)
<a href="#readme">
<img src="https://img.shields.io/badge/-返回顶部-7d09f1.svg" alt="#" align="right">
@@ -214,4 +215,4 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b
1. 允许作为后台服务直接商用,但不允许提供 SaaS 服务。
2. 未经商业授权,任何形式的商用服务均需保留相关版权信息。
3. 完整请查看 [FastGPT Open Source License](./LICENSE)
4. 联系方式Dennis@sealos.io[点击查看商业版定价策略](https://doc.tryfastgpt.ai/docs/commercial)
4. 联系方式Dennis@sealos.io[点击查看商业版定价策略](https://doc.tryfastgpt.ai/docs/shopping_cart/intro/)

Binary file not shown.

After

Width:  |  Height:  |  Size: 310 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 462 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 362 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 320 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 337 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 288 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 680 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 485 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 279 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 610 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 516 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 491 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 348 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 341 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 411 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 304 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 824 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 664 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 312 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 302 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 323 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

View File

@@ -4,7 +4,7 @@ description: 'FastGPT 配置参数介绍'
icon: 'settings'
draft: false
toc: true
weight: 708
weight: 707
---
由于环境变量不利于配置复杂的内容,新版 FastGPT 采用了 ConfigMap 的形式挂载配置文件,你可以在 `projects/app/data/config.json` 看到默认的配置文件。可以参考 [docker-compose 快速部署](/docs/development/docker/) 来挂载配置文件。
@@ -97,7 +97,9 @@ weight: 708
"customExtractPrompt": "",
"defaultSystemChatPrompt": "",
"defaultConfig": {
"temperature": 1
"temperature": 1,
"max_tokens": null,
"stream": false
}
},
{
@@ -122,7 +124,9 @@ weight: 708
"customExtractPrompt": "",
"defaultSystemChatPrompt": "",
"defaultConfig": {
"temperature": 1
"temperature": 1,
"max_tokens": null,
"stream": false
}
}
],
@@ -164,6 +168,7 @@ weight: 708
"reRankModels": [],
"audioSpeechModels": [
{
"provider": "OpenAI",
"model": "tts-1",
"name": "OpenAI TTS1",
"charsPointsPrice": 0,
@@ -178,6 +183,7 @@ weight: 708
}
],
"whisperModel": {
"provider": "OpenAI",
"model": "whisper-1",
"name": "Whisper1",
"charsPointsPrice": 0
@@ -185,7 +191,7 @@ weight: 708
}
```
## 模型提供商
## 内置的模型提供商ID
为了方便模型分类展示FastGPT 内置了部分模型提供商的名字和 Logo。如果你期望补充提供商可[提交 Issue](https://github.com/labring/FastGPT/issues),并提供几个信息:
@@ -197,7 +203,9 @@ weight: 708
- OpenAI
- Claude
- Gemini
- Meta
- MistralAI
- AliCloud - 阿里云
- Qwen - 通义千问
- Doubao - 豆包
- ChatGLM - 智谱
@@ -209,13 +217,40 @@ weight: 708
- Baichuan - 百川
- Yi - 零一万物
- Ernie - 文心一言
- StepFun - 阶跃星辰
- Ollama
- BAAI - 智源研究院
- FishAudio
- Other - 其他
## 特殊模型
## ReRank 模型接入
### ReRank 接入(私有部署)
由于 OneAPI 不支持 Rerank 模型,所以需要单独配置接入,这里
### 使用硅基流动的在线模型
有免费的 `bge-reranker-v2-m3` 模型可以使用。
1. [点击注册硅基流动账号](https://cloud.siliconflow.cn/i/TR9Ym0c4)
2. 进入控制台,获取 API key: https://cloud.siliconflow.cn/account/ak
3. 修改 FastGPT 配置文件
```json
{
"reRankModels": [
{
"model": "BAAI/bge-reranker-v2-m3", // 这里的model需要对应 siliconflow 的模型名
"name": "BAAI/bge-reranker-v2-m3",
"requestUrl": "https://api.siliconflow.cn/v1/rerank",
"requestAuth": "siliconflow 上申请的 key"
}
]
}
```
### 私有部署模型
请使用 4.6.6-alpha 以上版本,配置文件中的 `reRankModels` 为重排模型虽然是数组不过目前仅有第1个生效。
@@ -236,44 +271,3 @@ weight: 708
]
}
```
### ReRank 接入(硅基流动)
有免费的 `bge-reranker-v2-m3` 模型可以使用。
1. 注册硅基流动账号: https://siliconflow.cn/
2. 进入控制台,获取 API key: https://cloud.siliconflow.cn/account/ak
3. 修改 FastGPT 配置文件
```json
{
"reRankModels": [
{
"model": "BAAI/bge-reranker-v2-m3", // 这里的model需要对应 siliconflow 的模型名
"name": "BAAI/bge-reranker-v2-m3",
"requestUrl": "https://api.siliconflow.cn/v1/rerank",
"requestAuth": "siliconflow 上申请的 key"
}
]
}
```
### ReRank 接入Cohere
这个重排模型对中文不是很好,不如 bge 的好用。
1. 申请 Cohere 官方 Key: https://dashboard.cohere.com/api-keys
2. 修改 FastGPT 配置文件
```json
{
"reRankModels": [
{
"model": "rerank-multilingual-v2.0", // 这里的model需要对应 cohere 的模型名
"name": "rerank-multilingual-v2.0",
"requestUrl": "https://api.cohere.ai/v1/rerank",
"requestAuth": "Coherer上申请的key"
}
]
}
```

View File

@@ -23,7 +23,7 @@ FastGPT 默认使用了 OpenAI 的 LLM 模型和向量模型,如果想要私
也可以通过环境变量引入sk-key。有关docker环境变量引入的方法请自寻教程此处不再赘述。
```
## 接入 [One API](/docs/development/one-api/)
## 接入 [One API](/docs/development/modelconfig/one-api/)
为 chatglm2 和 m3e-large 各添加一个渠道,参数如下:

View File

@@ -102,7 +102,7 @@ xinference launch -n qwen-chat -s 14 -f pytorch
## 将本地模型接入 One API
One API 的部署和接入请参考[这里](/docs/development/one-api/)。
One API 的部署和接入请参考[这里](/docs/development/modelconfig/one-api/)。
为 qwen1.5-chat 添加一个渠道,这里的 Base URL 需要填 Xinference 服务的端点,并且注册 qwen-chat (模型的 UID) 。

View File

@@ -192,7 +192,7 @@ docker restart oneapi
可以通过`ip:3001`访问OneAPI默认账号为`root`密码为`123456`
在OneApi中添加合适的AI模型渠道。[点击查看相关教程](/docs/development/one-api/)
在OneApi中添加合适的AI模型渠道。[点击查看相关教程](/docs/development/modelconfig/one-api/)
### 5. 访问 FastGPT

View File

@@ -1,5 +1,5 @@
---
weight: 749
weight: 740
title: "私有部署常见问题"
description: "FastGPT 私有部署常见问题"
icon: upgrade

View File

@@ -0,0 +1,8 @@
---
weight: 745
title: '模型配置方案'
description: '本模型配置方案'
icon: 'code_blocks'
draft: false
images: []
---

View File

@@ -0,0 +1,189 @@
---
title: '通过 OneAPI 接入模型'
description: '通过 OneAPI 接入模型'
icon: 'api'
draft: false
toc: true
weight: 745
---
FastGPT 目前采用模型分离的部署方案FastGPT 中只兼容 OpenAI 的模型规范OpenAI 不存在的模型采用一个较为通用的规范),并通过 [One API](https://github.com/songquanpeng/one-api) 来实现对不同模型接口的统一。
[One API](https://github.com/songquanpeng/one-api) 是一个 OpenAI 接口管理 & 分发系统,可以通过标准的 OpenAI API 格式访问所有的大模型,开箱即用。
## FastGPT 与 One API 关系
可以把 One API 当做一个网关FastGPT 与 One API 关系:
![](/imgs/sealos-fastgpt.webp)
## 部署
### Docker 版本
`docker-compose.yml` 文件已加入了 OneAPI 配置,可直接使用。默认暴露在 3001 端口。
### Sealos 版本
* 北京区: [点击部署 OneAPI](https://hzh.sealos.run/?openapp=system-template%3FtemplateName%3Done-api)
* 新加坡区(可用 GPT) [点击部署 OneAPI](https://cloud.sealos.io/?openapp=system-template%3FtemplateName%3Done-api)
![alt text](/imgs/image-59.png)
部署完后,可以打开 OneAPI 访问链接,进行下一步操作。
## OneAPI 基础教程
### 概念
1. 渠道:
1. OneApi 中一个渠道对应一个 `Api Key`,这个 `Api Key` 可以是GPT、微软、ChatGLM、文心一言的。一个`Api Key`通常可以调用同一个厂商的多个模型。
2. One API 会根据请求传入的`模型`来决定使用哪一个`渠道`,如果一个模型对应了多个`渠道`,则会随机调用。
2. 令牌:访问 One API 所需的凭证,只需要这`1`个凭证即可访问`One API`上配置的模型。因此`FastGPT`中,只需要配置`One API``baseurl``令牌`即可。令牌不要设置任何的模型范围权限,否则容易报错。
![alt text](/imgs/image-60.png)
### 大致工作流程
1. 客户端请求 One API
2. 根据请求中的 `model` 参数,匹配对应的渠道(根据渠道里的模型进行匹配,必须完全一致)。如果匹配到多个渠道,则随机选择一个(同优先级)。
3. One API 向真正的地址发出请求。
4. One API 将结果返回给客户端。
### 1. 登录 One API
![step5](/imgs/oneapi-step5.png)
### 2. 创建渠道
在 One API 中添加对应渠道,直接点击 【添加基础模型】不要遗漏了向量模型Embedding
![step6](/imgs/oneapi-step6.png)
### 3. 创建令牌
| | |
| --- | --- |
| ![step7](/imgs/oneapi-step7.png) | ![alt text](/imgs/image-61.png) |
### 4. 修改账号余额
One API 默认 root 用户只有 200刀可以自行修改编辑。
![alt text](/imgs/image-62.png)
### 5. 修改 FastGPT 的环境变量
有了 One API 令牌后FastGPT 可以通过修改 `baseurl``key` 去请求到 One API再由 One API 去请求不同的模型。修改下面两个环境变量:
```bash
# 务必写上 v1。如果在同一个网络内可改成内网地址。
OPENAI_BASE_URL=https://xxxx.cloud.sealos.io/v1
# 下面的 key 是由 One API 提供的令牌
CHAT_API_KEY=sk-xxxxxx
```
## 接入其他模型
**以添加文心一言为例:**
### 1. OneAPI 新增模型渠道
类型选择百度文心千帆。
![](/imgs/oneapi-demo1.png)
### 2. 修改 FastGPT 配置文件
可以在 `/projects/app/src/data/config.json` 里找到配置文件(本地开发需要复制成 config.local.json,按下面内容修改配置文件,最新/更具体的配置说明,可查看[FastGPT 配置文件说明](/docs/development/configuration)。
配置模型关键点在于`model` 需要与 OneAPI 渠道中的模型一致。
```json
{
"llmModels": [ // 语言模型配置
{
"model": "ERNIE-Bot", // 这里的模型需要对应 One API 的模型
"name": "文心一言", // 对外展示的名称
"avatar": "/imgs/model/openai.svg", // 模型的logo
"maxContext": 16000, // 最大上下文
"maxResponse": 4000, // 最大回复
"quoteMaxToken": 13000, // 最大引用内容
"maxTemperature": 1.2, // 最大温度
"charsPointsPrice": 0,
"censor": false,
"vision": false, // 是否支持图片输入
"datasetProcess": true, // 是否设置为知识库处理模型
"usedInClassify": true, // 是否用于问题分类
"usedInExtractFields": true, // 是否用于字段提取
"usedInToolCall": true, // 是否用于工具调用
"usedInQueryExtension": true, // 是否用于问题优化
"toolChoice": true, // 是否支持工具选择
"functionCall": false, // 是否支持函数调用
"customCQPrompt": "", // 自定义文本分类提示词(不支持工具和函数调用的模型
"customExtractPrompt": "", // 自定义内容提取提示词
"defaultSystemChatPrompt": "", // 对话默认携带的系统提示词
"defaultConfig":{} // 请求API时挟带一些默认配置比如 GLM4 的 top_p
}
],
"vectorModels": [ // 向量模型配置
{
"model": "text-embedding-ada-002",
"name": "Embedding-2",
"avatar": "/imgs/model/openai.svg",
"charsPointsPrice": 0,
"defaultToken": 700,
"maxToken": 3000,
"weight": 100
},
]
}
```
### 3. 重启 FastGPT
**Docker 版本**
```bash
docker-compose down
docker-compose up -d
```
**Sealos 版本**
直接找到 FastGPT 服务,点击重启即可。
## 其他服务商接入参考
这章介绍一些提供商接入 OneAPI 的教程,配置后不要忘记修改 FastGPT 配置文件。
### 阿里通义千问
千问目前已经兼容 GPT 格式,可以直接选择 OpenAI 类型来接入即可。如下图,选择类型为`OpenAI`,代理填写阿里云的代理地址。
目前可以直接使用阿里云的语言模型和 `text-embedding-v3` 向量模型(实测已经归一化,可直接使用)
![alt text](/imgs/image-63.png)
### 硅基流动 —— 开源模型大合集
[硅基流动](https://cloud.siliconflow.cn/i/TR9Ym0c4) 是一个专门提供开源模型调用平台,并拥有自己的加速引擎。模型覆盖面广,非常适合低成本来测试开源模型。接入教程:
1. [点击注册硅基流动账号](https://cloud.siliconflow.cn/i/TR9Ym0c4)
2. 进入控制台,获取 API key: https://cloud.siliconflow.cn/account/ak
3. 新增 OneAPI 渠道,选择`OpenAI`类型,代理填写:`https://api.siliconflow.cn`,密钥是第二步创建的密钥。
![alt text](/imgs/image-64.png)
由于 OneAPI 未内置 硅基流动 的模型名,可以通过自定义模型名称来填入,下面是获取模型名称的教程:
1. 打开[硅基流动模型列表](https://siliconflow.cn/zh-cn/models)
2. 单击模型后,会打开模型详情。
3. 复制模型名到 OneAPI 中。
| | | |
| --- | --- | --- |
| ![alt text](/imgs/image-65.png) | ![alt text](/imgs/image-66.png)| ![alt text](/imgs/image-67.png) |

View File

@@ -0,0 +1,220 @@
---
title: '通过 SiliconCloud 体验开源模型'
description: '通过 SiliconCloud 体验开源模型'
icon: 'api'
draft: false
toc: true
weight: 746
---
[SiliconCloud(硅基流动)](https://cloud.siliconflow.cn/i/TR9Ym0c4) 是一个以提供开源模型调用为主的平台并拥有自己的加速引擎。帮助用户低成本、快速的进行开源模型的测试和使用。实际体验下来他们家模型的速度和稳定性都非常不错并且种类丰富覆盖语言、向量、重排、TTS、STT、绘图、视频生成模型可以满足 FastGPT 中所有模型需求。
如果你想部分模型使用 SiliconCloud 的模型,可额外参考[OneAPI接入硅基流动](/docs/development/modelconfig/one-api/#硅基流动--开源模型大合集)。
本文会介绍完全使用 SiliconCloud 模型来部署 FastGPT 的方案。
## 1. 注册 SiliconCloud 账号
1. [点击注册硅基流动账号](https://cloud.siliconflow.cn/i/TR9Ym0c4)
2. 进入控制台,获取 API key: https://cloud.siliconflow.cn/account/ak
## 2. 修改 FastGPT 环境变量
```bash
OPENAI_BASE_URL=https://api.siliconflow.cn/v1
# 填写 SiliconCloud 控制台提供的 Api Key
CHAT_API_KEY=sk-xxxxxx
```
## 3. 修改 FastGPT 配置文件
我们选取 SiliconCloud 中的模型作为 FastGPT 配置。这里配置了 `Qwen2.5 72b` 的纯语言和视觉模型;选择 `bge-m3` 作为向量模型;选择 `bge-reranker-v2-m3` 作为重排模型。选择 `fish-speech-1.5` 作为语音模型;选择 `SenseVoiceSmall` 作为语音输入模型。
注意ReRank 模型仍需配置一次 Api Key
```json
{
"llmModels": [
{
"provider": "Other", // 模型提供商主要用于分类展示目前已经内置提供商包括https://github.com/labring/FastGPT/blob/main/packages/global/core/ai/provider.ts, 可 pr 提供新的提供商,或直接填写 Other
"model": "Qwen/Qwen2.5-72B-Instruct", // 模型名(对应OneAPI中渠道的模型名)
"name": "Qwen2.5-72B-Instruct", // 模型别名
"maxContext": 32000, // 最大上下文
"maxResponse": 4000, // 最大回复
"quoteMaxToken": 30000, // 最大引用内容
"maxTemperature": 1, // 最大温度
"charsPointsPrice": 0, // n积分/1k token商业版
"censor": false, // 是否开启敏感校验(商业版)
"vision": false, // 是否支持图片输入
"datasetProcess": true, // 是否设置为文本理解模型QA务必保证至少有一个为true否则知识库会报错
"usedInClassify": true, // 是否用于问题分类务必保证至少有一个为true
"usedInExtractFields": true, // 是否用于内容提取务必保证至少有一个为true
"usedInToolCall": true, // 是否用于工具调用务必保证至少有一个为true
"usedInQueryExtension": true, // 是否用于问题优化务必保证至少有一个为true
"toolChoice": true, // 是否支持工具选择(分类,内容提取,工具调用会用到。)
"functionCall": false, // 是否支持函数调用(分类,内容提取,工具调用会用到。会优先使用 toolChoice如果为false则使用 functionCall如果仍为 false则使用提示词模式
"customCQPrompt": "", // 自定义文本分类提示词(不支持工具和函数调用的模型
"customExtractPrompt": "", // 自定义内容提取提示词
"defaultSystemChatPrompt": "", // 对话默认携带的系统提示词
"defaultConfig": {}, // 请求API时挟带一些默认配置比如 GLM4 的 top_p
"fieldMap": {} // 字段映射o1 模型需要把 max_tokens 映射为 max_completion_tokens
},
{
"provider": "Other",
"model": "Qwen/Qwen2-VL-72B-Instruct",
"name": "Qwen2-VL-72B-Instruct",
"maxContext": 32000,
"maxResponse": 4000,
"quoteMaxToken": 30000,
"maxTemperature": 1,
"charsPointsPrice": 0,
"censor": false,
"vision": true,
"datasetProcess": false,
"usedInClassify": false,
"usedInExtractFields": false,
"usedInToolCall": false,
"usedInQueryExtension": false,
"toolChoice": false,
"functionCall": false,
"customCQPrompt": "",
"customExtractPrompt": "",
"defaultSystemChatPrompt": "",
"defaultConfig": {}
}
],
"vectorModels": [
{
"provider": "Other",
"model": "Pro/BAAI/bge-m3",
"name": "Pro/BAAI/bge-m3",
"charsPointsPrice": 0,
"defaultToken": 512,
"maxToken": 5000,
"weight": 100
}
],
"reRankModels": [
{
"model": "BAAI/bge-reranker-v2-m3", // 这里的model需要对应 siliconflow 的模型名
"name": "BAAI/bge-reranker-v2-m3",
"requestUrl": "https://api.siliconflow.cn/v1/rerank",
"requestAuth": "siliconflow 上申请的 key"
}
],
"audioSpeechModels": [
{
"model": "fishaudio/fish-speech-1.5",
"name": "fish-speech-1.5",
"voices": [
{
"label": "fish-alex",
"value": "fishaudio/fish-speech-1.5:alex",
"bufferId": "fish-alex"
},
{
"label": "fish-anna",
"value": "fishaudio/fish-speech-1.5:anna",
"bufferId": "fish-anna"
},
{
"label": "fish-bella",
"value": "fishaudio/fish-speech-1.5:bella",
"bufferId": "fish-bella"
},
{
"label": "fish-benjamin",
"value": "fishaudio/fish-speech-1.5:benjamin",
"bufferId": "fish-benjamin"
},
{
"label": "fish-charles",
"value": "fishaudio/fish-speech-1.5:charles",
"bufferId": "fish-charles"
},
{
"label": "fish-claire",
"value": "fishaudio/fish-speech-1.5:claire",
"bufferId": "fish-claire"
},
{
"label": "fish-david",
"value": "fishaudio/fish-speech-1.5:david",
"bufferId": "fish-david"
},
{
"label": "fish-diana",
"value": "fishaudio/fish-speech-1.5:diana",
"bufferId": "fish-diana"
}
]
}
],
"whisperModel": {
"model": "FunAudioLLM/SenseVoiceSmall",
"name": "SenseVoiceSmall",
"charsPointsPrice": 0
}
}
```
## 4. 重启 FastGPT
## 5. 体验测试
### 测试对话和图片识别
随便新建一个简易应用,选择对应模型,并开启图片上传后进行测试:
| | |
| --- | --- |
| ![alt text](/imgs/image-68.png) | ![alt text](/imgs/image-70.png) |
可以看到72B 的模型,性能还是非常快的,这要是本地没几个 4090不说配置环境输出怕都要 30s 了。
### 测试知识库导入和知识库问答
新建一个知识库(由于只配置了一个向量模型,页面上不会展示向量模型选择)
| | |
| --- | --- |
| ![alt text](/imgs/image-72.png) | ![alt text](/imgs/image-71.png) |
导入本地文件直接选择文件然后一路下一步即可。79 个索引,大概花了 20s 的时间就完成了。现在我们去测试一下知识库问答。
首先回到我们刚创建的应用,选择知识库,调整一下参数后即可开始对话:
| | | |
| --- | --- | --- |
| ![alt text](/imgs/image-73.png) | ![alt text](/imgs/image-75.png) | ![alt text](/imgs/image-76.png) |
对话完成后,点击底部的引用,可以查看引用详情,同时可以看到具体的检索和重排得分:
| | |
| --- | --- |
| ![alt text](/imgs/image-77.png) | ![alt text](/imgs/image-78.png) |
### 测试语音播放
继续在刚刚的应用中,左侧配置中找到语音播放,点击后可以从弹窗中选择语音模型,并进行试听:
![alt text](/imgs/image-79.png)
### 测试语言输入
继续在刚刚的应用中,左侧配置中找到语音输入,点击后可以从弹窗中开启语言输入
![alt text](/imgs/image-80.png)
开启后,对话输入框中,会增加一个话筒的图标,点击可进行语音输入:
| | |
| --- | --- |
| ![alt text](/imgs/image-81.png) | ![alt text](/imgs/image-82.png) |
## 总结
如果你想快速的体验开源模型或者快速的使用 FastGPT不想在不同服务商申请各类 Api Key那么可以选择 SiliconCloud 的模型先进行快速体验。
如果你决定未来私有化部署模型和 FastGPT前期可通过 SiliconCloud 进行测试验证,后期再进行硬件采购,减少 POC 时间和成本。

View File

@@ -1,179 +0,0 @@
---
title: '使用 One API 接入 Azure、ChatGLM 和本地模型'
description: '部署和使用 One API实现 Azure、ChatGLM 和本地模型的接入。'
icon: 'api'
draft: false
toc: true
weight: 708
---
* 默认情况下FastGPT 只配置了 GPT 的模型,如果你需要接入其他模型,需要进行一些额外配置。
* [One API](https://github.com/songquanpeng/one-api) 是一个 OpenAI 接口管理 & 分发系统,可以通过标准的 OpenAI API 格式访问所有的大模型,开箱即用。
* FastGPT 可以通过接入 One API 来实现对不同大模型的支持。One API 的部署方法也很简单。
## FastGPT 与 One API 关系
可以把 One API 当做一个网关。
![](/imgs/sealos-fastgpt.webp)
## 部署
### Docker 版本
已加入最新的 `docker-compose.yml` 文件中。
### Sealos - MySQL 版本
MySQL 版本支持多实例,高并发。
直接点击以下按钮即可一键部署 👇
<a href="https://template.cloud.sealos.io/deploy?templateName=one-api" rel="external" target="_blank"><img src="https://cdn.jsdelivr.net/gh/labring-actions/templates@main/Deploy-on-Sealos.svg" alt="Deploy on Sealos"/></a>
部署完后会跳转「应用管理」,数据库在另一个应用「数据库」中。需要等待 1~3 分钟数据库运行后才能访问成功。
### Sealos - SqlLite 版本
SqlLite 版本不支持多实例,适合个人小流量使用,但是价格非常便宜。
**1. [点击打开 Sealos 公有云](https://cloud.sealos.io/)**
**2. 打开 AppLaunchpad(应用管理) 工具**
![step1](/imgs/oneapi-step1.webp)
**3. 点击创建新应用**
**4. 填写对应参数**
镜像ghcr.io/songquanpeng/one-api:latest
![step2](/imgs/oneapi-step2.png)
打开外网访问开关后Sealos 会自动分配一个可访问的地址,不需要自己配置。
![step3](/imgs/oneapi-step3.png)
填写完参数后,点击右上角部署即可。环境变量:
```
SESSION_SECRET=SESSION_SECRET
POLLING_INTERVAL=60
BATCH_UPDATE_ENABLED=true
BATCH_UPDATE_INTERVAL=60
```
## One API 使用教程
### 概念
1. 渠道:
1. OneApi 中一个渠道对应一个 `Api Key`,这个 `Api Key` 可以是GPT、微软、ChatGLM、文心一言的。一个`Api Key`通常可以调用同一个厂商的多个模型。
2. One API 会根据请求传入的`模型`来决定使用哪一个`Key`,如果一个模型对应了多个`Key`,则会随机调用。
2. 令牌:访问 One API 所需的凭证,只需要这`1`个凭证即可访问`One API`上配置的模型。因此`FastGPT`中,只需要配置`One API``baseurl``令牌`即可。
### 大致工作流程
1. 客户端请求 One API
2. 根据请求中的 `model` 参数,匹配对应的渠道(根据渠道里的模型进行匹配,必须完全一致)。如果匹配到多个渠道,则随机选择一个(同优先级)。
3. One API 向真正的地址发出请求。
4. One API 将结果返回给客户端。
### 1. 登录 One API
打开 【One API 应用详情】,找到访问地址:
![step4](/imgs/oneapi-step4.png)
登录 One API
![step5](/imgs/oneapi-step5.png)
### 2. 创建渠道和令牌
在 One API 中添加对应渠道,直接点击 【添加基础模型】不要遗漏了向量模型Embedding
![step6](/imgs/oneapi-step6.png)
创建一个令牌
![step7](/imgs/oneapi-step7.png)
### 3. 修改账号余额
One API 默认 root 用户只有 200刀可以自行修改编辑。
### 4. 修改 FastGPT 的环境变量
有了 One API 令牌后FastGPT 可以通过修改 `baseurl``key` 去请求到 One API再由 One API 去请求不同的模型。修改下面两个环境变量:
```bash
# 下面的地址是 Sealos 提供的,务必写上 v1 两个项目都在 sealos 部署时候https://xxxx.cloud.sealos.io 可以改用内网地址
OPENAI_BASE_URL=https://xxxx.cloud.sealos.io/v1
# 下面的 key 是由 One API 提供的令牌
CHAT_API_KEY=sk-xxxxxx
```
## 接入其他模型
**以添加文心一言为例:**
### 1. One API 添加对应模型渠道
![](/imgs/oneapi-demo1.png)
### 2. 修改 FastGPT 配置文件
可以在 `/projects/app/src/data/config.json` 里找到配置文件(本地开发需要复制成 config.local.json配置文件中有一项是**对话模型配置**
```json
"llmModels": [
...
{
"model": "ERNIE-Bot", // 这里的模型需要对应 One API 的模型
"name": "文心一言", // 对外展示的名称
"avatar": "/imgs/model/openai.svg", // 模型的logo
"maxContext": 16000, // 最大上下文
"maxResponse": 4000, // 最大回复
"quoteMaxToken": 13000, // 最大引用内容
"maxTemperature": 1.2, // 最大温度
"charsPointsPrice": 0,
"censor": false,
"vision": false, // 是否支持图片输入
"datasetProcess": true, // 是否设置为知识库处理模型
"usedInClassify": true, // 是否用于问题分类
"usedInExtractFields": true, // 是否用于字段提取
"usedInToolCall": true, // 是否用于工具调用
"usedInQueryExtension": true, // 是否用于问题优化
"toolChoice": true, // 是否支持工具选择
"functionCall": false, // 是否支持函数调用
"customCQPrompt": "", // 自定义文本分类提示词(不支持工具和函数调用的模型
"customExtractPrompt": "", // 自定义内容提取提示词
"defaultSystemChatPrompt": "", // 对话默认携带的系统提示词
"defaultConfig":{} // 请求API时挟带一些默认配置比如 GLM4 的 top_p
}
...
],
```
**添加向量模型:**
```json
"vectorModels": [
......
{
"model": "text-embedding-ada-002",
"name": "Embedding-2",
"avatar": "/imgs/model/openai.svg",
"charsPointsPrice": 0,
"defaultToken": 700,
"maxToken": 3000,
"weight": 100
},
......
]
```
### 3. 重启 FastGPT
```bash
docker-compose down
docker-compose up -d
```
重启 FastGPT 即可在选择文心一言模型进行对话。**添加向量模型也是类似操作,增加到 `vectorModels`里。**

View File

@@ -1424,7 +1424,11 @@ curl --location --request POST 'https://api.fastgpt.in/api/core/dataset/searchTe
"limit": 5000,
"similarity": 0,
"searchMode": "embedding",
"usingReRank": false
"usingReRank": false,
"datasetSearchUsingExtensionQuery": true,
"datasetSearchExtensionModel": "gpt-4o-mini",
"datasetSearchExtensionBg": ""
}'
```
@@ -1441,6 +1445,9 @@ curl --location --request POST 'https://api.fastgpt.in/api/core/dataset/searchTe
- similarity - 最低相关度0~1可选
- searchMode - 搜索模式embedding | fullTextRecall | mixedRecall
- usingReRank - 使用重排
- datasetSearchUsingExtensionQuery - 使用问题优化
- datasetSearchExtensionModel - 问题优化模型
- datasetSearchExtensionBg - 问题优化背景描述
{{% /alert %}}
{{< /markdownify >}}

View File

@@ -15,7 +15,7 @@ weight: 706
FastGPT 使用了 one-api 项目来管理模型池,其可以兼容 OpenAI 、Azure 、国内主流模型和本地模型等。
可参考:[Sealos 快速部署 OneAPI](/docs/development/one-api)
可参考:[Sealos 快速部署 OneAPI](/docs/development/modelconfig/one-api)
## 一键部署
@@ -163,4 +163,4 @@ SYSTEM_FAVICON 可以是一个网络地址
### One API 使用
[参考 OneAPI 使用步骤](/docs/development/one-api/)
[参考 OneAPI 使用步骤](/docs/development/modelconfig/one-api/)

View File

@@ -1,5 +1,5 @@
---
title: 'V4.8.16(进行中)'
title: 'V4.8.16(更新配置文件)'
description: 'FastGPT V4.8.16 更新说明'
icon: 'upgrade'
draft: false
@@ -11,9 +11,9 @@ weight: 808
### 1. 更新镜像:
- 更新 fastgpt 镜像 tag: v4.8.16-beta
- 更新 fastgpt-pro 商业版镜像 tag: v4.8.16-beta
- Sandbox 镜像 tag: v4.8.16-beta
- 更新 fastgpt 镜像 tag: v4.8.16
- 更新 fastgpt-pro 商业版镜像 tag: v4.8.16
- Sandbox 镜像 tag: v4.8.16
### 2. 更新配置文件
@@ -68,4 +68,6 @@ weight: 808
16. 修复 - 简易模式转工作流,没有把系统配置项转化。
17. 修复 - 插件独立运行,变量初始值未赋上。
18. 修复 - 工作流使用弹窗组件时,关闭弹窗后,有时候会出现页面偏移。
19. 修复 - 插件调试时,日志未保存插件输入参数。
19. 修复 - 插件调试时,日志未保存插件输入参数。
20. 修复 - 部分模板市场模板
21. 修复 - 设置NEXT_PUBLIC_BASE_URL时图片文件读取URL不正确

View File

@@ -0,0 +1,52 @@
---
title: 'V4.8.17(包含升级脚本)'
description: 'FastGPT V4.8.17 更新说明'
icon: 'upgrade'
draft: false
toc: true
weight: 807
---
## 更新指南
### 1. 更新镜像:
- 更新 fastgpt 镜像 tag: v4.8.17
- 更新 fastgpt-pro 商业版镜像 tag: v4.8.17
- Sandbox 镜像无需更新
### 2. 运行升级脚本
从任意终端,发起 1 个 HTTP 请求。其中 {{rootkey}} 替换成环境变量里的 `rootkey`{{host}} 替换成**FastGPT 域名**。
```bash
curl --location --request POST 'https://{{host}}/api/admin/initv4817' \
--header 'rootkey: {{rootkey}}' \
--header 'Content-Type: application/json'
```
会将用户绑定的 OpenAI 账号移动到团队中。
## 调整 completions 接口返回值
/api/v1/chat/completions 接口返回值调整,对话节点、工具节点等使用到模型的节点,将不再返回 `tokens` 字段,改为返回 `inputTokens``outputTokens` 字段,分别表示输入和输出的 Token 数量。
## 完整更新内容
1. 新增 - 简易模式工具调用支持数组类型插件。
2. 新增 - 工作流增加异常离开自动保存,避免工作流丢失。
3. 新增 - LLM 模型参数支持关闭 max_tokens 和 temperature。
4. 新增 - 商业版支持后台配置模板市场。
5. 新增 - 商业版支持后台配置自定义工作流变量,用于与业务系统鉴权打通。
6. 新增 - 搜索测试接口支持问题优化。
7. 新增 - 工作流中 Input Token 和 Output Token 分开记录展示。并修复部分请求未记录输出 Token 计费问题。
8. 优化 - Markdown 大小测试,超出 20 万字符不使用 Markdown 组件,避免崩溃。
9. 优化 - 知识库搜索参数,滑动条支持输入模式,可以更精准的控制。
10. 优化 - 可用模型展示UI。
11. 优化 - Mongo 查询语句,增加 virtual 字段。
12. 修复 - 文件返回接口缺少 Content-Length 头,导致通过非同源文件上传时,阿里 vision 模型无法识别图片。
13. 修复 - 去除判断器两端字符串隐藏换行符,避免判断器失效。
14. 修复 - 变量更新节点,手动输入更新内容时候,非字符串类型数据类型无法自动转化。
15. 修复 - 豆包模型无法工具调用。

View File

@@ -44,9 +44,9 @@ weight: 104
被放置在上下文数组的最前面role 为 system用于引导模型。
### 最大对话轮数(仅简易模式)
### 记忆轮数(仅简易模式)
可以配置模型支持的最大对话轮数,如果模型的超出上下文,系统会自动截断,尽可能保证不超模型上下文。
可以配置模型支持的记忆轮数,如果模型的超出上下文,系统会自动截断,尽可能保证不超模型上下文。
所以尽管配置 30 轮对话,实际运行时候,不一定会达到 30 轮。

View File

@@ -45,7 +45,7 @@ FastGPT 从 V4 版本开始采用新的交互方式来构建 AI 应用。使用
## 重点 - 工作流是如何运行的
FastGPT的工作流从【流程开始】节点开始执行可以理解为从用户输入问题开始没有**固定的出口**,是以节点运行结束作为出口,如果在一个轮调用中,所有节点都不再允许,则工作流结束。
FastGPT的工作流从【流程开始】节点开始执行可以理解为从用户输入问题开始没有**固定的出口**,是以节点运行结束作为出口,如果在一个轮调用中,所有节点都不再运行,则工作流结束。
下面我们来看下,工作流是如何运行的,以及每个节点何时被触发执行。

View File

@@ -19,7 +19,7 @@ weight: 232
## AI模型
可以通过 [config.json](/docs/development/configuration/) 配置可选的对话模型,通过 [one-api](/docs/development/one-api/) 来实现多模型接入。
可以通过 [config.json](/docs/development/configuration/) 配置可选的对话模型,通过 [one-api](/docs/development/modelconfig/one-api/) 来实现多模型接入。
点击AI模型后可以配置模型的相关参数。

View File

@@ -58,7 +58,7 @@ weight: 236
#### 用途
默认清空工具调用节点在决定调用工具后会将工具运行的结果返回给AI让 AI 对工具运行的结果进行总结输出。有时候,如果你不需要 AI 进行进一步的总结输出,可以使用该节点,将其接入对于工具流程的末尾。
默认情况工具调用节点在决定调用工具后会将工具运行的结果返回给AI让 AI 对工具运行的结果进行总结输出。有时候,如果你不需要 AI 进行进一步的总结输出,可以使用该节点,将其接入对于工具流程的末尾。
如下图,在执行知识库搜索后,发送给了 HTTP 请求,搜索将不会返回搜索的结果给工具调用进行 AI 总结。

View File

@@ -19,17 +19,20 @@ FastGPT 商业版是基于 FastGPT 开源版的增强版本,增加了一些独
| 应用管理与高级编排 | ✅ | ✅ | ✅ |
| 文档知识库 | ✅ | ✅ | ✅ |
| 外部使用 | ✅ | ✅ | ✅ |
| API 知识库 | ✅ | ✅ | ✅ |
| 最大应用数量 | 500 | 无限制 | 由付费套餐决定 |
| 最大知识库数量(单个知识库内容无限制) | 30 | 无限制 | 由付费套餐决定 |
| 自定义版权信息 | ❌ | ✅ | 设计中 |
| 多租户与支付 | ❌ | ✅ | ✅ |
| 团队空间 | ❌ | ✅ | ✅ |
| 团队空间 & 权限 | ❌ | ✅ | ✅ |
| 应用发布安全配置 | ❌ | ✅ | ✅ |
| 内容审核 | ❌ | ✅ | ✅ |
| web站点同步 | ❌ | ✅ | ✅ |
| 管理后台 | ❌ | ✅ | 不需要 |
| 主流文档库接入(目前支持:语雀、飞书) | ❌ | ✅ | |
| 增强训练模式 | ❌ | ✅ | ✅ |
| 第三方应用快速接入(飞书、公众号) | ❌ | ✅ | ✅ |
| 管理后台 | ❌ | ✅ | 不需要 |
| SSO 登录可自定义也可使用内置Github、公众号、钉钉、谷歌等 | ❌ | ✅ | 不需要 |
| 图片知识库 | ❌ | 设计中 | 设计中 |
| 对话日志运营分析 | ❌ | 设计中 | 设计中 |
| 完整商业授权 | ❌ | ✅ | ✅ |
@@ -50,8 +53,8 @@ FastGPT 商业版软件根据不同的部署方式,分为 3 类收费模式。
{{< table "table-hover table-striped-columns" >}}
| 部署方式 | 特有服务 | 上线时长 | 标品价格 |
| ---- | ---- | ---- | ---- |
| Sealos全托管 | 1. 有效期内免费升级。<br>2. 免运维服务&数据库。 | 半天 | 5000元起/月3个月起<br>或<br>50000元起/年 |
| 自有服务器部署 | 1. 6个版本的升级服务。 | 14天内 | 具体价格可[联系咨询](https://fael3z0zfze.feishu.cn/share/base/form/shrcnRxj3utrzjywsom96Px4sud) |
| Sealos全托管 | 1. 有效期内免费升级。<br>2. 免运维服务&数据库。 | 半天 | 6000元起/月3个月起<br>或<br>60000元起/年 |
| 自有服务器部署 | 1. 6个版本免费升级支持。 | 14天内 | 具体价格可[联系咨询](https://fael3z0zfze.feishu.cn/share/base/form/shrcnRxj3utrzjywsom96Px4sud) |
{{< /table >}}
{{% alert icon="🤖 " context="success" %}}
@@ -62,6 +65,10 @@ FastGPT 商业版软件根据不同的部署方式,分为 3 类收费模式。
- 高可用版适合对外提供在线服务,包含可视化监控、多副本、负载均衡、数据库自动备份等生产环境的基础设施。
{{% /alert %}}
## 联系方式
请填写[咨询问卷](https://fael3z0zfze.feishu.cn/share/base/form/shrcnRxj3utrzjywsom96Px4sud),我们会尽快与您联系。
## 技术支持
@@ -79,9 +86,6 @@ FastGPT 商业版软件根据不同的部署方式,分为 3 类收费模式。
跨版本更新或复杂更新可参考文档自行更新;或付费支持,标准与技术服务费一致。
## 联系方式
请填写[咨询问卷](https://fael3z0zfze.feishu.cn/share/base/form/shrcnRxj3utrzjywsom96Px4sud),我们会尽快与您联系。
## QA
@@ -95,8 +99,14 @@ FastGPT 商业版软件根据不同的部署方式,分为 3 类收费模式。
可以修改开源版部分代码,不支持修改商业版镜像。完整版本=开源版+商业版镜像,所以是可以修改部分内容的。但是如果二开了,后续则需要自己进行代码合并升级。
## Sealos 费用
### Sealos 运行费用
Sealos 云服务属于按量计费,下面是它的价格表:
![](/imgs/sealos_price.jpg)
![alt text](/imgs/image-58.png)
## 管理后台部分截图
| | | |
| ---- | ---- | ---- |
| ![alt text](/imgs/image-55.png) | ![alt text](/imgs/image-56.png) | ![alt text](/imgs/image-57.png) |

View File

@@ -17,9 +17,11 @@ weight: 506
![图片](/imgs/offiaccount-1.png)
## 2. 登录微信公众平台,获取 AppID 、 Secret和Token
## 2. 获取 AppID 、 Secret和Token
### 1. https://mp.weixin.qq.com 登录微信公众平台,选择您的公众号。
### 1. 登录微信公众平台,选择您的公众号。
打开微信公众号官网https://mp.weixin.qq.com
**只支持通过验证的公众号,未通过验证的公众号暂不支持。**
@@ -28,6 +30,7 @@ weight: 506
![图片](/imgs/offiaccount-2.png)
### 2. 把3个参数填入 FastGPT 配置弹窗中。
![图片](/imgs/offiaccount-3.png)
## 3. 在 IP 白名单中加入 FastGPT 的 IP
@@ -36,7 +39,7 @@ weight: 506
私有部署的用户可自行查阅自己的 IP 地址。
海外版用户cloud.tryfastgpt.ai)可以填写下面的 IP 白名单:
海外版用户cloud.tryfastgpt.ai可以填写下面的 IP 白名单:
```
35.240.227.100

View File

@@ -114,15 +114,15 @@ services:
# fastgpt
sandbox:
container_name: sandbox
image: ghcr.io/labring/fastgpt-sandbox:v4.8.15 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.15 # 阿里云
image: ghcr.io/labring/fastgpt-sandbox:v4.8.17 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.17 # 阿里云
networks:
- fastgpt
restart: always
fastgpt:
container_name: fastgpt
image: ghcr.io/labring/fastgpt:v4.8.15-fix2 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.15-fix2 # 阿里云
image: ghcr.io/labring/fastgpt:v4.8.17 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.17 # 阿里云
ports:
- 3000:3000
networks:

View File

@@ -72,15 +72,15 @@ services:
# fastgpt
sandbox:
container_name: sandbox
image: ghcr.io/labring/fastgpt-sandbox:v4.8.15 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.15 # 阿里云
image: ghcr.io/labring/fastgpt-sandbox:v4.8.17 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.17 # 阿里云
networks:
- fastgpt
restart: always
fastgpt:
container_name: fastgpt
image: ghcr.io/labring/fastgpt:v4.8.15-fix2 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.15-fix2 # 阿里云
image: ghcr.io/labring/fastgpt:v4.8.17 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.17 # 阿里云
ports:
- 3000:3000
networks:

View File

@@ -53,15 +53,15 @@ services:
wait $$!
sandbox:
container_name: sandbox
image: ghcr.io/labring/fastgpt-sandbox:v4.8.15 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.15 # 阿里云
image: ghcr.io/labring/fastgpt-sandbox:v4.8.17 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.17 # 阿里云
networks:
- fastgpt
restart: always
fastgpt:
container_name: fastgpt
image: ghcr.io/labring/fastgpt:v4.8.15-fix2 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.15-fix2 # 阿里云
image: ghcr.io/labring/fastgpt:v4.8.17 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.17 # 阿里云
ports:
- 3000:3000
networks:

View File

@@ -5,7 +5,7 @@ import type {
LLMModelItemType,
VectorModelItemType,
AudioSpeechModels,
WhisperModelType,
STTModelType,
ReRankModelItemType
} from '../../../core/ai/model.d';
import { SubTypeEnum } from '../../../support/wallet/sub/constants';
@@ -18,6 +18,14 @@ export type NavbarItemType = {
isActive: boolean;
};
export type ExternalProviderWorkflowVarType = {
name: string;
key: string;
intro: string;
isOpen: boolean;
url?: string;
};
/* fastgpt main */
export type FastGPTConfigFileType = {
feConfigs: FastGPTFeConfigsType;
@@ -27,7 +35,7 @@ export type FastGPTConfigFileType = {
vectorModels: VectorModelItemType[];
reRankModels: ReRankModelItemType[];
audioSpeechModels: AudioSpeechModelType[];
whisperModel: WhisperModelType;
whisperModel: STTModelType;
};
export type FastGPTFeConfigsType = {
@@ -84,6 +92,7 @@ export type FastGPTFeConfigsType = {
uploadFileMaxSize?: number;
lafEnv?: string;
navbarItems?: NavbarItemType[];
externalProviderWorkflowVariables?: ExternalProviderWorkflowVarType[];
};
export type SystemEnvType = {

View File

@@ -1,6 +1,13 @@
import type { ModelProviderIdType } from './provider';
export type LLMModelItemType = {
type PriceType = {
charsPointsPrice?: number; // 1k chars=n points; 60s=n points;
// If inputPrice is set, the input-output charging scheme is adopted
inputPrice?: number; // 1k tokens=n points
outputPrice?: number; // 1k tokens=n points
};
export type LLMModelItemType = PriceType & {
provider: ModelProviderIdType;
model: string;
name: string;
@@ -10,8 +17,6 @@ export type LLMModelItemType = {
quoteMaxToken: number;
maxTemperature: number;
charsPointsPrice: number; // 1k chars=n points
censor?: boolean;
vision?: boolean;
@@ -33,13 +38,12 @@ export type LLMModelItemType = {
fieldMap?: Record<string, string>;
};
export type VectorModelItemType = {
export type VectorModelItemType = PriceType & {
provider: ModelProviderIdType;
model: string; // model name
name: string; // show name
avatar?: string;
defaultToken: number; // split text default token
charsPointsPrice: number; // 1k tokens=n points
maxToken: number; // model max token
weight: number; // training weight
hidden?: boolean; // Disallow creation
@@ -48,23 +52,22 @@ export type VectorModelItemType = {
queryConfig?: Record<string, any>; // Custom parameters for query
};
export type ReRankModelItemType = {
export type ReRankModelItemType = PriceType & {
model: string;
name: string;
charsPointsPrice: number;
requestUrl: string;
requestAuth: string;
};
export type AudioSpeechModelType = {
export type AudioSpeechModelType = PriceType & {
provider: ModelProviderIdType;
model: string;
name: string;
charsPointsPrice: number;
voices: { label: string; value: string; bufferId: string }[];
};
export type WhisperModelType = {
export type STTModelType = PriceType & {
provider: ModelProviderIdType;
model: string;
name: string;
charsPointsPrice: number; // 60s = n points
};

View File

@@ -1,4 +1,5 @@
import type { LLMModelItemType, VectorModelItemType } from './model.d';
import { i18nT } from '../../../web/i18n/utils';
import type { LLMModelItemType, STTModelType, VectorModelItemType } from './model.d';
import { getModelProvider, ModelProviderIdType } from './provider';
export const defaultQAModels: LLMModelItemType[] = [
@@ -35,6 +36,13 @@ export const defaultVectorModels: VectorModelItemType[] = [
}
];
export const defaultWhisperModel: STTModelType = {
provider: 'OpenAI',
model: 'whisper-1',
name: 'whisper-1',
charsPointsPrice: 0
};
export const getModelFromList = (
modelList: { provider: ModelProviderIdType; name: string; model: string }[],
model: string
@@ -46,3 +54,16 @@ export const getModelFromList = (
avatar: provider.avatar
};
};
export enum ModelTypeEnum {
chat = 'chat',
embedding = 'embedding',
tts = 'tts',
stt = 'stt'
}
export const modelTypeList = [
{ label: i18nT('common:model.type.chat'), value: ModelTypeEnum.chat },
{ label: i18nT('common:model.type.embedding'), value: ModelTypeEnum.embedding },
{ label: i18nT('common:model.type.tts'), value: ModelTypeEnum.tts },
{ label: i18nT('common:model.type.stt'), value: ModelTypeEnum.stt }
];

View File

@@ -4,19 +4,25 @@ export type ModelProviderIdType =
| 'OpenAI'
| 'Claude'
| 'Gemini'
| 'Meta'
| 'MistralAI'
| 'Groq'
| 'AliCloud'
| 'Qwen'
| 'Doubao'
| 'ChatGLM'
| 'DeepSeek'
| 'Ernie'
| 'Moonshot'
| 'MiniMax'
| 'SparkDesk'
| 'Hunyuan'
| 'Baichuan'
| 'StepFun'
| 'Yi'
| 'Ernie'
| 'Ollama'
| 'BAAI'
| 'FishAudio'
| 'Other';
export type ModelProviderType = {
@@ -41,10 +47,25 @@ export const ModelProviderList: ModelProviderType[] = [
name: 'Gemini',
avatar: 'model/gemini'
},
{
id: 'Meta',
name: 'Meta',
avatar: 'model/meta'
},
{
id: 'MistralAI',
name: 'MistralAI',
avatar: 'model/huggingface'
avatar: 'model/mistral'
},
{
id: 'Groq',
name: 'Groq',
avatar: 'model/groq'
},
{
id: 'AliCloud',
name: i18nT('common:model_alicloud'),
avatar: 'model/alicloud'
},
{
id: 'Qwen',
@@ -61,6 +82,11 @@ export const ModelProviderList: ModelProviderType[] = [
name: i18nT('common:model_chatglm'),
avatar: 'model/chatglm'
},
{
id: 'Ernie',
name: i18nT('common:model_ernie'),
avatar: 'model/ernie'
},
{
id: 'DeepSeek',
name: 'DeepSeek',
@@ -91,21 +117,32 @@ export const ModelProviderList: ModelProviderType[] = [
name: i18nT('common:model_baichuan'),
avatar: 'model/baichuan'
},
{
id: 'StepFun',
name: i18nT('common:model_stepfun'),
avatar: 'model/stepfun'
},
{
id: 'Yi',
name: i18nT('common:model_yi'),
avatar: 'model/yi'
},
{
id: 'Ernie',
name: i18nT('common:model_ernie'),
avatar: 'model/ernie'
},
{
id: 'Ollama',
name: 'Ollama',
avatar: 'model/ollama'
},
{
id: 'BAAI',
name: i18nT('common:model_baai'),
avatar: 'model/BAAI'
},
{
id: 'FishAudio',
name: 'FishAudio',
avatar: 'model/fishaudio'
},
{
id: 'Other',
name: i18nT('common:model_other'),
@@ -113,7 +150,7 @@ export const ModelProviderList: ModelProviderType[] = [
}
];
export const ModelProviderMap = Object.fromEntries(
ModelProviderList.map((item) => [item.id, item])
ModelProviderList.map((item, index) => [item.id, { ...item, order: index }])
);
export const getModelProvider = (provider: ModelProviderIdType) => {

View File

@@ -13,6 +13,7 @@ import { StoreEdgeItemType } from '../workflow/type/edge';
import { AppPermission } from '../../support/permission/app/controller';
import { ParentIdType } from '../../common/parentFolder/type';
import { FlowNodeInputTypeEnum } from 'core/workflow/node/constant';
import { WorkflowTemplateBasicType } from '@fastgpt/global/core/workflow/type';
export type AppSchema = {
_id: string;
@@ -73,8 +74,8 @@ export type AppSimpleEditFormType = {
aiSettings: {
model: string;
systemPrompt?: string | undefined;
temperature: number;
maxToken: number;
temperature?: number;
maxToken?: number;
isResponseAnswerText: boolean;
maxHistories: number;
};
@@ -109,8 +110,8 @@ export type AppChatConfigType = {
};
export type SettingAIDataType = {
model: string;
temperature: number;
maxToken: number;
temperature?: number;
maxToken?: number;
isResponseAnswerText?: boolean;
maxHistories?: number;
[NodeInputKeyEnum.aiChatVision]?: boolean; // Is open vision mode
@@ -184,3 +185,28 @@ export type SystemPluginListItemType = {
name: string;
avatar: string;
};
export type AppTemplateSchemaType = {
templateId: string;
name: string;
intro: string;
avatar: string;
tags: string[];
type: string;
author?: string;
isActive?: boolean;
userGuide?: {
type: 'markdown' | 'link';
content?: string;
link?: string;
};
isQuickTemplate?: boolean;
order?: number;
workflow: WorkflowTemplateBasicType;
};
export type TemplateTypeSchemaType = {
typeName: string;
typeId: string;
typeOrder: number;
};

View File

@@ -1,9 +1,7 @@
import { DatasetCollectionTypeEnum, TrainingModeEnum, TrainingTypeMap } from '../constants';
import { CollectionWithDatasetType, DatasetCollectionSchemaType } from '../type';
import { DatasetCollectionSchemaType } from '../type';
export const getCollectionSourceData = (
collection?: CollectionWithDatasetType | DatasetCollectionSchemaType
) => {
export const getCollectionSourceData = (collection?: DatasetCollectionSchemaType) => {
return {
sourceId:
collection?.fileId ||

View File

@@ -133,11 +133,8 @@ export type DatasetTrainingSchemaType = {
indexes: Omit<DatasetDataIndexItemType, 'dataId'>[];
};
export type CollectionWithDatasetType = Omit<DatasetCollectionSchemaType, 'datasetId'> & {
datasetId: DatasetSchemaType;
};
export type DatasetDataWithCollectionType = Omit<DatasetDataSchemaType, 'collectionId'> & {
collectionId: DatasetCollectionSchemaType;
export type CollectionWithDatasetType = DatasetCollectionSchemaType & {
dataset: DatasetSchemaType;
};
/* ================= dataset ===================== */

View File

@@ -21,13 +21,20 @@ import { ReadFileNodeResponse } from '../template/system/readFiles/type';
import { UserSelectOptionType } from '../template/system/userSelect/type';
import { WorkflowResponseType } from '../../../../service/core/workflow/dispatch/type';
import { AiChatQuoteRoleType } from '../template/system/aiChat/type';
import { LafAccountType, OpenaiAccountType } from '../../../support/user/team/type';
export type ExternalProviderType = {
openaiAccount?: OpenaiAccountType;
externalWorkflowVariables?: Record<string, string>;
};
/* workflow props */
export type ChatDispatchProps = {
res?: NextApiResponse;
requestOrigin?: string;
mode: 'test' | 'chat' | 'debug';
user: UserModelSchema;
timezone: string;
externalProvider: ExternalProviderType;
runningAppInfo: {
id: string; // May be the id of the system plug-in (cannot be used directly to look up the table)
@@ -100,7 +107,9 @@ export type DispatchNodeResponseType = {
mergeSignId?: string;
// bill
tokens?: number;
tokens?: number; // deprecated
inputTokens?: number;
outputTokens?: number;
model?: string;
contextTotalLen?: number;
totalPoints?: number;
@@ -150,6 +159,8 @@ export type DispatchNodeResponseType = {
// tool
toolCallTokens?: number;
toolCallInputTokens?: number;
toolCallOutputTokens?: number;
toolDetail?: ChatHistoryItemResType[];
toolStop?: boolean;
@@ -201,13 +212,14 @@ export type DispatchNodeResultType<T = {}> = {
export type AIChatNodeProps = {
[NodeInputKeyEnum.aiModel]: string;
[NodeInputKeyEnum.aiSystemPrompt]?: string;
[NodeInputKeyEnum.aiChatTemperature]: number;
[NodeInputKeyEnum.aiChatMaxToken]: number;
[NodeInputKeyEnum.aiChatTemperature]?: number;
[NodeInputKeyEnum.aiChatMaxToken]?: number;
[NodeInputKeyEnum.aiChatIsResponseText]: boolean;
[NodeInputKeyEnum.aiChatVision]?: boolean;
[NodeInputKeyEnum.aiChatQuoteRole]?: AiChatQuoteRoleType;
[NodeInputKeyEnum.aiChatQuoteTemplate]?: string;
[NodeInputKeyEnum.aiChatQuotePrompt]?: string;
[NodeInputKeyEnum.aiChatVision]?: boolean;
[NodeInputKeyEnum.stringQuoteText]?: string;
[NodeInputKeyEnum.fileUrlList]?: string[];

View File

@@ -9,6 +9,7 @@ import { isValidReferenceValueFormat } from '../utils';
import { FlowNodeOutputItemType, ReferenceValueType } from '../type/io';
import { ChatItemType, NodeOutputItemType } from '../../../core/chat/type';
import { ChatItemValueTypeEnum, ChatRoleEnum } from '../../../core/chat/constants';
import { replaceVariable } from '../../../common/string/tools';
export const getMaxHistoryLimitFromNodes = (nodes: StoreNodeItemType[]): number => {
let limit = 10;
@@ -317,6 +318,8 @@ export function replaceEditorVariable({
}) {
if (typeof text !== 'string') return text;
text = replaceVariable(text, variables);
const variablePattern = /\{\{\$([^.]+)\.([^$]+)\$\}\}/g;
const matches = [...text.matchAll(variablePattern)];
if (matches.length === 0) return text;

View File

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

View File

@@ -69,6 +69,7 @@ export type FlowNodeTemplateType = FlowNodeCommonType & {
diagram?: string; // diagram url
courseUrl?: string; // course url
userGuide?: string; // user guide
};
export type NodeTemplateListItemType = {
@@ -87,6 +88,7 @@ export type NodeTemplateListItemType = {
currentCost?: number; // 当前积分消耗
hasTokenFee?: boolean; // 是否配置积分
instructions?: string; // 使用说明
courseUrl?: string; // 教程链接
};
export type NodeTemplateListType = {

View File

@@ -83,11 +83,6 @@ export type OutLinkSchema<T extends OutlinkAppType = undefined> = {
app: T;
};
// to handle MongoDB querying
export type OutLinkWithAppType = Omit<OutLinkSchema, 'appId'> & {
appId: AppSchema;
};
// Edit the Outlink
export type OutLinkEditType<T = undefined> = {
_id?: string;

View File

@@ -1,5 +1,6 @@
import { UserModelSchema } from '../user/type';
import { RequireOnlyOne } from '../../common/type/utils';
import { TeamMemberWithUserSchema } from '../user/team/type';
import { TeamMemberSchema } from '../user/team/type';
import { AuthUserTypeEnum, PermissionKeyEnum, PerResourceTypeEnum } from './constant';
import { MemberGroupSchemaType } from './memberGroup/type';
@@ -31,11 +32,7 @@ export type ResourcePermissionType = {
}>;
export type ResourcePerWithTmbWithUser = Omit<ResourcePermissionType, 'tmbId'> & {
tmbId: TeamMemberWithUserSchema;
};
export type ResourcePerWithGroup = Omit<ResourcePermissionType, 'groupId'> & {
groupId: MemberGroupSchemaType;
tmbId: TeamMemberSchema & { user: UserModelSchema };
};
export type PermissionSchemaType = {

View File

@@ -1,6 +1,6 @@
import { PermissionValueType } from '../../permission/type';
import { TeamMemberRoleEnum } from './constant';
import { LafAccountType, TeamMemberSchema } from './type';
import { LafAccountType, TeamMemberSchema, ThirdPartyAccountType } from './type';
export type AuthTeamRoleProps = {
teamId: string;
@@ -11,14 +11,13 @@ export type CreateTeamProps = {
name: string;
avatar?: string;
defaultTeam?: boolean;
lafAccount?: LafAccountType;
memberName?: string;
};
export type UpdateTeamProps = {
export type UpdateTeamProps = Omit<ThirdPartyAccountType, 'externalWorkflowVariable'> & {
name?: string;
avatar?: string;
teamDomain?: string;
lafAccount?: null | LafAccountType;
externalWorkflowVariable?: { key: string; value: string };
};
/* ------------- member ----------- */

View File

@@ -4,6 +4,12 @@ import { LafAccountType } from './type';
import { PermissionValueType, ResourcePermissionType } from '../../permission/type';
import { TeamPermission } from '../../permission/user/controller';
export type ThirdPartyAccountType = {
lafAccount?: LafAccountType;
openaiAccount?: OpenaiAccountType;
externalWorkflowVariables?: Record<string, string>;
};
export type TeamSchema = {
_id: string;
name: string;
@@ -16,9 +22,8 @@ export type TeamSchema = {
lastExportDatasetTime: Date;
lastWebsiteSyncTime: Date;
};
lafAccount: LafAccountType;
notificationAccount?: string;
};
} & ThirdPartyAccountType;
export type tagsType = {
label: string;
@@ -42,16 +47,9 @@ export type TeamMemberSchema = {
defaultTeam: boolean;
};
export type TeamMemberWithUserSchema = Omit<TeamMemberSchema, 'userId'> & {
userId: UserModelSchema;
};
export type TeamMemberWithTeamSchema = Omit<TeamMemberSchema, 'teamId'> & {
teamId: TeamSchema;
};
export type TeamMemberWithTeamAndUserSchema = Omit<TeamMemberWithTeamSchema, 'userId'> & {
userId: UserModelSchema;
export type TeamMemberWithTeamAndUserSchema = TeamMemberSchema & {
team: TeamSchema;
user: UserModelSchema;
};
export type TeamTmbItemType = {
@@ -66,10 +64,9 @@ export type TeamTmbItemType = {
defaultTeam: boolean;
role: `${TeamMemberRoleEnum}`;
status: `${TeamMemberStatusEnum}`;
lafAccount?: LafAccountType;
notificationAccount?: string;
permission: TeamPermission;
};
} & ThirdPartyAccountType;
export type TeamMemberItemType = {
userId: string;
@@ -88,11 +85,16 @@ export type TeamTagItemType = {
};
export type LafAccountType = {
token: string;
appid: string;
token: string;
pat: string;
};
export type OpenaiAccountType = {
key: string;
baseUrl: string;
};
export type TeamInvoiceHeaderType = {
teamName: string;
unifiedCreditCode: string;

View File

@@ -14,10 +14,6 @@ export type UserModelSchema = {
timezone: string;
status: `${UserStatusEnum}`;
lastLoginTmbId?: string;
openaiAccount?: {
key: string;
baseUrl: string;
};
fastgpt_sem?: {
keyword: string;
};
@@ -29,7 +25,6 @@ export type UserType = {
avatar: string;
timezone: string;
promotionRate: UserModelSchema['promotionRate'];
openaiAccount: UserModelSchema['openaiAccount'];
team: TeamTmbItemType;
standardInfo?: standardInfoType;
notificationAccount?: string;

View File

@@ -23,7 +23,8 @@ export type BillSchemaType = {
};
export type ChatNodeUsageType = {
tokens?: number;
inputTokens?: number;
outputTokens?: number;
totalPoints: number;
moduleName: string;
model?: string;

View File

@@ -2,9 +2,13 @@ import { CreateUsageProps } from './api';
import { UsageSourceEnum } from './constants';
export type UsageListItemCountType = {
tokens?: number;
inputTokens?: number;
outputTokens?: number;
charsLength?: number;
duration?: number;
// deprecated
tokens?: number;
};
export type UsageListItemType = UsageListItemCountType & {
moduleName: string;

View File

@@ -2,7 +2,7 @@
"author": "",
"version": "488",
"name": "飞书 webhook",
"avatar": "/appMarketTemplates/plugin-feishu/avatar.svg",
"avatar": "core/app/templates/plugin-feishu",
"intro": "向飞书机器人发起 webhook 请求。",
"courseUrl": "https://open.feishu.cn/document/client-docs/bot-v3/add-custom-bot#f62e72d5",
"showStatus": false,

View File

@@ -55,8 +55,7 @@
"maxFiles": 5,
"canSelectFile": true,
"canSelectImg": true,
"required": true,
"toolDescription": "部署的searXNG服务的链接"
"required": true
}
],
"outputs": [

View File

@@ -4,7 +4,12 @@ import { FastGPTConfigFileType } from '@fastgpt/global/common/system/types';
import { FastGPTProUrl } from '../constants';
export const getFastGPTConfigFromDB = async () => {
if (!FastGPTProUrl) return {} as FastGPTConfigFileType;
if (!FastGPTProUrl) {
return {
config: {} as FastGPTConfigFileType,
configId: undefined
};
}
const res = await MongoSystemConfigs.findOne({
type: SystemConfigsTypeEnum.fastgpt
@@ -14,5 +19,8 @@ export const getFastGPTConfigFromDB = async () => {
const config = res?.value || {};
return config as FastGPTConfigFileType;
return {
configId: res ? String(res._id) : undefined,
config: config as FastGPTConfigFileType
};
};

View File

@@ -15,6 +15,9 @@ export const initFastGPTConfig = (config?: FastGPTConfigFileType) => {
global.subPlans = config.subPlans;
global.llmModels = config.llmModels;
global.llmModelPriceType = global.llmModels.some((item) => typeof item.inputPrice === 'number')
? 'IO'
: 'Tokens';
global.vectorModels = config.vectorModels;
global.audioSpeechModels = config.audioSpeechModels;
global.whisperModel = config.whisperModel;

View File

@@ -1,4 +1,3 @@
import type { UserModelSchema } from '@fastgpt/global/support/user/type';
import OpenAI from '@fastgpt/global/core/ai';
import {
ChatCompletionCreateParamsNonStreaming,
@@ -7,13 +6,11 @@ import {
import { getErrText } from '@fastgpt/global/common/error/utils';
import { addLog } from '../../common/system/log';
import { i18nT } from '../../../web/i18n/utils';
import { OpenaiAccountType } from '@fastgpt/global/support/user/team/type';
export const openaiBaseUrl = process.env.OPENAI_BASE_URL || 'https://api.openai.com/v1';
export const getAIApi = (props?: {
userKey?: UserModelSchema['openaiAccount'];
timeout?: number;
}) => {
export const getAIApi = (props?: { userKey?: OpenaiAccountType; timeout?: number }) => {
const { userKey, timeout } = props || {};
const baseUrl =
@@ -29,7 +26,7 @@ export const getAIApi = (props?: {
});
};
export const getAxiosConfig = (props?: { userKey?: UserModelSchema['openaiAccount'] }) => {
export const getAxiosConfig = (props?: { userKey?: OpenaiAccountType }) => {
const { userKey } = props || {};
const baseUrl =
@@ -57,7 +54,7 @@ export const createChatCompletion = async <T extends CompletionsBodyType>({
options
}: {
body: T;
userKey?: UserModelSchema['openaiAccount'];
userKey?: OpenaiAccountType;
timeout?: number;
options?: OpenAI.RequestOptions;
}): Promise<{

View File

@@ -1,6 +1,6 @@
import type { ChatCompletionMessageParam } from '@fastgpt/global/core/ai/type.d';
import { createChatCompletion } from '../config';
import { countGptMessagesTokens } from '../../../common/string/tiktoken/index';
import { countGptMessagesTokens, countPromptTokens } from '../../../common/string/tiktoken/index';
import { loadRequestMessages } from '../../chat/utils';
import { llmCompletionsBodyFormat } from '../utils';
import {
@@ -20,7 +20,8 @@ export async function createQuestionGuide({
customPrompt?: string;
}): Promise<{
result: string[];
tokens: number;
inputTokens: number;
outputTokens: number;
}> {
const concatMessages: ChatCompletionMessageParam[] = [
...messages,
@@ -29,6 +30,10 @@ export async function createQuestionGuide({
content: `${customPrompt || PROMPT_QUESTION_GUIDE}\n${PROMPT_QUESTION_GUIDE_FOOTER}`
}
];
const requestMessages = await loadRequestMessages({
messages: concatMessages,
useVision: false
});
const { response: data } = await createChatCompletion({
body: llmCompletionsBodyFormat(
@@ -36,10 +41,7 @@ export async function createQuestionGuide({
model,
temperature: 0.1,
max_tokens: 200,
messages: await loadRequestMessages({
messages: concatMessages,
useVision: false
}),
messages: requestMessages,
stream: false
},
model
@@ -51,13 +53,15 @@ export async function createQuestionGuide({
const start = answer.indexOf('[');
const end = answer.lastIndexOf(']');
const tokens = await countGptMessagesTokens(concatMessages);
const inputTokens = await countGptMessagesTokens(requestMessages);
const outputTokens = await countPromptTokens(answer);
if (start === -1 || end === -1) {
addLog.warn('Create question guide error', { answer });
return {
result: [],
tokens: 0
inputTokens: 0,
outputTokens: 0
};
}
@@ -69,14 +73,16 @@ export async function createQuestionGuide({
try {
return {
result: json5.parse(jsonStr),
tokens
inputTokens,
outputTokens
};
} catch (error) {
console.log(error);
return {
result: [],
tokens: 0
inputTokens: 0,
outputTokens: 0
};
}
}

View File

@@ -1,7 +1,7 @@
import { replaceVariable } from '@fastgpt/global/common/string/tools';
import { createChatCompletion } from '../config';
import { ChatItemType } from '@fastgpt/global/core/chat/type';
import { countGptMessagesTokens } from '../../../common/string/tiktoken/index';
import { countGptMessagesTokens, countPromptTokens } from '../../../common/string/tiktoken/index';
import { chatValue2RuntimePrompt } from '@fastgpt/global/core/chat/adapt';
import { getLLMModel } from '../model';
import { llmCompletionsBodyFormat } from '../utils';
@@ -121,7 +121,8 @@ export const queryExtension = async ({
rawQuery: string;
extensionQueries: string[];
model: string;
tokens: number;
inputTokens: number;
outputTokens: number;
}> => {
const systemFewShot = chatBg
? `Q: 对话背景。
@@ -166,7 +167,8 @@ A: ${chatBg}
rawQuery: query,
extensionQueries: [],
model,
tokens: 0
inputTokens: 0,
outputTokens: 0
};
}
@@ -181,7 +183,8 @@ A: ${chatBg}
rawQuery: query,
extensionQueries: Array.isArray(queries) ? queries : [],
model,
tokens: await countGptMessagesTokens(messages)
inputTokens: await countGptMessagesTokens(messages),
outputTokens: await countPromptTokens(answer)
};
} catch (error) {
addLog.error(`Query extension error`, error);
@@ -189,7 +192,8 @@ A: ${chatBg}
rawQuery: query,
extensionQueries: [],
model,
tokens: 0
inputTokens: 0,
outputTokens: 0
};
}
};

View File

@@ -4,6 +4,7 @@ export const getLLMModel = (model?: string) => {
global.llmModels[0]
);
};
export const getDatasetModel = (model?: string) => {
return (
global.llmModels

View File

@@ -12,10 +12,12 @@ export const computedMaxToken = async ({
model,
filterMessages = []
}: {
maxToken: number;
maxToken?: number;
model: LLMModelItemType;
filterMessages: ChatCompletionMessageParam[];
}) => {
if (maxToken === undefined) return;
maxToken = Math.min(maxToken, model.maxResponse);
const tokensLimit = model.maxContext;
@@ -37,8 +39,6 @@ export const computedTemperature = ({
model: LLMModelItemType;
temperature: number;
}) => {
if (temperature < 1) return temperature;
temperature = +(model.maxTemperature * (temperature / 10)).toFixed(2);
temperature = Math.max(temperature, 0.01);
@@ -63,12 +63,13 @@ export const llmCompletionsBodyFormat = <T extends CompletionsBodyType>(
const requestBody: T = {
...body,
temperature: body.temperature
? computedTemperature({
model: modelData,
temperature: body.temperature
})
: undefined,
temperature:
typeof body.temperature === 'number'
? computedTemperature({
model: modelData,
temperature: body.temperature
})
: undefined,
...modelData?.defaultConfig
};

View File

@@ -131,6 +131,7 @@ export async function getChildAppPreviewNode({
name: app.name,
intro: app.intro,
courseUrl: app.courseUrl,
userGuide: app.userGuide,
showStatus: app.showStatus,
isTool: true,
version: app.version,

View File

@@ -0,0 +1,51 @@
import { AppTemplateSchemaType } from '@fastgpt/global/core/app/type';
import { connectionMongo, getMongoModel } from '../../../common/mongo/index';
const { Schema } = connectionMongo;
export const collectionName = 'app_templates';
const AppTemplateSchema = new Schema({
templateId: {
type: String,
required: true
},
name: {
type: String
},
intro: {
type: String
},
avatar: {
type: String
},
tags: {
type: [String],
default: undefined
},
type: {
type: String
},
isActive: {
type: Boolean
},
userGuide: {
type: Object
},
isQuickTemplate: {
type: Boolean
},
order: {
type: Number,
default: -1
},
workflow: {
type: Object
}
});
AppTemplateSchema.index({ templateId: 1 });
export const MongoAppTemplate = getMongoModel<AppTemplateSchemaType>(
collectionName,
AppTemplateSchema
);

View File

@@ -0,0 +1,25 @@
import { TemplateTypeSchemaType } from '@fastgpt/global/core/app/type';
import { connectionMongo, getMongoModel } from '../../../common/mongo/index';
const { Schema } = connectionMongo;
export const collectionName = 'app_template_types';
const TemplateTypeSchema = new Schema({
typeName: {
type: String,
required: true
},
typeId: {
type: String,
required: true
},
typeOrder: {
type: Number,
default: 0
}
});
export const MongoTemplateTypes = getMongoModel<TemplateTypeSchemaType>(
collectionName,
TemplateTypeSchema
);

View File

@@ -2,10 +2,16 @@ import { connectionMongo, getMongoModel, type Model } from '../../../common/mong
const { Schema, model, models } = connectionMongo;
import { AppVersionSchemaType } from '@fastgpt/global/core/app/version';
import { chatConfigType } from '../schema';
import { TeamMemberCollectionName } from '@fastgpt/global/support/user/team/constant';
export const AppVersionCollectionName = 'app_versions';
const AppVersionSchema = new Schema({
tmbId: {
type: String,
ref: TeamMemberCollectionName,
required: true
},
appId: {
type: Schema.Types.ObjectId,
ref: AppVersionCollectionName,
@@ -26,16 +32,8 @@ const AppVersionSchema = new Schema({
chatConfig: {
type: chatConfigType
},
isPublish: {
type: Boolean
},
versionName: {
type: String,
default: ''
},
tmbId: {
type: String
}
isPublish: Boolean,
versionName: String
});
try {

View File

@@ -104,9 +104,6 @@ export const loadRequestMessages = async ({
}) => {
// Load image to base64
const loadImageToBase64 = async (messages: ChatCompletionContentPart[]) => {
if (process.env.MULTIPLE_DATA_TO_BASE64 === 'false') {
return messages;
}
return Promise.all(
messages.map(async (item) => {
if (item.type === 'image_url') {
@@ -125,7 +122,7 @@ export const loadRequestMessages = async ({
try {
// If imgUrl is a local path, load image from local, and set url to base64
if (imgUrl.startsWith('/')) {
if (imgUrl.startsWith('/') || process.env.MULTIPLE_DATA_TO_BASE64 === 'true') {
addLog.debug('Load image from local server', {
baseUrl: serverRequestBaseUrl,
requestUrl: imgUrl

View File

@@ -4,11 +4,7 @@ import {
} from '@fastgpt/global/core/dataset/constants';
import type { CreateDatasetCollectionParams } from '@fastgpt/global/core/dataset/api.d';
import { MongoDatasetCollection } from './schema';
import {
CollectionWithDatasetType,
DatasetCollectionSchemaType,
DatasetSchemaType
} from '@fastgpt/global/core/dataset/type';
import { DatasetCollectionSchemaType, DatasetSchemaType } from '@fastgpt/global/core/dataset/type';
import { MongoDatasetTraining } from '../training/schema';
import { MongoDatasetData } from '../data/schema';
import { delImgByRelatedId } from '../../../common/file/image/controller';
@@ -230,7 +226,7 @@ export const delCollectionRelatedSource = async ({
collections,
session
}: {
collections: (CollectionWithDatasetType | DatasetCollectionSchemaType)[];
collections: DatasetCollectionSchemaType[];
session: ClientSession;
}) => {
if (collections.length === 0) return;
@@ -264,7 +260,7 @@ export async function delCollection({
session,
delRelatedSource
}: {
collections: (CollectionWithDatasetType | DatasetCollectionSchemaType)[];
collections: DatasetCollectionSchemaType[];
session: ClientSession;
delRelatedSource: boolean;
}) {
@@ -274,16 +270,7 @@ export async function delCollection({
if (!teamId) return Promise.reject('teamId is not exist');
const datasetIds = Array.from(
new Set(
collections.map((item) => {
if (typeof item.datasetId === 'string') {
return String(item.datasetId);
}
return String(item.datasetId._id);
})
)
);
const datasetIds = Array.from(new Set(collections.map((item) => String(item.datasetId))));
const collectionIds = collections.map((item) => String(item._id));
// delete training data
@@ -324,7 +311,7 @@ export async function delOnlyCollection({
collections,
session
}: {
collections: (CollectionWithDatasetType | DatasetCollectionSchemaType)[];
collections: DatasetCollectionSchemaType[];
session: ClientSession;
}) {
if (collections.length === 0) return;
@@ -333,16 +320,7 @@ export async function delOnlyCollection({
if (!teamId) return Promise.reject('teamId is not exist');
const datasetIds = Array.from(
new Set(
collections.map((item) => {
if (typeof item.datasetId === 'string') {
return String(item.datasetId);
}
return String(item.datasetId._id);
})
)
);
const datasetIds = Array.from(new Set(collections.map((item) => String(item.datasetId))));
const collectionIds = collections.map((item) => String(item._id));
// delete training data

View File

@@ -100,6 +100,13 @@ const DatasetCollectionSchema = new Schema({
}
});
DatasetCollectionSchema.virtual('dataset', {
ref: DatasetCollectionName,
localField: 'datasetId',
foreignField: '_id',
justOne: true
});
try {
// auth file
DatasetCollectionSchema.index({ teamId: 1, fileId: 1 });

View File

@@ -130,7 +130,7 @@ export const collectionTagsToTagLabel = async ({
};
export const syncCollection = async (collection: CollectionWithDatasetType) => {
const dataset = collection.datasetId;
const dataset = collection.dataset;
if (
collection.type !== DatasetCollectionTypeEnum.link &&
@@ -183,7 +183,7 @@ export const syncCollection = async (collection: CollectionWithDatasetType) => {
teamId: collection.teamId,
tmbId: collection.tmbId,
name: collection.name,
datasetId: collection.datasetId._id,
datasetId: collection.datasetId,
parentId: collection.parentId,
type: collection.type,

View File

@@ -1,4 +1,4 @@
import { CollectionWithDatasetType, DatasetSchemaType } from '@fastgpt/global/core/dataset/type';
import { DatasetSchemaType } from '@fastgpt/global/core/dataset/type';
import { MongoDatasetCollection } from './collection/schema';
import { MongoDataset } from './schema';
import { delCollectionRelatedSource } from './collection/controller';
@@ -49,9 +49,9 @@ export async function findDatasetAndAllChildren({
}
export async function getCollectionWithDataset(collectionId: string) {
const data = (await MongoDatasetCollection.findById(collectionId)
.populate('datasetId')
.lean()) as CollectionWithDatasetType;
const data = await MongoDatasetCollection.findById(collectionId)
.populate<{ dataset: DatasetSchemaType }>('dataset')
.lean();
if (!data) {
return Promise.reject('Collection is not exist');
}
@@ -71,19 +71,10 @@ export async function delDatasetRelevantData({
const teamId = datasets[0].teamId;
if (!teamId) {
return Promise.reject('teamId is required');
return Promise.reject('TeamId is required');
}
const datasetIds = datasets.map((item) => String(item._id));
// Get _id, teamId, fileId, metadata.relatedImgId for all collections
const collections = await MongoDatasetCollection.find(
{
teamId,
datasetId: { $in: datasetIds }
},
'_id teamId datasetId fileId metadata'
).lean();
const datasetIds = datasets.map((item) => item._id);
// delete training data
await MongoDatasetTraining.deleteMany({
@@ -91,20 +82,27 @@ export async function delDatasetRelevantData({
datasetId: { $in: datasetIds }
});
// image and file
await delCollectionRelatedSource({ collections, session });
// delete dataset.datas
await MongoDatasetData.deleteMany({ teamId, datasetId: { $in: datasetIds } }, { session });
// delete collections
await MongoDatasetCollection.deleteMany(
// Get _id, teamId, fileId, metadata.relatedImgId for all collections
const collections = await MongoDatasetCollection.find(
{
teamId,
datasetId: { $in: datasetIds }
},
'_id teamId datasetId fileId metadata',
{ session }
);
).lean();
// image and file
await delCollectionRelatedSource({ collections, session });
// delete collections
await MongoDatasetCollection.deleteMany({
teamId,
datasetId: { $in: datasetIds }
}).session(session);
// delete dataset.datas(Not need session)
await MongoDatasetData.deleteMany({ teamId, datasetId: { $in: datasetIds } });
// no session delete: delete files, vector data
await deleteDatasetDataVector({ teamId, datasetIds });

View File

@@ -77,21 +77,32 @@ const DatasetDataSchema = new Schema({
rebuilding: Boolean
});
// list collection and count data; list data; delete collection(relate data)
DatasetDataSchema.index({
teamId: 1,
datasetId: 1,
collectionId: 1,
chunkIndex: 1,
updateTime: -1
DatasetDataSchema.virtual('collection', {
ref: DatasetColCollectionName,
localField: 'collectionId',
foreignField: '_id',
justOne: true
});
// full text index
DatasetDataSchema.index({ teamId: 1, datasetId: 1, fullTextToken: 'text' });
// Recall vectors after data matching
DatasetDataSchema.index({ teamId: 1, datasetId: 1, collectionId: 1, 'indexes.dataId': 1 });
DatasetDataSchema.index({ updateTime: 1 });
// rebuild data
DatasetDataSchema.index({ rebuilding: 1, teamId: 1, datasetId: 1 });
try {
// list collection and count data; list data; delete collection(relate data)
DatasetDataSchema.index({
teamId: 1,
datasetId: 1,
collectionId: 1,
chunkIndex: 1,
updateTime: -1
});
// full text index
DatasetDataSchema.index({ teamId: 1, datasetId: 1, fullTextToken: 'text' });
// Recall vectors after data matching
DatasetDataSchema.index({ teamId: 1, datasetId: 1, collectionId: 1, 'indexes.dataId': 1 });
DatasetDataSchema.index({ updateTime: 1 });
// rebuild data
DatasetDataSchema.index({ rebuilding: 1, teamId: 1, datasetId: 1 });
} catch (error) {
console.log(error);
}
export const MongoDatasetData = getMongoModel<DatasetDataSchemaType>(
DatasetDataCollectionName,

View File

@@ -8,8 +8,8 @@ import { getVectorsByText } from '../../ai/embedding';
import { getVectorModel } from '../../ai/model';
import { MongoDatasetData } from '../data/schema';
import {
DatasetCollectionSchemaType,
DatasetDataSchemaType,
DatasetDataWithCollectionType,
SearchDataResponseItemType
} from '@fastgpt/global/core/dataset/type';
import { MongoDatasetCollection } from '../collection/schema';
@@ -267,7 +267,7 @@ export async function searchDatasetData(props: SearchDatasetDataProps) {
});
// get q and a
const dataList = (await MongoDatasetData.find(
const dataList = await MongoDatasetData.find(
{
teamId,
datasetId: { $in: datasetIds },
@@ -276,8 +276,11 @@ export async function searchDatasetData(props: SearchDatasetDataProps) {
},
'datasetId collectionId updateTime q a chunkIndex indexes'
)
.populate('collectionId', 'name fileId rawLink externalFileId externalFileUrl')
.lean()) as DatasetDataWithCollectionType[];
.populate<{ collection: DatasetCollectionSchemaType }>(
'collection',
'name fileId rawLink externalFileId externalFileUrl'
)
.lean();
// add score to data(It's already sorted. The first one is the one with the most points)
const concatResults = dataList.map((data) => {
@@ -307,8 +310,8 @@ export async function searchDatasetData(props: SearchDatasetDataProps) {
a: data.a,
chunkIndex: data.chunkIndex,
datasetId: String(data.datasetId),
collectionId: String(data.collectionId?._id),
...getCollectionSourceData(data.collectionId),
collectionId: String(data.collectionId),
...getCollectionSourceData(data.collection),
score: [{ type: SearchScoreTypeEnum.embedding, value: data.score, index }]
};

View File

@@ -34,7 +34,7 @@ export const pushDataListToTrainingQueueByCollectionId = async ({
session?: ClientSession;
} & PushDatasetDataProps) => {
const {
datasetId: { _id: datasetId, agentModel, vectorModel }
dataset: { _id: datasetId, agentModel, vectorModel }
} = await getCollectionWithDataset(collectionId);
return pushDataListToTrainingQueue({
...props,

View File

@@ -2,6 +2,5 @@ import { PluginTemplateType } from '@fastgpt/global/core/plugin/type.d';
import { SystemPluginTemplateItemType } from '@fastgpt/global/core/workflow/type';
declare global {
var communityPluginsV1: PluginTemplateType[];
var communityPlugins: SystemPluginTemplateItemType[];
}

View File

@@ -1,5 +1,8 @@
import { chats2GPTMessages } from '@fastgpt/global/core/chat/adapt';
import { countMessagesTokens } from '../../../../common/string/tiktoken/index';
import {
countGptMessagesTokens,
countPromptTokens
} from '../../../../common/string/tiktoken/index';
import type { ChatItemType } from '@fastgpt/global/core/chat/type.d';
import { ChatItemValueTypeEnum, ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
import { createChatCompletion } from '../../../ai/config';
@@ -35,7 +38,7 @@ type ActionProps = Props & { cqModel: LLMModelItemType };
/* request openai chat */
export const dispatchClassifyQuestion = async (props: Props): Promise<CQResponse> => {
const {
user,
externalProvider,
node: { nodeId, name },
histories,
params: { model, history = 6, agents, userChatInput }
@@ -49,7 +52,7 @@ export const dispatchClassifyQuestion = async (props: Props): Promise<CQResponse
const chatHistories = getHistories(history, histories);
const { arg, tokens } = await completions({
const { arg, inputTokens, outputTokens } = await completions({
...props,
histories: chatHistories,
cqModel
@@ -59,7 +62,8 @@ export const dispatchClassifyQuestion = async (props: Props): Promise<CQResponse
const { totalPoints, modelName } = formatModelChars2Points({
model: cqModel.model,
tokens,
inputTokens: inputTokens,
outputTokens: outputTokens,
modelType: ModelTypeEnum.llm
});
@@ -69,10 +73,11 @@ export const dispatchClassifyQuestion = async (props: Props): Promise<CQResponse
.filter((item) => item.key !== result.key)
.map((item) => getHandleId(nodeId, 'source', item.key)),
[DispatchNodeResponseKeyEnum.nodeResponse]: {
totalPoints: user.openaiAccount?.key ? 0 : totalPoints,
totalPoints: externalProvider.openaiAccount?.key ? 0 : totalPoints,
model: modelName,
query: userChatInput,
tokens,
inputTokens: inputTokens,
outputTokens: outputTokens,
cqList: agents,
cqResult: result.value,
contextTotalLen: chatHistories.length + 2
@@ -80,9 +85,10 @@ export const dispatchClassifyQuestion = async (props: Props): Promise<CQResponse
[DispatchNodeResponseKeyEnum.nodeDispatchUsages]: [
{
moduleName: name,
totalPoints: user.openaiAccount?.key ? 0 : totalPoints,
totalPoints: externalProvider.openaiAccount?.key ? 0 : totalPoints,
model: modelName,
tokens
inputTokens: inputTokens,
outputTokens: outputTokens
}
]
};
@@ -90,7 +96,7 @@ export const dispatchClassifyQuestion = async (props: Props): Promise<CQResponse
const completions = async ({
cqModel,
user,
externalProvider,
histories,
params: { agents, systemPrompt = '', userChatInput }
}: ActionProps) => {
@@ -131,7 +137,7 @@ const completions = async ({
},
cqModel
),
userKey: user.openaiAccount
userKey: externalProvider.openaiAccount
});
const answer = data.choices?.[0].message?.content || '';
@@ -148,7 +154,8 @@ const completions = async ({
}
return {
tokens: await countMessagesTokens(messages),
inputTokens: await countGptMessagesTokens(requestMessages),
outputTokens: await countPromptTokens(answer),
arg: { type: id }
};
};

View File

@@ -3,7 +3,8 @@ import { filterGPTMessageByMaxTokens, loadRequestMessages } from '../../../chat/
import type { ChatItemType } from '@fastgpt/global/core/chat/type.d';
import {
countMessagesTokens,
countGptMessagesTokens
countGptMessagesTokens,
countPromptTokens
} from '../../../../common/string/tiktoken/index';
import { ChatItemValueTypeEnum, ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
import { createChatCompletion } from '../../../ai/config';
@@ -46,7 +47,7 @@ const agentFunName = 'request_function';
export async function dispatchContentExtract(props: Props): Promise<Response> {
const {
user,
externalProvider,
node: { name },
histories,
params: { content, history = 6, model, description, extractKeys }
@@ -59,7 +60,7 @@ export async function dispatchContentExtract(props: Props): Promise<Response> {
const extractModel = getLLMModel(model);
const chatHistories = getHistories(history, histories);
const { arg, tokens } = await (async () => {
const { arg, inputTokens, outputTokens } = await (async () => {
if (extractModel.toolChoice) {
return toolChoice({
...props,
@@ -114,7 +115,8 @@ export async function dispatchContentExtract(props: Props): Promise<Response> {
const { totalPoints, modelName } = formatModelChars2Points({
model: extractModel.model,
tokens,
inputTokens: inputTokens,
outputTokens: outputTokens,
modelType: ModelTypeEnum.llm
});
@@ -123,10 +125,11 @@ export async function dispatchContentExtract(props: Props): Promise<Response> {
[NodeOutputKeyEnum.contextExtractFields]: JSON.stringify(arg),
...arg,
[DispatchNodeResponseKeyEnum.nodeResponse]: {
totalPoints: user.openaiAccount?.key ? 0 : totalPoints,
totalPoints: externalProvider.openaiAccount?.key ? 0 : totalPoints,
model: modelName,
query: content,
tokens,
inputTokens,
outputTokens,
extractDescription: description,
extractResult: arg,
contextTotalLen: chatHistories.length + 2
@@ -134,9 +137,10 @@ export async function dispatchContentExtract(props: Props): Promise<Response> {
[DispatchNodeResponseKeyEnum.nodeDispatchUsages]: [
{
moduleName: name,
totalPoints: user.openaiAccount?.key ? 0 : totalPoints,
totalPoints: externalProvider.openaiAccount?.key ? 0 : totalPoints,
model: modelName,
tokens
inputTokens,
outputTokens
}
]
};
@@ -211,7 +215,7 @@ ${description ? `- ${description}` : ''}
};
const toolChoice = async (props: ActionProps) => {
const { user, extractModel } = props;
const { externalProvider, extractModel } = props;
const { filterMessages, agentFunction } = await getFunctionCallSchema(props);
@@ -233,7 +237,7 @@ const toolChoice = async (props: ActionProps) => {
},
extractModel
),
userKey: user.openaiAccount
userKey: externalProvider.openaiAccount
});
const arg: Record<string, any> = (() => {
@@ -249,21 +253,24 @@ const toolChoice = async (props: ActionProps) => {
}
})();
const completeMessages: ChatCompletionMessageParam[] = [
...filterMessages,
const AIMessages: ChatCompletionMessageParam[] = [
{
role: ChatCompletionRequestMessageRoleEnum.Assistant,
tool_calls: response.choices?.[0]?.message?.tool_calls
}
];
const inputTokens = await countGptMessagesTokens(filterMessages, tools);
const outputTokens = await countGptMessagesTokens(AIMessages);
return {
tokens: await countGptMessagesTokens(completeMessages, tools),
inputTokens,
outputTokens,
arg
};
};
const functionCall = async (props: ActionProps) => {
const { user, extractModel } = props;
const { externalProvider, extractModel } = props;
const { agentFunction, filterMessages } = await getFunctionCallSchema(props);
const functions: ChatCompletionCreateParams.Function[] = [agentFunction];
@@ -281,22 +288,26 @@ const functionCall = async (props: ActionProps) => {
},
extractModel
),
userKey: user.openaiAccount
userKey: externalProvider.openaiAccount
});
try {
const arg = JSON.parse(response?.choices?.[0]?.message?.function_call?.arguments || '');
const completeMessages: ChatCompletionMessageParam[] = [
...filterMessages,
const AIMessages: ChatCompletionMessageParam[] = [
{
role: ChatCompletionRequestMessageRoleEnum.Assistant,
function_call: response.choices?.[0]?.message?.function_call
}
];
const inputTokens = await countGptMessagesTokens(filterMessages, undefined, functions);
const outputTokens = await countGptMessagesTokens(AIMessages);
return {
arg,
tokens: await countGptMessagesTokens(completeMessages, undefined, functions)
inputTokens,
outputTokens
};
} catch (error) {
console.log(response.choices?.[0]?.message);
@@ -305,14 +316,15 @@ const functionCall = async (props: ActionProps) => {
return {
arg: {},
tokens: 0
inputTokens: 0,
outputTokens: 0
};
}
};
const completions = async ({
extractModel,
user,
externalProvider,
histories,
params: { content, extractKeys, description = 'No special requirements' }
}: ActionProps) => {
@@ -360,7 +372,7 @@ Human: ${content}`
},
extractModel
),
userKey: user.openaiAccount
userKey: externalProvider.openaiAccount
});
const answer = data.choices?.[0].message?.content || '';
@@ -370,7 +382,8 @@ Human: ${content}`
if (!jsonStr) {
return {
rawResponse: answer,
tokens: await countMessagesTokens(messages),
inputTokens: await countMessagesTokens(messages),
outputTokens: await countPromptTokens(answer),
arg: {}
};
}
@@ -378,7 +391,8 @@ Human: ${content}`
try {
return {
rawResponse: answer,
tokens: await countMessagesTokens(messages),
inputTokens: await countMessagesTokens(messages),
outputTokens: await countPromptTokens(answer),
arg: json5.parse(jsonStr) as Record<string, any>
};
} catch (error) {
@@ -386,7 +400,8 @@ Human: ${content}`
console.log(error);
return {
rawResponse: answer,
tokens: await countMessagesTokens(messages),
inputTokens: await countMessagesTokens(messages),
outputTokens: await countPromptTokens(answer),
arg: {}
};
}

View File

@@ -43,10 +43,10 @@ export const runToolWithFunctionCall = async (
requestOrigin,
runtimeNodes,
runtimeEdges,
user,
externalProvider,
stream,
workflowStreamResponse,
params: { temperature = 0, maxToken = 4000, aiChatVision }
params: { temperature, maxToken, aiChatVision }
} = workflowProps;
// Interactive
@@ -109,7 +109,8 @@ export const runToolWithFunctionCall = async (
return {
dispatchFlowResponse: [toolRunResponse],
toolNodeTokens: 0,
toolNodeInputTokens: 0,
toolNodeOutputTokens: 0,
completeMessages: requestMessages,
assistantResponses: toolRunResponse.assistantResponses,
runTimes: toolRunResponse.runTimes,
@@ -126,7 +127,8 @@ export const runToolWithFunctionCall = async (
},
{
dispatchFlowResponse: [toolRunResponse],
toolNodeTokens: 0,
toolNodeInputTokens: 0,
toolNodeOutputTokens: 0,
assistantResponses: toolRunResponse.assistantResponses,
runTimes: toolRunResponse.runTimes
}
@@ -221,7 +223,7 @@ export const runToolWithFunctionCall = async (
getEmptyResponseTip
} = await createChatCompletion({
body: requestBody,
userKey: user.openaiAccount,
userKey: externalProvider.openaiAccount,
options: {
headers: {
Accept: 'application/json, text/plain, */*'
@@ -340,7 +342,9 @@ export const runToolWithFunctionCall = async (
assistantToolMsgParams
] as ChatCompletionMessageParam[];
// Only toolCall tokens are counted here, Tool response tokens count towards the next reply
const tokens = await countGptMessagesTokens(concatToolMessages, undefined, functions);
// const tokens = await countGptMessagesTokens(concatToolMessages, undefined, functions);
const inputTokens = await countGptMessagesTokens(requestMessages, undefined, functions);
const outputTokens = await countGptMessagesTokens([assistantToolMsgParams]);
/*
...
user
@@ -375,7 +379,12 @@ export const runToolWithFunctionCall = async (
const runTimes =
(response?.runTimes || 0) +
flatToolsResponseData.reduce((sum, item) => sum + item.runTimes, 0);
const toolNodeTokens = response?.toolNodeTokens ? response.toolNodeTokens + tokens : tokens;
const toolNodeInputTokens = response?.toolNodeInputTokens
? response.toolNodeInputTokens + inputTokens
: inputTokens;
const toolNodeOutputTokens = response?.toolNodeOutputTokens
? response.toolNodeOutputTokens + outputTokens
: outputTokens;
// Check stop signal
const hasStopSignal = flatToolsResponseData.some(
@@ -408,7 +417,8 @@ export const runToolWithFunctionCall = async (
return {
dispatchFlowResponse,
toolNodeTokens,
toolNodeInputTokens,
toolNodeOutputTokens,
completeMessages,
assistantResponses: toolNodeAssistants,
runTimes,
@@ -423,7 +433,8 @@ export const runToolWithFunctionCall = async (
},
{
dispatchFlowResponse,
toolNodeTokens,
toolNodeInputTokens,
toolNodeOutputTokens,
assistantResponses: toolNodeAssistants,
runTimes
}
@@ -435,7 +446,8 @@ export const runToolWithFunctionCall = async (
content: answer
};
const completeMessages = filterMessages.concat(gptAssistantResponse);
const tokens = await countGptMessagesTokens(completeMessages, undefined, functions);
const inputTokens = await countGptMessagesTokens(requestMessages, undefined, functions);
const outputTokens = await countGptMessagesTokens([gptAssistantResponse]);
// console.log(tokens, 'response token');
// concat tool assistant
@@ -443,7 +455,12 @@ export const runToolWithFunctionCall = async (
return {
dispatchFlowResponse: response?.dispatchFlowResponse || [],
toolNodeTokens: response?.toolNodeTokens ? response.toolNodeTokens + tokens : tokens,
toolNodeInputTokens: response?.toolNodeInputTokens
? response.toolNodeInputTokens + inputTokens
: inputTokens,
toolNodeOutputTokens: response?.toolNodeOutputTokens
? response.toolNodeOutputTokens + outputTokens
: outputTokens,
completeMessages,
assistantResponses: [...assistantResponses, ...toolNodeAssistant.value],
runTimes: (response?.runTimes || 0) + 1

View File

@@ -46,7 +46,7 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
requestOrigin,
chatConfig,
runningAppInfo: { teamId },
user,
externalProvider,
params: {
model,
systemPrompt,
@@ -153,7 +153,7 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
})();
// censor model and system key
if (toolModel.censor && !user.openaiAccount?.key) {
if (toolModel.censor && !externalProvider.openaiAccount?.key) {
await postTextCensor({
text: `${systemPrompt}
${userChatInput}
@@ -165,6 +165,8 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
toolWorkflowInteractiveResponse,
dispatchFlowResponse, // tool flow response
toolNodeTokens,
toolNodeInputTokens,
toolNodeOutputTokens,
completeMessages = [], // The actual message sent to AI(just save text)
assistantResponses = [], // FastGPT system store assistant.value response
runTimes
@@ -225,10 +227,11 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
const { totalPoints, modelName } = formatModelChars2Points({
model,
tokens: toolNodeTokens,
inputTokens: toolNodeInputTokens,
outputTokens: toolNodeOutputTokens,
modelType: ModelTypeEnum.llm
});
const toolAIUsage = user.openaiAccount?.key ? 0 : totalPoints;
const toolAIUsage = externalProvider.openaiAccount?.key ? 0 : totalPoints;
// flat child tool response
const childToolResponse = dispatchFlowResponse.map((item) => item.flowResponses).flat();
@@ -255,6 +258,8 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
// 展示的积分消耗
totalPoints: totalPointsUsage,
toolCallTokens: toolNodeTokens,
toolCallInputTokens: toolNodeInputTokens,
toolCallOutputTokens: toolNodeOutputTokens,
childTotalPoints: flatUsages.reduce((sum, item) => sum + item.totalPoints, 0),
model: modelName,
query: userChatInput,
@@ -270,9 +275,10 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
// 工具调用本身的积分消耗
{
moduleName: name,
totalPoints: toolAIUsage,
model: modelName,
tokens: toolNodeTokens
totalPoints: toolAIUsage,
inputTokens: toolNodeInputTokens,
outputTokens: toolNodeOutputTokens
},
// 工具的消耗
...flatUsages

View File

@@ -51,10 +51,10 @@ export const runToolWithPromptCall = async (
requestOrigin,
runtimeNodes,
runtimeEdges,
user,
externalProvider,
stream,
workflowStreamResponse,
params: { temperature = 0, maxToken = 4000, aiChatVision }
params: { temperature, maxToken, aiChatVision }
} = workflowProps;
if (interactiveEntryToolParams) {
@@ -115,7 +115,8 @@ export const runToolWithPromptCall = async (
return {
dispatchFlowResponse: [toolRunResponse],
toolNodeTokens: 0,
toolNodeInputTokens: 0,
toolNodeOutputTokens: 0,
completeMessages: concatMessages,
assistantResponses: toolRunResponse.assistantResponses,
runTimes: toolRunResponse.runTimes,
@@ -131,7 +132,8 @@ export const runToolWithPromptCall = async (
},
{
dispatchFlowResponse: [toolRunResponse],
toolNodeTokens: 0,
toolNodeInputTokens: 0,
toolNodeOutputTokens: 0,
assistantResponses: toolRunResponse.assistantResponses,
runTimes: toolRunResponse.runTimes
}
@@ -230,7 +232,7 @@ export const runToolWithPromptCall = async (
getEmptyResponseTip
} = await createChatCompletion({
body: requestBody,
userKey: user.openaiAccount,
userKey: externalProvider.openaiAccount,
options: {
headers: {
Accept: 'application/json, text/plain, */*'
@@ -286,15 +288,20 @@ export const runToolWithPromptCall = async (
content: replaceAnswer
};
const completeMessages = filterMessages.concat(gptAssistantResponse);
const tokens = await countGptMessagesTokens(completeMessages, undefined);
// console.log(tokens, 'response token');
const inputTokens = await countGptMessagesTokens(requestMessages);
const outputTokens = await countGptMessagesTokens([gptAssistantResponse]);
// concat tool assistant
const toolNodeAssistant = GPTMessages2Chats([gptAssistantResponse])[0] as AIChatItemType;
return {
dispatchFlowResponse: response?.dispatchFlowResponse || [],
toolNodeTokens: response?.toolNodeTokens ? response.toolNodeTokens + tokens : tokens,
toolNodeInputTokens: response?.toolNodeInputTokens
? response.toolNodeInputTokens + inputTokens
: inputTokens,
toolNodeOutputTokens: response?.toolNodeOutputTokens
? response.toolNodeOutputTokens + outputTokens
: outputTokens,
completeMessages,
assistantResponses: [...assistantResponses, ...toolNodeAssistant.value],
runTimes: (response?.runTimes || 0) + 1
@@ -366,17 +373,9 @@ export const runToolWithPromptCall = async (
function_call: toolJson
};
/*
...
user
assistant: tool data
*/
const concatToolMessages = [
...requestMessages,
assistantToolMsgParams
] as ChatCompletionMessageParam[];
// Only toolCall tokens are counted here, Tool response tokens count towards the next reply
const tokens = await countGptMessagesTokens(concatToolMessages, undefined);
const inputTokens = await countGptMessagesTokens(requestMessages);
const outputTokens = await countGptMessagesTokens([assistantToolMsgParams]);
/*
...
@@ -437,7 +436,12 @@ ANSWER: `;
}
const runTimes = (response?.runTimes || 0) + toolsRunResponse.toolResponse.runTimes;
const toolNodeTokens = response?.toolNodeTokens ? response.toolNodeTokens + tokens : tokens;
const toolNodeInputTokens = response?.toolNodeInputTokens
? response.toolNodeInputTokens + inputTokens
: inputTokens;
const toolNodeOutputTokens = response?.toolNodeOutputTokens
? response.toolNodeOutputTokens + outputTokens
: outputTokens;
// Check stop signal
const hasStopSignal = toolsRunResponse.toolResponse.flowResponses.some((item) => !!item.toolStop);
@@ -460,7 +464,8 @@ ANSWER: `;
return {
dispatchFlowResponse,
toolNodeTokens,
toolNodeInputTokens,
toolNodeOutputTokens,
completeMessages: filterMessages,
assistantResponses: toolNodeAssistants,
runTimes,
@@ -475,7 +480,8 @@ ANSWER: `;
},
{
dispatchFlowResponse,
toolNodeTokens,
toolNodeInputTokens,
toolNodeOutputTokens,
assistantResponses: toolNodeAssistants,
runTimes
}

View File

@@ -24,14 +24,13 @@ import { AIChatItemType } from '@fastgpt/global/core/chat/type';
import { formatToolResponse, initToolCallEdges, initToolNodes } from './utils';
import { computedMaxToken, llmCompletionsBodyFormat } from '../../../../ai/utils';
import { getNanoid, sliceStrStartEnd } from '@fastgpt/global/common/string/tools';
import { addLog } from '../../../../../common/system/log';
import { toolValueTypeList } from '@fastgpt/global/core/workflow/constants';
import { WorkflowInteractiveResponseType } from '@fastgpt/global/core/workflow/template/system/interactive/type';
import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants';
import { i18nT } from '../../../../../../web/i18n/utils';
import { getErrText } from '@fastgpt/global/common/error/utils';
type ToolRunResponseType = {
toolRunResponse: DispatchFlowResponse;
toolRunResponse?: DispatchFlowResponse;
toolMsgParams: ChatCompletionToolMessageParam;
}[];
@@ -92,9 +91,9 @@ export const runToolWithToolChoice = async (
runtimeNodes,
runtimeEdges,
stream,
user,
externalProvider,
workflowStreamResponse,
params: { temperature = 0, maxToken = 4000, aiChatVision }
params: { temperature, maxToken, aiChatVision }
} = workflowProps;
if (maxRunToolTimes <= 0 && response) {
@@ -160,7 +159,8 @@ export const runToolWithToolChoice = async (
return {
dispatchFlowResponse: [toolRunResponse],
toolNodeTokens: 0,
toolNodeInputTokens: 0,
toolNodeOutputTokens: 0,
completeMessages: requestMessages,
assistantResponses: toolRunResponse.assistantResponses,
runTimes: toolRunResponse.runTimes,
@@ -178,7 +178,8 @@ export const runToolWithToolChoice = async (
},
{
dispatchFlowResponse: [toolRunResponse],
toolNodeTokens: 0,
toolNodeInputTokens: 0,
toolNodeOutputTokens: 0,
assistantResponses: toolRunResponse.assistantResponses,
runTimes: toolRunResponse.runTimes
}
@@ -278,7 +279,7 @@ export const runToolWithToolChoice = async (
getEmptyResponseTip
} = await createChatCompletion({
body: requestBody,
userKey: user.openaiAccount,
userKey: externalProvider.openaiAccount,
options: {
headers: {
Accept: 'application/json, text/plain, */*'
@@ -344,59 +345,87 @@ export const runToolWithToolChoice = async (
return Promise.reject(getEmptyResponseTip());
}
// Run the selected tool by LLM.
const toolsRunResponse = (
await Promise.all(
toolCalls.map(async (tool) => {
const toolNode = toolNodes.find((item) => item.nodeId === tool.function?.name);
/* Run the selected tool by LLM.
Since only reference parameters are passed, if the same tool is run in parallel, it will get the same run parameters
*/
const toolsRunResponse: ToolRunResponseType = [];
for await (const tool of toolCalls) {
try {
const toolNode = toolNodes.find((item) => item.nodeId === tool.function?.name);
if (!toolNode) return;
if (!toolNode) continue;
const startParams = (() => {
try {
return json5.parse(tool.function.arguments);
} catch (error) {
return {};
const startParams = (() => {
try {
return json5.parse(tool.function.arguments);
} catch (error) {
return {};
}
})();
initToolNodes(runtimeNodes, [toolNode.nodeId], startParams);
const toolRunResponse = await dispatchWorkFlow({
...workflowProps,
isToolCall: true
});
const stringToolResponse = formatToolResponse(toolRunResponse.toolResponses);
const toolMsgParams: ChatCompletionToolMessageParam = {
tool_call_id: tool.id,
role: ChatCompletionRequestMessageRoleEnum.Tool,
name: tool.function.name,
content: stringToolResponse
};
workflowStreamResponse?.({
event: SseResponseEventEnum.toolResponse,
data: {
tool: {
id: tool.id,
toolName: '',
toolAvatar: '',
params: '',
response: sliceStrStartEnd(stringToolResponse, 5000, 5000)
}
})();
}
});
initToolNodes(runtimeNodes, [toolNode.nodeId], startParams);
const toolRunResponse = await dispatchWorkFlow({
...workflowProps,
isToolCall: true
});
toolsRunResponse.push({
toolRunResponse,
toolMsgParams
});
} catch (error) {
const err = getErrText(error);
workflowStreamResponse?.({
event: SseResponseEventEnum.toolResponse,
data: {
tool: {
id: tool.id,
toolName: '',
toolAvatar: '',
params: '',
response: sliceStrStartEnd(err, 5000, 5000)
}
}
});
const stringToolResponse = formatToolResponse(toolRunResponse.toolResponses);
const toolMsgParams: ChatCompletionToolMessageParam = {
toolsRunResponse.push({
toolRunResponse: undefined,
toolMsgParams: {
tool_call_id: tool.id,
role: ChatCompletionRequestMessageRoleEnum.Tool,
name: tool.function.name,
content: stringToolResponse
};
content: sliceStrStartEnd(err, 5000, 5000)
}
});
}
}
workflowStreamResponse?.({
event: SseResponseEventEnum.toolResponse,
data: {
tool: {
id: tool.id,
toolName: '',
toolAvatar: '',
params: '',
response: sliceStrStartEnd(stringToolResponse, 5000, 5000)
}
}
});
return {
toolRunResponse,
toolMsgParams
};
})
)
).filter(Boolean) as ToolRunResponseType;
const flatToolsResponseData = toolsRunResponse.map((item) => item.toolRunResponse).flat();
const flatToolsResponseData = toolsRunResponse
.map((item) => item.toolRunResponse)
.flat()
.filter(Boolean) as DispatchFlowResponse[];
// concat tool responses
const dispatchFlowResponse = response
? response.dispatchFlowResponse.concat(flatToolsResponseData)
@@ -430,24 +459,26 @@ export const runToolWithToolChoice = async (
] as ChatCompletionMessageParam[];
// Only toolCall tokens are counted here, Tool response tokens count towards the next reply
const tokens = await countGptMessagesTokens(concatToolMessages, tools);
const inputTokens = await countGptMessagesTokens(requestMessages, tools);
const outputTokens = await countGptMessagesTokens(assistantToolMsgParams);
/*
...
user
assistant: tool data
tool: tool response
*/
...
user
assistant: tool data
tool: tool response
*/
const completeMessages = [
...concatToolMessages,
...toolsRunResponse.map((item) => item?.toolMsgParams)
];
/*
Get tool node assistant response
history assistant
current tool assistant
tool child assistant
*/
Get tool node assistant response
history assistant
current tool assistant
tool child assistant
*/
const toolNodeAssistant = GPTMessages2Chats([
...assistantToolMsgParams,
...toolsRunResponse.map((item) => item?.toolMsgParams)
@@ -465,7 +496,10 @@ export const runToolWithToolChoice = async (
const runTimes =
(response?.runTimes || 0) +
flatToolsResponseData.reduce((sum, item) => sum + item.runTimes, 0);
const toolNodeTokens = response ? response.toolNodeTokens + tokens : tokens;
const toolNodeInputTokens = response ? response.toolNodeInputTokens + inputTokens : inputTokens;
const toolNodeOutputTokens = response
? response.toolNodeOutputTokens + outputTokens
: outputTokens;
// Check stop signal
const hasStopSignal = flatToolsResponseData.some(
@@ -473,12 +507,12 @@ export const runToolWithToolChoice = async (
);
// Check interactive response(Only 1 interaction is reserved)
const workflowInteractiveResponseItem = toolsRunResponse.find(
(item) => item.toolRunResponse.workflowInteractiveResponse
(item) => item.toolRunResponse?.workflowInteractiveResponse
);
if (hasStopSignal || workflowInteractiveResponseItem) {
// Get interactive tool data
const workflowInteractiveResponse =
workflowInteractiveResponseItem?.toolRunResponse.workflowInteractiveResponse;
workflowInteractiveResponseItem?.toolRunResponse?.workflowInteractiveResponse;
// Flashback traverses completeMessages, intercepting messages that know the first user
const firstUserIndex = completeMessages.findLastIndex((item) => item.role === 'user');
@@ -498,7 +532,8 @@ export const runToolWithToolChoice = async (
return {
dispatchFlowResponse,
toolNodeTokens,
toolNodeInputTokens,
toolNodeOutputTokens,
completeMessages,
assistantResponses: toolNodeAssistants,
runTimes,
@@ -514,7 +549,8 @@ export const runToolWithToolChoice = async (
},
{
dispatchFlowResponse,
toolNodeTokens,
toolNodeInputTokens,
toolNodeOutputTokens,
assistantResponses: toolNodeAssistants,
runTimes
}
@@ -526,14 +562,17 @@ export const runToolWithToolChoice = async (
content: answer
};
const completeMessages = filterMessages.concat(gptAssistantResponse);
const tokens = await countGptMessagesTokens(completeMessages, tools);
const inputTokens = await countGptMessagesTokens(requestMessages, tools);
const outputTokens = await countGptMessagesTokens([gptAssistantResponse]);
// concat tool assistant
const toolNodeAssistant = GPTMessages2Chats([gptAssistantResponse])[0] as AIChatItemType;
return {
dispatchFlowResponse: response?.dispatchFlowResponse || [],
toolNodeTokens: response ? response.toolNodeTokens + tokens : tokens,
toolNodeInputTokens: response ? response.toolNodeInputTokens + inputTokens : inputTokens,
toolNodeOutputTokens: response ? response.toolNodeOutputTokens + outputTokens : outputTokens,
completeMessages,
assistantResponses: [...assistantResponses, ...toolNodeAssistant.value],
runTimes: (response?.runTimes || 0) + 1
@@ -580,7 +619,8 @@ async function streamResponse({
text: content
})
});
} else if (responseChoice?.tool_calls?.[0]) {
}
if (responseChoice?.tool_calls?.[0]) {
const toolCall: ChatCompletionMessageToolCall = responseChoice.tool_calls[0];
// In a stream response, only one tool is returned at a time. If have id, description is executing a tool
if (toolCall.id || callingTool) {

View File

@@ -31,7 +31,9 @@ export type DispatchToolModuleProps = ModuleDispatchProps<{
export type RunToolResponse = {
dispatchFlowResponse: DispatchFlowResponse[];
toolNodeTokens: number;
toolNodeTokens?: number; // deprecated
toolNodeInputTokens: number;
toolNodeOutputTokens: number;
completeMessages?: ChatCompletionMessageParam[];
assistantResponses?: AIChatItemValueItemType[];
toolWorkflowInteractiveResponse?: WorkflowInteractiveResponseType;

View File

@@ -5,13 +5,17 @@ import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants';
import { textAdaptGptResponse } from '@fastgpt/global/core/workflow/runtime/utils';
import { createChatCompletion } from '../../../ai/config';
import type { ChatCompletion, StreamChatType } from '@fastgpt/global/core/ai/type.d';
import type {
ChatCompletion,
ChatCompletionMessageParam,
StreamChatType
} from '@fastgpt/global/core/ai/type.d';
import { formatModelChars2Points } from '../../../../support/wallet/usage/utils';
import type { LLMModelItemType } from '@fastgpt/global/core/ai/model.d';
import { postTextCensor } from '../../../../common/api/requestPlusApi';
import { ChatCompletionRequestMessageRoleEnum } from '@fastgpt/global/core/ai/constants';
import type { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type';
import { countMessagesTokens } from '../../../../common/string/tiktoken/index';
import { countGptMessagesTokens } from '../../../../common/string/tiktoken/index';
import {
chats2GPTMessages,
chatValue2RuntimePrompt,
@@ -62,7 +66,7 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
res,
requestOrigin,
stream = false,
user,
externalProvider,
histories,
node: { name },
query,
@@ -71,8 +75,8 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
chatConfig,
params: {
model,
temperature = 0,
maxToken = 4000,
temperature,
maxToken,
history = 6,
quoteQA,
userChatInput,
@@ -134,7 +138,7 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
}),
(() => {
// censor model and system key
if (modelConstantsData.censor && !user.openaiAccount?.key) {
if (modelConstantsData.censor && !externalProvider.openaiAccount?.key) {
return postTextCensor({
text: `${systemPrompt}
${userChatInput}
@@ -170,7 +174,7 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
// console.log(JSON.stringify(requestBody, null, 2), '===');
const { response, isStreamResponse, getEmptyResponseTip } = await createChatCompletion({
body: requestBody,
userKey: user.openaiAccount,
userKey: externalProvider.openaiAccount,
options: {
headers: {
Accept: 'application/json, text/plain, */*'
@@ -214,25 +218,34 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
return Promise.reject(getEmptyResponseTip());
}
const completeMessages = requestMessages.concat({
role: ChatCompletionRequestMessageRoleEnum.Assistant,
content: answerText
});
const AIMessages: ChatCompletionMessageParam[] = [
{
role: ChatCompletionRequestMessageRoleEnum.Assistant,
content: answerText
}
];
const completeMessages = [...requestMessages, ...AIMessages];
const chatCompleteMessages = GPTMessages2Chats(completeMessages);
const tokens = await countMessagesTokens(chatCompleteMessages);
const inputTokens = await countGptMessagesTokens(requestMessages);
const outputTokens = await countGptMessagesTokens(AIMessages);
const { totalPoints, modelName } = formatModelChars2Points({
model,
tokens,
inputTokens,
outputTokens,
modelType: ModelTypeEnum.llm
});
return {
answerText,
[DispatchNodeResponseKeyEnum.nodeResponse]: {
totalPoints: user.openaiAccount?.key ? 0 : totalPoints,
totalPoints: externalProvider.openaiAccount?.key ? 0 : totalPoints,
model: modelName,
tokens,
tokens: inputTokens + outputTokens,
inputTokens: inputTokens,
outputTokens: outputTokens,
query: `${userChatInput}`,
maxToken: max_tokens,
historyPreview: getHistoryPreview(
@@ -245,9 +258,10 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
[DispatchNodeResponseKeyEnum.nodeDispatchUsages]: [
{
moduleName: name,
totalPoints: user.openaiAccount?.key ? 0 : totalPoints,
totalPoints: externalProvider.openaiAccount?.key ? 0 : totalPoints,
model: modelName,
tokens
inputTokens: inputTokens,
outputTokens: outputTokens
}
],
[DispatchNodeResponseKeyEnum.toolResponses]: answerText,

View File

@@ -120,14 +120,14 @@ export async function dispatchDatasetSearch(
// vector
const { totalPoints, modelName } = formatModelChars2Points({
model: vectorModel.model,
tokens,
inputTokens: tokens,
modelType: ModelTypeEnum.vector
});
const responseData: DispatchNodeResponseType & { totalPoints: number } = {
totalPoints,
query: concatQueries.join('\n'),
model: modelName,
tokens,
inputTokens: tokens,
similarity: usingSimilarityFilter ? similarity : undefined,
limit,
searchMode,
@@ -139,19 +139,21 @@ export async function dispatchDatasetSearch(
totalPoints,
moduleName: node.name,
model: modelName,
tokens
inputTokens: tokens
}
];
if (aiExtensionResult) {
const { totalPoints, modelName } = formatModelChars2Points({
model: aiExtensionResult.model,
tokens: aiExtensionResult.tokens,
inputTokens: aiExtensionResult.inputTokens,
outputTokens: aiExtensionResult.outputTokens,
modelType: ModelTypeEnum.llm
});
responseData.totalPoints += totalPoints;
responseData.tokens = aiExtensionResult.tokens;
responseData.inputTokens = aiExtensionResult.inputTokens;
responseData.outputTokens = aiExtensionResult.outputTokens;
responseData.extensionModel = modelName;
responseData.extensionResult =
aiExtensionResult.extensionQueries?.join('\n') ||
@@ -161,7 +163,8 @@ export async function dispatchDatasetSearch(
totalPoints,
moduleName: 'core.module.template.Query extension',
model: modelName,
tokens: aiExtensionResult.tokens
inputTokens: aiExtensionResult.inputTokens,
outputTokens: aiExtensionResult.outputTokens
});
}

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