Commit bf172fab by Archer Committed by GitHub

perf: markdown more wrap (#365)

parent 36f5648c
...@@ -17,7 +17,9 @@ jobs: ...@@ -17,7 +17,9 @@ jobs:
with: with:
fetch-depth: 1 fetch-depth: 1
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1 uses: docker/setup-buildx-action@v2
with:
driver-opts: network=host
- name: Cache Docker layers - name: Cache Docker layers
uses: actions/cache@v2 uses: actions/cache@v2
with: with:
...@@ -34,7 +36,7 @@ jobs: ...@@ -34,7 +36,7 @@ jobs:
- name: Set DOCKER_REPO_TAGGED based on branch or tag - name: Set DOCKER_REPO_TAGGED based on branch or tag
run: | run: |
echo "DOCKER_REPO_TAGGED=ghcr.io/${{ github.repository_owner }}/fastgpt-pr:${{ github.event.pull_request.number }}" >> $GITHUB_ENV echo "DOCKER_REPO_TAGGED=ghcr.io/${{ github.repository_owner }}/fastgpt-pr:${{ github.event.pull_request.number }}" >> $GITHUB_ENV
- name: Build and publish image for PR - name: Build image for PR
env: env:
DOCKER_REPO_TAGGED: ${{ env.DOCKER_REPO_TAGGED }} DOCKER_REPO_TAGGED: ${{ env.DOCKER_REPO_TAGGED }}
run: | run: |
......
...@@ -80,8 +80,8 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b ...@@ -80,8 +80,8 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b
* [部署 FastGPT](https://doc.fastgpt.run/docs/installation) * [部署 FastGPT](https://doc.fastgpt.run/docs/installation)
* [系统配置文件说明](https://doc.fastgpt.run/docs/development/configuration/) * [系统配置文件说明](https://doc.fastgpt.run/docs/development/configuration/)
* [多模型配置](https://doc.fastgpt.run/docs/installation/one-api/) * [多模型配置](https://doc.fastgpt.run/docs/installation/one-api/)
* [版本升级](https://doc.fastgpt.run/docs/installation/upgrading) * [版本更新/升级介绍](https://doc.fastgpt.run/docs/installation/upgrading)
* [API 文档](https://doc.fastgpt.run/docs/development/openapi?pre_pathname=%2Fdrive%2Fhome%2F) * [API 文档](https://doc.fastgpt.run/docs/development/openapi/)
## 🏘️ 社区交流群 ## 🏘️ 社区交流群
...@@ -89,12 +89,6 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b ...@@ -89,12 +89,6 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b
![](https://otnvvf-imgs.oss.laf.run/wx300.jpg) ![](https://otnvvf-imgs.oss.laf.run/wx300.jpg)
## 👀 其他
- [FastGPT 常见问题](https://kjqvjse66l.feishu.cn/docx/HtrgdT0pkonP4kxGx8qcu6XDnGh)
- [docker 部署教程视频](https://www.bilibili.com/video/BV1jo4y147fT/)
- [FastGPT 知识库演示](https://www.bilibili.com/video/BV1Wo4y1p7i1/)
## 💪 相关项目 ## 💪 相关项目
- [Laf: 3 分钟快速接入三方应用](https://github.com/labring/laf) - [Laf: 3 分钟快速接入三方应用](https://github.com/labring/laf)
...@@ -102,10 +96,15 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b ...@@ -102,10 +96,15 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b
- [One API: 多模型管理,支持 Azure、文心一言等](https://github.com/songquanpeng/one-api) - [One API: 多模型管理,支持 Azure、文心一言等](https://github.com/songquanpeng/one-api)
- [TuShan: 5 分钟搭建后台管理系统](https://github.com/msgbyte/tushan) - [TuShan: 5 分钟搭建后台管理系统](https://github.com/msgbyte/tushan)
## 👀 其他
- [保姆级 FastGPT 教程](https://www.bilibili.com/video/BV1n34y1A7Bo/?spm_id_from=333.999.0.0)
- [接入飞书](https://www.bilibili.com/video/BV1Su4y1r7R3/?spm_id_from=333.999.0.0)
- [接入企微](https://www.bilibili.com/video/BV1Tp4y1n72T/?spm_id_from=333.999.0.0)
## 🤝 第三方生态 ## 🤝 第三方生态
- [OnWeChat 个人微信/企微机器人](https://doc.fastgpt.run/docs/use-cases/onwechat/) - [OnWeChat 个人微信/企微机器人](https://doc.fastgpt.run/docs/use-cases/onwechat/)
- [luolinAI: 企微机器人,开箱即用](https://github.com/luolin-ai/FastGPT-Enterprise-WeChatbot)
## 🌟 Star History ## 🌟 Star History
......
...@@ -3,8 +3,8 @@ ...@@ -3,8 +3,8 @@
"baseUrl": ".", "baseUrl": ".",
"paths": { "paths": {
"*": [ "*": [
"../../../../../.cache/hugo_cache/modules/filecache/modules/pkg/mod/github.com/gohugoio/hugo-mod-jslibs-dist/popperjs/v2@v2.21100.20000/package/dist/cjs/popper.js/*", "../../../../.cache/hugo_cache/modules/filecache/modules/pkg/mod/github.com/gohugoio/hugo-mod-jslibs-dist/popperjs/v2@v2.21100.20000/package/dist/cjs/popper.js/*",
"../../../../../.cache/hugo_cache/modules/filecache/modules/pkg/mod/github.com/twbs/bootstrap@v5.3.0+incompatible/js/*" "../../../../.cache/hugo_cache/modules/filecache/modules/pkg/mod/github.com/twbs/bootstrap@v5.3.0+incompatible/js/*"
] ]
} }
} }
......
...@@ -11,35 +11,19 @@ weight: 520 ...@@ -11,35 +11,19 @@ weight: 520
**开发环境下**,你需要将示例配置文件 `config.json` 复制成 `config.local.json` 文件才会生效。 **开发环境下**,你需要将示例配置文件 `config.json` 复制成 `config.local.json` 文件才会生效。
这个配置文件中包含了前端页面定制、系统级参数、AI 对话的模型等…… 这个配置文件中包含了系统级参数、AI 对话的模型、function 模型等……
{{% alert context="warning" %}}
注意:下面的配置介绍仅是局部介绍,你需要完整挂载整个 `config.json`,不能仅挂载一部分。你可以直接在默认的 config.json 基础上根据下面的介绍进行修改。挂载上去的配置文件不能包含注释。
{{% /alert %}}
## 基础字段粗略说明 ## 完整配置参数
这里介绍一些基础的配置字段: **使用时,请务必去除注释!**
```json ```json
... {
// 这个配置文件是系统级参数 "SystemParams": {
"SystemParams": {
"vectorMaxProcess": 15, // 向量生成最大进程,结合数据库性能和 key 来设置 "vectorMaxProcess": 15, // 向量生成最大进程,结合数据库性能和 key 来设置
"qaMaxProcess": 15, // QA 生成最大进程,结合数据库性能和 key 来设置 "qaMaxProcess": 15, // QA 生成最大进程,结合数据库性能和 key 来设置
"pgIvfflatProbe": 20 // pg vector 搜索探针。没有设置索引前可忽略,通常 50w 组以上才需要设置。 "pgIvfflatProbe": 20 // pg vector 搜索探针。没有设置索引前可忽略,通常 50w 组以上才需要设置。
},
...
```
## 完整配置参数
```json
{
"SystemParams": {
"vectorMaxProcess": 15,
"qaMaxProcess": 15,
"pgIvfflatProbe": 20
}, },
"ChatModels": [ "ChatModels": [
{ {
...@@ -79,25 +63,25 @@ weight: 520 ...@@ -79,25 +63,25 @@ weight: 520
"maxToken": 3000 "maxToken": 3000
} }
], ],
"QAModel": { "QAModel": { // QA 拆分模型
"model": "gpt-3.5-turbo-16k", "model": "gpt-3.5-turbo-16k",
"name": "GPT35-16k", "name": "GPT35-16k",
"maxToken": 0, "maxToken": 16000,
"price": 0 "price": 0
}, },
"ExtractModel": { "ExtractModel": { // 内容提取模型
"model": "gpt-3.5-turbo-16k", "model": "gpt-3.5-turbo-16k",
"functionCall": true, "functionCall": true, // 是否使用 functionCall
"name": "GPT35-16k", "name": "GPT35-16k",
"maxToken": 0, "maxToken": 16000,
"price": 0, "price": 0,
"prompt": "" "prompt": ""
}, },
"CQModel": { "CQModel": { // 问题分类模型
"model": "gpt-3.5-turbo-16k", "model": "gpt-3.5-turbo-16k",
"functionCall": true, "functionCall": true,
"name": "GPT35-16k", "name": "GPT35-16k",
"maxToken": 0, "maxToken": 16000,
"price": 0, "price": 0,
"prompt": "" "prompt": ""
} }
......
...@@ -10,30 +10,27 @@ weight: 510 ...@@ -10,30 +10,27 @@ weight: 510
本文档介绍了如何设置开发环境以构建和测试 [FastGPT](https://fastgpt.run) 本文档介绍了如何设置开发环境以构建和测试 [FastGPT](https://fastgpt.run)
## Tips
1. 用户默认的时区为 `Asia/Shanghai`,非 linux 环境时候,获取系统时间会异常,本地开发时候,可以将用户的时区调整成 UTC(+0)。
## 前置依赖项 ## 前置依赖项
您需要在计算机上安装和配置以下依赖项才能构建 [FastGPT](https://fastgpt.run) 您需要在计算机上安装和配置以下依赖项才能构建 [FastGPT](https://fastgpt.run)
- [Git](http://git-scm.com/) - [Git](http://git-scm.com/)
- [Docker](https://www.docker.com/) - [Docker](https://www.docker.com/)(构建镜像)
- [Docker Compose](https://docs.docker.com/compose/install/)
- [Node.js v18.x (LTS)](http://nodejs.org) - [Node.js v18.x (LTS)](http://nodejs.org)
- [npm](https://www.npmjs.com/) 版本 8.x.x 或 [Yarn](https://yarnpkg.com/) - [pnpm](https://pnpm.io/) 版本 8.x.x
## 本地开发 ## 开始本地开发
要设置一个可工作的开发环境,只需 Fork 项目的 Git 存储库,并部署一个数据库,然后开始进行开发测试。 **Tips**
### Fork 存储库 1. 用户默认的时区为 `Asia/Shanghai`,非 linux 环境时候,获取系统时间会异常,本地开发时候,可以将用户的时区调整成 UTC(+0)。
2. 建议先服务器装好数据库在进行本地开发。
### 1. Fork 存储库
您需要 Fork [存储库](https://github.com/labring/FastGPT) 您需要 Fork [存储库](https://github.com/labring/FastGPT)
### 克隆存储库 ### 2. 克隆存储库
克隆您在 GitHub 上 Fork 的存储库: 克隆您在 GitHub 上 Fork 的存储库:
...@@ -41,23 +38,27 @@ weight: 510 ...@@ -41,23 +38,27 @@ weight: 510
git clone git@github.com:<github_username>/FastGPT.git git clone git@github.com:<github_username>/FastGPT.git
``` ```
**projects 目录下为 FastGPT 应用代码。NextJS 框架前后端放在一起,API 服务位于 `src/pages/api` 目录内。** **目录简要说明**
1. `projects` 目录下为 FastGPT 应用代码。其中 `app` 为 FastGPT 核心应用。(后续可能会引入其他应用)
2. NextJS 框架前后端放在一起,API 服务位于 `src/pages/api` 目录内。
3. `packages` 目录为共用代码,通过 workspace 被注入到 `projects` 中,已配置 monorepo 自动注入,无需额外打包。
**packages 目录为相关的共用包。** ### 3. 安装数据库
### 安装数据库 第一次开发,需要先部署数据库,建议本地开发可以随便找一台 2C2G 的轻量小数据库实践。数据库部署教程:[Docker 快速部署](/docs/installation/docker/)。部署完了,可以本地访问其数据库。
第一次开发,需要先部署数据库,建议本地开发可以随便找一台 2C2G 的轻量小数据库实践。数据库部署教程:[Docker 快速部署](/docs/installation/docker/) ### 4. 初始配置
### 初始配置 以下文件均在 `projects/app` 路径下。
**1. 环境变量** **环境变量**
复制.env.template 文件,生成一个.env.local 环境变量文件夹,修改.env.local 里内容才是有效的变量。变量说明见 .env.template 复制.env.template 文件,生成一个.env.local 环境变量文件夹,修改.env.local 里内容才是有效的变量。变量说明见 .env.template
**2. config 配置文件** **config 配置文件**
复制 data/config.json 文件,生成一个 data/config.local.json 配置文件。具体的参数说明,可参考 [config 配置说名](/docs/development/configuration) 复制 data/config.json 文件,生成一个 data/config.local.json 配置文件,具体配置参数说明,可参考 [config 配置说明](/docs/development/configuration)
**注意:json 配置文件不能包含注释,介绍中为了方便看才加入的注释** **注意:json 配置文件不能包含注释,介绍中为了方便看才加入的注释**
...@@ -67,23 +68,29 @@ git clone git@github.com:<github_username>/FastGPT.git ...@@ -67,23 +68,29 @@ git clone git@github.com:<github_username>/FastGPT.git
- `qaMaxProcess`: QA 生成最大进程 - `qaMaxProcess`: QA 生成最大进程
- `pgIvfflatProbe`: PostgreSQL vector 搜索探针,没有添加 vector 索引时可忽略。 - `pgIvfflatProbe`: PostgreSQL vector 搜索探针,没有添加 vector 索引时可忽略。
### 运行 ### 5. 运行
```bash ```bash
# 代码根目录下执行,会安装根 package、projects 和 packages 内所有依赖
pnpm i pnpm i
cd projects/app # FastGPT 主程序 # 切换到应用目录
cd projects/app
# 开发模式运行
pnpm dev pnpm dev
``` ```
### 镜像打包 ### 6. 发布 - 镜像打包
```bash ```bash
# 根目录下执行
docker build -t dockername/fastgpt --build-arg name=app . docker build -t dockername/fastgpt --build-arg name=app .
``` ```
## 创建拉取请求 ## 提交代码至开源仓库
在进行更改后,打开一个拉取请求(PR)。提交拉取请求后,FastGPT 团队/社区的其他人将与您一起审查它。 1. 确保你的代码是 Fork [FastGPT](https://github.com/labring/FastGPT) 仓库
2. 尽可能少量的提交代码,每次提交仅解决一个问题。
3. 向 FastGPT 的 main 分支提交一个 PR,提交请求后,FastGPT 团队/社区的其他人将与您一起审查它。
如果遇到问题,比如合并冲突或不知道如何打开拉取请求,请查看 GitHub 的[拉取请求教程](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests),了解如何解决合并冲突和其他问题。一旦您的 PR 被合并,您将自豪地被列为[贡献者表](https://github.com/labring/FastGPT/graphs/contributors)中的一员。 如果遇到问题,比如合并冲突或不知道如何打开拉取请求,请查看 GitHub 的[拉取请求教程](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests),了解如何解决合并冲突和其他问题。一旦您的 PR 被合并,您将自豪地被列为[贡献者表](https://github.com/labring/FastGPT/graphs/contributors)中的一员。
......
--- ---
title: 'OpenAPI 使用' title: 'OpenAPI 使用(API Key 使用)'
description: 'FastGPT OpenAPI 文档' description: 'FastGPT OpenAPI 文档'
icon: 'api' icon: 'api'
draft: false draft: false
...@@ -29,19 +29,21 @@ FastGPT 的 API Key 有 2 类,一类是全局通用的 key;一类是携带 ...@@ -29,19 +29,21 @@ FastGPT 的 API Key 有 2 类,一类是全局通用的 key;一类是携带
{{% alert icon="🤖 " context="success" %}} {{% alert icon="🤖 " context="success" %}}
该接口 API Key 需使用应用特定的 key,否则会报错。 该接口 API Key 需使用应用特定的 key,否则会报错。
有些包的 BaseUrl 需要添加 `v1` 路径,有些不需要,建议都试一下。
{{% /alert %}} {{% /alert %}}
对话接口兼容 openai 的接口!如果你有第三方项目,可以直接通过修改 BaseUrl 和 Authorization 来访问 FastGpt 应用。缺点是你无法获取到响应的token值 对话接口兼容`GPT`的接口!如果你的项目使用的是标准的`GPT`官方接口,可以直接通过修改 `BaseUrl``Authorization` 来访问 FastGpt 应用
请求内容 请求参数说明
- headers.Authorization: Bearer apikey - headers.Authorization: Bearer apikey
- chatId: string | undefined 。 - chatId: string | undefined 。
- 为 undefined 时(不传入),不使用 FastGpt 提供的上下文功能,完全通过传入的 messages 构建上下文。 不会将你的记录存储到数据库中,你也无法在记录汇总中查阅到。 - 为 undefined 时(不传入),不使用 FastGpt 提供的上下文功能,完全通过传入的 messages 构建上下文。 不会将你的记录存储到数据库中,你也无法在记录汇总中查阅到。
- 为非空字符串时,意味着使用 chatId 进行对话,自动从 FastGpt 数据库取历史记录。并拼接 messages 数组最后一个内容作为完整请求。(自行确保 chatId 唯一,长度不限) - 为非空字符串时,意味着使用 chatId 进行对话,自动从 FastGpt 数据库取历史记录,并使用 messages 数组最后一个内容作为用户问题。(请自行确保 chatId 唯一,长度不限制)
- messages: 与 openai gpt 接口完全一致。 - messages: 结构与 [GPT接口](https://platform.openai.com/docs/api-reference/chat/object) 完全一致。
- detail: 是否返回详细值(模块状态,响应的完整结果),会通过event进行区分 - detail: 是否返回详细值(模块状态,响应的完整结果),`stream模式`下会通过event进行区分,`非stream模式`结果保存在responseData中。
- variables: 变量。一个对象,效果同全局变量 - variables: 变量内容,一个对象,会替换`{{key}}`变量。在`HTTP`模块中会发给接口,可作为身份凭证等标识
**请求示例:** **请求示例:**
......
...@@ -11,17 +11,17 @@ weight: 720 ...@@ -11,17 +11,17 @@ weight: 720
### 1. 准备好代理环境(国外服务器可忽略) ### 1. 准备好代理环境(国外服务器可忽略)
确保可以访问 OpenAI,具体方案可以参考:[Nginx 中转](/docs/installation/proxy/nginx/) 确保可以访问 OpenAI,具体方案可以参考:[代理方案](/docs/installation/proxy/)。或直接在 Sealos 上 [部署 OneAPI](/docs/installation/one-api),既解决代理问题也能实现多 Key 轮询、接入其他大模型。
### 2. 多模型支持 ### 2. 多模型支持
推荐使用 one-api 项目来管理模型池,兼容 OpenAI 、Azure 和国内主流模型等。 FastGPT 使用了 one-api 项目来管理模型池,其可以兼容 OpenAI 、Azure 、国内主流模型和本地模型等。
具体部署方法可参考该项目的 [README](https://github.com/songquanpeng/one-api),也可以直接通过以下按钮一键部署: 可选择 [Sealos 快速部署 OneAPI](/docs/installation/one-api),更多部署方法可参考该项目的 [README](https://github.com/songquanpeng/one-api),也可以直接通过以下按钮一键部署:
[![](https://fastly.jsdelivr.net/gh/labring-actions/templates@main/Deploy-on-Sealos.svg)](https://cloud.sealos.io/?openapp=system-fastdeploy%3FtemplateName%3Done-api) [![](https://fastly.jsdelivr.net/gh/labring-actions/templates@main/Deploy-on-Sealos.svg)](https://cloud.sealos.io/?openapp=system-fastdeploy%3FtemplateName%3Done-api)
## 安装 Docker 和 docker-compose ## 一、安装 Docker 和 docker-compose
{{< tabs tabTotal="3" >}} {{< tabs tabTotal="3" >}}
{{< tab tabName="Linux" >}} {{< tab tabName="Linux" >}}
...@@ -29,7 +29,7 @@ weight: 720 ...@@ -29,7 +29,7 @@ weight: 720
```bash ```bash
# 安装 Docker # 安装 Docker
curl -sSL https://get.daocloud.io/docker | sh curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun
systemctl enable --now docker systemctl enable --now docker
# 安装 docker-compose # 安装 docker-compose
curl -L https://github.com/docker/compose/releases/download/2.20.3/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose curl -L https://github.com/docker/compose/releases/download/2.20.3/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
...@@ -37,6 +37,7 @@ chmod +x /usr/local/bin/docker-compose ...@@ -37,6 +37,7 @@ chmod +x /usr/local/bin/docker-compose
# 验证安装 # 验证安装
docker -v docker -v
docker-compose -v docker-compose -v
# 如失效,自行百度~
``` ```
{{< /markdownify >}} {{< /markdownify >}}
...@@ -65,93 +66,35 @@ brew install orbstack ...@@ -65,93 +66,35 @@ brew install orbstack
{{< /tab >}} {{< /tab >}}
{{< /tabs >}} {{< /tabs >}}
## 创建 docker-compose.yml 文件 ## 二、创建目录并下载 docker-compose.yml
先创建一个目录(例如 fastgpt)并进入该目录,创建一个 docker-compose.yml 文件: 依次执行下面命令,创建 FastGPT 文件并拉取`docker-compose.yml``config.json`,执行完后目录下会有 2 个文件。
非 Linux 环境,可手动创建目录,并下载这2个文件。
**注意: 配置文件中 Mongo 为 5.x,部分服务器不支持,需手动更改其镜像版本为 4.4.24**
```bash ```bash
mkdir fastgpt mkdir fastgpt
cd fastgpt cd fastgpt
touch docker-compose.yml curl -O https://raw.githubusercontent.com/labring/FastGPT/main/files/deploy/fastgpt/docker-compose.yml
curl -O https://raw.githubusercontent.com/labring/FastGPT/main/projects/app/data/config.json
``` ```
粘贴下面的内容,仅需把 `CHAT_API_KEY` 修改成 openai key 即可。如果需要使用中转或 oneapi 还需要修改 `OPENAI_BASE_URL`:
```yaml
# 非 host 版本, 不使用本机代理
version: '3.3'
services:
pg:
image: ankane/pgvector:v0.4.2 # docker
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/pgvector:v0.4.2 # 阿里云
container_name: pg
restart: always
ports: # 生产环境建议不要暴露
- 5432:5432
networks:
- fastgpt
environment:
# 这里的配置只有首次运行生效。修改后,重启镜像是不会生效的。需要把持久化数据删除再重启,才有效果
- POSTGRES_USER=username
- POSTGRES_PASSWORD=password
- POSTGRES_DB=postgres
volumes:
- ./pg/data:/var/lib/postgresql/data
mongo:
image: mongo:5.0.18
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/mongo:5.0.18 # 阿里云
container_name: mongo
restart: always
ports: # 生产环境建议不要暴露
- 27017:27017
networks:
- fastgpt
environment:
# 这里的配置只有首次运行生效。修改后,重启镜像是不会生效的。需要把持久化数据删除再重启,才有效果
- MONGO_INITDB_ROOT_USERNAME=username
- MONGO_INITDB_ROOT_PASSWORD=password
volumes:
- ./mongo/data:/data/db
fastgpt:
container_name: fastgpt
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:latest # 阿里云
image: ghcr.io/labring/fastgpt:latest # github
ports:
- 3000:3000
networks:
- fastgpt
depends_on:
- mongo
- pg
restart: always
environment:
# root 密码,用户名为: root
- DEFAULT_ROOT_PSW=1234
# 中转地址,如果是用官方号,不需要管
- OPENAI_BASE_URL=https://api.openai.com/v1
- CHAT_API_KEY=sk-xxxx
- DB_MAX_LINK=5 # database max link
- TOKEN_KEY=any
- ROOT_KEY=root_key
- FILE_TOKEN_KEY=filetoken
# mongo 配置,不需要改. 如果连不上,可能需要去掉 ?authSource=admin
- MONGODB_URI=mongodb://username:password@mongo:27017/fastgpt?authSource=admin
# pg配置. 不需要改
- PG_URL=postgresql://username:password@pg:5432/postgres
networks:
fastgpt:
```
## 启动容器 ## 三、启动容器
修改`docker-compose.yml`中的`OPENAI_BASE_URL``CHAT_API_KEY`即可,对应为 API 的地址和 key。
```bash ```bash
# 在 docker-compose.yml 同级目录下执行 # 在 docker-compose.yml 同级目录下执行
docker-compose pull
docker-compose up -d docker-compose up -d
``` ```
## 访问 FastGPT ## 四、访问 FastGPT
目前可以通过 `ip:3000` 直接访问(注意防火墙)。登录用户名为 `root`,密码为刚刚环境变量里设置的 `DEFAULT_ROOT_PSW` 目前可以通过 `ip:3000` 直接访问(注意防火墙)。登录用户名为 `root`,密码为`docker-compose.yml`环境变量里设置的 `DEFAULT_ROOT_PSW`
如果需要域名访问,请自行安装并配置 Nginx。 如果需要域名访问,请自行安装并配置 Nginx。
...@@ -168,28 +111,30 @@ docker-compose up -d ...@@ -168,28 +111,30 @@ docker-compose up -d
### 如何自定义配置文件? ### 如何自定义配置文件?
需要在 `docker-compose.yml` 同级目录创建一个 `config.json` 文件,内容参考: [配置详解](/docs/development/configuration) 修改`config.json`文件,并执行`docker-compose up -d`重起容器。具体配置,参考[配置详解](/docs/development/configuration)
然后修改 `docker-compose.yml` 中的 `fastgpt` 容器内容,增加挂载选项即可: ### 如何检查自定义配置文件是否挂载
```yaml 1. `docker logs fastgpt` 可以查看日志,在启动容器后,第一次请求网页,会进行配置文件读取,可以看看有没有读取成功以及有无错误日志。
fastgpt: 2. `docker exec -it fastgpt sh` 进入 FastGPT 容器,可以通过`ls data`查看目录下是否成功挂载`config.json`文件。可通过`cat data/config.json`查看配置文件。
container_name: fastgpt
image: ghcr.io/labring/fastgpt:latest # github ### 为什么无法连接 oneapi 和 本地模型镜像。
ports:
- 3000:3000 `docker-compose.yml`中使用了桥接的模式建立了`fastgpt`网络,如想通过0.0.0.0或镜像名访问其它镜像,需将其它镜像也加入到网络中。
networks:
- fastgpt ### 端口冲突怎么解决?
depends_on:
- mongo docker-compose 端口定义为:`映射端口:运行端口`
- pg
restart: always 桥接模式下,容器运行端口不会有冲突,但是会有映射端口冲突,只需将映射端口修改成不同端口即可。
environment:
... 如果`容器1`需要连接`容器2`,使用`容器2:运行端口`来进行连接即可。
- DEFAULT_ROOT_PSW=1234
... (自行补习 docker 基本知识)
volumes:
- ./config.json:/app/data/config.json ### 错误排查方式
```
遇到问题先按下面方式排查。
> 参考[配置详解](/docs/development/configuration) 1. `docker ps -a` 查看所有容器运行状态,检查是否全部 running,如有异常,尝试`docker logs 容器名`查看对应日志。
2. 不懂 docker 不要瞎改端口,只需要改`OPENAI_BASE_URL``CHAT_API_KEY`即可。
--- ---
title: '部署 One API,实现多模型支持' title: '接入微软、ChatGLM、本地模型等'
description: '通过接入 One API 来实现对各种大模型的支持' description: '通过接入 One API 来实现对各种大模型的支持'
icon: 'Api' icon: 'Api'
draft: false draft: false
...@@ -7,9 +7,8 @@ toc: true ...@@ -7,9 +7,8 @@ toc: true
weight: 730 weight: 730
--- ---
默认情况下,FastGPT 只配置了 GPT 的 3 个模型,如果你需要接入其他模型,需要进行一些额外配置。 * 默认情况下,FastGPT 只配置了 GPT 的 3 个模型,如果你需要接入其他模型,需要进行一些额外配置。
* [One API](https://github.com/songquanpeng/one-api) 是一个 OpenAI 接口管理 & 分发系统,可以通过标准的 OpenAI API 格式访问所有的大模型,开箱即用。
[One API](https://github.com/songquanpeng/one-api) 是一个 OpenAI 接口管理 & 分发系统,可以通过标准的 OpenAI API 格式访问所有的大模型,开箱即用。
FastGPT 可以通过接入 One API 来实现对各种大模型的支持。部署方法也很简单。 FastGPT 可以通过接入 One API 来实现对各种大模型的支持。部署方法也很简单。
......
...@@ -23,6 +23,22 @@ Sealos 的服务器在国外,不需要额外处理网络问题,无需服务 ...@@ -23,6 +23,22 @@ Sealos 的服务器在国外,不需要额外处理网络问题,无需服务
> >
> 密码就是刚刚一键部署时设置的环境变量 > 密码就是刚刚一键部署时设置的环境变量
## 修改配置文件和环境变量
在 Sealos 中,你可以打开`应用管理`(App Launchpad)看到部署的 FastGPT,可以打开`数据库`(Database)看到对应的数据库。
`应用管理`中,选中 FastGPT,点击变更,可以看到对应的环境变量和配置文件。
![](/imgs/fastgptonsealos1.png)
{{% alert icon="🤖 " context="success" %}}
在 Sealos 上,FastGPT 一共运行了 1 个服务和 2 个数据库,如暂停和删除请注意数据库一同操作。(你可以白天启动,晚上暂停它们,省钱大法)
{{% /alert %}}
## 更新
点击重启会自动拉取最新镜像更新,请确保镜像`tag`正确。
## 部署架构图 ## 部署架构图
![](/imgs/sealos-fastgpt.webp) ![](/imgs/sealos-fastgpt.webp)
\ No newline at end of file
...@@ -26,6 +26,6 @@ curl --location --request POST 'https://{{host}}/api/admin/initv445' \ ...@@ -26,6 +26,6 @@ curl --location --request POST 'https://{{host}}/api/admin/initv445' \
### Fast GPT V4.4.5 ### Fast GPT V4.4.5
1. 新增 - 下一步指引选项,可以通过模型生成 3 个预测问题。 1. 新增 - 下一步指引选项,可以通过模型生成 3 个预测问题。
2. 新增 - 分享链接 hook 身份校验 2. 商业版新增 - 分享链接限制及 hook 身份校验(可对接已有的用户系统)
3. 新增 - Api Key 使用。增加别名、额度限制和过期时间。自带 appId,无需额外连接。 3. 商业版新增 - Api Key 使用。增加别名、额度限制和过期时间。自带 appId,无需额外连接。
4. 优化 - 全局变量与开场白合并成同一模块。 4. 优化 - 全局变量与开场白合并成同一模块。
\ No newline at end of file
{
"FeConfig": {
"show_emptyChat": true,
"show_register": false,
"show_appStore": false,
"show_userDetail": false,
"show_contact": true,
"show_git": true,
"show_doc": true,
"systemTitle": "FastGPT",
"authorText": "Made by FastGPT Team.",
"scripts": []
},
"SystemParams": {
"vectorMaxProcess": 15,
"qaMaxProcess": 15,
"pgIvfflatProbe": 20
},
"ChatModels": [
{
"model": "gpt-3.5-turbo",
"name": "GPT35-4k",
"contextMaxToken": 4000,
"quoteMaxToken": 2000,
"maxTemperature": 1.2,
"price": 0,
"defaultSystem": ""
},
{
"model": "gpt-3.5-turbo-16k",
"name": "GPT35-16k",
"contextMaxToken": 16000,
"quoteMaxToken": 8000,
"maxTemperature": 1.2,
"price": 0,
"defaultSystem": ""
},
{
"model": "gpt-4",
"name": "GPT4-8k",
"contextMaxToken": 8000,
"quoteMaxToken": 4000,
"maxTemperature": 1.2,
"price": 0,
"defaultSystem": ""
}
],
"VectorModels": [
{
"model": "text-embedding-ada-002",
"name": "Embedding-2",
"price": 0,
"defaultToken": 500,
"maxToken": 3000
}
],
"QAModel": {
"model": "gpt-3.5-turbo-16k",
"name": "GPT35-16k",
"maxToken": 16000,
"price": 0
}
}
...@@ -15,8 +15,9 @@ OPENAI_BASE_URL=https://api.openai.com/v1 ...@@ -15,8 +15,9 @@ OPENAI_BASE_URL=https://api.openai.com/v1
# 通用key。可以是 openai 的也可以是 oneapi 的。 # 通用key。可以是 openai 的也可以是 oneapi 的。
# 此处逻辑:优先走 ONEAPI_URL,如果填写了 ONEAPI_URL,key 也需要是 ONEAPI 的 key # 此处逻辑:优先走 ONEAPI_URL,如果填写了 ONEAPI_URL,key 也需要是 ONEAPI 的 key
CHAT_API_KEY=sk-xxxx CHAT_API_KEY=sk-xxxx
# db # mongo 数据库连接参数
MONGODB_URI=mongodb://username:password@0.0.0.0:27017/fastgpt?authSource=admin MONGODB_URI=mongodb://username:password@0.0.0.0:27017/fastgpt?authSource=admin
# PG 数据库连接参数
PG_URL=postgresql://username:password@host:port/postgres PG_URL=postgresql://username:password@host:port/postgres
# 首页路径 # 首页路径
HOME_URL=/ HOME_URL=/
{ {
"FeConfig": {
"show_emptyChat": true,
"show_contact": true,
"show_git": true,
"show_doc": true,
"systemTitle": "FastGPT",
"authorText": "Made by FastGPT Team.",
"limit": {
"exportLimitMinutes": 0
},
"scripts": []
},
"SystemParams": { "SystemParams": {
"vectorMaxProcess": 15, "vectorMaxProcess": 15,
"qaMaxProcess": 15, "qaMaxProcess": 15,
......
...@@ -237,7 +237,7 @@ ...@@ -237,7 +237,7 @@
"key tips": "你可以使用 API 秘钥访问一些特定的接口" "key tips": "你可以使用 API 秘钥访问一些特定的接口"
}, },
"outlink": { "outlink": {
"Copy Iframe": "复制嵌入", "Copy Iframe": "嵌入网页",
"Copy Link": "复制", "Copy Link": "复制",
"Create API Key": "创建新 Key", "Create API Key": "创建新 Key",
"Create Link": "创建链接", "Create Link": "创建链接",
......
...@@ -31,7 +31,7 @@ const SelectDataset = ({ ...@@ -31,7 +31,7 @@ const SelectDataset = ({
setParentId={setParentId} setParentId={setParentId}
tips={t('chat.Select Mark Kb Desc')} tips={t('chat.Select Mark Kb Desc')}
> >
<ModalBody flex={['1 0 0', '0 0 auto']} maxH={'80vh'} overflowY={'auto'}> <ModalBody flex={'1 0 0'} overflowY={'auto'}>
<Grid <Grid
gridTemplateColumns={['repeat(1,1fr)', 'repeat(2,1fr)', 'repeat(3,1fr)']} gridTemplateColumns={['repeat(1,1fr)', 'repeat(2,1fr)', 'repeat(3,1fr)']}
gridGap={3} gridGap={3}
......
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1696179048209" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4182" xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48"><path d="M373.333333 85.333333H266.666667a53.393333 53.393333 0 0 0-53.333334 53.333334v746.666666a53.393333 53.393333 0 0 0 53.333334 53.333334h106.666666a53.393333 53.393333 0 0 0 53.333334-53.333334V138.666667a53.393333 53.393333 0 0 0-53.333334-53.333334z m10.666667 800a10.666667 10.666667 0 0 1-10.666667 10.666667H266.666667a10.666667 10.666667 0 0 1-10.666667-10.666667V138.666667a10.666667 10.666667 0 0 1 10.666667-10.666667h106.666666a10.666667 10.666667 0 0 1 10.666667 10.666667z m373.333333-800H650.666667a53.393333 53.393333 0 0 0-53.333334 53.333334v746.666666a53.393333 53.393333 0 0 0 53.333334 53.333334h106.666666a53.393333 53.393333 0 0 0 53.333334-53.333334V138.666667a53.393333 53.393333 0 0 0-53.333334-53.333334z m10.666667 800a10.666667 10.666667 0 0 1-10.666667 10.666667H650.666667a10.666667 10.666667 0 0 1-10.666667-10.666667V138.666667a10.666667 10.666667 0 0 1 10.666667-10.666667h106.666666a10.666667 10.666667 0 0 1 10.666667 10.666667z" p-id="4183"></path></svg>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style=" background: rgb(255, 255, 255); display: block; shape-rendering: auto;" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid">
<g transform="rotate(0 50 50)">
<rect x="47.5" y="24" rx="2.5" ry="6" width="5" height="12" fill="#3370ff">
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.9166666666666666s" repeatCount="indefinite"></animate>
</rect>
</g><g transform="rotate(30 50 50)">
<rect x="47.5" y="24" rx="2.5" ry="6" width="5" height="12" fill="#3370ff">
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.8333333333333334s" repeatCount="indefinite"></animate>
</rect>
</g><g transform="rotate(60 50 50)">
<rect x="47.5" y="24" rx="2.5" ry="6" width="5" height="12" fill="#3370ff">
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.75s" repeatCount="indefinite"></animate>
</rect>
</g><g transform="rotate(90 50 50)">
<rect x="47.5" y="24" rx="2.5" ry="6" width="5" height="12" fill="#3370ff">
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.6666666666666666s" repeatCount="indefinite"></animate>
</rect>
</g><g transform="rotate(120 50 50)">
<rect x="47.5" y="24" rx="2.5" ry="6" width="5" height="12" fill="#3370ff">
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.5833333333333334s" repeatCount="indefinite"></animate>
</rect>
</g><g transform="rotate(150 50 50)">
<rect x="47.5" y="24" rx="2.5" ry="6" width="5" height="12" fill="#3370ff">
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.5s" repeatCount="indefinite"></animate>
</rect>
</g><g transform="rotate(180 50 50)">
<rect x="47.5" y="24" rx="2.5" ry="6" width="5" height="12" fill="#3370ff">
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.4166666666666667s" repeatCount="indefinite"></animate>
</rect>
</g><g transform="rotate(210 50 50)">
<rect x="47.5" y="24" rx="2.5" ry="6" width="5" height="12" fill="#3370ff">
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.3333333333333333s" repeatCount="indefinite"></animate>
</rect>
</g><g transform="rotate(240 50 50)">
<rect x="47.5" y="24" rx="2.5" ry="6" width="5" height="12" fill="#3370ff">
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.25s" repeatCount="indefinite"></animate>
</rect>
</g><g transform="rotate(270 50 50)">
<rect x="47.5" y="24" rx="2.5" ry="6" width="5" height="12" fill="#3370ff">
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.16666666666666666s" repeatCount="indefinite"></animate>
</rect>
</g><g transform="rotate(300 50 50)">
<rect x="47.5" y="24" rx="2.5" ry="6" width="5" height="12" fill="#3370ff">
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.08333333333333333s" repeatCount="indefinite"></animate>
</rect>
</g><g transform="rotate(330 50 50)">
<rect x="47.5" y="24" rx="2.5" ry="6" width="5" height="12" fill="#3370ff">
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="0s" repeatCount="indefinite"></animate>
</rect>
</g>
<!-- [ldio] generated by https://loading.io/ --></svg>
\ No newline at end of file
...@@ -87,7 +87,9 @@ const iconPaths = { ...@@ -87,7 +87,9 @@ const iconPaths = {
searchLight: () => import('./icons/light/search.svg'), searchLight: () => import('./icons/light/search.svg'),
plusFill: () => import('./icons/fill/plus.svg'), plusFill: () => import('./icons/fill/plus.svg'),
moveLight: () => import('./icons/light/move.svg'), moveLight: () => import('./icons/light/move.svg'),
questionGuide: () => import('./icons/app/questionGuide.svg') questionGuide: () => import('./icons/app/questionGuide.svg'),
loading: () => import('./icons/light/loading.svg'),
pause: () => import('./icons/common/pause.svg')
}; };
export type IconName = keyof typeof iconPaths; export type IconName = keyof typeof iconPaths;
......
...@@ -4,6 +4,7 @@ import ReactMarkdown from 'react-markdown'; ...@@ -4,6 +4,7 @@ import ReactMarkdown from 'react-markdown';
import RemarkGfm from 'remark-gfm'; import RemarkGfm from 'remark-gfm';
import RemarkMath from 'remark-math'; import RemarkMath from 'remark-math';
import RehypeKatex from 'rehype-katex'; import RehypeKatex from 'rehype-katex';
import RemarkBreaks from 'remark-breaks';
import { event } from '@/utils/plugin/eventbus'; import { event } from '@/utils/plugin/eventbus';
import 'katex/dist/katex.min.css'; import 'katex/dist/katex.min.css';
...@@ -38,12 +39,14 @@ function MyLink(e: any) { ...@@ -38,12 +39,14 @@ function MyLink(e: any) {
} }
const Guide = ({ text }: { text: string }) => { const Guide = ({ text }: { text: string }) => {
const formatText = useMemo(() => text.replace(/\[(.*?)\]($|\n)/g, '[$1]()\n'), [text]); const formatText = useMemo(
() => text.replace(/\[(.*?)\]($|\n)/g, '[$1]()\n').replace(/\\n/g, '\n&nbsp;'),
[text]
);
return ( return (
<ReactMarkdown <ReactMarkdown
className={`markdown ${styles.markdown}`} className={`markdown ${styles.markdown}`}
remarkPlugins={[RemarkGfm, RemarkMath]} remarkPlugins={[RemarkGfm, RemarkMath, RemarkBreaks]}
rehypePlugins={[RehypeKatex]} rehypePlugins={[RehypeKatex]}
components={{ components={{
a: MyLink, a: MyLink,
......
...@@ -62,6 +62,8 @@ const Markdown = ({ source, isChatting = false }: { source: string; isChatting?: ...@@ -62,6 +62,8 @@ const Markdown = ({ source, isChatting = false }: { source: string; isChatting?:
[] []
); );
const formatSource = source.replace(/\\n/g, '\n&nbsp;');
return ( return (
<ReactMarkdown <ReactMarkdown
className={`markdown ${styles.markdown} className={`markdown ${styles.markdown}
...@@ -73,9 +75,9 @@ const Markdown = ({ source, isChatting = false }: { source: string; isChatting?: ...@@ -73,9 +75,9 @@ const Markdown = ({ source, isChatting = false }: { source: string; isChatting?:
components={components} components={components}
linkTarget={'_blank'} linkTarget={'_blank'}
> >
{source} {formatSource}
</ReactMarkdown> </ReactMarkdown>
); );
}; };
export default Markdown; export default React.memo(Markdown);
...@@ -8,7 +8,7 @@ const PageContainer = ({ children, ...props }: BoxProps) => { ...@@ -8,7 +8,7 @@ const PageContainer = ({ children, ...props }: BoxProps) => {
<Box <Box
h={'100%'} h={'100%'}
bg={'white'} bg={'white'}
borderRadius={[0, '2xl']} borderRadius={props?.borderRadius || [0, '2xl']}
border={['none', theme.borders.lg]} border={['none', theme.borders.lg]}
overflow={'overlay'} overflow={'overlay'}
> >
......
...@@ -33,14 +33,8 @@ const DatasetSelectContainer = ({ ...@@ -33,14 +33,8 @@ const DatasetSelectContainer = ({
const { isPc } = useGlobalStore(); const { isPc } = useGlobalStore();
return ( return (
<MyModal <MyModal isOpen={isOpen} onClose={onClose} w={'100%'} maxW={['90vw', '900px']} isCentered>
isOpen={isOpen} <Flex flexDirection={'column'} h={'90vh'}>
onClose={onClose}
w={'100%'}
maxW={['90vw', '900px']}
isCentered={!isPc}
>
<Flex flexDirection={'column'} h={['90vh', 'auto']}>
<ModalHeader> <ModalHeader>
{!!parentId ? ( {!!parentId ? (
<Flex <Flex
......
...@@ -308,7 +308,7 @@ export const AnswerModule: FlowModuleTemplateType = { ...@@ -308,7 +308,7 @@ export const AnswerModule: FlowModuleTemplateType = {
value: '', value: '',
label: '回复的内容', label: '回复的内容',
description: description:
'可以使用 \\n 来实现换行。也可以通过外部模块输入实现回复,外部模块输入时会覆盖当前填写的内容' '可以使用 \\n 来实现连续换行。\n\n可以通过外部模块输入实现回复,外部模块输入时会覆盖当前填写的内容'
} }
], ],
outputs: [ outputs: [
......
...@@ -61,6 +61,12 @@ function App({ Component, pageProps }: AppProps) { ...@@ -61,6 +61,12 @@ function App({ Component, pageProps }: AppProps) {
url url
}); });
}; };
// log fastgpt
console.log(
'%cWelcome to FastGPT',
'font-family:Arial; color:#3370ff ; font-size:18px; font-weight:bold;',
`GitHub:https://github.com/labring/FastGPT`
);
return () => { return () => {
window.onerror = null; window.onerror = null;
}; };
......
...@@ -67,7 +67,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< ...@@ -67,7 +67,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
res.end('Error connecting to database'); res.end('Error connecting to database');
return; return;
} }
console.log('export data');
// create pg select stream // create pg select stream
const query = new QueryStream( const query = new QueryStream(
...@@ -77,16 +76,16 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< ...@@ -77,16 +76,16 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
); );
const stream = client.query(query); const stream = client.query(query);
res.setHeader('Content-Disposition', 'attachment; filename=dataset.csv'); res.setHeader('Content-Type', 'text/csv; charset=utf-8');
res.setHeader('Content-Type', 'text/csv'); res.setHeader('Content-Disposition', 'attachment; filename=dataset.csv; ');
res.write('index,content,source');
const write = responseWriteController({ const write = responseWriteController({
res, res,
readStream: stream readStream: stream
}); });
write('index,content,source');
// parse data every row // parse data every row
stream.on('data', ({ q, a, source }: { q: string; a: string; source?: string }) => { stream.on('data', ({ q, a, source }: { q: string; a: string; source?: string }) => {
if (res.closed) { if (res.closed) {
......
import React, { useCallback, useMemo, useRef } from 'react'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import Head from 'next/head'; import Head from 'next/head';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { initShareChatInfo } from '@/api/support/outLink'; import { initShareChatInfo } from '@/api/support/outLink';
...@@ -35,6 +35,7 @@ const OutLink = ({ ...@@ -35,6 +35,7 @@ const OutLink = ({
const { isOpen: isOpenSlider, onClose: onCloseSlider, onOpen: onOpenSlider } = useDisclosure(); const { isOpen: isOpenSlider, onClose: onCloseSlider, onOpen: onOpenSlider } = useDisclosure();
const { isPc } = useGlobalStore(); const { isPc } = useGlobalStore();
const forbidRefresh = useRef(false); const forbidRefresh = useRef(false);
const [isEmbed, setIdEmbed] = useState(true);
const ChatBoxRef = useRef<ComponentRef>(null); const ChatBoxRef = useRef<ComponentRef>(null);
...@@ -163,8 +164,12 @@ const OutLink = ({ ...@@ -163,8 +164,12 @@ const OutLink = ({
return loadAppInfo(shareId, chatId, authToken); return loadAppInfo(shareId, chatId, authToken);
}); });
useEffect(() => {
setIdEmbed(window !== parent);
}, []);
return ( return (
<PageContainer> <PageContainer {...(isEmbed ? { p: '0 !important', borderRadius: '0' } : {})}>
<Head> <Head>
<title>{shareChatData.app.name}</title> <title>{shareChatData.app.name}</title>
</Head> </Head>
......
...@@ -11,7 +11,7 @@ export function responseWriteController({ ...@@ -11,7 +11,7 @@ export function responseWriteController({
readStream.resume(); readStream.resume();
}); });
return (text: string) => { return (text: string | Buffer) => {
const writeResult = res.write(text); const writeResult = res.write(text);
if (!writeResult) { if (!writeResult) {
readStream.pause(); readStream.pause();
......
...@@ -23,7 +23,7 @@ export const dispatchAnswer = (props: Record<string, any>): AnswerResponse => { ...@@ -23,7 +23,7 @@ export const dispatchAnswer = (props: Record<string, any>): AnswerResponse => {
res, res,
event: detail ? sseResponseEventEnum.answer : undefined, event: detail ? sseResponseEventEnum.answer : undefined,
data: textAdaptGptResponse({ data: textAdaptGptResponse({
text: text.replace?.(/\\n/g, '\n') || '' text
}) })
}); });
} }
......
export const hasVoiceApi = typeof window !== 'undefined' && 'speechSynthesis' in window; import { useState, useCallback, useEffect, useMemo } from 'react';
/** import { useToast } from '@/hooks/useToast';
* voice broadcast import { getErrText } from '../tools';
*/
export const voiceBroadcast = ({ text }: { text: string }) => { export const useAudioPlay = (props?: { ttsUrl?: string }) => {
const { ttsUrl } = props || {};
const { toast } = useToast();
const [audio, setAudio] = useState<HTMLAudioElement>();
const [audioLoading, setAudioLoading] = useState(false);
const [audioPlaying, setAudioPlaying] = useState(false);
const hasAudio = useMemo(() => {
if (ttsUrl) return true;
const voices = window.speechSynthesis?.getVoices?.() || []; // 获取语言包
const voice = voices.find((item) => {
return item.lang === 'zh-CN';
});
return !!voice;
}, [ttsUrl]);
const playAudio = useCallback(
async (text: string) => {
text = text.replace(/\\n/g, '\n');
try {
if (audio && ttsUrl) {
setAudioLoading(true);
const response = await fetch(ttsUrl, {
method: 'POST',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify({
text
})
}).then((res) => res.blob());
const audioUrl = URL.createObjectURL(response);
audio.src = audioUrl;
audio.play();
} else {
// window speech
window.speechSynthesis?.cancel(); window.speechSynthesis?.cancel();
const msg = new SpeechSynthesisUtterance(text); const msg = new SpeechSynthesisUtterance(text);
const voices = window.speechSynthesis?.getVoices?.(); // 获取语言包 const voices = window.speechSynthesis?.getVoices?.() || []; // 获取语言包
const voice = voices.find((item) => { const voice = voices.find((item) => {
return item.name === 'Microsoft Yaoyao - Chinese (Simplified, PRC)'; return item.lang === 'zh-CN';
}); });
if (voice) { if (voice) {
msg.onstart = () => {
setAudioPlaying(true);
};
msg.onend = () => {
setAudioPlaying(false);
msg.onstart = null;
msg.onend = null;
};
msg.voice = voice; msg.voice = voice;
window.speechSynthesis?.speak(msg);
} }
}
} catch (error) {
toast({
status: 'error',
title: getErrText(error, '语音播报异常')
});
}
setAudioLoading(false);
},
[audio, toast, ttsUrl]
);
window.speechSynthesis?.speak(msg); const cancelAudio = useCallback(() => {
if (audio) {
audio.pause();
audio.src = '';
}
window.speechSynthesis?.cancel();
setAudioPlaying(false);
}, [audio]);
useEffect(() => {
if (ttsUrl) {
setAudio(new Audio());
} else {
setAudio(undefined);
}
}, [ttsUrl]);
msg.onerror = (e) => { useEffect(() => {
console.log(e); if (audio) {
audio.onplay = () => {
setAudioPlaying(true);
};
audio.onended = () => {
setAudioPlaying(false);
};
audio.onerror = () => {
setAudioPlaying(false);
};
}
const listen = () => {
cancelAudio();
}; };
window.addEventListener('beforeunload', listen);
return () => {
if (audio) {
audio.onplay = null;
audio.onended = null;
audio.onerror = null;
}
cancelAudio();
window.removeEventListener('beforeunload', listen);
};
}, [audio, cancelAudio]);
useEffect(() => {
return () => {
setAudio(undefined);
};
}, []);
return { return {
cancel: () => window.speechSynthesis?.cancel() audioPlaying,
audioLoading,
hasAudio,
playAudio,
cancelAudio
}; };
}; };
export const cancelBroadcast = () => {
window.speechSynthesis?.cancel();
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment