Compare commits

...

15 Commits

Author SHA1 Message Date
Archer
1dca5edcc6 v4.5.1-3 (#427) 2023-10-24 17:32:36 +08:00
Archer
1942cb0d67 perf: btn color (#423) 2023-10-24 13:19:23 +08:00
Archer
bf6dbfb245 v4.5.1-2 (#421) 2023-10-23 15:05:13 +08:00
Archer
d37433eacd Config file to set doc baseurl (#419) 2023-10-23 08:56:43 +08:00
Archer
a3534407bf v4.5.1 (#417) 2023-10-22 23:54:04 +08:00
Carson Yang
3091a90df6 Update README (#418)
Signed-off-by: Carson Yang <yangchuansheng33@gmail.com>
2023-10-22 23:47:09 +08:00
Carson Yang
41b8f4443c Docs: update qr for wechat group (#416)
Signed-off-by: Carson Yang <yangchuansheng33@gmail.com>
2023-10-22 23:28:46 +08:00
Carson Yang
777f089423 Docs: update README (#407)
Signed-off-by: Carson Yang <yangchuansheng33@gmail.com>
2023-10-18 15:51:51 +08:00
不做了睡大觉
b23e00f3e5 添加Baichuan2-7B-Chat模型接口文件 (#404)
* 更新镜像

* 更新镜像信息

* 更新镜像信息

* Create openai_api.py

* Create requirements.txt
2023-10-18 10:34:22 +08:00
Archer
3b776b6639 v4.5 (#403) 2023-10-17 10:00:32 +08:00
Archer
dd8f2744bf Extraction schema (#398) 2023-10-14 23:02:01 +08:00
左风
7db8d3ea0f Docs: add quick start and video link (#395) 2023-10-13 20:16:18 +08:00
Archer
ad7a17bf40 Optimize the project structure and introduce DDD design (#394) 2023-10-12 17:46:37 +08:00
李启爱
76ac5238b6 Update 447.md (#392) 2023-10-12 14:56:18 +08:00
Carson Yang
add73aa2c5 Docs: use docsearch (#391)
Signed-off-by: Carson Yang <yangchuansheng33@gmail.com>
2023-10-12 00:04:45 +08:00
473 changed files with 11960 additions and 23199 deletions

View File

@@ -15,7 +15,10 @@ jobs:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 1
ref: ${{ github.event.pull_request.head.ref }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
submodules: recursive # Fetch submodules
fetch-depth: 0 # Fetch all history for .GitInfo and .Lastmod
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
with:

View File

@@ -1,5 +1,5 @@
# Install dependencies only when needed
FROM node:current-alpine AS deps
FROM node:18.15-alpine AS deps
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
RUN apk add --no-cache libc6-compat && npm install -g pnpm
WORKDIR /app
@@ -10,34 +10,30 @@ ARG name
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
COPY ./packages ./packages
COPY ./projects/$name/package.json ./projects/$name/package.json
COPY ./projects/$name/pnpm-lock.yaml ./projects/$name/pnpm-lock.yaml
RUN \
[ -f pnpm-lock.yaml ] && pnpm install || \
(echo "Lockfile not found." && exit 1)
RUN [ -f pnpm-lock.yaml ] || (echo "Lockfile not found." && exit 1)
RUN pnpm prune
RUN pnpm install
# Rebuild the source code only when needed
FROM node:current-alpine AS builder
FROM node:18.15-alpine AS builder
WORKDIR /app
ARG name
# copy common node_modules and one project node_modules
COPY package.json pnpm-workspace.yaml ./
COPY --from=deps /app/node_modules ./node_modules
COPY --from=deps /app/packages ./packages
COPY ./projects/$name ./projects/$name
COPY --from=deps /app/projects/$name/node_modules ./projects/$name/node_modules
COPY pnpm-lock.yaml pnpm-workspace.yaml ./
COPY ./packages ./packages
# Uncomment the following line in case you want to disable telemetry during the build.
ENV NEXT_TELEMETRY_DISABLED 1
RUN npm install -g pnpm
RUN pnpm --filter=$name run build
FROM node:current-alpine AS runner
FROM node:18.15-alpine AS runner
WORKDIR /app
ARG name

View File

@@ -15,19 +15,19 @@ FastGPT 是一个基于 LLM 大语言模型的知识库问答系统,提供开
<p align="center">
<a href="https://fastgpt.run/">
<img height="21" src="https://img.shields.io/badge/在线使用-fff?style=flat-square&logo=spoj&logoColor=7d09f1" alt="cloud">
<img height="21" src="https://img.shields.io/badge/在线使用-d4eaf7?style=flat-square&logo=spoj&logoColor=7d09f1" alt="cloud">
</a>
<a href="https://doc.fastgpt.run/docs/intro">
<img height="21" src="https://img.shields.io/badge/相关文档-7d09f1?style=flat-square" alt="document">
</a>
<a href="https://doc.fastgpt.run/docs/development">
<img height="21" src="https://img.shields.io/badge/本地开发-%23fff?style=flat-square&logo=xcode&logoColor=7d09f1" alt="development">
<img height="21" src="https://img.shields.io/badge/本地开发-%23d4eaf7?style=flat-square&logo=xcode&logoColor=7d09f1" alt="development">
</a>
<a href="/#-%E7%9B%B8%E5%85%B3%E9%A1%B9%E7%9B%AE">
<img height="21" src="https://img.shields.io/badge/相关项目-7d09f1?style=flat-square" alt="project">
</a>
<a href="https://github.com/labring/FastGPT/blob/main/LICENSE">
<img height="21" src="https://img.shields.io/badge/License-Apache--2.0-ffffff?style=flat-square&labelColor=fff&color=7d09f1" alt="license">
<img height="21" src="https://img.shields.io/badge/License-Apache--2.0-ffffff?style=flat-square&labelColor=d4eaf7&color=7d09f1" alt="license">
</a>
</p>
@@ -35,7 +35,8 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b
## 🛸 在线使用
[fastgpt.run](https://fastgpt.run/)(服务器在新加坡,部分地区可能无法直连)
- 🌐 国内版:[ai.fastgpt.in](https://ai.fastgpt.in/)
- 🌍 海外版:[fastgpt.run](https://fastgpt.run/)
| | |
| ---------------------------------- | ---------------------------------- |
@@ -127,7 +128,7 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b
本仓库遵循 [FastGPT Open Source License](./LICENSE) 开源协议。
1. 允许作为后台服务直接商用,但不允许直接使用 SaaS 服务商用
1. 允许作为后台服务直接商用,但不允许提供 SaaS 服务。
2. 需保留相关版权信息。
3. 完整请查看 [FastGPT Open Source License](./LICENSE)
4. 联系方式yujinlong@sealos.io, [点击查看定价策略](https://doc.fastgpt.run/docs/commercial)

View File

@@ -15,19 +15,19 @@ FastGPT is a knowledge-based Q&A system built on the LLM, offers out-of-the-box
<p align="center">
<a href="https://fastgpt.run/">
<img height="21" src="https://img.shields.io/badge/Website-fff?style=flat-square&logo=spoj&logoColor=7d09f1" alt="cloud">
<img height="21" src="https://img.shields.io/badge/在线使用-d4eaf7?style=flat-square&logo=spoj&logoColor=7d09f1" alt="cloud">
</a>
<a href="https://doc.fastgpt.run/docs/intro">
<img height="21" src="https://img.shields.io/badge/Docs-7d09f1?style=flat-square" alt="document">
<img height="21" src="https://img.shields.io/badge/相关文档-7d09f1?style=flat-square" alt="document">
</a>
<a href="https://doc.fastgpt.run/docs/development">
<img height="21" src="https://img.shields.io/badge/Development-%23fff?style=flat-square&logo=xcode&logoColor=7d09f1" alt="development">
<img height="21" src="https://img.shields.io/badge/本地开发-%23d4eaf7?style=flat-square&logo=xcode&logoColor=7d09f1" alt="development">
</a>
<a href="/#-%E7%9B%B8%E5%85%B3%E9%A1%B9%E7%9B%AE">
<img height="21" src="https://img.shields.io/badge/Related Projects-7d09f1?style=flat-square" alt="project">
<img height="21" src="https://img.shields.io/badge/相关项目-7d09f1?style=flat-square" alt="project">
</a>
<a href="https://github.com/labring/FastGPT/blob/main/LICENSE">
<img height="21" src="https://img.shields.io/badge/License-Apache--2.0-ffffff?style=flat-square&labelColor=fff&color=7d09f1" alt="license">
<img height="21" src="https://img.shields.io/badge/License-Apache--2.0-ffffff?style=flat-square&labelColor=d4eaf7&color=7d09f1" alt="license">
</a>
</p>

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

View File

Before

Width:  |  Height:  |  Size: 256 KiB

After

Width:  |  Height:  |  Size: 256 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 177 KiB

After

Width:  |  Height:  |  Size: 970 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 382 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 289 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 153 KiB

After

Width:  |  Height:  |  Size: 103 KiB

View File

@@ -13,18 +13,20 @@ weight: 20
## 商业版
商业版最终交付版本功能与 https://fastgpt.run 完全一致,品牌内容可自定义。
商业版最终交付版本功能与 https://fastgpt.run 完全一致,品牌内容可自定义,商业授权时长同`License`有效期
{{% alert icon="🤖" context="warning" %}}
商业版开源版功能差异:(目前计划)
商业版开源版额外增加的内容:(目前计划)
1. 自定义 title 和 logo
1. 自定义 title 和 logo
2. 用户注册,支付 (已有微信扫码支付,后续会补充支付方式)
3. API 访问限制,可配置:额度、过期时间
4. 团队空间 (计划)
5. 完善的 OpenAPI计划
6. 高级编排额外插件(计划)
7. 后台管理系统
4. 分享链接限制可配置额度、过期时间、QPM、身份验证Hook
5. 内容审核(目前对接了百度
6. 团队空间 (计划)
7. 完善的 OpenAPI计划
8. 高级编排额外插件(计划)
9. 后台管理系统
a. 查询:用户、支付、应用、知识库
b. 变更:用户
c. 新增:用户
@@ -36,7 +38,7 @@ weight: 20
+ 使用 [Sealos 公有云](https://sealos.io)部署1万元/年/套 (无部署费用。赠送 8000 sealos 公有云额度,可用于 FastGPT 或其他云资源)。
+ 渠道商使用 Sealos 部署:返现 20% 成交额。
+ 私有服务器部署2万元/年/套(如需部署支持,按技术服务费计算)
+ 私有服务器部署2万元/年/套
+ 渠道商私有服务器部署1.3万元/年/套(渠道商合同单独约谈,累计 5 套以上可签)
#### 用户注册数量费用(按注册量算,不计量分享和 API
@@ -81,7 +83,7 @@ weight: 20
完整版应用 = 开源版镜像 + 商业版镜像
我们会提供一个商业版镜像给你使用,该镜像需要一个 license 启动,license 有效期为 1 年。此外,还会提供一个简单的后台管理系统(目前只设置了简单的查询功能)
我们会提供一个商业版镜像给你使用,该镜像需要一个 License 启动,License 有效期为 1 年。此外,还会提供一个简单的后台管理系统(目前只设置了简单的查询功能)
2. 二次开发如何操作?

View File

@@ -63,15 +63,15 @@ Authorization 为 sk-aaabbbcccdddeeefffggghhhiiijjjkkk。model 为刚刚在 One
```json
"ChatModels": [
//已有模型
//其他对话模型
{
"model": "chatglm2",
"name": "chatglm2",
"contextMaxToken": 8000,
"maxToken": 8000,
"price": 0,
"quoteMaxToken": 4000,
"maxTemperature": 1.2,
"price": 0,
"defaultSystem": ""
"defaultSystemChatPrompt": ""
}
],
"VectorModels": [

View File

@@ -107,11 +107,11 @@ Authorization 为 sk-aaabbbcccdddeeefffggghhhiiijjjkkk。model 为刚刚在 One
{
"model": "chatglm2",
"name": "chatglm2",
"contextMaxToken": 8000,
"maxToken": 8000,
"price": 0,
"quoteMaxToken": 4000,
"maxTemperature": 1.2,
"price": 0,
"defaultSystem": ""
"defaultSystemChatPrompt": ""
}
]
```

View File

@@ -23,35 +23,79 @@ weight: 520
"SystemParams": {
"vectorMaxProcess": 15, // 向量生成最大进程,结合数据库性能和 key 来设置
"qaMaxProcess": 15, // QA 生成最大进程,结合数据库性能和 key 来设置
"pgIvfflatProbe": 20 // pg vector 搜索探针。没有设置索引前可忽略,通常 50w 组以上才需要设置。
"pgHNSWEfSearch": 40 // pg vector 索引参数,越大精度高但速度慢
},
"ChatModels": [
{
"model": "gpt-3.5-turbo",
"name": "GPT35-4k",
"contextMaxToken": 4000, // 最大token均按 gpt35 计算
"model": "gpt-3.5-turbo", // 实际调用的模型
"name": "GPT35-4k", // 展示的名字
"maxToken": 4000, // 最大token均按 gpt35 计算
"quoteMaxToken": 2000, // 引用内容最大 token
"maxTemperature": 1.2, // 最大温度
"price": 0,
"defaultSystem": ""
"defaultSystemChatPrompt": ""
},
{
"model": "gpt-3.5-turbo-16k",
"name": "GPT35-16k",
"contextMaxToken": 16000,
"maxToken": 16000,
"quoteMaxToken": 8000,
"maxTemperature": 1.2,
"price": 0,
"defaultSystem": ""
"defaultSystemChatPrompt": ""
},
{
"model": "gpt-4",
"name": "GPT4-8k",
"contextMaxToken": 8000,
"maxToken": 8000,
"quoteMaxToken": 4000,
"maxTemperature": 1.2,
"price": 0,
"defaultSystem": ""
"defaultSystemChatPrompt": ""
}
],
"QAModels": [ // QA 拆分模型
{
"model": "gpt-3.5-turbo-16k",
"name": "GPT35-16k",
"maxToken": 16000,
"price": 0
}
],
"ExtractModels": [ // 内容提取模型
{
"model": "gpt-3.5-turbo-16k",
"name": "GPT35-16k",
"maxToken": 16000,
"price": 0,
"functionCall": true, // 是否支持 function call
"functionPrompt": "" // 自定义非 function call 提示词
}
],
"CQModels": [ // Classify Question: 问题分类模型
{
"model": "gpt-3.5-turbo-16k",
"name": "GPT35-16k",
"maxToken": 16000,
"price": 0,
"functionCall": true,
"functionPrompt": ""
},
{
"model": "gpt-4",
"name": "GPT4-8k",
"maxToken": 8000,
"price": 0,
"functionCall": true,
"functionPrompt": ""
}
],
"QGModels": [ // Question Generation: 生成下一步指引模型
{
"model": "gpt-3.5-turbo",
"name": "GPT35-4k",
"maxToken": 4000,
"price": 0
}
],
"VectorModels": [
@@ -62,36 +106,6 @@ weight: 520
"defaultToken": 500,
"maxToken": 3000
}
],
"QAModel": { // QA 拆分模型
"model": "gpt-3.5-turbo-16k",
"name": "GPT35-16k",
"maxToken": 16000,
"price": 0
},
"ExtractModel": { // 内容提取模型
"model": "gpt-3.5-turbo-16k",
"functionCall": true, // 是否使用 functionCall
"name": "GPT35-16k",
"maxToken": 16000,
"price": 0,
"prompt": ""
},
"CQModel": { // Classify Question: 问题分类模型
"model": "gpt-3.5-turbo-16k",
"functionCall": true,
"name": "GPT35-16k",
"maxToken": 16000,
"price": 0,
"prompt": ""
},
"QGModel": { // Question Generation: 生成下一步指引模型
"model": "gpt-3.5-turbo",
"name": "GPT35-4k",
"maxToken": 4000,
"price": 0,
"prompt": "",
"functionCall": false
}
]
}
```

View File

@@ -66,7 +66,7 @@ git clone git@github.com:<github_username>/FastGPT.git
- `vectorMaxProcess`: 向量生成最大进程,根据数据库和 key 的并发数来决定,通常单个 120 号2c4g 服务器设置 10~15。
- `qaMaxProcess`: QA 生成最大进程
- `pgIvfflatProbe`: PostgreSQL vector 搜索探针,没有添加 vector 索引时可忽略
- `pgHNSWEfSearch`: PostgreSQL vector 索引参数,越大搜索精度越高但是速度越慢,具体可看 pgvector 官方说明
### 5. 运行
@@ -94,7 +94,16 @@ docker build -t dockername/fastgpt --build-arg name=app .
如果遇到问题,比如合并冲突或不知道如何打开拉取请求,请查看 GitHub 的[拉取请求教程](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests),了解如何解决合并冲突和其他问题。一旦您的 PR 被合并,您将自豪地被列为[贡献者表](https://github.com/labring/FastGPT/graphs/contributors)中的一员。
## 加入社区
## QA
### 本地数据库无法连接
1. 如果你是连接远程的数据库,先检查对应的端口是否开放。
2. 如果是本地运行的数据库,可尝试`host`改成`localhost``127.0.0.1`
### 加入社区
遇到困难了吗?有任何问题吗? 加入微信群与开发者和用户保持沟通。

View File

@@ -120,7 +120,7 @@ event: answer
data: [DONE]
event: appStreamResponse
data: [{"moduleName":"KB Search","price":1.2000000000000002,"model":"Embedding-2","tokens":6,"similarity":0.61,"limit":3},{"moduleName":"AI Chat","price":463.5,"model":"FastAI-4k","tokens":309,"question":"导演是谁","answer":"电影《铃芽之旅》的导演是新海诚。","maxToken":2050,"quoteList":[{"kb_id":"646627f4f7b896cfd8910e38","id":"8099","q":"本作的主人公是谁?","a":"本作的主人公是名叫铃芽的少女。","source":"手动修改"},{"kb_id":"646627f4f7b896cfd8910e38","id":"8686","q":"电影《铃芽之旅》男主角是谁?","a":"电影《铃芽之旅》男主角是宗像草太,由松村北斗配音。","source":""},{"kb_id":"646627f4f7b896cfd8910e38","id":"19339","q":"电影《铃芽之旅》的导演是谁22","a":"电影《铃芽之旅》的导演是新海诚。","source":"手动修改"}],"completeMessages":[{"obj":"System","value":"下面是知识库内容:\n1. [本作的主人公是谁?\n本作的主人公是名叫铃芽的少女。]\n2. [电影《铃芽之旅》男主角是谁?\n电影《铃芽之旅》男主角是宗像草太由松村北斗配音。]\n3. [电影《铃芽之旅》的导演是谁22\n电影《铃芽之旅》的导演是新海诚。]\n"},{"obj":"System","value":"1.请记住,你的身份是百度的下一代知识增强语言模型,能够完全根据知识库提供的内容回答问题。\n\n2. 你忘记了关于电影《铃芽之旅》以外的内容。"},{"obj":"System","value":"你仅回答关于电影《玲芽之旅》的问题,其余问题直接回复: 我不清楚。"},{"obj":"Human","value":"导演是谁"},{"obj":"AI","value":"电影《铃芽之旅》的导演是新海诚。"}]}]
data: [{"moduleName":"KB Search","price":1.2000000000000002,"model":"Embedding-2","tokens":6,"similarity":0.61,"limit":3},{"moduleName":"AI Chat","price":463.5,"model":"FastAI-4k","tokens":309,"question":"导演是谁","answer":"电影《铃芽之旅》的导演是新海诚。","maxToken":2050,"quoteList":[{"dataset_id":"646627f4f7b896cfd8910e38","id":"8099","q":"本作的主人公是谁?","a":"本作的主人公是名叫铃芽的少女。","source":"手动修改"},{"dataset_id":"646627f4f7b896cfd8910e38","id":"8686","q":"电影《铃芽之旅》男主角是谁?","a":"电影《铃芽之旅》男主角是宗像草太,由松村北斗配音。","source":""},{"dataset_id":"646627f4f7b896cfd8910e38","id":"19339","q":"电影《铃芽之旅》的导演是谁22","a":"电影《铃芽之旅》的导演是新海诚。","source":"手动修改"}],"completeMessages":[{"obj":"System","value":"下面是知识库内容:\n1. [本作的主人公是谁?\n本作的主人公是名叫铃芽的少女。]\n2. [电影《铃芽之旅》男主角是谁?\n电影《铃芽之旅》男主角是宗像草太由松村北斗配音。]\n3. [电影《铃芽之旅》的导演是谁22\n电影《铃芽之旅》的导演是新海诚。]\n"},{"obj":"System","value":"1.请记住,你的身份是百度的下一代知识增强语言模型,能够完全根据知识库提供的内容回答问题。\n\n2. 你忘记了关于电影《铃芽之旅》以外的内容。"},{"obj":"System","value":"你仅回答关于电影《玲芽之旅》的问题,其余问题直接回复: 我不清楚。"},{"obj":"Human","value":"导演是谁"},{"obj":"AI","value":"电影《铃芽之旅》的导演是新海诚。"}]}]
```
{{< /markdownify >}}
@@ -150,21 +150,21 @@ data: [{"moduleName":"KB Search","price":1.2000000000000002,"model":"Embedding-2
"maxToken": 2050,
"quoteList": [
{
"kb_id": "646627f4f7b896cfd8910e38",
"dataset_id": "646627f4f7b896cfd8910e38",
"id": "8099",
"q": "本作的主人公是谁?",
"a": "本作的主人公是名叫铃芽的少女。",
"source": "手动修改"
},
{
"kb_id": "646627f4f7b896cfd8910e38",
"dataset_id": "646627f4f7b896cfd8910e38",
"id": "8686",
"q": "电影《铃芽之旅》男主角是谁?",
"a": "电影《铃芽之旅》男主角是宗像草太,由松村北斗配音。",
"source": ""
},
{
"kb_id": "646627f4f7b896cfd8910e38",
"dataset_id": "646627f4f7b896cfd8910e38",
"id": "19339",
"q": "电影《铃芽之旅》的导演是谁22",
"a": "电影《铃芽之旅》的导演是新海诚。",
@@ -225,9 +225,9 @@ data: [{"moduleName":"KB Search","price":1.2000000000000002,"model":"Embedding-2
此部分 API 需使用全局通用的 API Key。
{{% /alert %}}
| 如何获取知识库IDkbId | 如何获取文件IDfile_id |
| 如何获取知识库IDdatasetId | 如何获取文件IDfile_id |
| --------------------- | --------------------- |
| ![](/imgs/getKbId.png) | ![](/imgs/getfile_id.png) |
| ![](/imgs/getDatasetId.png) | ![](/imgs/getfile_id.png) |
### 知识库添加数据
@@ -241,7 +241,7 @@ curl --location --request POST 'https://fastgpt.run/api/core/dataset/data/pushDa
--header 'Authorization: Bearer apikey' \
--header 'Content-Type: application/json' \
--data-raw '{
    "kbId": "64663f451ba1676dbdef0499",
    "collectionId": "64663f451ba1676dbdef0499",
"mode": "index",
"prompt": "qa 拆分引导词index 模式下可以忽略",
"billId": "可选。如果有这个值,本次的数据会被聚合到一个订单中,这个值可以重复使用。可以参考 [创建训练订单] 获取该值。",
@@ -268,7 +268,7 @@ curl --location --request POST 'https://fastgpt.run/api/core/dataset/data/pushDa
```json
{
"kbId": "知识库的ID可以在知识库详情查看。",
"collectionId": "文件的ID参考上面的第二张图",
"mode": "index | qa ", // index 模式: 直接将 q 转成向量存起来a 直接入库。qa 模式: 只关注 data 里的 q将 q 丢给大模型,让其根据 prompt 拆分成 qa 问答对。
"prompt": "拆分提示词,需严格按照模板,建议不要传入。",
"data": [
@@ -351,7 +351,7 @@ curl --location --request POST 'https://fastgpt.run/api/core/dataset/searchTest'
--header 'Authorization: Bearer apiKey' \
--header 'Content-Type: application/json' \
--data-raw '{
"kbId": "xxxxx",
"datasetId": "知识库的ID",
"text": "导演是谁"
}'
```
@@ -487,21 +487,21 @@ curl --location --request POST '{{host}}/shareAuth/finish' \
"maxToken": 2050,
"quoteList": [
{
"kb_id": "646627f4f7b896cfd8910e38",
"dataset_id": "646627f4f7b896cfd8910e38",
"id": "8099",
"q": "本作的主人公是谁?",
"a": "本作的主人公是名叫铃芽的少女。",
"source": "手动修改"
},
{
"kb_id": "646627f4f7b896cfd8910e38",
"dataset_id": "646627f4f7b896cfd8910e38",
"id": "8686",
"q": "电影《铃芽之旅》男主角是谁?",
"a": "电影《铃芽之旅》男主角是宗像草太,由松村北斗配音。",
"source": ""
},
{
"kb_id": "646627f4f7b896cfd8910e38",
"dataset_id": "646627f4f7b896cfd8910e38",
"id": "19339",
"q": "电影《铃芽之旅》的导演是谁22",
"a": "电影《铃芽之旅》的导演是新海诚。",

View File

@@ -9,6 +9,8 @@ weight: 720
## 准备条件
服务器要求2C2G 起
### 1. 准备好代理环境(国外服务器可忽略)
确保可以访问 OpenAI具体方案可以参考[代理方案](/docs/installation/proxy/)。或直接在 Sealos 上 [部署 OneAPI](/docs/installation/one-api),既解决代理问题也能实现多 Key 轮询、接入其他大模型。
@@ -70,9 +72,9 @@ brew install orbstack
依次执行下面命令,创建 FastGPT 文件并拉取`docker-compose.yml``config.json`,执行完后目录下会有 2 个文件。
非 Linux 环境,可手动创建目录,并下载这2个文件。
非 Linux 环境或无法访问外网环境,可手动创建一个目录,并下载下面2个链接的文件: [docker-compose.yml](https://github.com/labring/FastGPT/blob/main/files/deploy/fastgpt/docker-compose.yml),[config.json](https://github.com/labring/FastGPT/blob/main/projects/app/data/config.json)
**注意: 配置文件中 Mongo 为 5.x部分服务器不支持需手动更改其镜像版本为 4.4.24**
**注意: `docker-compose.yml` 配置文件中 Mongo 为 5.x部分服务器不支持需手动更改其镜像版本为 4.4.24**
```bash
mkdir fastgpt
@@ -118,7 +120,12 @@ docker-compose up -d
1. `docker logs fastgpt` 可以查看日志,在启动容器后,第一次请求网页,会进行配置文件读取,可以看看有没有读取成功以及有无错误日志。
2. `docker exec -it fastgpt sh` 进入 FastGPT 容器,可以通过`ls data`查看目录下是否成功挂载`config.json`文件。可通过`cat data/config.json`查看配置文件。
### 为什么无法连接 oneapi 和 本地模型镜像。
**可能不生效的原因**
1. 挂载目录不正确
2. 配置文件不正确,日志中会提示`invalid json`,配置文件需要是标准的 JSON 文件。
### 为什么无法连接`本地模型`镜像。
`docker-compose.yml`中使用了桥接的模式建立了`fastgpt`网络如想通过0.0.0.0或镜像名访问其它镜像,需将其它镜像也加入到网络中。
@@ -132,6 +139,21 @@ docker-compose 端口定义为:`映射端口:运行端口`。
(自行补习 docker 基本知识)
### relation "modeldata" does not exist
PG 数据库没有连接上/初始化失败可以查看日志。FastGPT 会在每次连接上 PG 时进行表初始化,如果报错会有对应日志。
1. 检查数据库容器是否正常启动
2. 非 docker 部署的,需要手动安装 pg vector 插件
3. 查看 fastgpt 日志,有没有相关报错
### Operation `auth_codes.findOne()` buffering timed out after 10000ms
mongo连接失败检查
1. mongo 服务有没有起来(有些 cpu 不支持 AVX无法用 mongo5需要换成 mongo4.x可以dockerhub找个最新的4.x修改镜像版本重新运行
2. 环境变量账号密码注意host和port
### 错误排查方式
遇到问题先按下面方式排查。

View File

@@ -99,12 +99,12 @@ CHAT_API_KEY=sk-xxxxxx
{
"model": "ERNIE-Bot", // 这里的模型需要对应 One API 的模型
"name": "文心一言", // 对外展示的名称
"contextMaxToken": 4000, // 最大长下文 token无论什么模型都按 GPT35 的计算。GPT 外的模型需要自行大致计算下这个值。可以调用官方接口去比对 Token 的倍率,然后在这里粗略计算。
"maxToken": 4000, // 最大长下文 token无论什么模型都按 GPT35 的计算。GPT 外的模型需要自行大致计算下这个值。可以调用官方接口去比对 Token 的倍率,然后在这里粗略计算。
// 例如:文心一言的中英文 token 基本是 1:1而 GPT 的中文 Token 是 2:1如果文心一言官方最大 Token 是 4000那么这里就可以填 8000保险点就填 7000.
"price": 0, // 1个token 价格 => 1.5 / 100000 * 1000 = 0.015元/1k token
"quoteMaxToken": 2000, // 引用知识库的最大 Token
"maxTemperature": 1, // 最大温度
"price": 0, // 1个token 价格 => 1.5 / 100000 * 1000 = 0.015元/1k token
"defaultSystem": "" // 默认的系统提示词
"defaultSystemChatPrompt": "" // 默认的系统提示词
}
...
],

View File

@@ -11,7 +11,7 @@ weight: 840
发起 1 个 HTTP 请求({{rootkey}} 替换成环境变量里的`rootkey`{{host}}替换成自己域名)
1. https://xxxxx/api/admin/initv445
1. https://xxxxx/api/admin/initv447
```bash
curl --location --request POST 'https://{{host}}/api/admin/initv447' \
@@ -28,4 +28,4 @@ curl --location --request POST 'https://{{host}}/api/admin/initv447' \
1. 优化了数据库文件 crud。
2. 兼容链接读取,作为 source。
3. 区分手动录入和标注,可追数据至某个文件。
4. 升级 openai sdk。
4. 升级 openai sdk。

View File

@@ -0,0 +1,93 @@
---
title: 'V4.5(需进行较为复杂更新)'
description: 'FastGPT V4.5 更新'
icon: 'upgrade'
draft: false
toc: true
weight: 839
---
FastGPT V4.5 引入 PgVector0.5 版本的 HNSW 索引,极大的提高了知识库检索的速度,比起`IVFFlat`索引大致有3~10倍的性能提升可轻松实现百万数据毫秒级搜索。缺点在于构建索引的速度非常慢4c16g 500w 组数据使用`并行构建`大约花了 48 小时。具体参数配置可参考 [PgVector官方](https://github.com/pgvector/pgvector)
下面需要对数据库进行一些操作升级:
## PgVector升级Sealos 部署方案
1. 点击[Sealos桌面](https://cloud.sealos.io)的数据库应用。
2. 点击【pg】数据库的详情。
3. 点击右上角的重启,等待重启完成。
4. 点击左侧的一键链接,等待打开 Terminal。
5. 依次输入下方 sql 命令
```sql
-- 升级插件名
ALTER EXTENSION vector UPDATE;
-- 插件是否升级成功成功的话vector插件版本为 0.5.0,旧版的为 0.4.1
\dx
-- 下面两个语句会设置 pg 在构建索引时可用的内存大小,需根据自身的数据库规格来动态配置,可配置为 1/4 的内存大小
alter system set maintenance_work_mem = '2400MB';
select pg_reload_conf();
-- 重构数据库索引和排序
REINDEX DATABASE postgres;
-- 开始构建索引,该索引构建时间非常久,直接点击右上角的叉,退出 Terminal 即可
CREATE INDEX CONCURRENTLY vector_index ON modeldata USING hnsw (vector vector_ip_ops) WITH (m = 16, ef_construction = 64);
-- 可以再次点击一键链接,进入 Terminal输入下方命令如果看到 "vector_index" hnsw (vector vector_ip_ops) WITH (m='16', ef_construction='64') 则代表构建完成(注意,后面没有 INVALID
\d modeldata
```
| | |
| --------------------- | --------------------- |
| ![](/imgs/v45-1.png) | ![](/imgs/v45-2.png) |
| ![](/imgs/v45-3.png) | ![](/imgs/v45-4.png) |
## PgVector升级Docker-compose.yml 部署方案
下面的命令是基于给的 docker-compose 模板,如果数据库账号密码更换了,请自行调整。
1. 修改 `docker-compose.yml` 中pg的镜像版本改成 `ankane/pgvector:v0.5.0``registry.cn-hangzhou.aliyuncs.com/fastgpt/pgvector:v0.5.0`
2. 重启 pg 容器(docker-compose pull && docker-compose up -d),等待重启完成。
3. 进入容器: `docker exec -it pg bash`
4. 连接数据库: `psql 'postgresql://username:password@localhost:5432/postgres'`
5. 执行下面 sql 命令
```sql
-- 升级插件名
ALTER EXTENSION vector UPDATE;
-- 插件是否升级成功成功的话vector插件版本为 0.5.0,旧版的为 0.4.2
\dx
-- 下面两个语句会设置 pg 在构建索引时可用的内存大小,需根据自身的数据库规格来动态配置,可配置为 1/4 的内存大小
alter system set maintenance_work_mem = '2400MB';
select pg_reload_conf();
-- 重构数据库索引和排序
REINDEX DATABASE postgres;
ALTER DATABASE postgres REFRESH COLLATION VERSION;
-- 开始构建索引,该索引构建时间非常久,直接关掉终端即可,不要使用 ctrl+c 关闭
CREATE INDEX CONCURRENTLY vector_index ON modeldata USING hnsw (vector vector_ip_ops) WITH (m = 16, ef_construction = 64);
-- 可以再次连接数据库,输入下方命令。如果看到 "vector_index" hnsw (vector vector_ip_ops) WITH (m='16', ef_construction='64') 则代表构建完成(注意,后面没有 INVALID
\d modeldata
```
## 版本新功能介绍
### Fast GPT V4.5
1. 新增 - 升级 PgVector 插件,引入 HNSW 索引,极大加快的知识库搜索速度。
2. 新增 - AI对话模块增加【返回AI内容】选项可控制 AI 的内容不直接返回浏览器。
3. 新增 - 支持问题分类选择模型
4. 优化 - TextSplitter采用递归拆解法。
5. 优化 - 高级编排 UX 性能
6. 修复 - 分享链接鉴权问题
## 该版本需要修改 `config.json` 文件
最新配置可参考: [V45版本最新 config.json](/docs/development/configuration)

View File

@@ -0,0 +1,35 @@
---
title: 'V4.5.1(需进行初始化)'
description: 'FastGPT V4.5.1 更新'
icon: 'upgrade'
draft: false
toc: true
weight: 839
---
## 执行初始化 API
发起 1 个 HTTP 请求({{rootkey}} 替换成环境变量里的`rootkey`{{host}}替换成自己域名)
1. https://xxxxx/api/admin/initv451
```bash
curl --location --request POST 'https://{{host}}/api/admin/initv451' \
--header 'rootkey: {{rootkey}}' \
--header 'Content-Type: application/json'
```
初始化内容:
1. rename 数据库字段
2. 初始化 Mongo APP 表中知识库的相关字段
3. 初始化 PG 和 Mongo 的内容,为每个文件创建一个集合(存储 Mongo 中),并反馈赋值给 PG。
**该初始化接口可能速度很慢,返回超时不用管,注意看日志即可**
## 功能介绍
### Fast GPT V4.5.1
1. 新增知识库文件夹管理
2. 修复了 openai4.x sdk 无法兼容 oneapi 的智谱和阿里的接口。
3. 修复部分模块无法触发完成事件

View File

@@ -30,6 +30,10 @@ weight: 10
| 星火2.0 - 对话 | 0.01 |
| chatglm_pro - 对话 | 0.01 |
| 通义千问 - 对话 | 0.01 |
| 问题分类 | 0.03 |
| 内容提取 | 0.03 |
| 下一步指引 | 0.015 |
{{< /table >}}
{{% alert context="warning" %}}

View File

@@ -0,0 +1,54 @@
---
title: '快速上手'
description: '快速体验 FastGPT 基础功能'
icon: 'rocket_launch'
draft: false
toc: true
weight: 30
---
更多使用技巧,[查看视屏教程](https://www.bilibili.com/video/BV1n34y1A7Bo/?spm_id_from=333.337.search-card.all.click&vd_source=903c2b09b7412037c2eddc6a8fb9828b)
## 知识库
开始前请准备一份测试电子文档WORDPDFTXTexcelmarkdown 都可以,比如公司休假制度,不涉密的销售说辞,产品知识等等。
这里使用 FastGPT 中文 README 文件为例。
首先我们需要创建一个知识库。
![](/imgs/create-rep.png)
知识库创建完之后我们需要上传一点内容。
上传内容这里有四种模式:
- 手动输入:手动输入问答对,是最精准的数据
- QA 拆分选择文本文件让AI自动生成问答对
- 直接分段:选择文本文件,直接将其按分段进行处理
- CSV 导入:批量导入问答对
这里,我们选择 QA 拆分,让 AI 自动生成问答,若问答质量不高,可以后期手动修改。
![](/imgs/upload-data.png)
点击上传后我们需要等待数据处理完成,等到我们上传的文件状态为可用。
![](/imgs/upload-data2.png)
## 应用
点击「应用」按钮来新建一个应用,这里有四个模板,我们选择「知识库 + 对话引导」。
![](/imgs/create-app.png)
应用创建后来再应用详情页找到「知识库」模块,把我们刚刚创建的知识库添加进去。
![](/imgs/create-app2.png)
添加完知识库后记得点击「保存并预览」,这样我们的应用就和知识库关联起来了。
![](/imgs/create-app3.png)
然后我们就可以愉快的开始聊天啦。
![](/imgs/create-app4.png)

View File

@@ -0,0 +1,94 @@
---
title: "AI 高级配置说明"
description: "FastGPT AI 高级配置说明"
icon: "sign_language"
draft: false
toc: true
weight: 310
---
在 FastGPT 的 AI 对话模块中,有一个 AI 高级配置,里面包含了 AI 模型的参数配置,本文详细介绍这些配置的含义。
# 返回AI内容
这是一个开关,打开的时候,当 AI 对话模块运行时会将其输出的内容返回到浏览器API响应如果关闭AI 输出的内容不会返回到浏览器但是生成的内容仍可以通过【AI回复】进行输出。你可以将【AI回复】连接到其他模块中。
# 温度
可选范围0-10约大代表生成的内容约自由扩散越小代表约严谨。调节能力有限知识库问答场景通常设置为0。
# 回复上限
控制 AI 回复的最大 Tokens较小的值可以一定程度上减少 AI 的废话,但也可能导致 AI 回复不完整。
# 引用模板 & 引用提示词
这两个参数与知识库问答场景相关,可以控制知识库相关的提示词。
## AI 对话消息组成
想使用明白这两个变量,首先要了解传递传递给 AI 模型的消息格式。它是一个数组FastGPT 中这个数组的组成形式为:
```json
[
config.json
]
```
{{% alert icon="🍅" context="success" %}}
Tips: 可以通过点击上下文按键查看完整的
{{% /alert %}}
## 引用模板和提示词设计
引用模板和引用提示词通常是成对出现,引用提示词依赖引用模板。
FastGPT 知识库采用 QA 对(不一定都是问答格式,仅代表两个变量)的格式存储,在转义成字符串时候会根据**引用模板**来进行格式化。知识库包含 3 个变量: q, a, file_id, index, source可以通过 {{q}} {{a}} {{file_id}} {{index}} {{source}} 按需引入。下面一个模板例子:
**引用模板**
```
{instruction:"{{q}}",output:"{{a}}",source:"{{source}}"}
```
搜索到的知识库,会自动将 q,a,source 替换成对应的内容。每条搜索到的内容,会通过 `\n` 隔开。例如:
```
{instruction:"电影《铃芽之旅》的导演是谁?",output:"电影《铃芽之旅》的导演是新海诚。",source:"手动输入"}
{instruction:"本作的主人公是谁?",output:"本作的主人公是名叫铃芽的少女。",source:""}
{instruction:"电影《铃芽之旅》男主角是谁?",output:"电影《铃芽之旅》男主角是宗像草太,由松村北斗配音。",source:""}
{instruction:"电影《铃芽之旅》的编剧是谁22",output:"新海诚是本片的编剧。",source:"手动输入"}
```
**引用提示词**
引用模板需要和引用提示词一起使用,提示词中可以写引用模板的格式说明以及对话的要求等。可以使用 {{quote}} 来使用 **引用模板**,使用 {{question}} 来引入问题。例如:
```
你的背景知识:
"""
{{quote}}
"""
对话要求:
1. 背景知识是最新的,其中 instruction 是相关介绍output 是预期回答或补充。
2. 使用背景知识回答问题。
3. 背景知识无法回答问题时,你可以礼貌的的回答用户问题。
我的问题是:"{{question}}"
```
转义后则为:
```
你的背景知识:
"""
{instruction:"电影《铃芽之旅》的导演是谁?",output:"电影《铃芽之旅》的导演是新海诚。",source:"手动输入"}
{instruction:"本作的主人公是谁?",output:"本作的主人公是名叫铃芽的少女。",source:""}
{instruction:"电影《铃芽之旅》男主角是谁?",output:"电影《铃芽之旅》男主角是宗像草太,由松村北斗配音}
"""
对话要求:
1. 背景知识是最新的,其中 instruction 是相关介绍output 是预期回答或补充。
2. 使用背景知识回答问题。
3. 背景知识无法回答问题时,你可以礼貌的的回答用户问题。
我的问题是:"{{question}}"
```

View File

@@ -11,6 +11,8 @@ weight: 322
[Feishu OpenAI GitHub 地址](https://github.com/ConnectAI-E/Feishu-OpenAI)
[查看视频教程](https://www.bilibili.com/video/BV1Su4y1r7R3/?spm_id_from=333.999.list.card_archive.click)
由于 FastGPT 的 API 接口和 OpenAI 的规范一致,可以无需变更第三方应用即可使用 FastGPT 上编排好的应用。API 使用可参考 [这篇文章](/docs/use-cases/openapi/)。编排示例,可参考 [高级编排介绍](/docs/workflow/intro)
## 1. 获取 FastGPT 的 OpenAPI 秘钥

View File

@@ -1,109 +0,0 @@
---
title: "提示词 & 引用提示词"
description: "FastGPT 提示词 & 引用提示词说明"
icon: "sign_language"
draft: false
toc: true
weight: 310
---
限定词从 V4.4.3 版本后去除,被“引用提示词”和“引用模板”替代。
# AI 对话消息组成
传递给 AI 模型的消息是一个数组FastGPT 中这个数组的组成形式为:
```json
[
config.json
]
```
{{% alert icon="🍅" context="success" %}}
Tips: 可以通过点击上下文按键查看完整的
{{% /alert %}}
# 引用模板和提示词设计
知识库采用 QA 对的格式存储,在转义成字符串时候会根据**引用模板**来进行格式化。知识库包含 3 个变量: q,a 和 source可以通过 {{q}} {{a}} {{source}} 按需引入。下面一个模板例子:
**引用模板**
```
{instruction:"{{q}}",output:"{{a}}",source:"{{source}}"}
```
搜索到的知识库,会自动将 q,a,source 替换成对应的内容。每条搜索到的内容,会通过 `\n` 隔开。例如:
```
{instruction:"电影《铃芽之旅》的导演是谁?",output:"电影《铃芽之旅》的导演是新海诚。",source:"手动输入"}
{instruction:"本作的主人公是谁?",output:"本作的主人公是名叫铃芽的少女。",source:""}
{instruction:"电影《铃芽之旅》男主角是谁?",output:"电影《铃芽之旅》男主角是宗像草太,由松村北斗配音。",source:""}
{instruction:"电影《铃芽之旅》的编剧是谁22",output:"新海诚是本片的编剧。",source:"手动输入"}
```
**引用提示词**
引用模板需要和引用提示词一起使用,提示词中可以写引用模板的格式说明以及对话的要求等。可以使用 {{quote}} 来使用 **引用模板**,使用 {{question}} 来引入问题。例如:
```
你的背景知识:
"""
{{quote}}
"""
对话要求:
1. 背景知识是最新的,其中 instruction 是相关介绍output 是预期回答或补充。
2. 使用背景知识回答问题。
3. 背景知识无法回答问题时,你可以礼貌的的回答用户问题。
我的问题是:"{{question}}"
```
# 提示词案例
## 仅回复知识库里的内容
**引用提示词**里添加:
```
你的背景知识:
"""
{{quote}}
"""
对话要求:
1. 回答前,请先判断背景知识是否足够回答问题,如果无法回答,请直接回复:“对不起,我无法回答你的问题~”。
2. 背景知识是最新的,其中 instruction 是相关介绍output 是预期回答或补充。
3. 使用背景知识回答问题。
我的问题是:"{{question}}"
```
## 说明引用来源
**引用模板:**
```
{instruction:"{{q}}",output:"{{a}}",source:"{{source}}"}
```
**引用提示词:**
```
你的背景知识:
"""
{{quote}}
"""
对话要求:
1. 背景知识是最新的,其中 instruction 是相关介绍output 是预期回答或补充source是背景来源。
2. 使用背景知识回答问题。
3. 在回答问题后,你需要给出本次回答对应的背景来源,来源展示格式如下:
这是AI作答。本次知识来源
1. source1
2. source2
......
我的问题是:"{{question}}"
```

View File

@@ -232,7 +232,7 @@ weight: 142
"outputs": [
{
"key": "answerText",
"label": "模型回复",
"label": "AI回复",
"description": "将在 stream 回复完毕后触发",
"valueType": "string",
"type": "source",

View File

@@ -432,7 +432,7 @@ export default async function (ctx: FunctionContext) {
"outputs": [
{
"key": "answerText",
"label": "模型回复",
"label": "AI回复",
"description": "直接响应,无需配置",
"type": "hidden",
"targets": []

View File

@@ -527,7 +527,7 @@ HTTP 模块允许你调用任意 POST 类型的 HTTP 接口,从而实验一些
{
"moduleId": "zltb5l",
"name": "知识库搜索",
"flowType": "kbSearchNode",
"flowType": "datasetSearchNode",
"showStatus": true,
"position": {
"x": 1634.995464753433,
@@ -751,7 +751,7 @@ HTTP 模块允许你调用任意 POST 类型的 HTTP 接口,从而实验一些
"outputs": [
{
"key": "answerText",
"label": "模型回复",
"label": "模型AI回复回复",
"description": "将在 stream 回复完毕后触发",
"valueType": "string",
"type": "source",

View File

@@ -97,7 +97,7 @@ weight: 144
{
"moduleId": "nkxlso",
"name": "知识库搜索",
"flowType": "kbSearchNode",
"flowType": "datasetSearchNode",
"showStatus": true,
"position": {
"x": 1542.6434554710224,
@@ -313,7 +313,7 @@ weight: 144
"outputs": [
{
"key": "answerText",
"label": "模型回复",
"label": "AI回复",
"description": "将在 stream 回复完毕后触发",
"valueType": "string",
"type": "source",

View File

@@ -745,7 +745,7 @@ PS2配置中的问题分类还包含着“联网搜索”这个是另一
"outputs": [
{
"key": "answerText",
"label": "模型回复",
"label": "AI回复",
"description": "将在 stream 回复完毕后触发",
"valueType": "string",
"type": "source",
@@ -903,7 +903,7 @@ PS2配置中的问题分类还包含着“联网搜索”这个是另一
"outputs": [
{
"key": "answerText",
"label": "模型回复",
"label": "AI回复",
"description": "将在 stream 回复完毕后触发",
"valueType": "string",
"type": "source",
@@ -1117,7 +1117,7 @@ PS2配置中的问题分类还包含着“联网搜索”这个是另一
"outputs": [
{
"key": "answerText",
"label": "模型回复",
"label": "AI回复",
"description": "将在 stream 回复完毕后触发",
"valueType": "string",
"type": "source",
@@ -1484,7 +1484,7 @@ PS2配置中的问题分类还包含着“联网搜索”这个是另一
"outputs": [
{
"key": "answerText",
"label": "模型回复",
"label": "AI回复",
"description": "将在 stream 回复完毕后触发",
"valueType": "string",
"type": "source",

View File

@@ -9,6 +9,8 @@ weight: 110
FastGPT 从 V4 版本开始采用新的交互方式来构建 AI 应用。使用了 Flow 节点编排的方式来实现复杂工作流,提高可玩性和扩展性。但同时也提高了上手的门槛,有一定开发背景的用户使用起来会比较容易。
[查看视频教程](https://www.bilibili.com/video/BV1aB4y1Z7Hy/?spm_id_from=333.999.list.card_archive.click&vd_source=903c2b09b7412037c2eddc6a8fb9828b)
![](/imgs/flow-intro1.png)
## 什么是模块?

View File

@@ -42,17 +42,17 @@ weight: 123
```ts
type DataType = {
kb_id?: string;
dataset_id?: string;
id?: string;
q: string;
a: string;
source?: string;
};
// 如果是外部引入的内容,尽量不要携带 kb_id 和 id
// 如果是外部引入的内容,尽量不要携带 dataset_id 和 id
const quoteList: DataType[] = [
{ kb_id: '11', id: '222', q: '你还', a: '哈哈', source: '' },
{ kb_id: '11', id: '333', q: '你还', a: '哈哈', source: '' },
{ kb_id: '11', id: '444', q: '你还', a: '哈哈', source: '' }
{ dataset_id: '11', id: '222', q: '你还', a: '哈哈', source: '' },
{ dataset_id: '11', id: '333', q: '你还', a: '哈哈', source: '' },
{ dataset_id: '11', id: '444', q: '你还', a: '哈哈', source: '' }
];
```

View File

@@ -110,8 +110,8 @@ defaultContentLanguage = 'zh-cn'
listDescTrunc = 100 # Number of characters by which to truncate the list card description
[params.flexsearch] # Parameters for FlexSearch
enabled = true
tokenize = "full"
# enabled = true
# tokenize = "full"
# optimize = true
# cache = 100
# minQueryChar = 3 # default is 0 (disabled)
@@ -119,9 +119,9 @@ defaultContentLanguage = 'zh-cn'
# searchSectionsIndex = []
[params.docsearch] # Parameters for DocSearch
# appID = "O2QIOCBDAK" # Algolia Application ID
# apiKey = "fdc60eee76a72a35d739b54521498b77" # Algolia Search-Only API (Public) Key
# indexName = "prod_lotusdocs.dev" # Index Name to perform search on (or set env variable HUGO_PARAM_DOCSEARCH_indexName)
appID = "5BEWEMH0YA" # Algolia Application ID
apiKey = "14834e919a87217d919d6d881fcacac3" # Algolia Search-Only API (Public) Key
indexName = "fastgpt" # Index Name to perform search on (or set env variable HUGO_PARAM_DOCSEARCH_indexName)
[params.analytics] # Parameters for Analytics (Google, Plausible)
# plausibleURL = "/docs/s" # (or set via env variable HUGO_PARAM_ANALYTICS_plausibleURL)

View File

@@ -19,6 +19,15 @@
[search_no_results]
other = "没有结果:"
[search_no_recent_searches]
other = "没有最近搜索"
[search_try_search]
other = "试试搜索"
[search_search_by]
other = "搜索提供"
[feedback_yes]
other = "Yes"

View File

@@ -0,0 +1,39 @@
<script>
window.addEventListener('DOMContentLoaded', function() {
// DocSearch Config
docsearch({
container: '#docsearch',
appId: '{{ .Site.Params.docsearch.appID }}',
apiKey: '{{ .Site.Params.docsearch.apiKey }}',
indexName: '{{ .Site.Params.docsearch.indexName }}',
placeholder: '{{ i18n "search_title" }}',
translations: {
button: {
buttonText: '{{ i18n "search_title" }}',
buttonAriaLabel: '{{ i18n "search_title" }}',
},
modal: {
startScreen: {
noRecentSearchesText: '{{ i18n "search_no_recent_searches" }}',
},
footer: {
selectText: '{{ i18n "search_select" }}',
selectKeyAriaLabel: 'Enter key',
navigateText: '{{ i18n "search_navigate" }}',
navigateUpKeyAriaLabel: 'Arrow up',
navigateDownKeyAriaLabel: 'Arrow down',
closeText: '{{ i18n "search_close" }}',
closeKeyAriaLabel: 'Escape key',
searchByText: '{{ i18n "search_search_by" }}',
},
noResultsScreen: {
noResultsText: '{{ i18n "search_no_results" }}',
suggestedQueryText: '{{ i18n "search_try_search" }}',
reportMissingResultsText: 'Believe this query should return results?',
reportMissingResultsLinkText: 'Let us know.',
},
},
}
});
});
</script>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 153 KiB

After

Width:  |  Height:  |  Size: 103 KiB

View File

@@ -2,8 +2,8 @@
version: '3.3'
services:
pg:
image: ankane/pgvector:v0.4.2 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/pgvector:v0.4.2 # 阿里云
image: ankane/pgvector:v0.5.0 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/pgvector:v0.5.0 # 阿里云
container_name: pg
restart: always
ports: # 生产环境建议不要暴露
@@ -66,8 +66,8 @@ networks:
# version: '3.3'
# services:
# pg:
# image: ankane/pgvector:v0.4.2 # dockerhub
# # image: registry.cn-hangzhou.aliyuncs.com/fastgpt/pgvector:v0.4.2 # 阿里云
# image: ankane/pgvector:v0.5.0 # dockerhub
# # image: registry.cn-hangzhou.aliyuncs.com/fastgpt/pgvector:v0.5.0 # 阿里云
# container_name: pg
# restart: always
# ports: # 生产环境建议不要暴露

View File

@@ -0,0 +1,233 @@
# coding=utf-8
# Implements API for Baichuan2-7B-Chat in OpenAI's format. (https://platform.openai.com/docs/api-reference/chat)
# Usage: python openai_api.py
import gc
import time
import torch
import uvicorn
from pydantic import BaseModel, Field, validator
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from contextlib import asynccontextmanager
from typing import Any, Dict, List, Optional, Union
from transformers import AutoModelForCausalLM, AutoTokenizer
from sse_starlette.sse import ServerSentEvent, EventSourceResponse
from transformers.generation.utils import GenerationConfig
import random
import string
@asynccontextmanager
async def lifespan(app: FastAPI): # collects GPU memory
yield
if torch.cuda.is_available():
torch.cuda.empty_cache()
torch.cuda.ipc_collect()
app = FastAPI(lifespan=lifespan)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
class ModelCard(BaseModel):
id: str
object: str = "model"
created: int = Field(default_factory=lambda: int(time.time()))
owned_by: str = "owner"
root: Optional[str] = None
parent: Optional[str] = None
permission: Optional[list] = None
class ModelList(BaseModel):
object: str = "list"
data: List[str] = [] # Assuming ModelCard is a string type. Replace with the correct type if not.
class ChatMessage(BaseModel):
role: str
content: str
@validator('role')
def check_role(cls, v):
if v not in ["user", "assistant", "system"]:
raise ValueError('role must be one of "user", "assistant", "system"')
return v
class DeltaMessage(BaseModel):
role: Optional[str] = None
content: Optional[str] = None
@validator('role', allow_reuse=True)
def check_role(cls, v):
if v is not None and v not in ["user", "assistant", "system"]:
raise ValueError('role must be one of "user", "assistant", "system"')
return v
class ChatCompletionRequest(BaseModel):
model: str
messages: List[ChatMessage]
temperature: Optional[float] = None
top_p: Optional[float] = None
max_length: Optional[int] = 8192 # max_length should be an integer.
stream: Optional[bool] = False
class ChatCompletionResponseChoice(BaseModel):
index: int
message: ChatMessage
finish_reason: str
@validator('finish_reason')
def check_finish_reason(cls, v):
if v not in ["stop", "length"]:
raise ValueError('finish_reason must be one of "stop" or "length"')
return v
class ChatCompletionResponseStreamChoice(BaseModel):
index: int
delta: DeltaMessage
finish_reason: Optional[str]
@validator('finish_reason', allow_reuse=True)
def check_finish_reason(cls, v):
if v is not None and v not in ["stop", "length"]:
raise ValueError('finish_reason must be one of "stop" or "length"')
return v
class ChatCompletionResponse(BaseModel):
id:str
object:str
@validator('object')
def check_object(cls,v):
if v not in ["chat.completion","chat.completion.chunk"]:
raise ValueError("object must be one of 'chat.completion' or 'chat.completion.chunk'")
return v
created :Optional[int]=Field(default_factory=lambda:int(time.time()))
model:str
choices :List[Union[ChatCompletionResponseChoice,ChatCompletionResponseStreamChoice]]
def generate_id():
possible_characters = string.ascii_letters + string.digits
random_string = ''.join(random.choices(possible_characters, k=29))
return 'chatcmpl-' + random_string
@app.get("/v1/models", response_model=ModelList)
async def list_models():
global model_args
model_card = ModelCard(id="gpt-3.5-turbo")
return ModelList(data=[model_card])
@app.post("/v1/chat/completions", response_model=ChatCompletionResponse)
async def create_chat_completion(request: ChatCompletionRequest):
global model, tokenizer
if request.messages[-1].role != "user":
raise HTTPException(status_code=400, detail="Invalid request")
query = request.messages[-1].content
prev_messages = request.messages[:-1]
if len(prev_messages) > 0 and prev_messages[0].role == "system":
query = prev_messages.pop(0).content + query
messages = []
for message in prev_messages:
messages.append({"role": message.role, "content": message.content})
messages.append({"role": "user", "content": query})
if request.stream:
generate = predict(messages, request.model)
return EventSourceResponse(generate, media_type="text/event-stream")
response = '本接口不支持非stream模式'
choice_data = ChatCompletionResponseChoice(
index=0,
message=ChatMessage(role="assistant", content=response),
finish_reason="stop"
)
id='chatcmpl-7QyqpwdfhqwajicIEznoc6Q47XAyW'
return ChatCompletionResponse(id=id,model=request.model, choices=[choice_data], object="chat.completion")
async def predict(messages: List[List[str]], model_id: str):
global model, tokenizer
id = generate_id()
created = int(time.time())
choice_data = ChatCompletionResponseStreamChoice(
index=0,
delta=DeltaMessage(role="assistant",content=""),
finish_reason=None
)
chunk = ChatCompletionResponse(id=id,object="chat.completion.chunk",created=created,model=model_id, choices=[choice_data])
yield "{}".format(chunk.json(exclude_unset=True, ensure_ascii=False))
current_length = 0
for new_response in model.chat(tokenizer, messages, stream=True):
if len(new_response) == current_length:
continue
new_text = new_response[current_length:]
current_length = len(new_response)
choice_data = ChatCompletionResponseStreamChoice(
index=0,
delta=DeltaMessage(content=new_text),
finish_reason=None
)
chunk = ChatCompletionResponse(id=id,object="chat.completion.chunk",created=created,model=model_id, choices=[choice_data])
yield "{}".format(chunk.json(exclude_unset=True, ensure_ascii=False))
choice_data = ChatCompletionResponseStreamChoice(
index=0,
delta=DeltaMessage(),
finish_reason="stop"
)
chunk = ChatCompletionResponse(id=id,object="chat.completion.chunk",created=created,model=model_id, choices=[choice_data])
yield "{}".format(chunk.json(exclude_unset=True, ensure_ascii=False))
yield '[DONE]'
def load_models():
print("本次加载的大语言模型为: Baichuan-13B-Chat")
tokenizer = AutoTokenizer.from_pretrained("baichuan-inc/Baichuan2-7B-Chat", use_fast=False, trust_remote_code=True)
# model = AutoModelForCausalLM.from_pretrained("Baichuan2-13B-Chat", torch_dtype=torch.float32, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained("baichuan-inc/Baichuan2-7B-Chat", torch_dtype=torch.float16, trust_remote_code=True)
model = model.cuda()
model.generation_config = GenerationConfig.from_pretrained("baichuan-inc/Baichuan2-7B-Chat")
return tokenizer, model
if __name__ == "__main__":
tokenizer, model = load_models()
uvicorn.run(app, host='0.0.0.0', port=6006, workers=1)
while True:
try:
# 在这里执行您的程序逻辑
# 检查显存使用情况如果超过阈值例如90%),则触发垃圾回收
if torch.cuda.is_available():
gpu_memory_usage = torch.cuda.memory_allocated() / torch.cuda.max_memory_allocated()
if gpu_memory_usage > 0.9:
gc.collect()
torch.cuda.empty_cache()
except RuntimeError as e:
if "out of memory" in str(e):
print("显存不足,正在重启程序...")
gc.collect()
torch.cuda.empty_cache()
time.sleep(5) # 等待一段时间以确保显存已释放
tokenizer, model = load_models()
else:
raise e

View File

@@ -0,0 +1,14 @@
protobuf
transformers==4.30.2
cpm_kernels
torch>=2.0
gradio
mdtex2html
sentencepiece
accelerate
sse-starlette
fastapi==0.99.1
pydantic==1.10.7
uvicorn==0.21.1
xformers
bitsandbytes

View File

@@ -8,11 +8,11 @@
},
"devDependencies": {
"husky": "^8.0.3",
"i18next": "^23.2.11",
"lint-staged": "^13.2.1",
"next-i18next": "^14.0.0",
"prettier": "^3.0.3",
"react-i18next": "^13.0.2"
"i18next": "^23.2.11",
"react-i18next": "^13.0.2",
"next-i18next": "^14.0.0"
},
"lint-staged": {
"./**/**/*.{ts,tsx,scss}": "npm run format"

View File

@@ -1,4 +0,0 @@
{
"name": "@fastgpt/common",
"version": "1.0.0"
}

View File

@@ -1,5 +0,0 @@
export function strIsLink(str?: string) {
if (!str) return false;
if (/^((http|https)?:\/\/|www\.|\/)[^\s/$.?#].[^\s]*$/i.test(str)) return true;
return false;
}

View File

@@ -1,15 +0,0 @@
export enum DatasetSpecialIdEnum {
manual = 'manual',
mark = 'mark'
}
export const datasetSpecialIdMap = {
[DatasetSpecialIdEnum.manual]: {
name: 'kb.Manual Data',
sourceName: 'kb.Manual Input'
},
[DatasetSpecialIdEnum.mark]: {
name: 'kb.Mark Data',
sourceName: 'kb.Manual Mark'
}
};
export const datasetSpecialIds: string[] = [DatasetSpecialIdEnum.manual, DatasetSpecialIdEnum.mark];

View File

@@ -1,8 +0,0 @@
import { datasetSpecialIds } from './constant';
import { strIsLink } from '@fastgpt/common/tools/str';
export function isSpecialFileId(id: string) {
if (datasetSpecialIds.includes(id)) return true;
if (strIsLink(id)) return true;
return false;
}

View File

@@ -1,14 +0,0 @@
{
"name": "@fastgpt/core",
"version": "1.0.0",
"dependencies": {
"@fastgpt/common": "workspace:*",
"@fastgpt/support": "workspace:*",
"encoding": "^0.1.13",
"openai": "^4.11.1",
"tunnel": "^0.0.6"
},
"devDependencies": {
"@types/tunnel": "^0.0.4"
}
}

View File

@@ -27,7 +27,8 @@ export enum ERROR_ENUM {
insufficientQuota = 'insufficientQuota',
unAuthModel = 'unAuthModel',
unAuthApiKey = 'unAuthApiKey',
unAuthKb = 'unAuthKb',
unAuthDataset = 'unAuthDataset',
unAuthDatasetCollection = 'unAuthDatasetCollection',
unAuthFile = 'unAuthFile'
}
export const ERROR_RESPONSE: Record<
@@ -57,9 +58,9 @@ export const ERROR_RESPONSE: Record<
message: '无权使用该模型',
data: null
},
[ERROR_ENUM.unAuthKb]: {
[ERROR_ENUM.unAuthDataset]: {
code: 512,
statusText: ERROR_ENUM.unAuthKb,
statusText: ERROR_ENUM.unAuthDataset,
message: '无权使用该知识库',
data: null
},
@@ -74,5 +75,11 @@ export const ERROR_RESPONSE: Record<
statusText: ERROR_ENUM.unAuthApiKey,
message: 'Api Key 不合法',
data: null
},
[ERROR_ENUM.unAuthDatasetCollection]: {
code: 515,
statusText: ERROR_ENUM.unAuthDatasetCollection,
message: '无权使用该知识库文件',
data: null
}
};

View File

@@ -0,0 +1,5 @@
export const getErrText = (err: any, def = '') => {
const msg: string = typeof err === 'string' ? err : err?.message || def || '';
msg && console.log('error =>', msg);
return msg;
};

View File

@@ -1,4 +1,4 @@
import { strIsLink } from './str';
import { strIsLink } from '../string/tools';
export const fileImgs = [
{ suffix: 'pdf', src: '/imgs/files/pdf.svg' },
@@ -10,14 +10,7 @@ export const fileImgs = [
];
export function getFileIcon(name = '') {
return fileImgs.find((item) => new RegExp(item.suffix, 'gi').test(name))?.src;
}
export function getSpecialFileIcon(name = '') {
if (name === 'manual') {
return '/imgs/files/manual.svg';
} else if (name === 'mark') {
return '/imgs/files/mark.svg';
} else if (strIsLink(name)) {
return '/imgs/files/link.svg';
}
return (
fileImgs.find((item) => new RegExp(item.suffix, 'gi').test(name))?.src || '/imgs/files/file.svg'
);
}

View File

@@ -0,0 +1,9 @@
export const formatFileSize = (bytes: number): string => {
if (bytes === 0) return '0 B';
const k = 1024;
const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
};

View File

@@ -0,0 +1,4 @@
export type ParentTreePathItemType = {
parentId: string;
parentName: string;
};

View File

@@ -0,0 +1,22 @@
import crypto from 'crypto';
export function strIsLink(str?: string) {
if (!str) return false;
if (/^((http|https)?:\/\/|www\.|\/)[^\s/$.?#].[^\s]*$/i.test(str)) return true;
return false;
}
export const hashStr = (psw: string) => {
return crypto.createHash('sha256').update(psw).digest('hex');
};
/* simple text, remove chinese space and extra \n */
export const simpleText = (text: string) => {
text = text.replace(/([\u4e00-\u9fa5])[\s&&[^\n]]+([\u4e00-\u9fa5])/g, '$1$2');
text = text.replace(/\r\n|\r/g, '\n');
text = text.replace(/\n{3,}/g, '\n\n');
text = text.replace(/[\s&&[^\n]]{2,}/g, ' ');
text = text.replace(/[\x00-\x08]/g, ' ');
return text;
};

View File

@@ -0,0 +1,39 @@
export type FeConfigsType = {
show_emptyChat?: boolean;
show_register?: boolean;
show_appStore?: boolean;
show_contact?: boolean;
show_git?: boolean;
show_doc?: boolean;
show_pay?: boolean;
show_openai_account?: boolean;
show_promotion?: boolean;
hide_app_flow?: boolean;
docUrl?: string;
openAPIDocUrl?: string;
systemTitle?: string;
authorText?: string;
googleClientVerKey?: string;
isPlus?: boolean;
oauth?: {
github?: string;
google?: string;
};
limit?: {
exportLimitMinutes?: number;
};
scripts?: { [key: string]: string }[];
};
export type SystemEnvType = {
pluginBaseUrl?: string;
openapiPrefix?: string;
vectorMaxProcess: number;
qaMaxProcess: number;
pgHNSWEfSearch: number;
};
declare global {
var feConfigs: FeConfigsType;
var systemEnv: SystemEnvType;
}

View File

@@ -1,4 +1,3 @@
import { loginOut } from '@/api/user';
import timezones from 'timezones-list';
import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc';
@@ -7,23 +6,6 @@ import timezone from 'dayjs/plugin/timezone';
dayjs.extend(utc);
dayjs.extend(timezone);
const tokenKey = 'token';
export const clearToken = () => {
try {
loginOut();
localStorage.removeItem(tokenKey);
} catch (error) {
error;
}
};
export const setToken = (token: string) => {
localStorage.setItem(tokenKey, token);
};
export const getToken = () => {
return localStorage.getItem(tokenKey) || '';
};
/**
* Returns the offset from UTC in hours for the current locale.
* @param {string} timeZone Timezone to get offset for

View File

@@ -4,3 +4,9 @@ export type ChatCompletion = OpenAI.Chat.ChatCompletion;
export type CreateChatCompletionRequest = OpenAI.Chat.ChatCompletionCreateParams;
export type StreamChatType = Stream<OpenAI.Chat.ChatCompletionChunk>;
export type PromptTemplateItem = {
title: string;
desc: string;
value: string;
};

View File

@@ -0,0 +1,62 @@
export enum DatasetTypeEnum {
folder = 'folder',
dataset = 'dataset'
}
export const DatasetTypeMap = {
[DatasetTypeEnum.folder]: {
name: 'folder'
},
[DatasetTypeEnum.dataset]: {
name: 'dataset'
}
};
export enum DatasetCollectionTypeEnum {
file = 'file',
folder = 'folder',
link = 'link',
virtual = 'virtual'
}
export const DatasetCollectionTypeMap = {
[DatasetCollectionTypeEnum.file]: {
name: 'dataset.file'
},
[DatasetCollectionTypeEnum.folder]: {
name: 'dataset.folder'
},
[DatasetCollectionTypeEnum.link]: {
name: 'dataset.link'
},
[DatasetCollectionTypeEnum.virtual]: {
name: 'dataset.Virtual File'
}
};
export enum TrainingModeEnum {
'qa' = 'qa',
'index' = 'index'
}
export const TrainingTypeMap = {
[TrainingModeEnum.qa]: 'qa',
[TrainingModeEnum.index]: 'index'
};
export enum DatasetSpecialIdEnum {
manual = 'manual',
mark = 'mark'
}
export const datasetSpecialIdMap = {
[DatasetSpecialIdEnum.manual]: {
name: 'kb.Manual Data',
sourceName: 'kb.Manual Input'
},
[DatasetSpecialIdEnum.mark]: {
name: 'kb.Mark Data',
sourceName: 'kb.Manual Mark'
}
};
export const datasetSpecialIds: string[] = [DatasetSpecialIdEnum.manual, DatasetSpecialIdEnum.mark];
export const FolderAvatarSrc = '/imgs/files/folder.svg';

75
packages/global/core/dataset/type.d.ts vendored Normal file
View File

@@ -0,0 +1,75 @@
import { DatasetCollectionTypeEnum, DatasetTypeEnum, TrainingModeEnum } from './constant';
export type DatasetSchemaType = {
_id: string;
userId: string;
parentId: string;
updateTime: Date;
avatar: string;
name: string;
vectorModel: string;
tags: string[];
type: `${DatasetTypeEnum}`;
};
export type DatasetCollectionSchemaType = {
_id: string;
userId: string;
datasetId: string;
parentId?: string;
name: string;
type: `${DatasetCollectionTypeEnum}`;
updateTime: Date;
metadata: {
fileId?: string;
rawLink?: string;
pgCollectionId?: string;
};
};
export type DatasetTrainingSchemaType = {
_id: string;
userId: string;
datasetId: string;
datasetCollectionId: string;
billId: string;
expireAt: Date;
lockTime: Date;
mode: `${TrainingModeEnum}`;
model: string;
prompt: string;
q: string;
a: string;
};
/* ================= dataset ===================== */
/* ================= collection ===================== */
/* ================= data ===================== */
export type PgDataItemType = {
id: string;
q: string;
a: string;
dataset_id: string;
collection_id: string;
};
export type DatasetChunkItemType = {
q: string;
a: string;
};
export type DatasetDataItemType = DatasetChunkItemType & {
id: string;
datasetId: string;
collectionId: string;
sourceName: string;
sourceId?: string;
};
/* ============= search =============== */
export type SearchDataResultItemType = PgDataItemType & {
score: number;
};
export type SearchDataResponseItemType = DatasetDataItemType & {
score: number;
};

View File

@@ -0,0 +1,21 @@
import { DatasetCollectionTypeEnum } from './constant';
import { getFileIcon } from '../../common/file/icon';
export function getCollectionIcon(
type: `${DatasetCollectionTypeEnum}` = DatasetCollectionTypeEnum.file,
name = ''
) {
if (type === DatasetCollectionTypeEnum.folder) {
return '/imgs/files/folder.svg';
} else if (type === DatasetCollectionTypeEnum.link) {
return '/imgs/files/link.svg';
} else if (type === DatasetCollectionTypeEnum.virtual) {
if (name === '手动录入') {
return '/imgs/files/manual.svg';
} else if (name === '手动标注') {
return '/imgs/files/mark.svg';
}
return '/imgs/files/collection.svg';
}
return getFileIcon(name);
}

View File

@@ -0,0 +1,14 @@
{
"name": "@fastgpt/global",
"version": "1.0.0",
"dependencies": {
"axios": "^1.5.1",
"timezones-list": "^3.0.2",
"dayjs": "^1.11.7",
"encoding": "^0.1.13",
"openai": "^4.12.1"
},
"devDependencies": {
"@types/node": "^20.8.5"
}
}

View File

@@ -0,0 +1,5 @@
export enum OutLinkTypeEnum {
share = 'share',
iframe = 'iframe',
apikey = 'apikey'
}

View File

@@ -1,6 +1,6 @@
import { OutLinkTypeEnum } from '@/constants/chat';
import { OutLinkTypeEnum } from './constant';
export interface OutLinkSchema {
export type OutLinkSchema = {
_id: string;
shareId: string;
userId: string;
@@ -16,7 +16,7 @@ export interface OutLinkSchema {
credit: number;
hookUrl?: string;
};
}
};
export type OutLinkEditType = {
_id?: string;

View File

@@ -15,5 +15,6 @@ export type UserModelSchema = {
};
limit: {
exportKbTime?: Date;
datasetMaxCount?: number;
};
};

View File

@@ -0,0 +1,19 @@
import type { NextApiResponse, NextApiHandler, NextApiRequest } from 'next';
import NextCors from 'nextjs-cors';
export function withNextCors(handler: NextApiHandler): NextApiHandler {
return async function nextApiHandlerWrappedWithNextCors(
req: NextApiRequest,
res: NextApiResponse
) {
const methods = ['GET', 'eHEAD', 'PUT', 'PATCH', 'POST', 'DELETE'];
const origin = req.headers.origin;
await NextCors(req, res, {
methods,
origin: origin,
optionsSuccessStatus: 200
});
return handler(req, res);
};
}

View File

@@ -0,0 +1,6 @@
import mongoose from 'mongoose';
export default mongoose;
export * from 'mongoose';
export const connectionMongo = global.mongodb || mongoose;

View File

@@ -0,0 +1,76 @@
import mongoose from './index';
import 'winston-mongodb';
import { createLogger, format, transports } from 'winston';
/**
* connect MongoDB and init data
*/
export async function connectMongo({
beforeHook,
afterHook
}: {
beforeHook?: () => any;
afterHook?: () => any;
}): Promise<void> {
if (global.mongodb) {
return;
}
global.mongodb = mongoose;
beforeHook && (await beforeHook());
// logger
initLogger();
console.log('mongo start connect');
try {
mongoose.set('strictQuery', true);
await mongoose.connect(process.env.MONGODB_URI as string, {
bufferCommands: true,
maxConnecting: Number(process.env.DB_MAX_LINK || 5),
maxPoolSize: Number(process.env.DB_MAX_LINK || 5),
minPoolSize: 2,
connectTimeoutMS: 20000,
waitQueueTimeoutMS: 20000
});
console.log('mongo connected');
afterHook && (await afterHook());
} catch (error) {
console.log('error->', 'mongo connect error', error);
global.mongodb = undefined;
}
}
function initLogger() {
global.logger = createLogger({
transports: [
new transports.MongoDB({
db: process.env.MONGODB_URI as string,
collection: 'server_logs',
options: {
useUnifiedTopology: true
},
cappedSize: 500000000,
tryReconnect: true,
metaKey: 'meta',
format: format.combine(format.timestamp(), format.json())
}),
new transports.Console({
format: format.combine(
format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
format.printf((info) => {
if (info.level === 'error') {
console.log(info.meta);
return `[${info.level.toLocaleUpperCase()}]: ${[info.timestamp]}: ${info.message}`;
}
return `[${info.level.toLocaleUpperCase()}]: ${[info.timestamp]}: ${info.message}${
info.meta ? `: ${JSON.stringify(info.meta)}` : ''
}`;
})
)
})
]
});
}

View File

@@ -0,0 +1,39 @@
import mongoose from './index';
export class MongoSession {
tasks: (() => Promise<any>)[] = [];
session: mongoose.mongo.ClientSession | null = null;
opts: {
session: mongoose.mongo.ClientSession;
new: boolean;
} | null = null;
constructor() {}
async init() {
this.session = await mongoose.startSession();
this.opts = { session: this.session, new: true };
}
push(
tasks: ((opts: {
session: mongoose.mongo.ClientSession;
new: boolean;
}) => () => Promise<any>)[] = []
) {
if (!this.opts) return;
// this.tasks = this.tasks.concat(tasks.map((item) => item(this.opts)));
}
async run() {
if (!this.session || !this.opts) return;
try {
this.session.startTransaction();
const opts = { session: this.session, new: true };
await this.session.commitTransaction();
} catch (error) {
await this.session.abortTransaction();
console.error(error);
}
this.session.endSession();
}
}

View File

@@ -0,0 +1,7 @@
import type { Mongoose } from 'mongoose';
import type { Logger } from 'winston';
declare global {
var mongodb: Mongoose | undefined;
var logger: Logger;
}

View File

@@ -14,7 +14,7 @@ export function responseWriteController({
return (text: string | Buffer) => {
const writeResult = res.write(text);
if (!writeResult) {
readStream.pause();
readStream?.pause();
}
};
}

View File

@@ -1,4 +1,4 @@
import { UserModelSchema } from '../user/type';
import type { UserModelSchema } from '@fastgpt/global/support/user/type';
import OpenAI from 'openai';
export const openaiBaseUrl = process.env.OPENAI_BASE_URL || 'https://api.openai.com/v1';
@@ -11,6 +11,7 @@ export const getAIApi = (props?: UserModelSchema['openaiAccount'], timeout = 600
apiKey: props?.key || systemAIChatKey,
baseURL: props?.baseUrl || baseUrl,
httpAgent: global.httpsAgent,
timeout
timeout,
maxRetries: 2
});
};

View File

@@ -1,4 +1,4 @@
import { ChatCompletionRequestMessage } from '../type';
import type { ChatCompletionRequestMessage } from '@fastgpt/global/core/ai/type.d';
import { getAIApi } from '../config';
export const Prompt_QuestionGuide = `我不太清楚问你什么问题,请帮我生成 3 个问题引导我继续提问。问题的长度应小于20个字符按 JSON 格式返回: ["问题1", "问题2", "问题3"]`;
@@ -10,7 +10,7 @@ export async function createQuestionGuide({
messages: ChatCompletionRequestMessage[];
model: string;
}) {
const ai = getAIApi();
const ai = getAIApi(undefined, 48000);
const data = await ai.chat.completions.create({
model: model,
temperature: 0,
@@ -25,7 +25,7 @@ export async function createQuestionGuide({
stream: false
});
const answer = data.choices?.[0].message?.content || '';
const answer = data.choices?.[0]?.message?.content || '';
const totalTokens = data.usage?.total_tokens || 0;
const start = answer.indexOf('[');

View File

@@ -0,0 +1,26 @@
import { ERROR_ENUM } from '@fastgpt/global/common/error/errorCode';
import { MongoDatasetCollection } from './collection/schema';
import { DatasetSchemaType } from '@fastgpt/global/core/dataset/type';
export async function authCollection({
collectionId,
userId
}: {
collectionId: string;
userId: string;
}) {
const collection = await MongoDatasetCollection.findOne({
_id: collectionId,
userId
})
.populate('datasetId')
.lean();
if (collection) {
return {
...collection,
dataset: collection.datasetId as unknown as DatasetSchemaType
};
}
return Promise.reject(ERROR_ENUM.unAuthDataset);
}

View File

@@ -0,0 +1,66 @@
import { connectionMongo, type Model } from '../../../common/mongo';
const { Schema, model, models } = connectionMongo;
import { DatasetCollectionSchemaType } from '@fastgpt/global/core/dataset/type.d';
import { DatasetCollectionTypeMap } from '@fastgpt/global/core/dataset/constant';
import { DatasetCollectionName } from '../schema';
export const DatasetColCollectionName = 'dataset.collections';
const DatasetCollectionSchema = new Schema({
parentId: {
type: Schema.Types.ObjectId,
ref: DatasetColCollectionName,
default: null
},
userId: {
type: Schema.Types.ObjectId,
ref: 'user',
required: true
},
datasetId: {
type: Schema.Types.ObjectId,
ref: DatasetCollectionName,
required: true
},
name: {
type: String,
required: true
},
type: {
type: String,
enum: Object.keys(DatasetCollectionTypeMap),
required: true
},
updateTime: {
type: Date,
default: () => new Date()
},
metadata: {
type: {
fileId: {
type: Schema.Types.ObjectId,
ref: 'dataset.files'
},
rawLink: {
type: String,
default: ''
},
// 451 初始化
pgCollectionId: {
type: String
}
},
default: {}
}
});
try {
DatasetCollectionSchema.index({ datasetId: 1 });
DatasetCollectionSchema.index({ userId: 1 });
DatasetCollectionSchema.index({ updateTime: -1 });
} catch (error) {
console.log(error);
}
export const MongoDatasetCollection: Model<DatasetCollectionSchemaType> =
models[DatasetColCollectionName] || model(DatasetColCollectionName, DatasetCollectionSchema);

View File

@@ -0,0 +1,62 @@
import { MongoDatasetCollection } from './schema';
import { ParentTreePathItemType } from '@fastgpt/global/common/parentFolder/type';
/**
* get all collection by top collectionId
*/
export async function findCollectionAndChild(id: string, fields = '_id parentId name metadata') {
async function find(id: string) {
// find children
const children = await MongoDatasetCollection.find({ parentId: id }, fields);
let collections = children;
for (const child of children) {
const grandChildrenIds = await find(child._id);
collections = collections.concat(grandChildrenIds);
}
return collections;
}
const [collection, childCollections] = await Promise.all([
MongoDatasetCollection.findById(id, fields),
find(id)
]);
if (!collection) {
return Promise.reject('Collection not found');
}
return [collection, ...childCollections];
}
export async function getDatasetCollectionPaths({
parentId = '',
userId
}: {
parentId?: string;
userId: string;
}): Promise<ParentTreePathItemType[]> {
async function find(parentId?: string): Promise<ParentTreePathItemType[]> {
if (!parentId) {
return [];
}
const parent = await MongoDatasetCollection.findOne({ _id: parentId, userId }, 'name parentId');
if (!parent) return [];
const paths = await find(parent.parentId);
paths.push({ parentId, parentName: parent.name });
return paths;
}
return await find(parentId);
}
export function getCollectionUpdateTime({ name, time }: { time?: Date; name: string }) {
if (time) return time;
if (name.startsWith('手动') || ['manual', 'mark'].includes(name)) return new Date('2999/9/9');
return new Date();
}

View File

@@ -0,0 +1,55 @@
import { connectionMongo, type Model } from '../../common/mongo';
const { Schema, model, models } = connectionMongo;
import { DatasetSchemaType } from '@fastgpt/global/core/dataset/type.d';
import { DatasetTypeMap } from '@fastgpt/global/core/dataset/constant';
export const DatasetCollectionName = 'datasets';
const DatasetSchema = new Schema({
parentId: {
type: Schema.Types.ObjectId,
ref: DatasetCollectionName,
default: null
},
userId: {
type: Schema.Types.ObjectId,
ref: 'user',
required: true
},
updateTime: {
type: Date,
default: () => new Date()
},
avatar: {
type: String,
default: '/icon/logo.svg'
},
name: {
type: String,
required: true
},
vectorModel: {
type: String,
required: true,
default: 'text-embedding-ada-002'
},
type: {
type: String,
enum: Object.keys(DatasetTypeMap),
required: true,
default: 'dataset'
},
tags: {
type: [String],
default: []
}
});
try {
DatasetSchema.index({ userId: 1 });
} catch (error) {
console.log(error);
}
export const MongoDataset: Model<DatasetSchemaType> =
models[DatasetCollectionName] || model(DatasetCollectionName, DatasetSchema);

View File

@@ -1,18 +1,36 @@
/* 模型的知识库 */
import { Schema, model, models, Model as MongoModel } from 'mongoose';
import { TrainingDataSchema as TrainingDateType } from '@/types/mongoSchema';
import { TrainingTypeMap } from '@/constants/plugin';
import { connectionMongo, type Model } from '../../../common/mongo';
const { Schema, model, models } = connectionMongo;
import { DatasetTrainingSchemaType } from '@fastgpt/global/core/dataset/type';
import { TrainingTypeMap } from '@fastgpt/global/core/dataset/constant';
import { DatasetColCollectionName } from '../collection/schema';
import { DatasetCollectionName } from '../schema';
export const DatasetTrainingCollectionName = 'dataset.trainings';
// pgList and vectorList, Only one of them will work
const TrainingDataSchema = new Schema({
userId: {
type: Schema.Types.ObjectId,
ref: 'user',
required: true
},
kbId: {
datasetId: {
type: Schema.Types.ObjectId,
ref: 'kb',
ref: DatasetCollectionName,
required: true
},
datasetCollectionId: {
type: Schema.Types.ObjectId,
ref: DatasetColCollectionName,
required: true
},
billId: {
type: String,
default: ''
},
mode: {
type: String,
enum: Object.keys(TrainingTypeMap),
required: true
},
expireAt: {
@@ -23,16 +41,10 @@ const TrainingDataSchema = new Schema({
type: Date,
default: () => new Date('2000/1/1')
},
mode: {
model: {
type: String,
enum: Object.keys(TrainingTypeMap),
required: true
},
vectorModel: {
type: String,
required: true,
default: 'text-embedding-ada-002'
},
prompt: {
// qa split prompt
type: String,
@@ -40,23 +52,11 @@ const TrainingDataSchema = new Schema({
},
q: {
type: String,
default: ''
required: true
},
a: {
type: String,
default: ''
},
source: {
type: String,
default: ''
},
file_id: {
type: String,
default: ''
},
billId: {
type: String,
default: ''
}
});
@@ -68,5 +68,5 @@ try {
console.log(error);
}
export const TrainingData: MongoModel<TrainingDateType> =
models['trainingData'] || model('trainingData', TrainingDataSchema);
export const MongoDatasetTraining: Model<DatasetTrainingSchemaType> =
models[DatasetTrainingCollectionName] || model(DatasetTrainingCollectionName, TrainingDataSchema);

View File

@@ -0,0 +1,24 @@
{
"name": "@fastgpt/service",
"version": "1.0.0",
"dependencies": {
"@fastgpt/global": "workspace:*",
"axios": "^1.5.1",
"nextjs-cors": "^2.1.2",
"next": "13.5.2",
"cookie": "^0.5.0",
"jsonwebtoken": "^9.0.2",
"mongoose": "^7.0.2",
"winston": "^3.10.0",
"winston-mongodb": "^5.1.1",
"tunnel": "^0.0.6",
"encoding": "^0.1.13",
"openai": "^4.12.4"
},
"devDependencies": {
"@types/tunnel": "^0.0.4",
"@types/node": "^20.8.5",
"@types/cookie": "^0.5.2",
"@types/jsonwebtoken": "^9.0.3"
}
}

View File

@@ -0,0 +1,32 @@
import { ERROR_ENUM } from '@fastgpt/global/common/error/errorCode';
import { updateApiKeyUsedTime } from './tools';
import { MongoOpenApi } from './schema';
import { POST } from '../../common/api/plusRequest';
import type { OpenApiSchema } from '@fastgpt/global/support/openapi/type';
export type AuthOpenApiLimitProps = { openApi: OpenApiSchema };
export async function authOpenApiKey({ apikey }: { apikey: string }) {
if (!apikey) {
return Promise.reject(ERROR_ENUM.unAuthApiKey);
}
try {
const openApi = await MongoOpenApi.findOne({ apiKey: apikey });
if (!openApi) {
return Promise.reject(ERROR_ENUM.unAuthApiKey);
}
const userId = String(openApi.userId);
// auth limit
if (global.feConfigs?.isPlus) {
await POST('/support/openapi/authLimit', { openApi } as AuthOpenApiLimitProps);
}
updateApiKeyUsedTime(openApi._id);
return { apikey, userId, appId: openApi.appId };
} catch (error) {
return Promise.reject(error);
}
}

View File

@@ -1,7 +1,8 @@
import { Schema, model, models, Model } from 'mongoose';
import { OpenApiSchema } from '@/types/support/openapi';
import { PRICE_SCALE } from '@fastgpt/common/bill/constants';
import { formatPrice } from '@fastgpt/common/bill/index';
import { connectionMongo, type Model } from '../../common/mongo';
const { Schema, model, models } = connectionMongo;
import type { OpenApiSchema } from '@fastgpt/global/support/openapi/type';
import { PRICE_SCALE } from '@fastgpt/global/common/bill/constants';
import { formatPrice } from '@fastgpt/global/common/bill/tools';
const OpenApiSchema = new Schema(
{
@@ -54,4 +55,5 @@ const OpenApiSchema = new Schema(
}
);
export const OpenApi: Model<OpenApiSchema> = models['openapi'] || model('openapi', OpenApiSchema);
export const MongoOpenApi: Model<OpenApiSchema> =
models['openapi'] || model('openapi', OpenApiSchema);

View File

@@ -1,13 +1,13 @@
import { OpenApi } from './schema';
import { MongoOpenApi } from './schema';
export async function updateApiKeyUsedTime(id: string) {
await OpenApi.findByIdAndUpdate(id, {
await MongoOpenApi.findByIdAndUpdate(id, {
lastUsedTime: new Date()
});
}
export async function updateApiKeyUsage({ apikey, usage }: { apikey: string; usage: number }) {
await OpenApi.findOneAndUpdate(
await MongoOpenApi.findOneAndUpdate(
{ apiKey: apikey },
{
$inc: {

View File

@@ -0,0 +1,68 @@
import { AuthUserTypeEnum, authBalanceByUid } from '../user/auth';
import { MongoOutLink } from './schema';
import { POST } from '../../common/api/plusRequest';
import { OutLinkSchema } from '@fastgpt/global/support/outLink/type';
export type AuthLinkProps = { ip?: string | null; authToken?: string; question: string };
export type AuthLinkLimitProps = AuthLinkProps & { outLink: OutLinkSchema };
export async function authOutLinkChat({
shareId,
ip,
authToken,
question
}: AuthLinkProps & {
shareId: string;
}) {
// get outLink
const outLink = await MongoOutLink.findOne({
shareId
});
if (!outLink) {
return Promise.reject('分享链接无效');
}
const uid = String(outLink.userId);
const [user] = await Promise.all([
authBalanceByUid(uid), // authBalance
...(global.feConfigs?.isPlus ? [authOutLinkLimit({ outLink, ip, authToken, question })] : []) // limit auth
]);
return {
user,
userId: String(outLink.userId),
appId: String(outLink.appId),
authType: AuthUserTypeEnum.token,
responseDetail: outLink.responseDetail
};
}
export function authOutLinkLimit(data: AuthLinkLimitProps) {
return POST('/support/outLink/authLimit', data);
}
export async function authOutLinkId({ id }: { id: string }) {
const outLink = await MongoOutLink.findOne({
shareId: id
});
if (!outLink) {
return Promise.reject('分享链接无效');
}
return {
userId: String(outLink.userId)
};
}
export type AuthShareChatInitProps = {
authToken?: string;
tokenUrl?: string;
};
export function authShareChatInit(data: AuthShareChatInitProps) {
if (!global.feConfigs?.isPlus) return;
return POST('/support/outLink/authShareChatInit', data);
}

View File

@@ -1,6 +1,7 @@
import { Schema, model, models, Model } from 'mongoose';
import { OutLinkSchema as SchemaType } from '@/types/support/outLink';
import { OutLinkTypeEnum } from '@/constants/chat';
import { connectionMongo, type Model } from '../../common/mongo';
const { Schema, model, models } = connectionMongo;
import { OutLinkSchema as SchemaType } from '@fastgpt/global/support/outLink/type';
import { OutLinkTypeEnum } from '@fastgpt/global/support/outLink/constant';
const OutLinkSchema = new Schema({
shareId: {
@@ -55,4 +56,5 @@ const OutLinkSchema = new Schema({
}
});
export const OutLink: Model<SchemaType> = models['outlinks'] || model('outlinks', OutLinkSchema);
export const MongoOutLink: Model<SchemaType> =
models['outlinks'] || model('outlinks', OutLinkSchema);

View File

@@ -1,7 +1,5 @@
import { addLog } from '@/service/utils/tools';
import { ChatHistoryItemResType } from '@/types/chat';
import axios from 'axios';
import { OutLink } from './schema';
import { MongoOutLink } from './schema';
export const updateOutLinkUsage = async ({
shareId,
@@ -11,7 +9,7 @@ export const updateOutLinkUsage = async ({
total: number;
}) => {
try {
await OutLink.findOneAndUpdate(
await MongoOutLink.findOneAndUpdate(
{ shareId },
{
$inc: { total },
@@ -19,7 +17,7 @@ export const updateOutLinkUsage = async ({
}
);
} catch (err) {
addLog.error('update shareChat error', err);
console.log('update shareChat error', err);
}
};
@@ -30,11 +28,11 @@ export const pushResult2Remote = async ({
}: {
authToken?: string;
shareId?: string;
responseData?: ChatHistoryItemResType[];
responseData?: any[];
}) => {
if (!shareId || !authToken) return;
try {
const outLink = await OutLink.findOne({
const outLink = await MongoOutLink.findOne({
shareId
});
if (!outLink?.limit?.hookUrl) return;

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