Compare commits

...

47 Commits

Author SHA1 Message Date
Archer
09205e4666 fix: price page init data;perf: usage code;fix: reasoning tokens;fix: workflow basic node cannot upgrade (#3816)
* fix: img read

* fix: price page init data

* perf: ai model avatar

* perf: refresh in change team

* perf: null checker

* perf: usage code

* fix: reasoning tokens

* fix: workflow basic node cannot upgrade

* perf: model refresh

* perf: icon refresh
2025-02-18 20:50:25 +08:00
Finley Ge
ccf28d83b8 fix: app version addSourcemember tmbid could be empty (#3822) 2025-02-18 20:26:49 +08:00
LGiki
420aaad48e chore: fix typo in docs (#3819) 2025-02-18 20:25:51 +08:00
heheer
8ba2339890 download fetch baseurl & node select dnd (#3820) 2025-02-18 20:25:15 +08:00
Archer
e7b8934367 Update 4818.md (#3818) 2025-02-18 14:26:21 +08:00
Finley Ge
3e13397614 fix: refresh memberlist when switching account (#3814) 2025-02-18 13:54:56 +08:00
Archer
b14674cc6f fix: whisper checker;fix: img read (#3813)
* fix: img read

* fix: whisper checker

* perf: dev doc

* perf: dev doc

* remove invalid code
2025-02-18 10:08:25 +08:00
Archer
4d20274a97 feat: think tag parse (#3805) (#3808)
* feat: think tag parse

* remove some model config

* feat: parse think tag test
2025-02-17 20:57:36 +08:00
heheer
4447e40364 fix template market simple app (#3804) 2025-02-17 20:56:46 +08:00
John Chen
23949230ee fix document (#3806)
V2版本“获取集合列表”接口的path区分了大小写,使用/api/core/dataset/collection/listv2会返回404,必须使用大写V
2025-02-17 20:55:34 +08:00
saikidev
cd7a897304 chore: add ppio provider (#3789) 2025-02-14 17:04:43 +08:00
Archer
18aff8b8db update yml version (#3787) 2025-02-14 12:50:54 +08:00
Archer
d2b60ec785 fix: model check circle tip (#3786)
* model config

* feat: normalization embedding

* remove log

* version doc

* version doc

* fix: model check circle tip

* uml
2025-02-14 11:42:14 +08:00
a.e.
1226fe42a1 fix: skip thirdparty sso state verification (#3721) (#3782) 2025-02-14 11:39:34 +08:00
Finley Ge
abd375cdec fix: app/dataset list api return private flag (#3784) 2025-02-14 11:38:48 +08:00
Archer
7aacce8b0b 4.9.0 test (#3779)
* model config

* feat: normalization embedding

* remove log

* version doc

* version doc
2025-02-13 16:27:41 +08:00
heheer
686b09afd1 chatbot not overflow (#3777)
* chatbot not overflow

* add comment
2025-02-13 15:10:22 +08:00
heheer
3cfec37e9d fix embed chatbot default open (#3774) 2025-02-13 13:36:56 +08:00
Archer
d3641c877c perf: unlogin user fetch data (#3775)
* model config

* feat: normalization embedding

* perf: unlogin user fetch data
2025-02-13 13:36:33 +08:00
Archer
1094c65f2b perf: http empty params (#3773)
* model config

* feat: normalization embedding

* perf: http empty params

* doc
2025-02-13 10:35:11 +08:00
Archer
abe082b9ab i18n perf (#3770)
* model config

* feat: normalization embedding

* perf: mark ui

* perf: i18n

* fix: rerank error tip
2025-02-12 16:36:21 +08:00
heheer
132cf69372 optimize dnd drag code (#3768) 2025-02-12 15:25:31 +08:00
heheer
06a8a5e23d fix: simple mode variables dnd (#3767)
* fix: simple mode variables dnd

* optimize dnd drag
2025-02-12 14:36:04 +08:00
heheer
c42deab63b global variable & interactive node dnd (#3764) 2025-02-12 12:27:36 +08:00
Archer
58f715e878 perf: request quantity;perf: share page error circulation;perf: share chat toast (#3763)
* model config

* feat: normalization embedding

* perf: share page error circulation

* perf: request quantity

* perf: share chat toast

* perf: queue
2025-02-12 11:36:29 +08:00
Archer
116936ffa9 更新 share.md (#3757) 2025-02-11 23:54:42 +08:00
heheer
f5d045eece export csv format & log title debounce (#3754) 2025-02-11 17:36:00 +08:00
sbcyk
8ac6494e60 Update chat.md (#3746)
示例代码的json内容少了一个引号
2025-02-11 17:31:30 +08:00
heheer
f002896a24 chat logs filter & export (#3737)
* chat logs filter & export

* export chat detail
2025-02-11 16:32:47 +08:00
Archer
8738c32fb0 4.8.21 feature (#3742)
* model config

* feat: normalization embedding

* adapt unstrea  reasoning response

* remove select app

* perf: dataset search code

* fix: multiple audio video show

* perf: query extension output

* perf: link check

* perf: faq doc

* fix: ts

* feat: support reasoning text output

* feat: workflow support reasoning output
2025-02-11 13:53:08 +08:00
heheer
896a3f1472 add plugin unexist error tips (#3717)
* add plugin unexist error tips

* throw error when run plugin

* check workflow

* plugin data avoid request twice

* auth owner tmbId

* fix
2025-02-10 15:20:49 +08:00
John Chen
4284b78707 Update configuration.md (#3725)
由于4.8.20版本放弃在config.json中配置模型,在说明文档中,修正二级标题的版本号,并添加注释
2025-02-10 09:13:17 +08:00
Archer
fac5b6b50d 更新 4820.md (#3730) 2025-02-09 10:06:08 +08:00
Archer
51e17a47fa feat: normalization embedding;feat: model top_p param config (#3723)
* edit form force close image select

* model config

* feat: normalization embedding

* perf: add share page title force refresh
2025-02-08 12:16:46 +08:00
Archer
42b2046f96 4.8.21 feature (#3720)
* agent search demo

* edit form force close image select

* feat: llm params and doubao1.5

* perf: model error tip

* fix: template register path

* package
2025-02-08 10:44:33 +08:00
heheer
bb82b515e0 feat: auto adapt outlink chatwindow position (#3707) 2025-02-08 09:49:41 +08:00
clidxhk
fe688cdf2d Update utils.ts (#3699)
本地windows平台开发,加载model列表出现两次盘符导致加载失败,修改代码确保生成的路径不会包含重复的盘符,从而避免 ENOENT 错误。
2025-02-07 09:52:08 +08:00
Archer
0d35326909 fix: yml (#3709) 2025-02-06 16:03:45 +08:00
Archer
d857a391b3 4.8.20 update (#3706)
* fix: rerank auth token

* feat: check null value

* bind notify

* perf: reasoning config

* Adapt mongo 4.x index
2025-02-06 14:34:43 +08:00
Archer
772c1cde77 remove log (#3692) 2025-02-05 11:17:38 +08:00
Archer
b6e441c5eb fix: replace img host (#3691) 2025-02-05 10:21:35 +08:00
Archer
ac95828660 update doc (#3690) 2025-02-05 10:01:57 +08:00
Archer
f252918228 model config doc (#3689) 2025-02-05 09:52:03 +08:00
Archer
5c360b5ae6 doc (#3688) 2025-02-05 01:34:10 +08:00
Archer
09fa602dde readme (#3687)
* fix: doc deploy

* readme
2025-02-05 00:26:24 +08:00
Archer
db2c0a0bdb V4.8.20 feature (#3686)
* Aiproxy (#3649)

* model config

* feat: model config ui

* perf: rename variable

* feat: custom request url

* perf: model buffer

* perf: init model

* feat: json model config

* auto login

* fix: ts

* update packages

* package

* fix: dockerfile

* feat: usage filter & export & dashbord (#3538)

* feat: usage filter & export & dashbord

* adjust ui

* fix tmb scroll

* fix code & selecte all

* merge

* perf: usages list;perf: move components (#3654)

* perf: usages list

* team sub plan load

* perf: usage dashboard code

* perf: dashboard ui

* perf: move components

* add default model config (#3653)

* 4.8.20 test (#3656)

* provider

* perf: model config

* model perf (#3657)

* fix: model

* dataset quote

* perf: model config

* model tag

* doubao model config

* perf: config model

* feat: model test

* fix: POST 500 error on dingtalk bot (#3655)

* feat: default model (#3662)

* move model config

* feat: default model

* fix: false triggerd org selection (#3661)

* export usage csv i18n (#3660)

* export usage csv i18n

* fix build

* feat: markdown extension (#3663)

* feat: markdown extension

* media cros

* rerank test

* default price

* perf: default model

* fix: cannot custom provider

* fix: default model select

* update bg

* perf: default model selector

* fix: usage export

* i18n

* fix: rerank

* update init extension

* perf: ip limit check

* doubao model order

* web default modle

* perf: tts selector

* perf: tts error

* qrcode package

* reload buffer (#3665)

* reload buffer

* reload buffer

* tts selector

* fix: err tip (#3666)

* fix: err tip

* perf: training queue

* doc

* fix interactive edge (#3659)

* fix interactive edge

* fix

* comment

* add gemini model

* fix: chat model select

* perf: supplement assistant empty response (#3669)

* perf: supplement assistant empty response

* check array

* perf: max_token count;feat: support resoner output;fix: member scroll (#3681)

* perf: supplement assistant empty response

* check array

* perf: max_token count

* feat: support resoner output

* member scroll

* update provider order

* i18n

* fix: stream response (#3682)

* perf: supplement assistant empty response

* check array

* fix: stream response

* fix: model config cannot set to null

* fix: reasoning response (#3684)

* perf: supplement assistant empty response

* check array

* fix: reasoning response

* fix: reasoning response

* doc (#3685)

* perf: supplement assistant empty response

* check array

* doc

* lock

* animation

* update doc

* update compose

* doc

* doc

---------

Co-authored-by: heheer <heheer@sealos.io>
Co-authored-by: a.e. <49438478+I-Info@users.noreply.github.com>
2025-02-05 00:10:47 +08:00
Ge
c393002f1d feat: support mssql in databaseConnection plugin (#3674)
* feat: support mssql in databaseConnection plugin

* feat: trust server certificate for mssql
2025-02-01 10:53:20 +08:00
579 changed files with 13370 additions and 6142 deletions

View File

@@ -58,7 +58,7 @@ jobs:
# Step 4 - Builds the site using Hugo # Step 4 - Builds the site using Hugo
- name: Build - name: Build
run: cd docSite && hugo mod get -u github.com/colinwilson/lotusdocs && hugo -v --minify run: cd docSite && hugo mod get -u github.com/colinwilson/lotusdocs@6d0568e && hugo -v --minify
# Step 5 - Push our generated site to Vercel # Step 5 - Push our generated site to Vercel
- name: Deploy to Vercel - name: Deploy to Vercel

View File

@@ -58,7 +58,7 @@ jobs:
# Step 4 - Builds the site using Hugo # Step 4 - Builds the site using Hugo
- name: Build - name: Build
run: cd docSite && hugo mod get -u github.com/colinwilson/lotusdocs && hugo -v --minify run: cd docSite && hugo mod get -u github.com/colinwilson/lotusdocs@6d0568e && hugo -v --minify
# Step 5 - Push our generated site to Vercel # Step 5 - Push our generated site to Vercel
- name: Deploy to Vercel - name: Deploy to Vercel

View File

@@ -83,6 +83,7 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b
- [x] 统一查阅对话记录,并对数据进行标注 - [x] 统一查阅对话记录,并对数据进行标注
`6` 其他 `6` 其他
- [x] 可视化模型配置。
- [x] 支持语音输入和输出 (可配置语音输入语音回答) - [x] 支持语音输入和输出 (可配置语音输入语音回答)
- [x] 模糊输入提示 - [x] 模糊输入提示
- [x] 模板市场 - [x] 模板市场

View File

@@ -3,7 +3,7 @@ FROM hugomods/hugo:0.117.0 AS builder
WORKDIR /app WORKDIR /app
ADD ./docSite hugo ADD ./docSite hugo
RUN cd /app/hugo && hugo mod get -u github.com/colinwilson/lotusdocs && hugo -v --minify RUN cd /app/hugo && hugo mod get -u github.com/colinwilson/lotusdocs@6d0568e && hugo -v --minify
FROM fholzer/nginx-brotli:latest FROM fholzer/nginx-brotli:latest

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 321 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 353 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 380 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 393 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 377 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 368 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 322 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 323 KiB

View File

@@ -13,8 +13,8 @@ weight: 707
下面配置文件示例中包含了系统参数和各个模型配置: 下面配置文件示例中包含了系统参数和各个模型配置:
## 4.6.8+ 版本新配置文件示例 ## 4.8.20+ 版本新配置文件示例
> 从4.8.20版本开始,模型在页面中进行配置。
```json ```json
{ {
"feConfigs": { "feConfigs": {
@@ -25,251 +25,6 @@ weight: 707
"qaMaxProcess": 15, // 问答拆分线程数量 "qaMaxProcess": 15, // 问答拆分线程数量
"tokenWorkers": 50, // Token 计算线程保持数,会持续占用内存,不能设置太大。 "tokenWorkers": 50, // Token 计算线程保持数,会持续占用内存,不能设置太大。
"pgHNSWEfSearch": 100 // 向量搜索参数。越大搜索越精确但是速度越慢。设置为100有99%+精度。 "pgHNSWEfSearch": 100 // 向量搜索参数。越大搜索越精确但是速度越慢。设置为100有99%+精度。
},
"llmModels": [
{
"provider": "OpenAI", // 模型提供商主要用于分类展示目前已经内置提供商包括https://github.com/labring/FastGPT/blob/main/packages/global/core/ai/provider.ts, 可 pr 提供新的提供商,或直接填写 Other
"model": "gpt-4o-mini", // 模型名(对应OneAPI中渠道的模型名)
"name": "gpt-4o-mini", // 模型别名
"maxContext": 125000, // 最大上下文
"maxResponse": 16000, // 最大回复
"quoteMaxToken": 120000, // 最大引用内容
"maxTemperature": 1.2, // 最大温度
"charsPointsPrice": 0, // n积分/1k token商业版
"censor": false, // 是否开启敏感校验(商业版)
"vision": true, // 是否支持图片输入
"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": "OpenAI",
"model": "gpt-4o",
"name": "gpt-4o",
"maxContext": 125000,
"maxResponse": 4000,
"quoteMaxToken": 120000,
"maxTemperature": 1.2,
"charsPointsPrice": 0,
"censor": false,
"vision": true,
"datasetProcess": true,
"usedInClassify": true,
"usedInExtractFields": true,
"usedInToolCall": true,
"usedInQueryExtension": true,
"toolChoice": true,
"functionCall": false,
"customCQPrompt": "",
"customExtractPrompt": "",
"defaultSystemChatPrompt": "",
"defaultConfig": {},
"fieldMap": {}
},
{
"provider": "OpenAI",
"model": "o1-mini",
"name": "o1-mini",
"maxContext": 125000,
"maxResponse": 65000,
"quoteMaxToken": 120000,
"maxTemperature": 1.2,
"charsPointsPrice": 0,
"censor": false,
"vision": false,
"datasetProcess": true,
"usedInClassify": true,
"usedInExtractFields": true,
"usedInToolCall": true,
"usedInQueryExtension": true,
"toolChoice": false,
"functionCall": false,
"customCQPrompt": "",
"customExtractPrompt": "",
"defaultSystemChatPrompt": "",
"defaultConfig": {
"temperature": 1,
"max_tokens": null,
"stream": false
}
},
{
"provider": "OpenAI",
"model": "o1-preview",
"name": "o1-preview",
"maxContext": 125000,
"maxResponse": 32000,
"quoteMaxToken": 120000,
"maxTemperature": 1.2,
"charsPointsPrice": 0,
"censor": false,
"vision": false,
"datasetProcess": true,
"usedInClassify": true,
"usedInExtractFields": true,
"usedInToolCall": true,
"usedInQueryExtension": true,
"toolChoice": false,
"functionCall": false,
"customCQPrompt": "",
"customExtractPrompt": "",
"defaultSystemChatPrompt": "",
"defaultConfig": {
"temperature": 1,
"max_tokens": null,
"stream": false
}
}
],
"vectorModels": [
{
"provider": "OpenAI",
"model": "text-embedding-3-small",
"name": "text-embedding-3-small",
"charsPointsPrice": 0,
"defaultToken": 512,
"maxToken": 3000,
"weight": 100
},
{
"provider": "OpenAI",
"model": "text-embedding-3-large",
"name": "text-embedding-3-large",
"charsPointsPrice": 0,
"defaultToken": 512,
"maxToken": 3000,
"weight": 100,
"defaultConfig": {
"dimensions": 1024
}
},
{
"provider": "OpenAI",
"model": "text-embedding-ada-002", // 模型名与OneAPI对应
"name": "Embedding-2", // 模型展示名
"charsPointsPrice": 0, // n积分/1k token
"defaultToken": 700, // 默认文本分割时候的 token
"maxToken": 3000, // 最大 token
"weight": 100, // 优先训练权重
"defaultConfig": {}, // 自定义额外参数。例如,如果希望使用 embedding3-large 的话,可以传入 dimensions:1024来返回1024维度的向量。目前必须小于1536维度
"dbConfig": {}, // 存储时的额外参数(非对称向量模型时候需要用到)
"queryConfig": {} // 参训时的额外参数
}
],
"reRankModels": [],
"audioSpeechModels": [
{
"provider": "OpenAI",
"model": "tts-1",
"name": "OpenAI TTS1",
"charsPointsPrice": 0,
"voices": [
{ "label": "Alloy", "value": "alloy", "bufferId": "openai-Alloy" },
{ "label": "Echo", "value": "echo", "bufferId": "openai-Echo" },
{ "label": "Fable", "value": "fable", "bufferId": "openai-Fable" },
{ "label": "Onyx", "value": "onyx", "bufferId": "openai-Onyx" },
{ "label": "Nova", "value": "nova", "bufferId": "openai-Nova" },
{ "label": "Shimmer", "value": "shimmer", "bufferId": "openai-Shimmer" }
]
}
],
"whisperModel": {
"provider": "OpenAI",
"model": "whisper-1",
"name": "Whisper1",
"charsPointsPrice": 0
} }
} }
``` ```
## 内置的模型提供商ID
为了方便模型分类展示FastGPT 内置了部分模型提供商的名字和 Logo。如果你期望补充提供商可[提交 Issue](https://github.com/labring/FastGPT/issues),并提供几个信息:
1. 厂商官网地址
2. 厂商 SVG logo建议是正方形图片。
目前已支持的提供商, 复制 "-" 之前的字符串,作为 provider 的值。
- OpenAI
- Claude
- Gemini
- Meta
- MistralAI
- AliCloud - 阿里云
- Qwen - 通义千问
- Doubao - 豆包
- ChatGLM - 智谱
- DeepSeek - 深度求索
- Moonshot - 月之暗面
- MiniMax
- SparkDesk - 讯飞星火
- Hunyuan - 腾讯混元
- Baichuan - 百川
- Yi - 零一万物
- Ernie - 文心一言
- StepFun - 阶跃星辰
- Ollama
- BAAI - 智源研究院
- FishAudio
- Intern - 书生
- Moka - Moka-AI
- Other - 其他
## 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个生效。
1. [部署 ReRank 模型](/docs/development/custom-models/bge-rerank/)
1. 找到 FastGPT 的配置文件中的 `reRankModels` 4.6.6 以前是 `ReRankModels`
2. 修改对应的值:
```json
{
"reRankModels": [
{
"model": "bge-reranker-base", // 随意
"name": "检索重排-base", // 随意
"charsPointsPrice": 0,
"requestUrl": "{{host}}/v1/rerank",
"requestAuth": "安全凭证,已自动补 Bearer"
}
]
}
```

View File

@@ -118,10 +118,17 @@ services:
``` ```
## 接入 FastGPT ## 接入 FastGPT
参考 [ReRank模型接入](/docs/development/configuration/#rerank-接入)host 变量为部署的域名 1. 打开 FastGPT 模型配置,新增一个重排模型
2. 填写模型配置表单:模型 ID 为`bge-reranker-base`,地址填写`{{host}}/v1/rerank`host 为你部署的域名/IP:Port。
![alt text](/imgs/image-102.png)
## QA ## QA
### 403报错
FastGPT中自定义请求 Token 和环境变量的 ACCESS_TOKEN 不一致。
### Docker 运行提示 `Bus error (core dumped)` ### Docker 运行提示 `Bus error (core dumped)`
尝试增加 `docker-compose.yml` 配置项 `shm_size` ,以增加容器中的共享内存目录大小。 尝试增加 `docker-compose.yml` 配置项 `shm_size` ,以增加容器中的共享内存目录大小。

View File

@@ -144,7 +144,6 @@ curl --location --request POST 'https://<oneapi_url>/v1/chat/completions' \
"usedInClassify": true, // 是否用于问题分类务必保证至少有一个为true "usedInClassify": true, // 是否用于问题分类务必保证至少有一个为true
"usedInExtractFields": true, // 是否用于内容提取务必保证至少有一个为true "usedInExtractFields": true, // 是否用于内容提取务必保证至少有一个为true
"usedInToolCall": true, // 是否用于工具调用务必保证至少有一个为true "usedInToolCall": true, // 是否用于工具调用务必保证至少有一个为true
"usedInQueryExtension": true, // 是否用于问题优化务必保证至少有一个为true
"toolChoice": true, // 是否支持工具选择(分类,内容提取,工具调用会用到。) "toolChoice": true, // 是否支持工具选择(分类,内容提取,工具调用会用到。)
"functionCall": false, // 是否支持函数调用(分类,内容提取,工具调用会用到。会优先使用 toolChoice如果为false则使用 functionCall如果仍为 false则使用提示词模式 "functionCall": false, // 是否支持函数调用(分类,内容提取,工具调用会用到。会优先使用 toolChoice如果为false则使用 functionCall如果仍为 false则使用提示词模式
"customCQPrompt": "", // 自定义文本分类提示词(不支持工具和函数调用的模型 "customCQPrompt": "", // 自定义文本分类提示词(不支持工具和函数调用的模型

View File

@@ -7,6 +7,13 @@ toc: true
weight: 707 weight: 707
--- ---
## 前置知识
1. 基础的网络知识:端口,防火墙……
2. Docker 和 Docker Compose 基础知识
3. 大模型相关接口和参数
4. RAG 相关知识:向量模型,向量数据库,向量检索
## 部署架构图 ## 部署架构图
![](/imgs/sealos-fastgpt.webp) ![](/imgs/sealos-fastgpt.webp)
@@ -202,6 +209,12 @@ docker restart oneapi
首次运行,会自动初始化 root 用户,密码为 `1234`(与环境变量中的`DEFAULT_ROOT_PSW`一致),日志里会提示一次`MongoServerError: Unable to read from a snapshot due to pending collection catalog changes;`可忽略。 首次运行,会自动初始化 root 用户,密码为 `1234`(与环境变量中的`DEFAULT_ROOT_PSW`一致),日志里会提示一次`MongoServerError: Unable to read from a snapshot due to pending collection catalog changes;`可忽略。
### 6. 配置模型
务必先配置至少一组模型,否则系统无法正常使用。
[点击查看模型配置教程](/docs/development/modelConfig/intro/)
## FAQ ## FAQ
### Mongo 副本集自动初始化失败 ### Mongo 副本集自动初始化失败

View File

@@ -9,25 +9,39 @@ images: []
## 一、错误排查方式 ## 一、错误排查方式
遇到问题先按下面方式排查。 可以先找找[Issue](https://github.com/labring/FastGPT/issues),或新提 Issue私有部署错误务必提供详细的操作步骤、日志、截图否则很难排查。
### 获取后端错误
1. `docker ps -a` 查看所有容器运行状态,检查是否全部 running如有异常尝试`docker logs 容器名`查看对应日志。 1. `docker ps -a` 查看所有容器运行状态,检查是否全部 running如有异常尝试`docker logs 容器名`查看对应日志。
2. 容器都运行正常的,`docker logs 容器名` 查看报错日志 2. 容器都运行正常的,`docker logs 容器名` 查看报错日志
3. 带有`requestId`的,都是 OneAPI 提示错误,大部分都是因为模型接口报错。
4. 无法解决时,可以找找[Issue](https://github.com/labring/FastGPT/issues),或新提 Issue私有部署错误务必提供详细的日志否则很难排查。
### 前端错误
前端报错时,页面会出现崩溃,并提示检查控制台日志。可以打开浏览器控制台,并查看`console`中的 log 日志。还可以点击对应 log 的超链接,会提示到具体错误文件,可以把这些详细错误信息提供,方便排查。
### OneAPI 错误
带有`requestId`的,都是 OneAPI 提示错误,大部分都是因为模型接口报错。可以参考 [OneAPI 常见错误](/docs/development/faq/#三常见的-oneapi-错误)
## 二、通用问题 ## 二、通用问题
### 前端页面崩溃
1. 90% 情况是模型配置不正确:确保每类模型都至少有一个启用;检查模型中一些`对象`参数是否异常(数组和对象),如果为空,可以尝试给个空数组或空对象。
2. 少部分是由于浏览器兼容问题,由于项目中包含一些高阶语法,可能低版本浏览器不兼容,可以将具体操作步骤和控制台中错误信息提供 issue。
3. 关闭浏览器翻译功能,如果浏览器开启了翻译,可能会导致页面崩溃。
### 通过sealos部署的话是否没有本地部署的一些限制 ### 通过sealos部署的话是否没有本地部署的一些限制
![](/imgs/faq1.png) ![](/imgs/faq1.png)
这是索引模型的长度限制,通过任何方式部署都一样的,但不同索引模型的配置不一样,可以在后台修改参数。 这是索引模型的长度限制,通过任何方式部署都一样的,但不同索引模型的配置不一样,可以在后台修改参数。
### sealos怎么挂载 小程序配置文件 ### 怎么挂载小程序配置文件
新增配置文件/app/projects/app/public/xxxx.txt 将验证文件,挂载到指定位置/app/projects/app/public/xxxx.txt
: 然后重启。例如:
![](/imgs/faq2.png) ![](/imgs/faq2.png)
@@ -130,7 +144,7 @@ OneAPI 的 API Key 配置错误,需要修改`OPENAI_API_KEY`环境变量,并
## 四、常见模型问题 ## 四、常见模型问题
### 如何检查模型问题 ### 如何检查模型可用性问题
1. 私有部署模型,先确认部署的模型是否正常。 1. 私有部署模型,先确认部署的模型是否正常。
2. 通过 CURL 请求,直接测试上游模型是否正常运行(云端模型或私有模型均进行测试) 2. 通过 CURL 请求,直接测试上游模型是否正常运行(云端模型或私有模型均进行测试)
@@ -403,3 +417,7 @@ curl --location --request POST 'https://oneapi.xxxx/v1/chat/completions' \
"tool_choice": "auto" "tool_choice": "auto"
}' }'
``` ```
### 向量检索得分大于 1
由于模型没有归一化导致的。目前仅支持归一化的模型。

View File

@@ -15,8 +15,8 @@ weight: 705
- [Git](http://git-scm.com/) - [Git](http://git-scm.com/)
- [Docker](https://www.docker.com/)(构建镜像) - [Docker](https://www.docker.com/)(构建镜像)
- [Node.js v18.17 / v20.x](http://nodejs.org)版本尽量一样可以使用nvm管理node版本 - [Node.js v20.14.0](http://nodejs.org)版本尽量一样可以使用nvm管理node版本
- [pnpm](https://pnpm.io/) 版本 8.6.0 (目前官方的开发环境) - [pnpm](https://pnpm.io/) 推荐版本 9.4.0 (目前官方的开发环境)
- make命令: 根据不同平台,百度安装 (官方是GNU Make 4.3) - make命令: 根据不同平台,百度安装 (官方是GNU Make 4.3)
## 开始本地开发 ## 开始本地开发
@@ -77,8 +77,6 @@ Mongo 数据库需要注意,需要注意在连接地址中增加 `directConnec
可参考项目根目录下的 `dev.md`,第一次编译运行可能会有点慢,需要点耐心哦 可参考项目根目录下的 `dev.md`,第一次编译运行可能会有点慢,需要点耐心哦
```bash ```bash
# 给自动化脚本代码执行权限(非 linux 系统, 可以手动执行里面的 postinstall.sh 文件内容)
chmod -R +x ./scripts/
# 代码根目录下执行,会安装根 package、projects 和 packages 内所有依赖 # 代码根目录下执行,会安装根 package、projects 和 packages 内所有依赖
# 如果提示 isolate-vm 安装失败可以参考https://github.com/laverdet/isolated-vm?tab=readme-ov-file#requirements # 如果提示 isolate-vm 安装失败可以参考https://github.com/laverdet/isolated-vm?tab=readme-ov-file#requirements
pnpm i pnpm i

View File

@@ -0,0 +1,470 @@
---
title: 'FastGPT 模型配置说明'
description: 'FastGPT 模型配置说明'
icon: 'api'
draft: false
toc: true
weight: 744
---
在 4.8.20 版本以前FastGPT 模型配置在 `config.json` 文件中声明,你可以在 https://github.com/labring/FastGPT/blob/main/projects/app/data/model.json 中找到旧版的配置文件示例。
从 4.8.20 版本开始,你可以直接在 FastGPT 页面中进行模型配置,并且系统内置了大量模型,无需从 0 开始配置。下面介绍模型配置的基本流程:
## 配置模型
### 1. 使用 OneAPI 对接模型提供商
可以使用 [OneAPI 接入教程](/docs/development/modelconfig/one-api) 来进行模型聚合,从而可以对接更多模型提供商。你需要先在各服务商申请好 API 接入 OneAPI 后,才能在 FastGPT 中使用这些模型。示例流程如下:
![alt text](/imgs/image-95.png)
除了各模型官方的服务外,还有一些第三方服务商提供模型接入服务,当然你也可以用 Ollama 等来部署本地模型,最终都需要接入 OneAPI下面是一些第三方服务商
{{% alert icon=" " context="info" %}}
- [SiliconCloud(硅基流动)](https://cloud.siliconflow.cn/i/TR9Ym0c4): 提供开源模型调用的平台。
- [Sealos AIProxy](https://hzh.sealos.run/?openapp=system-aiproxy): 提供国内各家模型代理,无需逐一申请 api。
{{% /alert %}}
在 OneAPI 配置好模型后,你就可以打开 FastGPT 页面,启用对应模型了。
### 2. 登录 root 用户
仅 root 用户可以进行模型配置。
### 3. 进入模型配置页面
登录 root 用户后,在`账号-模型提供商-模型配置`中,你可以看到所有内置的模型和自定义模型,以及哪些模型启用了。
![alt text](/image-90.png)
### 4. 配置介绍
{{% alert icon="🤖 " context="success" %}}
注意:
1. 目前语音识别模型和重排模型仅会生效一个,所以配置时候,只需要配置一个即可。
2. 系统至少需要一个语言模型和一个索引模型才能正常使用。
{{% /alert %}}
#### 核心配置
- 模型 ID接口请求时候Body 中`model`字段的值,全局唯一。
- 自定义请求地址/Key如果需要绕过`OneAPI`,可以设置自定义请求地址和 Token。一般情况下不需要如果 OneAPI 不支持某些模型,可以使用该特性。
#### 模型类型
1. 语言模型 - 进行文本对话,多模态模型支持图片识别。
2. 索引模型 - 对文本块进行索引,用于相关文本检索。
3. 重排模型 - 对检索结果进行重排,用于优化检索排名。
4. 语音合成 - 将文本转换为语音。
5. 语音识别 - 将语音转换为文本。
#### 启用模型
系统内置了目前主流厂商的模型,如果你不熟悉配置,直接点击`启用`即可,需要注意的是,`模型 ID`需要和 OneAPI 中渠道的`模型`一致。
| | |
| --- | --- |
| ![alt text](/imgs/image-91.png) | ![alt text](/imgs/image-92.png) |
#### 修改模型配置
点击模型右侧的齿轮即可进行模型配置,不同类型模型的配置有区别。
| | |
| --- | --- |
| ![alt text](/imgs/image-93.png) | ![alt text](/imgs/image-94.png) |
## 新增自定义模型
如果系统内置的模型无法满足你的需求,你可以添加自定义模型。自定义模型中,如果`模型 ID`与系统内置的模型 ID 一致,则会被认为是修改系统模型。
| | |
| --- | --- |
| ![alt text](/imgs/image-96.png) | ![alt text](/imgs/image-97.png) |
#### 通过配置文件配置
如果你觉得通过页面配置模型比较麻烦,你也可以通过配置文件来配置模型。或者希望快速将一个系统的配置,复制到另一个系统,也可以通过配置文件来实现。
| | |
| --- | --- |
| ![alt text](/imgs/image-98.png) | ![alt text](/imgs/image-99.png) |
**语言模型字段说明:**
```json
{
"model": "模型 ID",
"metadata": {
"isCustom": true, // 是否为自定义模型
"isActive": true, // 是否启用
"provider": "OpenAI", // 模型提供商主要用于分类展示目前已经内置提供商包括https://github.com/labring/FastGPT/blob/main/packages/global/core/ai/provider.ts, 可 pr 提供新的提供商,或直接填写 Other
"model": "gpt-4o-mini", // 模型ID(对应OneAPI中渠道的模型名)
"name": "gpt-4o-mini", // 模型别名
"maxContext": 125000, // 最大上下文
"maxResponse": 16000, // 最大回复
"quoteMaxToken": 120000, // 最大引用内容
"maxTemperature": 1.2, // 最大温度
"charsPointsPrice": 0, // n积分/1k token商业版
"censor": false, // 是否开启敏感校验(商业版)
"vision": true, // 是否支持图片输入
"datasetProcess": true, // 是否设置为文本理解模型QA务必保证至少有一个为true否则知识库会报错
"usedInClassify": true, // 是否用于问题分类务必保证至少有一个为true
"usedInExtractFields": true, // 是否用于内容提取务必保证至少有一个为true
"usedInToolCall": 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
}
}
```
**索引模型字段说明:**
```json
{
"model": "模型 ID",
"metadata": {
"isCustom": true, // 是否为自定义模型
"isActive": true, // 是否启用
"provider": "OpenAI", // 模型提供商
"model": "text-embedding-3-small", // 模型ID
"name": "text-embedding-3-small", // 模型别名
"charsPointsPrice": 0, // n积分/1k token
"defaultToken": 512, // 默认文本分割时候的 token
"maxToken": 3000 // 最大 token
}
}
```
**重排模型字段说明:**
```json
{
"model": "模型 ID",
"metadata": {
"isCustom": true, // 是否为自定义模型
"isActive": true, // 是否启用
"provider": "BAAI", // 模型提供商
"model": "bge-reranker-v2-m3", // 模型ID
"name": "ReRanker-Base", // 模型别名
"requestUrl": "", // 自定义请求地址
"requestAuth": "", // 自定义请求认证
"type": "rerank" // 模型类型
}
}
```
**语音合成模型字段说明:**
```json
{
"model": "模型 ID",
"metadata": {
"isActive": true, // 是否启用
"isCustom": true, // 是否为自定义模型
"type": "tts", // 模型类型
"provider": "FishAudio", // 模型提供商
"model": "fishaudio/fish-speech-1.5", // 模型ID
"name": "fish-speech-1.5", // 模型别名
"voices": [ // 音色
{
"label": "fish-alex", // 音色名称
"value": "fishaudio/fish-speech-1.5:alex", // 音色ID
},
{
"label": "fish-anna", // 音色名称
"value": "fishaudio/fish-speech-1.5:anna", // 音色ID
}
],
"charsPointsPrice": 0 // n积分/1k token
}
}
```
**语音识别模型字段说明:**
```json
{
"model": "whisper-1",
"metadata": {
"isActive": true, // 是否启用
"isCustom": true, // 是否为自定义模型
"provider": "OpenAI", // 模型提供商
"model": "whisper-1", // 模型ID
"name": "whisper-1", // 模型别名
"charsPointsPrice": 0, // n积分/1k token
"type": "stt" // 模型类型
}
}
```
## 模型测试
FastGPT 页面上提供了每类模型的简单测试,可以初步检查模型是否正常工作,会实际按模板发送一个请求。
![alt text](/imgs/image-105.png)
## 特殊接入示例
### ReRank 模型接入
由于 OneAPI 不支持 Rerank 模型所以需要单独配置。FastGPT 中,模型配置支持自定义请求地址,可以绕过 OneAPI直接向提供商发起请求可以利用这个特性来接入 Rerank 模型。
#### 使用硅基流动的在线模型
有免费的 `bge-reranker-v2-m3` 模型可以使用。
1. [点击注册硅基流动账号](https://cloud.siliconflow.cn/i/TR9Ym0c4)
2. 进入控制台,获取 API key: https://cloud.siliconflow.cn/account/ak
3. 打开 FastGPT 模型配置,新增一个`BAAI/bge-reranker-v2-m3`的重排模型(如果系统内置了,也可以直接变更,无需新增)。
![alt text](/imgs/image-101.png)
#### 私有部署模型
[点击查看部署 ReRank 模型教程](/docs/development/custom-models/bge-rerank/)
### 接入语音识别模型
OneAPI 的语言识别接口,无法正确的识别其他模型(会始终识别成 whisper-1所以如果想接入其他模型可以通过自定义请求地址来实现。例如接入硅基流动的 `FunAudioLLM/SenseVoiceSmall` 模型,可以参考如下配置:
点击模型编辑:
![alt text](/imgs/image-106.png)
填写硅基流动的地址:`https://api.siliconflow.cn/v1/audio/transcriptions`,并填写硅基流动的 API Key。
![alt text](/imgs/image-107.png)
## 其他配置项说明
### 自定义请求地址
如果填写了该值,则可以允许你绕过 OneAPI直接向自定义请求地址发起请求。需要填写完整的请求地址例如
- LLM: {{host}}/v1/chat/completions
- Embedding: {{host}}/v1/embeddings
- STT: {{host}}/v1/audio/transcriptions
- TTS: {{host}}/v1/audio/speech
- Rerank: {{host}}/v1/rerank
自定义请求 Key则是向自定义请求地址发起请求时候携带请求头Authorization: Bearer xxx 进行请求。
所有接口均遵循 OpenAI 提供的模型格式,可参考 [OpenAI API 文档](https://platform.openai.com/docs/api-reference/introduction) 进行配置。
由于 OpenAI 没有提供 ReRank 模型,遵循的是 Cohere 的格式。[点击查看接口请求示例](/docs/development/faq/#如何检查模型问题)
### 模型价格配置
商业版用户可以通过配置模型价格,来进行账号计费。系统包含两种计费模式:按总 tokens 计费和输入输出 Tokens 分开计费。
如果需要配置`输入输出 Tokens 分开计费模式`,则填写`模型输入价格``模型输出价格`两个值。
如果需要配置`按总 tokens 计费模式`,则填写`模型综合价格`一个值。
## 如何提交内置模型
由于模型更新非常频繁,官方不一定及时更新,如果未能找到你期望的内置模型,你可以[提交 Issue](https://github.com/labring/FastGPT/issues),提供模型的名字和对应官网。或者直接[提交 PR](https://github.com/labring/FastGPT/pulls),提供模型配置。
### 添加模型提供商
如果你需要添加模型提供商,需要修改以下代码:
1. FastGPT/packages/web/components/common/Icon/icons/model - 在此目录下,添加模型提供商的 svg 头像地址。
2. 在 FastGPT 根目录下,运行`pnpm initIcon`,将图片加载到配置文件中。
3. FastGPT/packages/global/core/ai/provider.ts - 在此文件中,追加模型提供商的配置。
### 添加模型
你可以在`FastGPT/packages/service/core/ai/config/provider`目录下,找对应模型提供商的配置文件,并追加模型配置。请自行全文检查,`model`字段,必须在所有模型中唯一。具体配置字段说明,参考[模型配置字段说明](/docs/development/modelconfig/intro/#通过配置文件配置)
## 旧版模型配置说明
配置好 OneAPI 后,需要在`config.json`文件中,手动的增加模型配置,并重启。
由于环境变量不利于配置复杂的内容FastGPT 采用了 ConfigMap 的形式挂载配置文件,你可以在 `projects/app/data/config.json` 看到默认的配置文件。可以参考 [docker-compose 快速部署](/docs/development/docker/) 来挂载配置文件。
**开发环境下**,你需要将示例配置文件 `config.json` 复制成 `config.local.json` 文件才会生效。
**Docker部署**,修改`config.json` 文件,需要重启容器。
下面配置文件示例中包含了系统参数和各个模型配置:
```json
{
"feConfigs": {
"lafEnv": "https://laf.dev" // laf环境。 https://laf.run (杭州阿里云) ,或者私有化的laf环境。如果使用 Laf openapi 功能,需要最新版的 laf 。
},
"systemEnv": {
"vectorMaxProcess": 15, // 向量处理线程数量
"qaMaxProcess": 15, // 问答拆分线程数量
"tokenWorkers": 50, // Token 计算线程保持数,会持续占用内存,不能设置太大。
"pgHNSWEfSearch": 100 // 向量搜索参数。越大搜索越精确但是速度越慢。设置为100有99%+精度。
},
"llmModels": [
{
"provider": "OpenAI", // 模型提供商主要用于分类展示目前已经内置提供商包括https://github.com/labring/FastGPT/blob/main/packages/global/core/ai/provider.ts, 可 pr 提供新的提供商,或直接填写 Other
"model": "gpt-4o-mini", // 模型名(对应OneAPI中渠道的模型名)
"name": "gpt-4o-mini", // 模型别名
"maxContext": 125000, // 最大上下文
"maxResponse": 16000, // 最大回复
"quoteMaxToken": 120000, // 最大引用内容
"maxTemperature": 1.2, // 最大温度
"charsPointsPrice": 0, // n积分/1k token商业版
"censor": false, // 是否开启敏感校验(商业版)
"vision": true, // 是否支持图片输入
"datasetProcess": true, // 是否设置为文本理解模型QA务必保证至少有一个为true否则知识库会报错
"usedInClassify": true, // 是否用于问题分类务必保证至少有一个为true
"usedInExtractFields": true, // 是否用于内容提取务必保证至少有一个为true
"usedInToolCall": 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": "OpenAI",
"model": "gpt-4o",
"name": "gpt-4o",
"maxContext": 125000,
"maxResponse": 4000,
"quoteMaxToken": 120000,
"maxTemperature": 1.2,
"charsPointsPrice": 0,
"censor": false,
"vision": true,
"datasetProcess": true,
"usedInClassify": true,
"usedInExtractFields": true,
"usedInToolCall": true,
"toolChoice": true,
"functionCall": false,
"customCQPrompt": "",
"customExtractPrompt": "",
"defaultSystemChatPrompt": "",
"defaultConfig": {},
"fieldMap": {}
},
{
"provider": "OpenAI",
"model": "o1-mini",
"name": "o1-mini",
"maxContext": 125000,
"maxResponse": 65000,
"quoteMaxToken": 120000,
"maxTemperature": 1.2,
"charsPointsPrice": 0,
"censor": false,
"vision": false,
"datasetProcess": true,
"usedInClassify": true,
"usedInExtractFields": true,
"usedInToolCall": true,
"toolChoice": false,
"functionCall": false,
"customCQPrompt": "",
"customExtractPrompt": "",
"defaultSystemChatPrompt": "",
"defaultConfig": {
"temperature": 1,
"max_tokens": null,
"stream": false
}
},
{
"provider": "OpenAI",
"model": "o1-preview",
"name": "o1-preview",
"maxContext": 125000,
"maxResponse": 32000,
"quoteMaxToken": 120000,
"maxTemperature": 1.2,
"charsPointsPrice": 0,
"censor": false,
"vision": false,
"datasetProcess": true,
"usedInClassify": true,
"usedInExtractFields": true,
"usedInToolCall": true,
"toolChoice": false,
"functionCall": false,
"customCQPrompt": "",
"customExtractPrompt": "",
"defaultSystemChatPrompt": "",
"defaultConfig": {
"temperature": 1,
"max_tokens": null,
"stream": false
}
}
],
"vectorModels": [
{
"provider": "OpenAI",
"model": "text-embedding-3-small",
"name": "text-embedding-3-small",
"charsPointsPrice": 0,
"defaultToken": 512,
"maxToken": 3000,
"weight": 100
},
{
"provider": "OpenAI",
"model": "text-embedding-3-large",
"name": "text-embedding-3-large",
"charsPointsPrice": 0,
"defaultToken": 512,
"maxToken": 3000,
"weight": 100,
"defaultConfig": {
"dimensions": 1024
}
},
{
"provider": "OpenAI",
"model": "text-embedding-ada-002", // 模型名与OneAPI对应
"name": "Embedding-2", // 模型展示名
"charsPointsPrice": 0, // n积分/1k token
"defaultToken": 700, // 默认文本分割时候的 token
"maxToken": 3000, // 最大 token
"weight": 100, // 优先训练权重
"defaultConfig": {}, // 自定义额外参数。例如,如果希望使用 embedding3-large 的话,可以传入 dimensions:1024来返回1024维度的向量。目前必须小于1536维度
"dbConfig": {}, // 存储时的额外参数(非对称向量模型时候需要用到)
"queryConfig": {} // 参训时的额外参数
}
],
"reRankModels": [],
"audioSpeechModels": [
{
"provider": "OpenAI",
"model": "tts-1",
"name": "OpenAI TTS1",
"charsPointsPrice": 0,
"voices": [
{ "label": "Alloy", "value": "alloy", "bufferId": "openai-Alloy" },
{ "label": "Echo", "value": "echo", "bufferId": "openai-Echo" },
{ "label": "Fable", "value": "fable", "bufferId": "openai-Fable" },
{ "label": "Onyx", "value": "onyx", "bufferId": "openai-Onyx" },
{ "label": "Nova", "value": "nova", "bufferId": "openai-Nova" },
{ "label": "Shimmer", "value": "shimmer", "bufferId": "openai-Shimmer" }
]
}
],
"whisperModel": {
"provider": "OpenAI",
"model": "whisper-1",
"name": "Whisper1",
"charsPointsPrice": 0
}
}
```

View File

@@ -94,70 +94,16 @@ CHAT_API_KEY=sk-xxxxxx
![](/imgs/oneapi-demo1.png) ![](/imgs/oneapi-demo1.png)
### 2. 修改 FastGPT 配置文件 ### 2. 修改 FastGPT 模型配置
可以在 `/projects/app/src/data/config.json` 里找到配置文件(本地开发需要复制成 config.local.json,按下面内容修改配置文件,最新/更具体的配置说明,可查看[FastGPT 配置文件说明](/docs/development/configuration) 打开 FastGPT 模型配置,启动文心千帆模型,如果希望未内置,可以通过新增模型来配置
配置模型关键点在于`model` 需要与 OneAPI 渠道中的模型一致。 ![alt text](/imgs/image-103.png)
```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 配置文件 这章介绍一些提供商接入 OneAPI 的教程,配置后不要忘记 FastGPT 模型配置中启用
### 阿里通义千问 ### 阿里通义千问

View File

@@ -27,139 +27,13 @@ OPENAI_BASE_URL=https://api.siliconflow.cn/v1
CHAT_API_KEY=sk-xxxxxx CHAT_API_KEY=sk-xxxxxx
``` ```
## 3. 修改 FastGPT 配置文件 ## 3. 修改 FastGPT 模型配置
我们选取 SiliconCloud 中的模型作为 FastGPT 配置。这里配置了 `Qwen2.5 72b` 的纯语言和视觉模型;选择 `bge-m3` 作为向量模型;选择 `bge-reranker-v2-m3` 作为重排模型。选择 `fish-speech-1.5` 作为语音模型;选择 `SenseVoiceSmall` 作为语音输入模型 系统内置了几个硅基流动的模型进行体验,如果需要其他模型,可以手动添加
注意ReRank 模型仍需配置一次 Api Key 这里启动了 `Qwen2.5 72b` 的纯语言和视觉模型;选择 `bge-m3` 作为向量模型;选择 `bge-reranker-v2-m3` 作为重排模型。选择 `fish-speech-1.5` 作为语音模型;选择 `SenseVoiceSmall` 作为语音输入模型。
```json ![alt text](/imgs/image-104.png)
{
"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. 体验测试 ## 5. 体验测试

View File

@@ -672,7 +672,7 @@ curl --location --request POST 'http://localhost:3000/api/core/chat/getHistories
"appId": "appId", "appId": "appId",
"offset": 0, "offset": 0,
"pageSize": 20, "pageSize": 20,
"source: "api" "source": "api"
}' }'
``` ```

View File

@@ -735,7 +735,7 @@ data 为集合的 ID。
**4.8.19+** **4.8.19+**
```bash ```bash
curl --location --request POST 'http://localhost:3000/api/core/dataset/collection/listv2' \ curl --location --request POST 'http://localhost:3000/api/core/dataset/collection/listV2' \
--header 'Authorization: Bearer {{authorization}}' \ --header 'Authorization: Bearer {{authorization}}' \
--header 'Content-Type: application/json' \ --header 'Content-Type: application/json' \
--data-raw '{ --data-raw '{

View File

@@ -11,7 +11,7 @@ weight: 860
在 FastGPT V4.6.4 中,我们修改了分享链接的数据读取方式,为每个用户生成一个 localId用于标识用户从云端拉取对话记录。但是这种方式仅能保障用户在同一设备同一浏览器中使用如果切换设备或者清空浏览器缓存则会丢失这些记录。这种方式存在一定的风险因此我们仅允许用户拉取近`30天``20条`记录。 在 FastGPT V4.6.4 中,我们修改了分享链接的数据读取方式,为每个用户生成一个 localId用于标识用户从云端拉取对话记录。但是这种方式仅能保障用户在同一设备同一浏览器中使用如果切换设备或者清空浏览器缓存则会丢失这些记录。这种方式存在一定的风险因此我们仅允许用户拉取近`30天``20条`记录。
分享链接身份鉴权设计的目的在于,将 FastGPT 的对话框快速、安全的接入到你现有的系统中,仅需 2 个接口即可实现。 分享链接身份鉴权设计的目的在于,将 FastGPT 的对话框快速、安全的接入到你现有的系统中,仅需 2 个接口即可实现。该功能目前只在商业版中提供。
## 使用说明 ## 使用说明

View File

@@ -34,7 +34,7 @@ FastGPT 使用了 one-api 项目来管理模型池,其可以兼容 OpenAI 、A
<a href="https://bja.sealos.run/?openapp=system-template%3FtemplateName%3Dfastgpt" rel="external" target="_blank"><img src="https://raw.githubusercontent.com/labring-actions/templates/main/Deploy-on-Sealos.svg" alt="Deploy on Sealos"/></a> <a href="https://bja.sealos.run/?openapp=system-template%3FtemplateName%3Dfastgpt" rel="external" target="_blank"><img src="https://raw.githubusercontent.com/labring-actions/templates/main/Deploy-on-Sealos.svg" alt="Deploy on Sealos"/></a>
### 开始部署 ### 1. 开始部署
由于需要部署数据库,部署完后需要等待 2~4 分钟才能正常访问。默认用了最低配置,首次访问时会有些慢。 由于需要部署数据库,部署完后需要等待 2~4 分钟才能正常访问。默认用了最低配置,首次访问时会有些慢。
@@ -52,27 +52,19 @@ FastGPT 使用了 one-api 项目来管理模型池,其可以兼容 OpenAI 、A
![](/imgs/sealos2.png) ![](/imgs/sealos2.png)
### 登录 ### 2. 登录
用户名:`root` 用户名:`root`
密码是刚刚一键部署时设置的`root_password` 密码是刚刚一键部署时设置的`root_password`
### 修改配置文件和环境变量 ### 3. 配置模型
在 Sealos 中,你可以打开`应用管理`App Launchpad看到部署的 FastGPT可以打开`数据库`Database看到对应的数据库。 ### 4. 配置模型
`应用管理`中,选中 FastGPT点击变更可以看到对应的环境变量和配置文件 务必先配置至少一组模型,否则系统无法正常使用
![](/imgs/fastgptonsealos1.png) [点击查看模型配置教程](/docs/development/modelConfig/intro/)
{{% alert icon="🤖 " context="success" %}}
在 Sealos 上FastGPT 一共运行了 1 个服务和 2 个数据库,如暂停和删除请注意数据库一同操作。(你可以白天启动,晚上暂停它们,省钱大法)
{{% /alert %}}
### 更新
点击变更或重启会自动拉取镜像更新,请确保镜像`tag`正确。建议不要使用`latest`,改成固定版本号。
## 收费 ## 收费
@@ -88,7 +80,20 @@ FastGPT 商业版共包含了2个应用fastgpt, fastgpt-plus和2个数据
点击右侧的详情,可以查看对应应用的详细信息。 点击右侧的详情,可以查看对应应用的详细信息。
### 修改配置文件和环境变量
在 Sealos 中,你可以打开`应用管理`App Launchpad看到部署的 FastGPT可以打开`数据库`Database看到对应的数据库。
`应用管理`中,选中 FastGPT点击变更可以看到对应的环境变量和配置文件。
![](/imgs/fastgptonsealos1.png)
{{% alert icon="🤖 " context="success" %}}
在 Sealos 上FastGPT 一共运行了 1 个服务和 2 个数据库,如暂停和删除请注意数据库一同操作。(你可以白天启动,晚上暂停它们,省钱大法)
{{% /alert %}}
### 如何更新/升级 FastGPT ### 如何更新/升级 FastGPT
[升级脚本文档](https://doc.tryfastgpt.ai/docs/development/upgrading/)先看下文档,看下需要升级哪个版本。注意,不要跨版本升级!!!!! [升级脚本文档](https://doc.tryfastgpt.ai/docs/development/upgrading/)先看下文档,看下需要升级哪个版本。注意,不要跨版本升级!!!!!
例如目前是4.5 版本要升级到4.5.1就先把镜像版本改成v4.5.1,执行一下升级脚本,等待完成后再继续升级。如果目标版本不需要执行初始化,则可以跳过。 例如目前是4.5 版本要升级到4.5.1就先把镜像版本改成v4.5.1,执行一下升级脚本,等待完成后再继续升级。如果目标版本不需要执行初始化,则可以跳过。
@@ -148,8 +153,6 @@ SYSTEM_FAVICON 可以是一个网络地址
![](/imgs/onsealos8.png) ![](/imgs/onsealos8.png)
### 管理后台(已合并到plus)
### 商业版镜像配置文件 ### 商业版镜像配置文件
``` ```

View File

@@ -31,7 +31,6 @@ weight: 813
"usedInClassify": true, "usedInClassify": true,
"usedInExtractFields": true, "usedInExtractFields": true,
"usedInToolCall": true, "usedInToolCall": true,
"usedInQueryExtension": true,
"toolChoice": false, "toolChoice": false,
"functionCall": false, "functionCall": false,
"customCQPrompt": "", "customCQPrompt": "",
@@ -56,7 +55,6 @@ weight: 813
"usedInClassify": true, "usedInClassify": true,
"usedInExtractFields": true, "usedInExtractFields": true,
"usedInToolCall": true, "usedInToolCall": true,
"usedInQueryExtension": true,
"toolChoice": false, "toolChoice": false,
"functionCall": false, "functionCall": false,
"customCQPrompt": "", "customCQPrompt": "",

View File

@@ -1,5 +1,5 @@
--- ---
title: 'V4.8.18' title: 'V4.8.18(包含升级脚本)'
description: 'FastGPT V4.8.18 更新说明' description: 'FastGPT V4.8.18 更新说明'
icon: 'upgrade' icon: 'upgrade'
draft: false draft: false

View File

@@ -1,5 +1,5 @@
--- ---
title: 'V4.8.19(进行中)' title: 'V4.8.19(包含升级脚本)'
description: 'FastGPT V4.8.19 更新说明' description: 'FastGPT V4.8.19 更新说明'
icon: 'upgrade' icon: 'upgrade'
draft: false draft: false

View File

@@ -0,0 +1,50 @@
---
title: 'V4.8.20(包含升级脚本)'
description: 'FastGPT V4.8.20 更新说明'
icon: 'upgrade'
draft: false
toc: true
weight: 804
---
## 更新指南
### 1. 做好数据库备份
### 2. 更新环境变量
如果有很早版本用户,配置了`ONEAPI_URL`的,需要统一改成`OPENAI_BASE_URL`
### 3. 更新镜像:
- 更新 fastgpt 镜像 tag: v4.8.20-fix2
- 更新 fastgpt-pro 商业版镜像 tag: v4.8.20-fix2
- Sandbox 镜像无需更新
### 4. 运行升级脚本
从任意终端,发起 1 个 HTTP 请求。其中 {{rootkey}} 替换成环境变量里的 `rootkey`{{host}} 替换成**FastGPT 域名**。
```bash
curl --location --request POST 'https://{{host}}/api/admin/initv4820' \
--header 'rootkey: {{rootkey}}' \
--header 'Content-Type: application/json'
```
脚本会自动把原配置文件的模型加载到新版模型配置中。
## 完整更新内容
1. 新增 - 可视化模型参数配置,取代原配置文件配置模型。预设超过 100 个模型配置。同时支持所有类型模型的一键测试。(预计下个版本会完全支持在页面上配置渠道)。
2. 新增 - DeepSeek resoner 模型支持输出思考过程。
3. 新增 - 使用记录导出和仪表盘。
4. 新增 - markdown 语法扩展,支持音视频(代码块 audio 和 video
5. 新增 - 调整 max_tokens 计算逻辑。优先保证 max_tokens 为配置值,如超出最大上下文,则减少历史记录。例如:如果申请 8000 的 max_tokens则上下文长度会减少 8000。
6. 优化 - 问题优化增加上下文过滤,避免超出上下文。
7. 优化 - 页面组件抽离,减少页面组件路由。
8. 优化 - 全文检索,忽略大小写。
9. 优化 - 问答生成和增强索引改成流输出,避免部分模型超时。
10. 优化 - 自动给 assistant 空 content补充 null同时合并连续的 text assistant避免部分模型抛错。
11. 优化 - 调整图片 Host 取消上传时补充 FE_DOMAIN改成发送对话前补充避免替换域名后原图片无法正常使用。
12. 修复 - 部分场景成员列表无法触底加载。
13. 修复 - 工作流递归执行,部分条件下无法正常运行。

View File

@@ -0,0 +1,39 @@
---
title: 'V4.8.21'
description: 'FastGPT V4.8.21 更新说明'
icon: 'upgrade'
draft: false
toc: true
weight: 803
---
## 更新指南
### 1. 做好数据库备份
### 2. 更新镜像:
- 更新 fastgpt 镜像 tag: v4.8.21-fix
- 更新 fastgpt-pro 商业版镜像 tag: v4.8.21-fix
- Sandbox 镜像无需更新
## 完整更新内容
1. 新增 - 弃用/已删除的插件提示。
2. 新增 - 对话日志按来源分类、标题检索、导出功能。
3. 新增 - 全局变量支持拖拽排序。
4. 新增 - LLM 模型支持 top_p, response_format, json_schema 参数。
5. 新增 - Doubao1.5 模型预设。阿里 embedding3 预设。
6. 新增 - 向量模型支持归一化配置,以便适配未归一化的向量模型,例如 Doubao 的 embedding 模型。
6. 新增 - AI 对话节点,支持输出思考过程结果,可用于其他节点引用。
7. 优化 - 网站嵌入式聊天窗口,增加窗口位置适配。
8. 优化 - 模型未配置时错误提示。
9. 优化 - 适配非 Stream 模式思考输出。
10. 优化 - 增加 TTS voice 未配置时的空指针保护。
11. 优化 - Markdown 链接解析分割规则,改成严格匹配模式,牺牲兼容多种情况,减少误解析。
12. 优化 - 减少未登录用户的数据获取范围,提高系统隐私性。
13. 修复 - 简易模式,切换到其他非视觉模型时候,会强制关闭图片识别。
14. 修复 - o1,o3 模型,在测试时候字段映射未生效导致报错。
15. 修复 - 公众号对话空指针异常。
16. 修复 - 多个音频/视频文件展示异常。
17. 修复 - 分享链接鉴权报错后无限循环。

View File

@@ -0,0 +1,23 @@
---
title: 'V4.8.22(进行中)'
description: 'FastGPT V4.8.22 更新说明'
icon: 'upgrade'
draft: false
toc: true
weight: 802
---
## 完整更新内容
1. 新增 - AI 对话节点解析 <think></think> 标签内容,便于各类模型进行思考链输出。
2. 优化 - 模型未配置时提示,减少冲突提示。
3. 优化 - 使用记录代码。
4. 修复 - 思考内容未进入到输出 Tokens.
5. 修复 - 思考链流输出时,有时与正文顺序偏差。
6. 修复 - API 调用工作流,如果传递的图片不支持 Head 检测时,图片会被过滤。已增加该类错误检测,避免被错误过滤。
7. 修复 - 模板市场部分模板错误。
8. 修复 - 免登录窗口无法正常判断语言识别是否开启。
9. 修复 - 对话日志导出,未兼容 sub path。
10. 修复 - list 接口在联查 member 时,存在空指针可能性。
11. 修复 - 工作流基础节点无法升级。

View File

@@ -7,7 +7,7 @@ toc: true
weight: 234 weight: 234
--- ---
知识库搜索具体参数说明,以及内部逻辑请移步:[FastGPT知识库搜索方案](/docs/course/data_search/) 知识库搜索具体参数说明,以及内部逻辑请移步:[FastGPT知识库搜索方案](/docs/guide/knowledge_base/rag/)
## 特点 ## 特点
@@ -27,7 +27,7 @@ weight: 234
### 输入 - 搜索参数 ### 输入 - 搜索参数
[点击查看参数介绍](/docs/course/data_search/#搜索参数) [点击查看参数介绍](/docs/guide/knowledge_base/dataset_engine/#搜索参数)
### 输出 - 引用内容 ### 输出 - 引用内容

View File

@@ -20,7 +20,7 @@ weight: 502
![](/imgs/fastgpt-api1.jpg) ![](/imgs/fastgpt-api1.jpg)
{{% alert icon="🍅" context="success" %}} {{% alert icon="🍅" context="success" %}}
Tips: 安全起见,你可以设置一个额度或者过期时间,放置 key 被滥用。 Tips: 安全起见,你可以设置一个额度或者过期时间,防止 key 被滥用。
{{% /alert %}} {{% /alert %}}

View File

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

View File

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

View File

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

View File

@@ -23,7 +23,6 @@ data:
"usedInClassify": true, "usedInClassify": true,
"usedInExtractFields": true, "usedInExtractFields": true,
"usedInToolCall": true, "usedInToolCall": true,
"usedInQueryExtension": true,
"toolChoice": true, "toolChoice": true,
"functionCall": false, "functionCall": false,
"customCQPrompt": "", "customCQPrompt": "",
@@ -45,7 +44,6 @@ data:
"usedInClassify": true, "usedInClassify": true,
"usedInExtractFields": true, "usedInExtractFields": true,
"usedInToolCall": true, "usedInToolCall": true,
"usedInQueryExtension": true,
"toolChoice": true, "toolChoice": true,
"functionCall": false, "functionCall": false,
"customCQPrompt": "", "customCQPrompt": "",
@@ -67,7 +65,6 @@ data:
"usedInClassify": true, "usedInClassify": true,
"usedInExtractFields": true, "usedInExtractFields": true,
"usedInToolCall": true, "usedInToolCall": true,
"usedInQueryExtension": true,
"toolChoice": true, "toolChoice": true,
"functionCall": false, "functionCall": false,
"customCQPrompt": "", "customCQPrompt": "",
@@ -89,7 +86,6 @@ data:
"usedInClassify": false, "usedInClassify": false,
"usedInExtractFields": false, "usedInExtractFields": false,
"usedInToolCall": false, "usedInToolCall": false,
"usedInQueryExtension": false,
"toolChoice": true, "toolChoice": true,
"functionCall": false, "functionCall": false,
"customCQPrompt": "", "customCQPrompt": "",

View File

@@ -7,7 +7,7 @@
"format-code": "prettier --config \"./.prettierrc.js\" --write \"./**/src/**/*.{ts,tsx,scss}\"", "format-code": "prettier --config \"./.prettierrc.js\" --write \"./**/src/**/*.{ts,tsx,scss}\"",
"format-doc": "zhlint --dir ./docSite *.md --fix", "format-doc": "zhlint --dir ./docSite *.md --fix",
"gen:theme-typings": "chakra-cli tokens packages/web/styles/theme.ts --out node_modules/.pnpm/node_modules/@chakra-ui/styled-system/dist/theming.types.d.ts", "gen:theme-typings": "chakra-cli tokens packages/web/styles/theme.ts --out node_modules/.pnpm/node_modules/@chakra-ui/styled-system/dist/theming.types.d.ts",
"postinstall": "sh ./scripts/postinstall.sh", "postinstall": "pnpm gen:theme-typings",
"initIcon": "node ./scripts/icon/init.js", "initIcon": "node ./scripts/icon/init.js",
"previewIcon": "node ./scripts/icon/index.js", "previewIcon": "node ./scripts/icon/index.js",
"api:gen": "tsc ./scripts/openapi/index.ts && node ./scripts/openapi/index.js && npx @redocly/cli build-docs ./scripts/openapi/openapi.json -o ./projects/app/public/openapi/index.html", "api:gen": "tsc ./scripts/openapi/index.ts && node ./scripts/openapi/index.js && npx @redocly/cli build-docs ./scripts/openapi/openapi.json -o ./projects/app/public/openapi/index.html",

View File

@@ -2,12 +2,22 @@ import { ErrType } from '../errorCode';
import { i18nT } from '../../../../web/i18n/utils'; import { i18nT } from '../../../../web/i18n/utils';
/* team: 503000 */ /* team: 503000 */
export enum UserErrEnum { export enum UserErrEnum {
notUser = 'notUser',
userExist = 'userExist',
unAuthRole = 'unAuthRole', unAuthRole = 'unAuthRole',
account_psw_error = 'account_psw_error', account_psw_error = 'account_psw_error',
balanceNotEnough = 'balanceNotEnough', balanceNotEnough = 'balanceNotEnough',
unAuthSso = 'unAuthSso' unAuthSso = 'unAuthSso'
} }
const errList = [ const errList = [
{
statusText: UserErrEnum.notUser,
message: i18nT('common:code_error.account_not_found')
},
{
statusText: UserErrEnum.userExist,
message: i18nT('common:code_error.account_exist')
},
{ {
statusText: UserErrEnum.account_psw_error, statusText: UserErrEnum.account_psw_error,
message: i18nT('common:code_error.account_error') message: i18nT('common:code_error.account_error')

View File

@@ -16,8 +16,8 @@ export const bucketNameMap = {
} }
}; };
export const ReadFileBaseUrl = `${process.env.FE_DOMAIN || ''}${process.env.NEXT_PUBLIC_BASE_URL || ''}/api/common/file/read`; export const ReadFileBaseUrl = `${process.env.FILE_DOMAIN || process.env.FE_DOMAIN || ''}${process.env.NEXT_PUBLIC_BASE_URL || ''}/api/common/file/read`;
export const documentFileType = '.txt, .docx, .csv, .xlsx, .pdf, .md, .html, .pptx'; export const documentFileType = '.txt, .docx, .csv, .xlsx, .pdf, .md, .html, .pptx';
export const imageFileType = export const imageFileType =
'.jpg, .jpeg, .png, .gif, .bmp, .webp, .svg, .tiff, .tif, .ico, .heic, .heif, .avif'; '.jpg, .jpeg, .png, .gif, .bmp, .webp, .svg, .tiff, .tif, .ico, .heic, .heif, .avif, .raw, .cr2, .nef, .arw, .dng, .psd, .ai, .eps, .emf, .wmf, .jfif, .exif, .pgm, .ppm, .pbm, .jp2, .j2k, .jpf, .jpx, .jpm, .mj2, .xbm, .pcx';

View File

@@ -1,5 +1,5 @@
import { detect } from 'jschardet'; import { detect } from 'jschardet';
import { documentFileType, imageFileType } from './constants'; import { documentFileType } from './constants';
import { ChatFileTypeEnum } from '../../core/chat/constants'; import { ChatFileTypeEnum } from '../../core/chat/constants';
import { UserChatItemValueItemType } from '../../core/chat/type'; import { UserChatItemValueItemType } from '../../core/chat/type';
import * as fs from 'fs'; import * as fs from 'fs';
@@ -25,6 +25,7 @@ export const detectFileEncodingByPath = async (path: string) => {
const fd = await fs.promises.open(path, 'r'); const fd = await fs.promises.open(path, 'r');
try { try {
// Read file head // Read file head
// @ts-ignore
const { bytesRead } = await fd.read(buffer, 0, MAX_BYTES, 0); const { bytesRead } = await fd.read(buffer, 0, MAX_BYTES, 0);
const actualBuffer = buffer.slice(0, bytesRead); const actualBuffer = buffer.slice(0, bytesRead);
@@ -37,40 +38,49 @@ export const detectFileEncodingByPath = async (path: string) => {
// Url => user upload file type // Url => user upload file type
export const parseUrlToFileType = (url: string): UserChatItemValueItemType['file'] | undefined => { export const parseUrlToFileType = (url: string): UserChatItemValueItemType['file'] | undefined => {
if (typeof url !== 'string') return; if (typeof url !== 'string') return;
const parseUrl = new URL(url, 'https://locaohost:3000');
const filename = (() => { // Handle base64 image
// Check base64 image if (url.startsWith('data:')) {
if (url.startsWith('data:image/')) { const matches = url.match(/^data:([^;]+);base64,/);
const mime = url.split(',')[0].split(':')[1].split(';')[0]; if (!matches) return;
return `image.${mime.split('/')[1]}`;
}
// Old version file url: https://xxx.com/file/read?filename=xxx.pdf
const filenameQuery = parseUrl.searchParams.get('filename');
if (filenameQuery) return filenameQuery;
// Common file https://xxx.com/xxx.pdf?xxxx=xxx const mimeType = matches[1].toLowerCase();
const pathname = parseUrl.pathname; if (!mimeType.startsWith('image/')) return;
if (pathname) return pathname.split('/').pop();
})();
if (!filename) return; const extension = mimeType.split('/')[1];
const extension = filename.split('.').pop()?.toLowerCase() || '';
if (!extension) return;
if (documentFileType.includes(extension)) {
return { return {
type: ChatFileTypeEnum.file, type: ChatFileTypeEnum.image,
name: filename, name: `image.${extension}`,
url url
}; };
} }
if (imageFileType.includes(extension)) {
try {
const parseUrl = new URL(url, 'https://localhost:3000');
// Get filename from URL
const filename = parseUrl.searchParams.get('filename') || parseUrl.pathname.split('/').pop();
const extension = filename?.split('.').pop()?.toLowerCase() || '';
// If it's a document type, return as file, otherwise treat as image
if (extension && documentFileType.includes(extension)) {
return {
type: ChatFileTypeEnum.file,
name: filename || 'null',
url
};
}
// Default to image type for non-document files
return { return {
type: ChatFileTypeEnum.image, type: ChatFileTypeEnum.image,
name: filename, name: filename || 'null.png',
url
};
} catch (error) {
return {
type: ChatFileTypeEnum.image,
name: 'invalid.png',
url url
}; };
} }

View File

@@ -26,7 +26,7 @@ export const simpleText = (text = '') => {
}; };
export const valToStr = (val: any) => { export const valToStr = (val: any) => {
if (val === undefined) return 'undefined'; if (val === undefined) return '';
if (val === null) return 'null'; if (val === null) return 'null';
if (typeof val === 'object') return JSON.stringify(val); if (typeof val === 'object') return JSON.stringify(val);

View File

@@ -3,7 +3,7 @@ import type {
ChatModelItemType, ChatModelItemType,
FunctionModelItemType, FunctionModelItemType,
LLMModelItemType, LLMModelItemType,
VectorModelItemType, EmbeddingModelItemType,
AudioSpeechModels, AudioSpeechModels,
STTModelType, STTModelType,
ReRankModelItemType ReRankModelItemType
@@ -31,11 +31,13 @@ export type FastGPTConfigFileType = {
feConfigs: FastGPTFeConfigsType; feConfigs: FastGPTFeConfigsType;
systemEnv: SystemEnvType; systemEnv: SystemEnvType;
subPlans?: SubPlanType; subPlans?: SubPlanType;
llmModels: ChatModelItemType[];
vectorModels: VectorModelItemType[]; // Abandon
reRankModels: ReRankModelItemType[]; llmModels?: ChatModelItemType[];
audioSpeechModels: AudioSpeechModelType[]; vectorModels?: EmbeddingModelItemType[];
whisperModel: STTModelType; reRankModels?: ReRankModelItemType[];
audioSpeechModels?: TTSModelType[];
whisperModel?: STTModelType;
}; };
export type FastGPTFeConfigsType = { export type FastGPTFeConfigsType = {

View File

@@ -15,15 +15,13 @@ export enum LLMModelTypeEnum {
all = 'all', all = 'all',
classify = 'classify', classify = 'classify',
extractFields = 'extractFields', extractFields = 'extractFields',
toolCall = 'toolCall', toolCall = 'toolCall'
queryExtension = 'queryExtension'
} }
export const llmModelTypeFilterMap = { export const llmModelTypeFilterMap = {
[LLMModelTypeEnum.all]: 'model', [LLMModelTypeEnum.all]: 'model',
[LLMModelTypeEnum.classify]: 'usedInClassify', [LLMModelTypeEnum.classify]: 'usedInClassify',
[LLMModelTypeEnum.extractFields]: 'usedInExtractFields', [LLMModelTypeEnum.extractFields]: 'usedInExtractFields',
[LLMModelTypeEnum.toolCall]: 'usedInToolCall', [LLMModelTypeEnum.toolCall]: 'usedInToolCall'
[LLMModelTypeEnum.queryExtension]: 'usedInQueryExtension'
}; };
export enum EmbeddingTypeEnm { export enum EmbeddingTypeEnm {

View File

@@ -1,3 +1,4 @@
import { ModelTypeEnum } from './model';
import type { ModelProviderIdType } from './provider'; import type { ModelProviderIdType } from './provider';
type PriceType = { type PriceType = {
@@ -7,68 +8,80 @@ type PriceType = {
inputPrice?: number; // 1k tokens=n points inputPrice?: number; // 1k tokens=n points
outputPrice?: number; // 1k tokens=n points outputPrice?: number; // 1k tokens=n points
}; };
export type LLMModelItemType = PriceType & { type BaseModelItemType = {
provider: ModelProviderIdType; provider: ModelProviderIdType;
model: string; model: string;
name: string; name: string;
avatar?: string; // model icon, from provider avatar?: string; // model icon, from provider
maxContext: number;
maxResponse: number;
quoteMaxToken: number;
maxTemperature: number;
censor?: boolean; isActive?: boolean;
vision?: boolean; isCustom?: boolean;
isDefault?: boolean;
// diff function model // If has requestUrl, it will request the model directly
datasetProcess?: boolean; // dataset requestUrl?: string;
usedInClassify?: boolean; // classify requestAuth?: string;
usedInExtractFields?: boolean; // extract fields
usedInToolCall?: boolean; // tool call
usedInQueryExtension?: boolean; // query extension
functionCall: boolean;
toolChoice: boolean;
customCQPrompt: string;
customExtractPrompt: string;
defaultSystemChatPrompt?: string;
defaultConfig?: Record<string, any>;
fieldMap?: Record<string, string>;
}; };
export type VectorModelItemType = PriceType & { export type LLMModelItemType = PriceType &
provider: ModelProviderIdType; BaseModelItemType & {
model: string; // model name type: ModelTypeEnum.llm;
name: string; // show name // Model params
avatar?: string; maxContext: number;
defaultToken: number; // split text default token maxResponse: number;
maxToken: number; // model max token quoteMaxToken: number;
weight: number; // training weight maxTemperature?: number;
hidden?: boolean; // Disallow creation
defaultConfig?: Record<string, any>; // post request config
dbConfig?: Record<string, any>; // Custom parameters for storage
queryConfig?: Record<string, any>; // Custom parameters for query
};
export type ReRankModelItemType = PriceType & { showTopP?: boolean;
provider: ModelProviderIdType; responseFormatList?: string[];
model: string; showStopSign?: boolean;
name: string;
requestUrl: string;
requestAuth: string;
};
export type AudioSpeechModelType = PriceType & { censor?: boolean;
provider: ModelProviderIdType; vision?: boolean;
model: string; reasoning?: boolean;
name: string;
voices: { label: string; value: string; bufferId: string }[];
};
export type STTModelType = PriceType & { // diff function model
provider: ModelProviderIdType; datasetProcess?: boolean; // dataset
model: string; usedInClassify?: boolean; // classify
name: string; usedInExtractFields?: boolean; // extract fields
}; usedInToolCall?: boolean; // tool call
functionCall: boolean;
toolChoice: boolean;
customCQPrompt: string;
customExtractPrompt: string;
defaultSystemChatPrompt?: string;
defaultConfig?: Record<string, any>;
fieldMap?: Record<string, string>;
};
export type EmbeddingModelItemType = PriceType &
BaseModelItemType & {
type: ModelTypeEnum.embedding;
defaultToken: number; // split text default token
maxToken: number; // model max token
weight: number; // training weight
hidden?: boolean; // Disallow creation
normalization?: boolean; // normalization processing
defaultConfig?: Record<string, any>; // post request config
dbConfig?: Record<string, any>; // Custom parameters for storage
queryConfig?: Record<string, any>; // Custom parameters for query
};
export type ReRankModelItemType = PriceType &
BaseModelItemType & {
type: ModelTypeEnum.rerank;
};
export type TTSModelType = PriceType &
BaseModelItemType & {
type: ModelTypeEnum.tts;
voices: { label: string; value: string }[];
};
export type STTModelType = PriceType &
BaseModelItemType & {
type: ModelTypeEnum.stt;
};

View File

@@ -1,9 +1,18 @@
import { i18nT } from '../../../web/i18n/utils'; import { i18nT } from '../../../web/i18n/utils';
import type { LLMModelItemType, STTModelType, VectorModelItemType } from './model.d'; import type { LLMModelItemType, STTModelType, EmbeddingModelItemType } from './model.d';
import { getModelProvider, ModelProviderIdType } from './provider'; import { getModelProvider, ModelProviderIdType } from './provider';
export enum ModelTypeEnum {
llm = 'llm',
embedding = 'embedding',
tts = 'tts',
stt = 'stt',
rerank = 'rerank'
}
export const defaultQAModels: LLMModelItemType[] = [ export const defaultQAModels: LLMModelItemType[] = [
{ {
type: ModelTypeEnum.llm,
provider: 'OpenAI', provider: 'OpenAI',
model: 'gpt-4o-mini', model: 'gpt-4o-mini',
name: 'gpt-4o-mini', name: 'gpt-4o-mini',
@@ -24,8 +33,9 @@ export const defaultQAModels: LLMModelItemType[] = [
} }
]; ];
export const defaultVectorModels: VectorModelItemType[] = [ export const defaultVectorModels: EmbeddingModelItemType[] = [
{ {
type: ModelTypeEnum.embedding,
provider: 'OpenAI', provider: 'OpenAI',
model: 'text-embedding-3-small', model: 'text-embedding-3-small',
name: 'Embedding-2', name: 'Embedding-2',
@@ -36,18 +46,24 @@ export const defaultVectorModels: VectorModelItemType[] = [
} }
]; ];
export const defaultWhisperModel: STTModelType = { export const defaultSTTModels: STTModelType[] = [
provider: 'OpenAI', {
model: 'whisper-1', type: ModelTypeEnum.stt,
name: 'whisper-1', provider: 'OpenAI',
charsPointsPrice: 0 model: 'whisper-1',
}; name: 'whisper-1',
charsPointsPrice: 0
}
];
export const getModelFromList = ( export const getModelFromList = (
modelList: { provider: ModelProviderIdType; name: string; model: string }[], modelList: { provider: ModelProviderIdType; name: string; model: string }[],
model: string model: string
) => { ) => {
const modelData = modelList.find((item) => item.model === model) ?? modelList[0]; const modelData = modelList.find((item) => item.model === model) ?? modelList[0];
if (!modelData) {
throw new Error('No Key model is configured');
}
const provider = getModelProvider(modelData.provider); const provider = getModelProvider(modelData.provider);
return { return {
...modelData, ...modelData,
@@ -55,15 +71,10 @@ export const getModelFromList = (
}; };
}; };
export enum ModelTypeEnum {
chat = 'chat',
embedding = 'embedding',
tts = 'tts',
stt = 'stt'
}
export const modelTypeList = [ export const modelTypeList = [
{ label: i18nT('common:model.type.chat'), value: ModelTypeEnum.chat }, { label: i18nT('common:model.type.chat'), value: ModelTypeEnum.llm },
{ label: i18nT('common:model.type.embedding'), value: ModelTypeEnum.embedding }, { label: i18nT('common:model.type.embedding'), value: ModelTypeEnum.embedding },
{ label: i18nT('common:model.type.tts'), value: ModelTypeEnum.tts }, { label: i18nT('common:model.type.tts'), value: ModelTypeEnum.tts },
{ label: i18nT('common:model.type.stt'), value: ModelTypeEnum.stt } { label: i18nT('common:model.type.stt'), value: ModelTypeEnum.stt },
{ label: i18nT('common:model.type.reRank'), value: ModelTypeEnum.rerank }
]; ];

View File

@@ -7,11 +7,12 @@ export type ModelProviderIdType =
| 'Meta' | 'Meta'
| 'MistralAI' | 'MistralAI'
| 'Groq' | 'Groq'
| 'Grok'
| 'AliCloud' | 'AliCloud'
| 'Qwen' | 'Qwen'
| 'Doubao' | 'Doubao'
| 'ChatGLM'
| 'DeepSeek' | 'DeepSeek'
| 'ChatGLM'
| 'Ernie' | 'Ernie'
| 'Moonshot' | 'Moonshot'
| 'MiniMax' | 'MiniMax'
@@ -20,6 +21,8 @@ export type ModelProviderIdType =
| 'Baichuan' | 'Baichuan'
| 'StepFun' | 'StepFun'
| 'Yi' | 'Yi'
| 'Siliconflow'
| 'PPIO'
| 'Ollama' | 'Ollama'
| 'BAAI' | 'BAAI'
| 'FishAudio' | 'FishAudio'
@@ -29,7 +32,7 @@ export type ModelProviderIdType =
export type ModelProviderType = { export type ModelProviderType = {
id: ModelProviderIdType; id: ModelProviderIdType;
name: string; name: any;
avatar: string; avatar: string;
}; };
@@ -59,6 +62,11 @@ export const ModelProviderList: ModelProviderType[] = [
name: 'MistralAI', name: 'MistralAI',
avatar: 'model/mistral' avatar: 'model/mistral'
}, },
{
id: 'Grok',
name: 'Grok',
avatar: 'model/grok'
},
{ {
id: 'Groq', id: 'Groq',
name: 'Groq', name: 'Groq',
@@ -155,6 +163,16 @@ export const ModelProviderList: ModelProviderType[] = [
name: i18nT('common:model_moka'), name: i18nT('common:model_moka'),
avatar: 'model/moka' avatar: 'model/moka'
}, },
{
id: 'Siliconflow',
name: i18nT('common:model_siliconflow'),
avatar: 'model/siliconflow'
},
{
id: 'PPIO',
name: i18nT('common:model_ppio'),
avatar: 'model/ppio'
},
{ {
id: 'Other', id: 'Other',
name: i18nT('common:model_other'), name: i18nT('common:model_other'),
@@ -165,6 +183,7 @@ export const ModelProviderMap = Object.fromEntries(
ModelProviderList.map((item, index) => [item.id, { ...item, order: index }]) ModelProviderList.map((item, index) => [item.id, { ...item, order: index }])
); );
export const getModelProvider = (provider: ModelProviderIdType) => { export const getModelProvider = (provider?: ModelProviderIdType) => {
if (!provider) return ModelProviderMap.Other;
return ModelProviderMap[provider] ?? ModelProviderMap.Other; return ModelProviderMap[provider] ?? ModelProviderMap.Other;
}; };

View File

@@ -1,14 +1,12 @@
import openai from 'openai'; import openai from 'openai';
import type { import type {
ChatCompletionMessageToolCall, ChatCompletionMessageToolCall,
ChatCompletionChunk,
ChatCompletionMessageParam as SdkChatCompletionMessageParam, ChatCompletionMessageParam as SdkChatCompletionMessageParam,
ChatCompletionToolMessageParam, ChatCompletionToolMessageParam,
ChatCompletionContentPart as SdkChatCompletionContentPart, ChatCompletionContentPart as SdkChatCompletionContentPart,
ChatCompletionUserMessageParam as SdkChatCompletionUserMessageParam, ChatCompletionUserMessageParam as SdkChatCompletionUserMessageParam,
ChatCompletionToolMessageParam as SdkChatCompletionToolMessageParam, ChatCompletionToolMessageParam as SdkChatCompletionToolMessageParam,
ChatCompletionAssistantMessageParam as SdkChatCompletionAssistantMessageParam, ChatCompletionAssistantMessageParam as SdkChatCompletionAssistantMessageParam
ChatCompletionContentPartText
} from 'openai/resources'; } from 'openai/resources';
import { ChatMessageTypeEnum } from './constants'; import { ChatMessageTypeEnum } from './constants';
import { WorkflowInteractiveResponseType } from '../workflow/template/system/interactive/type'; import { WorkflowInteractiveResponseType } from '../workflow/template/system/interactive/type';
@@ -48,6 +46,7 @@ export type ChatCompletionMessageParam = (
| CustomChatCompletionToolMessageParam | CustomChatCompletionToolMessageParam
| CustomChatCompletionAssistantMessageParam | CustomChatCompletionAssistantMessageParam
) & { ) & {
reasoning_text?: string;
dataId?: string; dataId?: string;
hideInUI?: boolean; hideInUI?: boolean;
}; };
@@ -71,7 +70,8 @@ export type ChatCompletionMessageFunctionCall =
}; };
// Stream response // Stream response
export type StreamChatType = Stream<ChatCompletionChunk>; export type StreamChatType = Stream<openai.Chat.Completions.ChatCompletionChunk>;
export type UnStreamChatType = openai.Chat.Completions.ChatCompletion;
export default openai; export default openai;
export * from 'openai'; export * from 'openai';

View File

@@ -74,12 +74,17 @@ export type AppDetailType = AppSchema & {
export type AppSimpleEditFormType = { export type AppSimpleEditFormType = {
// templateId: string; // templateId: string;
aiSettings: { aiSettings: {
model: string; [NodeInputKeyEnum.aiModel]: string;
systemPrompt?: string | undefined; [NodeInputKeyEnum.aiSystemPrompt]?: string | undefined;
temperature?: number; [NodeInputKeyEnum.aiChatTemperature]?: number;
maxToken?: number; [NodeInputKeyEnum.aiChatMaxToken]?: number;
isResponseAnswerText: boolean; [NodeInputKeyEnum.aiChatIsResponseText]: boolean;
maxHistories: number; maxHistories: number;
[NodeInputKeyEnum.aiChatReasoning]?: boolean; // Is open reasoning mode
[NodeInputKeyEnum.aiChatTopP]?: number;
[NodeInputKeyEnum.aiChatStopSign]?: string;
[NodeInputKeyEnum.aiChatResponseFormat]?: string;
[NodeInputKeyEnum.aiChatJsonSchema]?: string;
}; };
dataset: { dataset: {
datasets: SelectedDatasetType; datasets: SelectedDatasetType;
@@ -117,6 +122,11 @@ export type SettingAIDataType = {
isResponseAnswerText?: boolean; isResponseAnswerText?: boolean;
maxHistories?: number; maxHistories?: number;
[NodeInputKeyEnum.aiChatVision]?: boolean; // Is open vision mode [NodeInputKeyEnum.aiChatVision]?: boolean; // Is open vision mode
[NodeInputKeyEnum.aiChatReasoning]?: boolean; // Is open reasoning mode
[NodeInputKeyEnum.aiChatTopP]?: number;
[NodeInputKeyEnum.aiChatStopSign]?: string;
[NodeInputKeyEnum.aiChatResponseFormat]?: string;
[NodeInputKeyEnum.aiChatJsonSchema]?: string;
}; };
// variable // variable

View File

@@ -7,6 +7,8 @@ import { StoreNodeItemType } from '../workflow/type/node';
import { DatasetSearchModeEnum } from '../dataset/constants'; import { DatasetSearchModeEnum } from '../dataset/constants';
import { WorkflowTemplateBasicType } from '../workflow/type'; import { WorkflowTemplateBasicType } from '../workflow/type';
import { AppTypeEnum } from './constants'; import { AppTypeEnum } from './constants';
import { AppErrEnum } from '../../common/error/code/app';
import { PluginErrEnum } from '../../common/error/code/plugin';
export const getDefaultAppForm = (): AppSimpleEditFormType => { export const getDefaultAppForm = (): AppSimpleEditFormType => {
return { return {
@@ -16,7 +18,8 @@ export const getDefaultAppForm = (): AppSimpleEditFormType => {
temperature: 0, temperature: 0,
isResponseAnswerText: true, isResponseAnswerText: true,
maxHistories: 6, maxHistories: 6,
maxToken: 4000 maxToken: 4000,
aiChatReasoning: true
}, },
dataset: { dataset: {
datasets: [], datasets: [],
@@ -116,7 +119,8 @@ export const appWorkflow2Form = ({
version: node.version, version: node.version,
inputs: node.inputs, inputs: node.inputs,
outputs: node.outputs, outputs: node.outputs,
templateType: FlowNodeTemplateTypeEnum.other templateType: FlowNodeTemplateTypeEnum.other,
pluginData: node.pluginData
}); });
} else if (node.flowNodeType === FlowNodeTypeEnum.systemConfig) { } else if (node.flowNodeType === FlowNodeTypeEnum.systemConfig) {
defaultAppForm.chatConfig = getAppChatConfig({ defaultAppForm.chatConfig = getAppChatConfig({
@@ -146,3 +150,18 @@ export const getAppType = (config?: WorkflowTemplateBasicType | AppSimpleEditFor
} }
return ''; return '';
}; };
export const checkAppUnExistError = (error?: string) => {
const unExistError: Array<string> = [
AppErrEnum.unAuthApp,
AppErrEnum.unExist,
PluginErrEnum.unAuth,
PluginErrEnum.unExist
];
if (!!error && unExistError.includes(error)) {
return error;
} else {
return undefined;
}
};

View File

@@ -46,7 +46,16 @@ export const chats2GPTMessages = ({
messages.forEach((item) => { messages.forEach((item) => {
const dataId = reserveId ? item.dataId : undefined; const dataId = reserveId ? item.dataId : undefined;
if (item.obj === ChatRoleEnum.Human) { if (item.obj === ChatRoleEnum.System) {
const content = item.value?.[0]?.text?.content;
if (content) {
results.push({
dataId,
role: ChatCompletionRequestMessageRoleEnum.System,
content
});
}
} else if (item.obj === ChatRoleEnum.Human) {
const value = item.value const value = item.value
.map((item) => { .map((item) => {
if (item.type === ChatItemValueTypeEnum.text) { if (item.type === ChatItemValueTypeEnum.text) {
@@ -80,15 +89,6 @@ export const chats2GPTMessages = ({
role: ChatCompletionRequestMessageRoleEnum.User, role: ChatCompletionRequestMessageRoleEnum.User,
content: simpleUserContentPart(value) content: simpleUserContentPart(value)
}); });
} else if (item.obj === ChatRoleEnum.System) {
const content = item.value?.[0]?.text?.content;
if (content) {
results.push({
dataId,
role: ChatCompletionRequestMessageRoleEnum.System,
content
});
}
} else { } else {
const aiResults: ChatCompletionMessageParam[] = []; const aiResults: ChatCompletionMessageParam[] = [];
@@ -349,7 +349,7 @@ export const chatValue2RuntimePrompt = (value: ChatItemValueItemType[]): Runtime
}; };
value.forEach((item) => { value.forEach((item) => {
if (item.type === 'file' && item.file) { if (item.type === 'file' && item.file) {
prompt.files?.push(item.file); prompt.files.push(item.file);
} else if (item.text) { } else if (item.text) {
prompt.text += item.text.content; prompt.text += item.text.content;
} }

View File

@@ -25,7 +25,8 @@ export enum ChatItemValueTypeEnum {
text = 'text', text = 'text',
file = 'file', file = 'file',
tool = 'tool', tool = 'tool',
interactive = 'interactive' interactive = 'interactive',
reasoning = 'reasoning'
} }
export enum ChatSourceEnum { export enum ChatSourceEnum {
@@ -75,5 +76,3 @@ export enum ChatStatusEnum {
running = 'running', running = 'running',
finish = 'finish' finish = 'finish'
} }
export const MARKDOWN_QUOTE_SIGN = 'QUOTE SIGN';

View File

@@ -70,14 +70,23 @@ export type SystemChatItemType = {
obj: ChatRoleEnum.System; obj: ChatRoleEnum.System;
value: SystemChatItemValueItemType[]; value: SystemChatItemValueItemType[];
}; };
export type AIChatItemValueItemType = { export type AIChatItemValueItemType = {
type: ChatItemValueTypeEnum.text | ChatItemValueTypeEnum.tool | ChatItemValueTypeEnum.interactive; type:
| ChatItemValueTypeEnum.text
| ChatItemValueTypeEnum.reasoning
| ChatItemValueTypeEnum.tool
| ChatItemValueTypeEnum.interactive;
text?: { text?: {
content: string; content: string;
}; };
reasoning?: {
content: string;
};
tools?: ToolModuleResponseItemType[]; tools?: ToolModuleResponseItemType[];
interactive?: WorkflowInteractiveResponseType; interactive?: WorkflowInteractiveResponseType;
}; };
export type AIChatItemType = { export type AIChatItemType = {
obj: ChatRoleEnum.AI; obj: ChatRoleEnum.AI;
value: AIChatItemValueItemType[]; value: AIChatItemValueItemType[];

View File

@@ -1,4 +1,4 @@
import type { LLMModelItemType, VectorModelItemType } from '../../core/ai/model.d'; import type { LLMModelItemType, EmbeddingModelItemType } from '../../core/ai/model.d';
import { PermissionTypeEnum } from '../../support/permission/constant'; import { PermissionTypeEnum } from '../../support/permission/constant';
import { PushDatasetDataChunkProps } from './api'; import { PushDatasetDataChunkProps } from './api';
import { import {
@@ -152,7 +152,7 @@ export type DatasetSimpleItemType = {
_id: string; _id: string;
avatar: string; avatar: string;
name: string; name: string;
vectorModel: VectorModelItemType; vectorModel: EmbeddingModelItemType;
}; };
export type DatasetListItemType = { export type DatasetListItemType = {
_id: string; _id: string;
@@ -163,14 +163,14 @@ export type DatasetListItemType = {
intro: string; intro: string;
type: `${DatasetTypeEnum}`; type: `${DatasetTypeEnum}`;
permission: DatasetPermission; permission: DatasetPermission;
vectorModel: VectorModelItemType; vectorModel: EmbeddingModelItemType;
inheritPermission: boolean; inheritPermission: boolean;
private?: boolean; private?: boolean;
sourceMember?: SourceMemberType; sourceMember?: SourceMemberType;
}; };
export type DatasetItemType = Omit<DatasetSchemaType, 'vectorModel' | 'agentModel'> & { export type DatasetItemType = Omit<DatasetSchemaType, 'vectorModel' | 'agentModel'> & {
vectorModel: VectorModelItemType; vectorModel: EmbeddingModelItemType;
agentModel: LLMModelItemType; agentModel: LLMModelItemType;
permission: DatasetPermission; permission: DatasetPermission;
}; };

View File

@@ -1,4 +1,4 @@
import { VectorModelItemType } from '../ai/model.d'; import { EmbeddingModelItemType } from '../ai/model.d';
import { NodeInputKeyEnum } from './constants'; import { NodeInputKeyEnum } from './constants';
export type SelectedDatasetType = { datasetId: string }[]; export type SelectedDatasetType = { datasetId: string }[];

View File

@@ -33,8 +33,10 @@ export enum WorkflowIOValueTypeEnum {
dynamic = 'dynamic', dynamic = 'dynamic',
// plugin special type // plugin special type
selectApp = 'selectApp', selectDataset = 'selectDataset',
selectDataset = 'selectDataset'
// abandon
selectApp = 'selectApp'
} }
export const toolValueTypeList = [ export const toolValueTypeList = [
@@ -141,6 +143,11 @@ export enum NodeInputKeyEnum {
aiChatDatasetQuote = 'quoteQA', aiChatDatasetQuote = 'quoteQA',
aiChatVision = 'aiChatVision', aiChatVision = 'aiChatVision',
stringQuoteText = 'stringQuoteText', stringQuoteText = 'stringQuoteText',
aiChatReasoning = 'aiChatReasoning',
aiChatTopP = 'aiChatTopP',
aiChatStopSign = 'aiChatStopSign',
aiChatResponseFormat = 'aiChatResponseFormat',
aiChatJsonSchema = 'aiChatJsonSchema',
// dataset // dataset
datasetSelectList = 'datasets', datasetSelectList = 'datasets',
@@ -153,6 +160,10 @@ export enum NodeInputKeyEnum {
datasetSearchExtensionBg = 'datasetSearchExtensionBg', datasetSearchExtensionBg = 'datasetSearchExtensionBg',
collectionFilterMatch = 'collectionFilterMatch', collectionFilterMatch = 'collectionFilterMatch',
authTmbId = 'authTmbId', authTmbId = 'authTmbId',
datasetDeepSearch = 'datasetDeepSearch',
datasetDeepSearchModel = 'datasetDeepSearchModel',
datasetDeepSearchMaxTimes = 'datasetDeepSearchMaxTimes',
datasetDeepSearchBg = 'datasetDeepSearchBg',
// concat dataset // concat dataset
datasetQuoteList = 'system_datasetQuoteList', datasetQuoteList = 'system_datasetQuoteList',
@@ -220,7 +231,8 @@ export enum NodeOutputKeyEnum {
// common // common
userChatInput = 'userChatInput', userChatInput = 'userChatInput',
history = 'history', history = 'history',
answerText = 'answerText', // module answer. the value will be show and save to history answerText = 'answerText', // node answer. the value will be show and save to history
reasoningText = 'reasoningText', // node reasoning. the value will be show but not save to history
success = 'success', success = 'success',
failed = 'failed', failed = 'failed',
error = 'error', error = 'error',

View File

@@ -140,7 +140,14 @@ export enum FlowNodeTypeEnum {
} }
// node IO value type // node IO value type
export const FlowValueTypeMap = { export const FlowValueTypeMap: Record<
WorkflowIOValueTypeEnum,
{
label: string;
value: WorkflowIOValueTypeEnum;
abandon?: boolean;
}
> = {
[WorkflowIOValueTypeEnum.string]: { [WorkflowIOValueTypeEnum.string]: {
label: 'String', label: 'String',
value: WorkflowIOValueTypeEnum.string value: WorkflowIOValueTypeEnum.string
@@ -189,10 +196,6 @@ export const FlowValueTypeMap = {
label: i18nT('common:core.workflow.Dataset quote'), label: i18nT('common:core.workflow.Dataset quote'),
value: WorkflowIOValueTypeEnum.datasetQuote value: WorkflowIOValueTypeEnum.datasetQuote
}, },
[WorkflowIOValueTypeEnum.selectApp]: {
label: i18nT('common:plugin.App'),
value: WorkflowIOValueTypeEnum.selectApp
},
[WorkflowIOValueTypeEnum.selectDataset]: { [WorkflowIOValueTypeEnum.selectDataset]: {
label: i18nT('common:core.chat.Select dataset'), label: i18nT('common:core.chat.Select dataset'),
value: WorkflowIOValueTypeEnum.selectDataset value: WorkflowIOValueTypeEnum.selectDataset
@@ -200,6 +203,11 @@ export const FlowValueTypeMap = {
[WorkflowIOValueTypeEnum.dynamic]: { [WorkflowIOValueTypeEnum.dynamic]: {
label: i18nT('common:core.workflow.dynamic_input'), label: i18nT('common:core.workflow.dynamic_input'),
value: WorkflowIOValueTypeEnum.dynamic value: WorkflowIOValueTypeEnum.dynamic
},
[WorkflowIOValueTypeEnum.selectApp]: {
label: 'selectApp',
value: WorkflowIOValueTypeEnum.selectApp,
abandon: true
} }
}; };
@@ -219,3 +227,6 @@ export const datasetQuoteValueDesc = `{
q: string; q: string;
a: string a: string
}[]`; }[]`;
export const datasetSelectValueDesc = `{
datasetId: string;
}[]`;

View File

@@ -123,6 +123,7 @@ export type DispatchNodeResponseType = {
temperature?: number; temperature?: number;
maxToken?: number; maxToken?: number;
quoteList?: SearchDataResponseItemType[]; quoteList?: SearchDataResponseItemType[];
reasoningText?: string;
historyPreview?: { historyPreview?: {
obj: `${ChatRoleEnum}`; obj: `${ChatRoleEnum}`;
value: string; value: string;
@@ -133,9 +134,17 @@ export type DispatchNodeResponseType = {
limit?: number; limit?: number;
searchMode?: `${DatasetSearchModeEnum}`; searchMode?: `${DatasetSearchModeEnum}`;
searchUsingReRank?: boolean; searchUsingReRank?: boolean;
extensionModel?: string; queryExtensionResult?: {
extensionResult?: string; model: string;
extensionTokens?: number; inputTokens: number;
outputTokens: number;
query: string;
};
deepSearchResult?: {
model: string;
inputTokens: number;
outputTokens: number;
};
// dataset concat // dataset concat
concatLength?: number; concatLength?: number;
@@ -198,6 +207,11 @@ export type DispatchNodeResponseType = {
// tool params // tool params
toolParamsResult?: Record<string, any>; toolParamsResult?: Record<string, any>;
// abandon
extensionModel?: string;
extensionResult?: string;
extensionTokens?: number;
}; };
export type DispatchNodeResultType<T = {}> = { export type DispatchNodeResultType<T = {}> = {
@@ -220,6 +234,11 @@ export type AIChatNodeProps = {
[NodeInputKeyEnum.aiChatMaxToken]?: number; [NodeInputKeyEnum.aiChatMaxToken]?: number;
[NodeInputKeyEnum.aiChatIsResponseText]: boolean; [NodeInputKeyEnum.aiChatIsResponseText]: boolean;
[NodeInputKeyEnum.aiChatVision]?: boolean; [NodeInputKeyEnum.aiChatVision]?: boolean;
[NodeInputKeyEnum.aiChatReasoning]?: boolean;
[NodeInputKeyEnum.aiChatTopP]?: number;
[NodeInputKeyEnum.aiChatStopSign]?: string;
[NodeInputKeyEnum.aiChatResponseFormat]?: string;
[NodeInputKeyEnum.aiChatJsonSchema]?: string;
[NodeInputKeyEnum.aiChatQuoteRole]?: AiChatQuoteRoleType; [NodeInputKeyEnum.aiChatQuoteRole]?: AiChatQuoteRoleType;
[NodeInputKeyEnum.aiChatQuoteTemplate]?: string; [NodeInputKeyEnum.aiChatQuoteTemplate]?: string;

View File

@@ -10,6 +10,7 @@ import { FlowNodeOutputItemType, ReferenceValueType } from '../type/io';
import { ChatItemType, NodeOutputItemType } from '../../../core/chat/type'; import { ChatItemType, NodeOutputItemType } from '../../../core/chat/type';
import { ChatItemValueTypeEnum, ChatRoleEnum } from '../../../core/chat/constants'; import { ChatItemValueTypeEnum, ChatRoleEnum } from '../../../core/chat/constants';
import { replaceVariable, valToStr } from '../../../common/string/tools'; import { replaceVariable, valToStr } from '../../../common/string/tools';
import { ChatCompletionChunk } from 'openai/resources';
export const getMaxHistoryLimitFromNodes = (nodes: StoreNodeItemType[]): number => { export const getMaxHistoryLimitFromNodes = (nodes: StoreNodeItemType[]): number => {
let limit = 10; let limit = 10;
@@ -176,6 +177,7 @@ export const checkNodeRunStatus = ({
} }
visited.add(edge.source); visited.add(edge.source);
// 递归检测后面的 edge如果有其中一个成环则返回 true
const nextEdges = allEdges.filter((item) => item.target === edge.source); const nextEdges = allEdges.filter((item) => item.target === edge.source);
return nextEdges.some((nextEdge) => checkIsCircular(nextEdge, new Set(visited))); return nextEdges.some((nextEdge) => checkIsCircular(nextEdge, new Set(visited)));
}; };
@@ -207,7 +209,23 @@ export const checkNodeRunStatus = ({
currentNode: node currentNode: node
}); });
// check skip(其中一组边,全 skip // check active(其中一组边,至少有一个 active且没有 waiting 即可运行
if (
commonEdges.length > 0 &&
commonEdges.some((item) => item.status === 'active') &&
commonEdges.every((item) => item.status !== 'waiting')
) {
return 'run';
}
if (
recursiveEdges.length > 0 &&
recursiveEdges.some((item) => item.status === 'active') &&
recursiveEdges.every((item) => item.status !== 'waiting')
) {
return 'run';
}
// check skip其中一组边全是 skiped 则跳过运行)
if (commonEdges.length > 0 && commonEdges.every((item) => item.status === 'skipped')) { if (commonEdges.length > 0 && commonEdges.every((item) => item.status === 'skipped')) {
return 'skip'; return 'skip';
} }
@@ -215,14 +233,6 @@ export const checkNodeRunStatus = ({
return 'skip'; return 'skip';
} }
// check active有一类边不全是 wait 即可运行)
if (commonEdges.length > 0 && commonEdges.every((item) => item.status !== 'waiting')) {
return 'run';
}
if (recursiveEdges.length > 0 && recursiveEdges.every((item) => item.status !== 'waiting')) {
return 'run';
}
return 'wait'; return 'wait';
}; };
@@ -283,13 +293,12 @@ export const getReferenceVariableValue = ({
export const formatVariableValByType = (val: any, valueType?: WorkflowIOValueTypeEnum) => { export const formatVariableValByType = (val: any, valueType?: WorkflowIOValueTypeEnum) => {
if (!valueType) return val; if (!valueType) return val;
if (val === undefined || val === null) return;
// Value type check, If valueType invalid, return undefined // Value type check, If valueType invalid, return undefined
if (valueType.startsWith('array') && !Array.isArray(val)) return undefined; if (valueType.startsWith('array') && !Array.isArray(val)) return undefined;
if (valueType === WorkflowIOValueTypeEnum.boolean) return Boolean(val); if (valueType === WorkflowIOValueTypeEnum.boolean) return Boolean(val);
if (valueType === WorkflowIOValueTypeEnum.number) return Number(val); if (valueType === WorkflowIOValueTypeEnum.number) return Number(val);
if (valueType === WorkflowIOValueTypeEnum.string) { if (valueType === WorkflowIOValueTypeEnum.string) {
if (val === undefined) return 'undefined';
if (val === null) return 'null';
return typeof val === 'object' ? JSON.stringify(val) : String(val); return typeof val === 'object' ? JSON.stringify(val) : String(val);
} }
if ( if (
@@ -355,12 +364,14 @@ export function replaceEditorVariable({
export const textAdaptGptResponse = ({ export const textAdaptGptResponse = ({
text, text,
reasoning_content,
model = '', model = '',
finish_reason = null, finish_reason = null,
extraData = {} extraData = {}
}: { }: {
model?: string; model?: string;
text: string | null; text?: string | null;
reasoning_content?: string | null;
finish_reason?: null | 'stop'; finish_reason?: null | 'stop';
extraData?: Object; extraData?: Object;
}) => { }) => {
@@ -372,10 +383,11 @@ export const textAdaptGptResponse = ({
model, model,
choices: [ choices: [
{ {
delta: delta: {
text === null role: ChatCompletionRequestMessageRoleEnum.Assistant,
? {} content: text,
: { role: ChatCompletionRequestMessageRoleEnum.Assistant, content: text }, ...(reasoning_content && { reasoning_content })
},
index: 0, index: 0,
finish_reason finish_reason
} }
@@ -408,3 +420,137 @@ export function rewriteNodeOutputByHistories(
}; };
}); });
} }
// Parse <think></think> tags to think and answer - unstream response
export const parseReasoningContent = (text: string): [string, string] => {
const regex = /<think>([\s\S]*?)<\/think>/;
const match = text.match(regex);
if (!match) {
return ['', text];
}
const thinkContent = match[1].trim();
// Add answer (remaining text after think tag)
const answerContent = text.slice(match.index! + match[0].length);
return [thinkContent, answerContent];
};
// Parse <think></think> tags to think and answer - stream response
export const parseReasoningStreamContent = () => {
let isInThinkTag: boolean | undefined;
const startTag = '<think>';
let startTagBuffer = '';
const endTag = '</think>';
let endTagBuffer = '';
/*
parseReasoning - 只控制是否主动解析 <think></think>,如果接口已经解析了,仍然会返回 think 内容。
*/
const parsePart = (
part: {
choices: {
delta: {
content?: string;
reasoning_content?: string;
};
}[];
},
parseReasoning = false
): [string, string] => {
const content = part.choices?.[0]?.delta?.content || '';
// @ts-ignore
const reasoningContent = part.choices?.[0]?.delta?.reasoning_content || '';
if (reasoningContent || !parseReasoning) {
isInThinkTag = false;
return [reasoningContent, content];
}
if (!content) {
return ['', ''];
}
// 如果不在 think 标签中,或者有 reasoningContent(接口已解析),则返回 reasoningContent 和 content
if (isInThinkTag === false) {
return ['', content];
}
// 检测是否为 think 标签开头的数据
if (isInThinkTag === undefined) {
// Parse content think and answer
startTagBuffer += content;
// 太少内容时候,暂时不解析
if (startTagBuffer.length < startTag.length) {
return ['', ''];
}
if (startTagBuffer.startsWith(startTag)) {
isInThinkTag = true;
return [startTagBuffer.slice(startTag.length), ''];
}
// 如果未命中 think 标签,则认为不在 think 标签中,返回 buffer 内容作为 content
isInThinkTag = false;
return ['', startTagBuffer];
}
// 确认是 think 标签内容,开始返回 think 内容,并实时检测 </think>
/*
检测 </think> 方案。
存储所有疑似 </think> 的内容,直到检测到完整的 </think> 标签或超出 </think> 长度。
content 返回值包含以下几种情况:
abc - 完全未命中尾标签
abc<th - 命中一部分尾标签
abc</think> - 完全命中尾标签
abc</think>abc - 完全命中尾标签
</think>abc - 完全命中尾标签
k>abc - 命中一部分尾标签
*/
// endTagBuffer 专门用来记录疑似尾标签的内容
if (endTagBuffer) {
endTagBuffer += content;
if (endTagBuffer.includes(endTag)) {
isInThinkTag = false;
const answer = endTagBuffer.slice(endTag.length);
return ['', answer];
} else if (endTagBuffer.length >= endTag.length) {
// 缓存内容超出尾标签长度,且仍未命中 </think>,则认为本次猜测 </think> 失败,仍处于 think 阶段。
const tmp = endTagBuffer;
endTagBuffer = '';
return [tmp, ''];
}
return ['', ''];
} else if (content.includes(endTag)) {
// 返回内容,完整命中</think>,直接结束
isInThinkTag = false;
const [think, answer] = content.split(endTag);
return [think, answer];
} else {
// 无 buffer且未命中 </think>,开始疑似 </think> 检测。
for (let i = 1; i < endTag.length; i++) {
const partialEndTag = endTag.slice(0, i);
// 命中一部分尾标签
if (content.endsWith(partialEndTag)) {
const think = content.slice(0, -partialEndTag.length);
endTagBuffer += partialEndTag;
return [think, ''];
}
}
}
// 完全未命中尾标签,还是 think 阶段。
return [content, ''];
};
const getStartTagBuffer = () => startTagBuffer;
return {
parsePart,
getStartTagBuffer
};
};

View File

@@ -63,14 +63,12 @@ export const AiChatModule: FlowNodeTemplateType = {
key: NodeInputKeyEnum.aiChatTemperature, key: NodeInputKeyEnum.aiChatTemperature,
renderTypeList: [FlowNodeInputTypeEnum.hidden], // Set in the pop-up window renderTypeList: [FlowNodeInputTypeEnum.hidden], // Set in the pop-up window
label: '', label: '',
value: 0,
valueType: WorkflowIOValueTypeEnum.number valueType: WorkflowIOValueTypeEnum.number
}, },
{ {
key: NodeInputKeyEnum.aiChatMaxToken, key: NodeInputKeyEnum.aiChatMaxToken,
renderTypeList: [FlowNodeInputTypeEnum.hidden], // Set in the pop-up window renderTypeList: [FlowNodeInputTypeEnum.hidden], // Set in the pop-up window
label: '', label: '',
value: 2000,
valueType: WorkflowIOValueTypeEnum.number valueType: WorkflowIOValueTypeEnum.number
}, },
@@ -91,6 +89,37 @@ export const AiChatModule: FlowNodeTemplateType = {
valueType: WorkflowIOValueTypeEnum.boolean, valueType: WorkflowIOValueTypeEnum.boolean,
value: true value: true
}, },
{
key: NodeInputKeyEnum.aiChatReasoning,
renderTypeList: [FlowNodeInputTypeEnum.hidden],
label: '',
valueType: WorkflowIOValueTypeEnum.boolean,
value: true
},
{
key: NodeInputKeyEnum.aiChatTopP,
renderTypeList: [FlowNodeInputTypeEnum.hidden],
label: '',
valueType: WorkflowIOValueTypeEnum.number
},
{
key: NodeInputKeyEnum.aiChatStopSign,
renderTypeList: [FlowNodeInputTypeEnum.hidden],
label: '',
valueType: WorkflowIOValueTypeEnum.string
},
{
key: NodeInputKeyEnum.aiChatResponseFormat,
renderTypeList: [FlowNodeInputTypeEnum.hidden],
label: '',
valueType: WorkflowIOValueTypeEnum.string
},
{
key: NodeInputKeyEnum.aiChatJsonSchema,
renderTypeList: [FlowNodeInputTypeEnum.hidden],
label: '',
valueType: WorkflowIOValueTypeEnum.string
},
// settings modal --- // settings modal ---
{ {
...Input_Template_System_Prompt, ...Input_Template_System_Prompt,
@@ -101,7 +130,6 @@ export const AiChatModule: FlowNodeTemplateType = {
Input_Template_History, Input_Template_History,
Input_Template_Dataset_Quote, Input_Template_Dataset_Quote,
Input_Template_File_Link_Prompt, Input_Template_File_Link_Prompt,
{ ...Input_Template_UserChatInput, toolDescription: i18nT('workflow:user_question') } { ...Input_Template_UserChatInput, toolDescription: i18nT('workflow:user_question') }
], ],
outputs: [ outputs: [
@@ -123,6 +151,20 @@ export const AiChatModule: FlowNodeTemplateType = {
description: i18nT('common:core.module.output.description.Ai response content'), description: i18nT('common:core.module.output.description.Ai response content'),
valueType: WorkflowIOValueTypeEnum.string, valueType: WorkflowIOValueTypeEnum.string,
type: FlowNodeOutputTypeEnum.static type: FlowNodeOutputTypeEnum.static
},
{
id: NodeOutputKeyEnum.reasoningText,
key: NodeOutputKeyEnum.reasoningText,
required: false,
label: i18nT('workflow:reasoning_text'),
valueType: WorkflowIOValueTypeEnum.string,
type: FlowNodeOutputTypeEnum.static,
invalid: true,
invalidCondition: ({ inputs, llmModelList }) => {
const model = inputs.find((item) => item.key === NodeInputKeyEnum.aiModel)?.value;
const modelItem = llmModelList.find((item) => item.model === model);
return modelItem?.reasoning !== true;
}
} }
] ]
}; };

View File

@@ -1,5 +1,6 @@
import { import {
datasetQuoteValueDesc, datasetQuoteValueDesc,
datasetSelectValueDesc,
FlowNodeInputTypeEnum, FlowNodeInputTypeEnum,
FlowNodeOutputTypeEnum, FlowNodeOutputTypeEnum,
FlowNodeTypeEnum FlowNodeTypeEnum
@@ -38,7 +39,8 @@ export const DatasetSearchModule: FlowNodeTemplateType = {
label: i18nT('common:core.module.input.label.Select dataset'), label: i18nT('common:core.module.input.label.Select dataset'),
value: [], value: [],
valueType: WorkflowIOValueTypeEnum.selectDataset, valueType: WorkflowIOValueTypeEnum.selectDataset,
required: true required: true,
valueDesc: datasetSelectValueDesc
}, },
{ {
key: NodeInputKeyEnum.datasetSimilarity, key: NodeInputKeyEnum.datasetSimilarity,

View File

@@ -31,10 +31,7 @@ export const AiQueryExtension: FlowNodeTemplateType = {
showStatus: true, showStatus: true,
version: '481', version: '481',
inputs: [ inputs: [
{ Input_Template_SelectAIModel,
...Input_Template_SelectAIModel,
llmModelType: LLMModelTypeEnum.queryExtension
},
{ {
key: NodeInputKeyEnum.aiSystemPrompt, key: NodeInputKeyEnum.aiSystemPrompt,
renderTypeList: [FlowNodeInputTypeEnum.textarea, FlowNodeInputTypeEnum.reference], renderTypeList: [FlowNodeInputTypeEnum.textarea, FlowNodeInputTypeEnum.reference],

View File

@@ -43,14 +43,12 @@ export const ToolModule: FlowNodeTemplateType = {
key: NodeInputKeyEnum.aiChatTemperature, key: NodeInputKeyEnum.aiChatTemperature,
renderTypeList: [FlowNodeInputTypeEnum.hidden], // Set in the pop-up window renderTypeList: [FlowNodeInputTypeEnum.hidden], // Set in the pop-up window
label: '', label: '',
value: 0,
valueType: WorkflowIOValueTypeEnum.number valueType: WorkflowIOValueTypeEnum.number
}, },
{ {
key: NodeInputKeyEnum.aiChatMaxToken, key: NodeInputKeyEnum.aiChatMaxToken,
renderTypeList: [FlowNodeInputTypeEnum.hidden], // Set in the pop-up window renderTypeList: [FlowNodeInputTypeEnum.hidden], // Set in the pop-up window
label: '', label: '',
value: 2000,
valueType: WorkflowIOValueTypeEnum.number valueType: WorkflowIOValueTypeEnum.number
}, },
{ {
@@ -60,6 +58,30 @@ export const ToolModule: FlowNodeTemplateType = {
valueType: WorkflowIOValueTypeEnum.boolean, valueType: WorkflowIOValueTypeEnum.boolean,
value: true value: true
}, },
{
key: NodeInputKeyEnum.aiChatTopP,
renderTypeList: [FlowNodeInputTypeEnum.hidden],
label: '',
valueType: WorkflowIOValueTypeEnum.number
},
{
key: NodeInputKeyEnum.aiChatStopSign,
renderTypeList: [FlowNodeInputTypeEnum.hidden],
label: '',
valueType: WorkflowIOValueTypeEnum.string
},
{
key: NodeInputKeyEnum.aiChatResponseFormat,
renderTypeList: [FlowNodeInputTypeEnum.hidden],
label: '',
valueType: WorkflowIOValueTypeEnum.string
},
{
key: NodeInputKeyEnum.aiChatJsonSchema,
renderTypeList: [FlowNodeInputTypeEnum.hidden],
label: '',
valueType: WorkflowIOValueTypeEnum.string
},
{ {
...Input_Template_System_Prompt, ...Input_Template_System_Prompt,

View File

@@ -1,3 +1,4 @@
import { LLMModelItemType } from '../../ai/model.d';
import { LLMModelTypeEnum } from '../../ai/constants'; import { LLMModelTypeEnum } from '../../ai/constants';
import { WorkflowIOValueTypeEnum, NodeInputKeyEnum, NodeOutputKeyEnum } from '../constants'; import { WorkflowIOValueTypeEnum, NodeInputKeyEnum, NodeOutputKeyEnum } from '../constants';
import { FlowNodeInputTypeEnum, FlowNodeOutputTypeEnum } from '../node/constant'; import { FlowNodeInputTypeEnum, FlowNodeOutputTypeEnum } from '../node/constant';
@@ -77,6 +78,12 @@ export type FlowNodeOutputItemType = {
defaultValue?: any; defaultValue?: any;
required?: boolean; required?: boolean;
invalid?: boolean;
invalidCondition?: (e: {
inputs: FlowNodeInputItemType[];
llmModelList: LLMModelItemType[];
}) => boolean;
// component params // component params
customFieldConfig?: CustomFieldConfigType; customFieldConfig?: CustomFieldConfigType;
}; };

View File

@@ -43,6 +43,17 @@ export type FlowNodeCommonType = {
pluginId?: string; pluginId?: string;
isFolder?: boolean; isFolder?: boolean;
// pluginType?: AppTypeEnum; // pluginType?: AppTypeEnum;
pluginData?: PluginDataType;
};
export type PluginDataType = {
version: string;
diagram?: string;
userGuide?: string;
courseUrl?: string;
name?: string;
avatar?: string;
error?: string;
}; };
type HandleType = { type HandleType = {

View File

@@ -6,6 +6,22 @@ export type CreateTrainingUsageProps = {
datasetId: string; datasetId: string;
}; };
export type GetUsageProps = {
dateStart: Date;
dateEnd: Date;
sources?: UsageSourceEnum[];
teamMemberIds?: string[];
projectName?: string;
};
export type GetUsageDashboardProps = GetUsageProps & {
unit: 'day' | 'month';
};
export type GetUsageDashboardResponseItem = {
date: Date;
totalPoints: number;
};
export type ConcatUsageProps = UsageListItemCountType & { export type ConcatUsageProps = UsageListItemCountType & {
teamId: string; teamId: string;
tmbId: string; tmbId: string;

View File

@@ -18,30 +18,30 @@ export const UsageSourceMap = {
label: i18nT('common:core.chat.logs.online') label: i18nT('common:core.chat.logs.online')
}, },
[UsageSourceEnum.api]: { [UsageSourceEnum.api]: {
label: 'Api' label: 'API'
}, },
[UsageSourceEnum.shareLink]: { [UsageSourceEnum.shareLink]: {
label: i18nT('common:core.chat.logs.free_login') label: i18nT('common:core.chat.logs.free_login')
}, },
[UsageSourceEnum.training]: { [UsageSourceEnum.training]: {
label: 'dataset.Training Name' label: i18nT('common:dataset.Training Name')
}, },
[UsageSourceEnum.cronJob]: { [UsageSourceEnum.cronJob]: {
label: i18nT('common:cron_job_run_app') label: i18nT('common:cron_job_run_app')
}, },
[UsageSourceEnum.feishu]: { [UsageSourceEnum.feishu]: {
label: i18nT('user:usage.feishu') label: i18nT('account_usage:feishu')
}, },
[UsageSourceEnum.official_account]: { [UsageSourceEnum.official_account]: {
label: i18nT('user:usage.official_account') label: i18nT('account_usage:official_account')
}, },
[UsageSourceEnum.share]: { [UsageSourceEnum.share]: {
label: i18nT('user:usage.share') label: i18nT('account_usage:share')
}, },
[UsageSourceEnum.wecom]: { [UsageSourceEnum.wecom]: {
label: i18nT('user:usage.wecom') label: i18nT('account_usage:wecom')
}, },
[UsageSourceEnum.dingtalk]: { [UsageSourceEnum.dingtalk]: {
label: i18nT('user:usage.dingtalk') label: i18nT('account_usage:dingtalk')
} }
}; };

View File

@@ -1,3 +1,4 @@
import { SourceMemberType } from '../../../support/user/type';
import { CreateUsageProps } from './api'; import { CreateUsageProps } from './api';
import { UsageSourceEnum } from './constants'; import { UsageSourceEnum } from './constants';
@@ -10,6 +11,7 @@ export type UsageListItemCountType = {
// deprecated // deprecated
tokens?: number; tokens?: number;
}; };
export type UsageListItemType = UsageListItemCountType & { export type UsageListItemType = UsageListItemCountType & {
moduleName: string; moduleName: string;
amount: number; amount: number;
@@ -28,4 +30,5 @@ export type UsageItemType = {
source: UsageSchemaType['source']; source: UsageSchemaType['source'];
totalPoints: number; totalPoints: number;
list: UsageSchemaType['list']; list: UsageSchemaType['list'];
sourceMember: SourceMemberType;
}; };

View File

@@ -10,6 +10,7 @@
"echarts": "5.4.1", "echarts": "5.4.1",
"expr-eval": "^2.0.2", "expr-eval": "^2.0.2",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"mssql": "^11.0.1",
"mysql2": "^3.11.3", "mysql2": "^3.11.3",
"json5": "^2.2.3", "json5": "^2.2.3",
"pg": "^8.10.0", "pg": "^8.10.0",

View File

@@ -1,5 +1,6 @@
import { Client as PgClient } from 'pg'; // PostgreSQL 客户端 import { Client as PgClient } from 'pg'; // PostgreSQL 客户端
import mysql from 'mysql2/promise'; // MySQL 客户端 import mysql from 'mysql2/promise'; // MySQL 客户端
import mssql from 'mssql'; // SQL Server 客户端
type Props = { type Props = {
databaseType: string; databaseType: string;
@@ -52,6 +53,20 @@ const main = async ({
const [rows] = await connection.execute(sql); const [rows] = await connection.execute(sql);
result = rows; result = rows;
await connection.end(); await connection.end();
} else if (databaseType === 'Microsoft SQL Server') {
const pool = await mssql.connect({
server: host,
port: parseInt(port, 10),
database: databaseName,
user,
password,
options: {
trustServerCertificate: true
}
});
result = await pool.query(sql);
await pool.close();
} }
return { return {
result result

View File

@@ -42,6 +42,10 @@
{ {
"label": "PostgreSQL", "label": "PostgreSQL",
"value": "PostgreSQL" "value": "PostgreSQL"
},
{
"label": "Microsoft SQL Server",
"value": "Microsoft SQL Server"
} }
], ],
"required": true "required": true

View File

@@ -5,6 +5,7 @@ import { ClientSession, Types } from '../../../common/mongo';
import { guessBase64ImageType } from '../utils'; import { guessBase64ImageType } from '../utils';
import { readFromSecondary } from '../../mongo/utils'; import { readFromSecondary } from '../../mongo/utils';
import { addHours } from 'date-fns'; import { addHours } from 'date-fns';
import { imageFileType } from '@fastgpt/global/common/file/constants';
export const maxImgSize = 1024 * 1024 * 12; export const maxImgSize = 1024 * 1024 * 12;
const base64MimeRegex = /data:image\/([^\)]+);base64/; const base64MimeRegex = /data:image\/([^\)]+);base64/;
@@ -25,12 +26,19 @@ export async function uploadMongoImg({
const [base64Mime, base64Data] = base64Img.split(','); const [base64Mime, base64Data] = base64Img.split(',');
// Check if mime type is valid // Check if mime type is valid
if (!base64MimeRegex.test(base64Mime)) { if (!base64MimeRegex.test(base64Mime)) {
return Promise.reject('Invalid image mime type'); return Promise.reject('Invalid image base64');
} }
const mime = `image/${base64Mime.match(base64MimeRegex)?.[1] ?? 'image/jpeg'}`; const mime = `image/${base64Mime.match(base64MimeRegex)?.[1] ?? 'image/jpeg'}`;
const binary = Buffer.from(base64Data, 'base64'); const binary = Buffer.from(base64Data, 'base64');
const extension = mime.split('/')[1]; let extension = mime.split('/')[1];
if (extension.startsWith('x-')) {
extension = extension.substring(2); // Remove 'x-' prefix
}
if (!extension || !imageFileType.includes(`.${extension}`)) {
return Promise.reject(`Invalid image file type: ${mime}`);
}
const { _id } = await MongoImage.create({ const { _id } = await MongoImage.create({
teamId, teamId,
@@ -40,7 +48,7 @@ export async function uploadMongoImg({
expiredTime: forever ? undefined : addHours(new Date(), 1) expiredTime: forever ? undefined : addHours(new Date(), 1)
}); });
return `${process.env.FE_DOMAIN || ''}${process.env.NEXT_PUBLIC_BASE_URL || ''}${imageBaseUrl}${String(_id)}.${extension}`; return `${process.env.NEXT_PUBLIC_BASE_URL || ''}${imageBaseUrl}${String(_id)}.${extension}`;
} }
const getIdFromPath = (path?: string) => { const getIdFromPath = (path?: string) => {

View File

@@ -1,6 +1,5 @@
import { ApiRequestProps } from '../../type/next'; import { ApiRequestProps } from '../../type/next';
import requestIp from 'request-ip'; import requestIp from 'request-ip';
import { ERROR_ENUM } from '@fastgpt/global/common/error/errorCode';
import { authFrequencyLimit } from '../system/frequencyLimit/utils'; import { authFrequencyLimit } from '../system/frequencyLimit/utils';
import { addSeconds } from 'date-fns'; import { addSeconds } from 'date-fns';
import { NextApiResponse } from 'next'; import { NextApiResponse } from 'next';
@@ -9,7 +8,17 @@ import { jsonRes } from '../response';
// unit: times/s // unit: times/s
// how to use? // how to use?
// export default NextAPI(useQPSLimit(10), handler); // limit 10 times per second for a ip // export default NextAPI(useQPSLimit(10), handler); // limit 10 times per second for a ip
export function useReqFrequencyLimit(seconds: number, limit: number, force = false) { export function useIPFrequencyLimit({
id,
seconds,
limit,
force = false
}: {
id: string;
seconds: number;
limit: number;
force?: boolean;
}) {
return async (req: ApiRequestProps, res: NextApiResponse) => { return async (req: ApiRequestProps, res: NextApiResponse) => {
const ip = requestIp.getClientIp(req); const ip = requestIp.getClientIp(req);
if (!ip || (process.env.USE_IP_LIMIT !== 'true' && !force)) { if (!ip || (process.env.USE_IP_LIMIT !== 'true' && !force)) {
@@ -17,14 +26,14 @@ export function useReqFrequencyLimit(seconds: number, limit: number, force = fal
} }
try { try {
await authFrequencyLimit({ await authFrequencyLimit({
eventId: 'ip-qps-limit' + ip, eventId: `ip-qps-limit-${id}-` + ip,
maxAmount: limit, maxAmount: limit,
expiredTime: addSeconds(new Date(), seconds) expiredTime: addSeconds(new Date(), seconds)
}); });
} catch (_) { } catch (_) {
jsonRes(res, { jsonRes(res, {
code: 429, code: 429,
error: ERROR_ENUM.tooManyRequest error: `Too many request, request ${limit} times every ${seconds} seconds`
}); });
} }
}; };

View File

@@ -63,6 +63,13 @@ export const getMongoModel = <T>(name: string, schema: mongoose.Schema) => {
const model = connectionMongo.model<T>(name, schema); const model = connectionMongo.model<T>(name, schema);
// Sync index
syncMongoIndex(model);
return model;
};
const syncMongoIndex = async (model: Model<any>) => {
if (process.env.SYNC_INDEX !== '0' && process.env.NODE_ENV !== 'test') { if (process.env.SYNC_INDEX !== '0' && process.env.NODE_ENV !== 'test') {
try { try {
model.syncIndexes({ background: true }); model.syncIndexes({ background: true });
@@ -70,8 +77,6 @@ export const getMongoModel = <T>(name: string, schema: mongoose.Schema) => {
addLog.error('Create index error', error); addLog.error('Create index error', error);
} }
} }
return model;
}; };
export const ReadPreference = connectionMongo.mongo.ReadPreference; export const ReadPreference = connectionMongo.mongo.ReadPreference;

View File

@@ -33,7 +33,15 @@ export const jsonRes = <T = any>(
addLog.error(`Api response error: ${url}`, ERROR_RESPONSE[errResponseKey]); addLog.error(`Api response error: ${url}`, ERROR_RESPONSE[errResponseKey]);
return res.status(code).json(ERROR_RESPONSE[errResponseKey]); res.status(code);
if (message) {
res.send(message);
} else {
res.json(ERROR_RESPONSE[errResponseKey]);
}
return;
} }
// another error // another error

View File

@@ -25,7 +25,7 @@ export const countGptMessagesTokens = async (
number number
>({ >({
name: WorkerNameEnum.countGptMessagesTokens, name: WorkerNameEnum.countGptMessagesTokens,
maxReservedThreads: global.systemEnv?.tokenWorkers || 50 maxReservedThreads: global.systemEnv?.tokenWorkers || 30
}); });
const total = await workerController.run({ messages, tools, functionCall }); const total = await workerController.run({ messages, tools, functionCall });

View File

@@ -6,8 +6,7 @@ import { FastGPTProUrl } from '../constants';
export const getFastGPTConfigFromDB = async () => { export const getFastGPTConfigFromDB = async () => {
if (!FastGPTProUrl) { if (!FastGPTProUrl) {
return { return {
config: {} as FastGPTConfigFileType, config: {} as FastGPTConfigFileType
configId: undefined
}; };
} }
@@ -18,9 +17,35 @@ export const getFastGPTConfigFromDB = async () => {
}); });
const config = res?.value || {}; const config = res?.value || {};
// 利用配置文件的创建时间(更新时间)来做缓存,如果前端命中缓存,则不需要再返回配置文件
global.systemInitBufferId = res ? res.createTime.getTime().toString() : undefined;
return { return {
configId: res ? String(res._id) : undefined,
config: config as FastGPTConfigFileType config: config as FastGPTConfigFileType
}; };
}; };
export const updateFastGPTConfigBuffer = async () => {
const res = await MongoSystemConfigs.findOne({
type: SystemConfigsTypeEnum.fastgpt
}).sort({
createTime: -1
});
if (!res) return;
res.createTime = new Date();
await res.save();
global.systemInitBufferId = res.createTime.getTime().toString();
};
export const reloadFastGPTConfigBuffer = async () => {
const res = await MongoSystemConfigs.findOne({
type: SystemConfigsTypeEnum.fastgpt
}).sort({
createTime: -1
});
if (!res) return;
global.systemInitBufferId = res.createTime.getTime().toString();
};

View File

@@ -13,15 +13,6 @@ export const initFastGPTConfig = (config?: FastGPTConfigFileType) => {
global.feConfigs = config.feConfigs; global.feConfigs = config.feConfigs;
global.systemEnv = config.systemEnv; global.systemEnv = config.systemEnv;
global.subPlans = config.subPlans; 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;
global.reRankModels = config.reRankModels;
}; };
export const systemStartCb = () => { export const systemStartCb = () => {

View File

@@ -2,7 +2,7 @@
import { PgVectorCtrl } from './pg/class'; import { PgVectorCtrl } from './pg/class';
import { getVectorsByText } from '../../core/ai/embedding'; import { getVectorsByText } from '../../core/ai/embedding';
import { InsertVectorProps } from './controller.d'; import { InsertVectorProps } from './controller.d';
import { VectorModelItemType } from '@fastgpt/global/core/ai/model.d'; import { EmbeddingModelItemType } from '@fastgpt/global/core/ai/model.d';
import { MILVUS_ADDRESS, PG_ADDRESS } from './constants'; import { MILVUS_ADDRESS, PG_ADDRESS } from './constants';
import { MilvusCtrl } from './milvus/class'; import { MilvusCtrl } from './milvus/class';
@@ -28,7 +28,7 @@ export const insertDatasetDataVector = async ({
...props ...props
}: InsertVectorProps & { }: InsertVectorProps & {
query: string; query: string;
model: VectorModelItemType; model: EmbeddingModelItemType;
}) => { }) => {
const { vectors, tokens } = await getVectorsByText({ const { vectors, tokens } = await getVectorsByText({
model, model,

View File

@@ -1,5 +1,6 @@
import type { NextApiResponse } from 'next'; import type { NextApiResponse } from 'next';
import { getAIApi } from '../config'; import { getAIApi } from '../config';
import { getTTSModel } from '../model';
export async function text2Speech({ export async function text2Speech({
res, res,
@@ -18,15 +19,26 @@ export async function text2Speech({
voice: string; voice: string;
speed?: number; speed?: number;
}) { }) {
const modelData = getTTSModel(model)!;
const ai = getAIApi(); const ai = getAIApi();
const response = await ai.audio.speech.create({ const response = await ai.audio.speech.create(
model, {
// @ts-ignore model,
voice, // @ts-ignore
input, voice,
response_format: 'mp3', input,
speed response_format: 'mp3',
}); speed
},
modelData.requestUrl && modelData.requestAuth
? {
path: modelData.requestUrl,
headers: {
Authorization: `Bearer ${modelData.requestAuth}`
}
}
: {}
);
const readableStream = response.body as unknown as NodeJS.ReadableStream; const readableStream = response.body as unknown as NodeJS.ReadableStream;
readableStream.pipe(res); readableStream.pipe(res);

View File

@@ -2,6 +2,7 @@ import fs from 'fs';
import { getAxiosConfig } from '../config'; import { getAxiosConfig } from '../config';
import axios from 'axios'; import axios from 'axios';
import FormData from 'form-data'; import FormData from 'form-data';
import { getSTTModel } from '../model';
export const aiTranscriptions = async ({ export const aiTranscriptions = async ({
model, model,
@@ -14,13 +15,21 @@ export const aiTranscriptions = async ({
data.append('model', model); data.append('model', model);
data.append('file', fileStream); data.append('file', fileStream);
const modelData = getSTTModel(model);
const aiAxiosConfig = getAxiosConfig(); const aiAxiosConfig = getAxiosConfig();
const { data: result } = await axios<{ text: string }>({ const { data: result } = await axios<{ text: string }>({
method: 'post', method: 'post',
baseURL: aiAxiosConfig.baseUrl, ...(modelData.requestUrl
url: '/audio/transcriptions', ? { url: modelData.requestUrl }
: {
baseURL: aiAxiosConfig.baseUrl,
url: '/audio/transcriptions'
}),
headers: { headers: {
Authorization: aiAxiosConfig.authorization, Authorization: modelData.requestAuth
? `Bearer ${modelData.requestAuth}`
: aiAxiosConfig.authorization,
...data.getHeaders() ...data.getHeaders()
}, },
data: data data: data

View File

@@ -1,20 +1,22 @@
import OpenAI from '@fastgpt/global/core/ai'; import OpenAI from '@fastgpt/global/core/ai';
import { import {
ChatCompletionCreateParamsNonStreaming, ChatCompletionCreateParamsNonStreaming,
ChatCompletionCreateParamsStreaming ChatCompletionCreateParamsStreaming,
StreamChatType,
UnStreamChatType
} from '@fastgpt/global/core/ai/type'; } from '@fastgpt/global/core/ai/type';
import { getErrText } from '@fastgpt/global/common/error/utils'; import { getErrText } from '@fastgpt/global/common/error/utils';
import { addLog } from '../../common/system/log'; import { addLog } from '../../common/system/log';
import { i18nT } from '../../../web/i18n/utils'; import { i18nT } from '../../../web/i18n/utils';
import { OpenaiAccountType } from '@fastgpt/global/support/user/team/type'; import { OpenaiAccountType } from '@fastgpt/global/support/user/team/type';
import { getLLMModel } from './model';
export const openaiBaseUrl = process.env.OPENAI_BASE_URL || 'https://api.openai.com/v1'; export const openaiBaseUrl = process.env.OPENAI_BASE_URL || 'https://api.openai.com/v1';
export const getAIApi = (props?: { userKey?: OpenaiAccountType; timeout?: number }) => { export const getAIApi = (props?: { userKey?: OpenaiAccountType; timeout?: number }) => {
const { userKey, timeout } = props || {}; const { userKey, timeout } = props || {};
const baseUrl = const baseUrl = userKey?.baseUrl || global?.systemEnv?.oneapiUrl || openaiBaseUrl;
userKey?.baseUrl || global?.systemEnv?.oneapiUrl || process.env.ONEAPI_URL || openaiBaseUrl;
const apiKey = userKey?.key || global?.systemEnv?.chatApiKey || process.env.CHAT_API_KEY || ''; const apiKey = userKey?.key || global?.systemEnv?.chatApiKey || process.env.CHAT_API_KEY || '';
return new OpenAI({ return new OpenAI({
@@ -29,8 +31,7 @@ export const getAIApi = (props?: { userKey?: OpenaiAccountType; timeout?: number
export const getAxiosConfig = (props?: { userKey?: OpenaiAccountType }) => { export const getAxiosConfig = (props?: { userKey?: OpenaiAccountType }) => {
const { userKey } = props || {}; const { userKey } = props || {};
const baseUrl = const baseUrl = userKey?.baseUrl || global?.systemEnv?.oneapiUrl || openaiBaseUrl;
userKey?.baseUrl || global?.systemEnv?.oneapiUrl || process.env.ONEAPI_URL || openaiBaseUrl;
const apiKey = userKey?.key || global?.systemEnv?.chatApiKey || process.env.CHAT_API_KEY || ''; const apiKey = userKey?.key || global?.systemEnv?.chatApiKey || process.env.CHAT_API_KEY || '';
return { return {
@@ -39,36 +40,48 @@ export const getAxiosConfig = (props?: { userKey?: OpenaiAccountType }) => {
}; };
}; };
type CompletionsBodyType = export const createChatCompletion = async ({
| ChatCompletionCreateParamsNonStreaming
| ChatCompletionCreateParamsStreaming;
type InferResponseType<T extends CompletionsBodyType> =
T extends ChatCompletionCreateParamsStreaming
? OpenAI.Chat.Completions.ChatCompletionChunk
: OpenAI.Chat.Completions.ChatCompletion;
export const createChatCompletion = async <T extends CompletionsBodyType>({
body, body,
userKey, userKey,
timeout, timeout,
options options
}: { }: {
body: T; body: ChatCompletionCreateParamsNonStreaming | ChatCompletionCreateParamsStreaming;
userKey?: OpenaiAccountType; userKey?: OpenaiAccountType;
timeout?: number; timeout?: number;
options?: OpenAI.RequestOptions; options?: OpenAI.RequestOptions;
}): Promise<{ }): Promise<
response: InferResponseType<T>; {
isStreamResponse: boolean; getEmptyResponseTip: () => string;
getEmptyResponseTip: () => string; } & (
}> => { | {
response: StreamChatType;
isStreamResponse: true;
}
| {
response: UnStreamChatType;
isStreamResponse: false;
}
)
> => {
try { try {
const modelConstantsData = getLLMModel(body.model);
const formatTimeout = timeout ? timeout : body.stream ? 60000 : 600000; const formatTimeout = timeout ? timeout : body.stream ? 60000 : 600000;
const ai = getAIApi({ const ai = getAIApi({
userKey, userKey,
timeout: formatTimeout timeout: formatTimeout
}); });
const response = await ai.chat.completions.create(body, options); const response = await ai.chat.completions.create(body, {
...options,
...(modelConstantsData.requestUrl ? { path: modelConstantsData.requestUrl } : {}),
headers: {
...options?.headers,
...(modelConstantsData.requestAuth
? { Authorization: `Bearer ${modelConstantsData.requestAuth}` }
: {})
}
});
const isStreamResponse = const isStreamResponse =
typeof response === 'object' && typeof response === 'object' &&
@@ -86,9 +99,17 @@ export const createChatCompletion = async <T extends CompletionsBodyType>({
return i18nT('chat:LLM_model_response_empty'); return i18nT('chat:LLM_model_response_empty');
}; };
if (isStreamResponse) {
return {
response,
isStreamResponse: true,
getEmptyResponseTip
};
}
return { return {
response: response as InferResponseType<T>, response,
isStreamResponse, isStreamResponse: false,
getEmptyResponseTip getEmptyResponseTip
}; };
} catch (error) { } catch (error) {

View File

@@ -1,11 +0,0 @@
{
"provider": "OpenAI",
"model": "text-embedding-ada-002",
"name": "text-embedding-ada-002",
"defaultToken": 512, // 默认分块 token
"maxToken": 3000, // 最大分块 token
"weight": 0, // 权重
"charsPointsPrice": 0 // 积分/1k token
}

View File

@@ -1,33 +0,0 @@
{
"provider": "OpenAI",
"model": "gpt-4o-mini",
"name": "GPT-4o-mini", // alias
"maxContext": 125000, // 最大上下文
"maxResponse": 16000, // 最大回复
"quoteMaxToken": 60000, // 最大引用
"maxTemperature": 1.2, // 最大温度
"presencePenaltyRange": [-2, 2], // 惩罚系数范围
"frequencyPenaltyRange": [-2, 2], // 频率惩罚系数范围
"responseFormatList": ["text", "json_object", "json_schema"], // 响应格式
"showStopSign": true, // 是否显示停止符号
"vision": true, // 是否支持图片识别
"toolChoice": true, // 是否支持工具调用
"functionCall": false, // 是否支持函数调用(一般都可以 false 了,基本不用了)
"defaultSystemChatPrompt": "", // 默认系统提示
"datasetProcess": true, // 用于知识库文本处理
"usedInClassify": true, // 用于问题分类
"customCQPrompt": "", // 自定义问题分类提示
"usedInExtractFields": true, // 用于提取字段
"customExtractPrompt": "", // 自定义提取提示
"usedInToolCall": true, // 用于工具调用
"usedInQueryExtension": true, // 用于问题优化
"defaultConfig": {}, // 额外的自定义 body
"fieldMap": {}, // body 字段映射
"censor": false, // 是否开启敏感词过滤
"charsPointsPrice": 0 // n 积分/1k token
}

View File

@@ -0,0 +1,4 @@
{
"provider": "AliCloud",
"list": []
}

View File

@@ -0,0 +1,17 @@
{
"provider": "BAAI",
"list": [
{
"model": "bge-m3",
"name": "bge-m3",
"defaultToken": 512,
"maxToken": 8000,
"type": "embedding"
},
{
"model": "bge-reranker-v2-m3",
"name": "bge-reranker-v2-m3",
"type": "rerank"
}
]
}

View File

@@ -0,0 +1,4 @@
{
"provider": "Baichuan",
"list": []
}

View File

@@ -0,0 +1,175 @@
{
"provider": "ChatGLM",
"list": [
{
"model": "glm-4-air",
"name": "glm-4-air",
"maxContext": 128000,
"maxResponse": 4000,
"quoteMaxToken": 120000,
"maxTemperature": 0.99,
"showTopP": true,
"responseFormatList": [
"text",
"json_object"
],
"showStopSign": true,
"vision": false,
"toolChoice": true,
"functionCall": false,
"defaultSystemChatPrompt": "",
"datasetProcess": true,
"usedInClassify": true,
"customCQPrompt": "",
"usedInExtractFields": true,
"usedInQueryExtension": true,
"customExtractPrompt": "",
"usedInToolCall": true,
"defaultConfig": {},
"fieldMap": {},
"type": "llm"
},
{
"model": "glm-4-flash",
"name": "glm-4-flash",
"maxContext": 128000,
"maxResponse": 4000,
"quoteMaxToken": 120000,
"maxTemperature": 0.99,
"showTopP": true,
"responseFormatList": [
"text",
"json_object"
],
"showStopSign": true,
"vision": false,
"toolChoice": true,
"functionCall": false,
"defaultSystemChatPrompt": "",
"datasetProcess": true,
"usedInClassify": true,
"customCQPrompt": "",
"usedInExtractFields": true,
"usedInQueryExtension": true,
"customExtractPrompt": "",
"usedInToolCall": true,
"defaultConfig": {},
"fieldMap": {},
"type": "llm"
},
{
"model": "glm-4-long",
"name": "glm-4-long",
"maxContext": 1000000,
"maxResponse": 4000,
"quoteMaxToken": 900000,
"maxTemperature": 0.99,
"showTopP": true,
"responseFormatList": [
"text",
"json_object"
],
"showStopSign": true,
"vision": false,
"toolChoice": false,
"functionCall": false,
"defaultSystemChatPrompt": "",
"datasetProcess": true,
"usedInClassify": true,
"customCQPrompt": "",
"usedInExtractFields": true,
"usedInQueryExtension": true,
"customExtractPrompt": "",
"usedInToolCall": true,
"defaultConfig": {},
"fieldMap": {},
"type": "llm"
},
{
"model": "glm-4-plus",
"name": "GLM-4-plus",
"maxContext": 128000,
"maxResponse": 4000,
"quoteMaxToken": 120000,
"maxTemperature": 0.99,
"showTopP": true,
"responseFormatList": [
"text",
"json_object"
],
"showStopSign": true,
"vision": false,
"toolChoice": true,
"functionCall": false,
"defaultSystemChatPrompt": "",
"datasetProcess": true,
"usedInClassify": true,
"customCQPrompt": "",
"usedInExtractFields": true,
"usedInQueryExtension": true,
"customExtractPrompt": "",
"usedInToolCall": true,
"defaultConfig": {},
"fieldMap": {},
"type": "llm"
},
{
"model": "glm-4v-flash",
"name": "glm-4v-flash",
"maxContext": 8000,
"maxResponse": 1000,
"quoteMaxToken": 6000,
"maxTemperature": 0.99,
"showTopP": true,
"showStopSign": true,
"vision": true,
"toolChoice": false,
"functionCall": false,
"defaultSystemChatPrompt": "",
"datasetProcess": true,
"usedInClassify": true,
"customCQPrompt": "",
"usedInExtractFields": true,
"usedInQueryExtension": true,
"customExtractPrompt": "",
"usedInToolCall": true,
"defaultConfig": {},
"fieldMap": {},
"type": "llm"
},
{
"model": "glm-4v-plus",
"name": "GLM-4v-plus",
"maxContext": 8000,
"maxResponse": 1000,
"quoteMaxToken": 6000,
"maxTemperature": 0.99,
"showTopP": true,
"showStopSign": true,
"vision": true,
"toolChoice": false,
"functionCall": false,
"defaultSystemChatPrompt": "",
"datasetProcess": true,
"usedInClassify": true,
"customCQPrompt": "",
"usedInExtractFields": true,
"usedInQueryExtension": true,
"customExtractPrompt": "",
"usedInToolCall": true,
"defaultConfig": {},
"fieldMap": {},
"type": "llm"
},
{
"model": "embedding-3",
"name": "embedding-3",
"defaultToken": 512,
"maxToken": 8000,
"defaultConfig": {
"dimensions": 1024
},
"type": "embedding"
}
]
}

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