Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c7f0db0811 | ||
|
|
c16d59671f |
@@ -14,13 +14,9 @@ aliSignName=xxx
|
|||||||
aliTemplateCode=SMS_xxx
|
aliTemplateCode=SMS_xxx
|
||||||
# token
|
# token
|
||||||
TOKEN_KEY=xxx
|
TOKEN_KEY=xxx
|
||||||
# root key, 最高权限
|
|
||||||
ROOT_KEY=xxx
|
|
||||||
# 是否进行安全校验(1: 开启,0: 关闭)
|
|
||||||
SENSITIVE_CHECK=1
|
|
||||||
# openai
|
# openai
|
||||||
# OPENAI_BASE_URL=https://api.openai.com/v1
|
# OPENAI_BASE_URL=https://api.openai.com/v1
|
||||||
# OPENAI_BASE_URL_AUTH=可选的安全凭证(不需要的时候,记得去掉)
|
# OPENAI_BASE_URL_AUTH=可选的安全凭证
|
||||||
OPENAIKEY=sk-xxx
|
OPENAIKEY=sk-xxx
|
||||||
GPT4KEY=sk-xxx
|
GPT4KEY=sk-xxx
|
||||||
# claude
|
# claude
|
||||||
|
|||||||
@@ -52,8 +52,6 @@ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
|||||||
|
|
||||||
USER nextjs
|
USER nextjs
|
||||||
|
|
||||||
ENV PORT=3000
|
|
||||||
|
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
|
|
||||||
CMD ["node", "server.js"]
|
CMD ["node", "server.js"]
|
||||||
|
|||||||
@@ -21,8 +21,7 @@ Fast GPT 允许你使用自己的 openai API KEY 来快速的调用 openai 接
|
|||||||
|
|
||||||
## 🚀 私有化部署
|
## 🚀 私有化部署
|
||||||
|
|
||||||
- [docker-compose 部署教程](docs/deploy/docker.md)
|
[docker-compose 部署教程](docs/deploy/docker.md)
|
||||||
- [由社区贡献的宝塔部署和本地运行教程](https://space.bilibili.com/431177525/channel/collectiondetail?sid=1370663)
|
|
||||||
|
|
||||||
## :point_right: RoadMap
|
## :point_right: RoadMap
|
||||||
|
|
||||||
@@ -30,13 +29,12 @@ Fast GPT 允许你使用自己的 openai API KEY 来快速的调用 openai 接
|
|||||||
|
|
||||||
## 🏘️ 交流群
|
## 🏘️ 交流群
|
||||||
|
|
||||||
添加 wx 进入:
|
wx: fastgpt123
|
||||||

|

|
||||||
|
|
||||||
## 👀 其他
|
## 👀 其他
|
||||||
|
|
||||||
- [FastGpt 常见问题](https://kjqvjse66l.feishu.cn/docx/HtrgdT0pkonP4kxGx8qcu6XDnGh)
|
- [FastGpt 常见问题](https://kjqvjse66l.feishu.cn/docx/HtrgdT0pkonP4kxGx8qcu6XDnGh)
|
||||||
- [公众号接入](https://www.bilibili.com/video/BV1xh4y1t7fy/)
|
|
||||||
- [FastGpt + Laf 最佳实践,将知识库装入公众号,点击去 Laf 公众号体验效果](https://b4jky7-fastgpt.oss.laf.run/lafercode.png)
|
- [FastGpt + Laf 最佳实践,将知识库装入公众号,点击去 Laf 公众号体验效果](https://b4jky7-fastgpt.oss.laf.run/lafercode.png)
|
||||||
- [FastGpt V3.4 更新集合](https://www.bilibili.com/video/BV1Lo4y147Qh/?vd_source=92041a1a395f852f9d89158eaa3f61b4)
|
- [FastGpt V3.4 更新集合](https://www.bilibili.com/video/BV1Lo4y147Qh/?vd_source=92041a1a395f852f9d89158eaa3f61b4)
|
||||||
- [FastGpt 知识库演示](https://www.bilibili.com/video/BV1Wo4y1p7i1/)
|
- [FastGpt 知识库演示](https://www.bilibili.com/video/BV1Wo4y1p7i1/)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## 代理环境(国外服务器可忽略)
|
## 代理环境(国外服务器可忽略)
|
||||||
|
|
||||||
选择一个即可。这只是代理!!!不是项目。
|
选择一个即可。
|
||||||
|
|
||||||
1. [sealos nginx 方案](./proxy/sealos.md) - 推荐。约等于不用钱,不需要额外准备任何东西。
|
1. [sealos nginx 方案](./proxy/sealos.md) - 推荐。约等于不用钱,不需要额外准备任何东西。
|
||||||
2. [clash 方案](./proxy/clash.md) - 仅需一台服务器(需要有 clash)
|
2. [clash 方案](./proxy/clash.md) - 仅需一台服务器(需要有 clash)
|
||||||
@@ -35,10 +35,185 @@ docker-compose -v
|
|||||||
|
|
||||||
### 2. 创建 3 个初始化文件
|
### 2. 创建 3 个初始化文件
|
||||||
|
|
||||||
fastgpt 文件夹。分别为:fastgpt/docker-compose.yaml, fastgpt/pg/init.sql, fastgpt/nginx/nginx.conf
|
|
||||||
|
|
||||||
手动创建或者直接把 fastgpt 文件夹复制过去。
|
手动创建或者直接把 fastgpt 文件夹复制过去。
|
||||||
|
|
||||||
|
**/root/fastgpt/pg/init.sql PG 数据库初始化**
|
||||||
|
|
||||||
|
```sql
|
||||||
|
set -e
|
||||||
|
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
|
||||||
|
|
||||||
|
CREATE EXTENSION vector;
|
||||||
|
-- init table
|
||||||
|
CREATE TABLE modelData (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
vector VECTOR(1536),
|
||||||
|
status VARCHAR(50) NOT NULL,
|
||||||
|
user_id VARCHAR(50) NOT NULL,
|
||||||
|
model_id VARCHAR(50) NOT NULL,
|
||||||
|
q TEXT NOT NULL,
|
||||||
|
a TEXT NOT NULL
|
||||||
|
);
|
||||||
|
-- create index
|
||||||
|
CREATE INDEX modelData_status_index ON modelData USING HASH (status);
|
||||||
|
CREATE INDEX modelData_userId_index ON modelData USING HASH (user_id);
|
||||||
|
CREATE INDEX modelData_modelId_index ON modelData USING HASH (model_id);
|
||||||
|
EOSQL
|
||||||
|
```
|
||||||
|
|
||||||
|
**/root/fastgpt/nginx/nginx.conf Nginx 配置**
|
||||||
|
|
||||||
|
```conf
|
||||||
|
user nginx;
|
||||||
|
worker_processes auto;
|
||||||
|
worker_rlimit_nofile 51200;
|
||||||
|
|
||||||
|
events {
|
||||||
|
worker_connections 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
resolver 8.8.8.8;
|
||||||
|
proxy_ssl_server_name on;
|
||||||
|
|
||||||
|
access_log off;
|
||||||
|
server_names_hash_bucket_size 512;
|
||||||
|
client_header_buffer_size 64k;
|
||||||
|
large_client_header_buffers 4 64k;
|
||||||
|
client_max_body_size 50M;
|
||||||
|
|
||||||
|
proxy_connect_timeout 240s;
|
||||||
|
proxy_read_timeout 240s;
|
||||||
|
proxy_buffer_size 128k;
|
||||||
|
proxy_buffers 4 256k;
|
||||||
|
|
||||||
|
gzip on;
|
||||||
|
gzip_min_length 1k;
|
||||||
|
gzip_buffers 4 8k;
|
||||||
|
gzip_http_version 1.1;
|
||||||
|
gzip_comp_level 6;
|
||||||
|
gzip_vary on;
|
||||||
|
gzip_types text/plain application/x-javascript text/css application/javascript application/json application/xml;
|
||||||
|
gzip_disable "MSIE [1-6]\.";
|
||||||
|
|
||||||
|
open_file_cache max=1000 inactive=1d;
|
||||||
|
open_file_cache_valid 30s;
|
||||||
|
open_file_cache_min_uses 8;
|
||||||
|
open_file_cache_errors off;
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443 ssl;
|
||||||
|
# 改成自己的域名和证书
|
||||||
|
server_name docgpt.ahapocket.cn;
|
||||||
|
ssl_certificate /ssl/docgpt.pem;
|
||||||
|
ssl_certificate_key /ssl/docgpt.key;
|
||||||
|
ssl_session_timeout 5m;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://localhost:3000;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name docgpt.ahapocket.cn;
|
||||||
|
rewrite ^(.*) https://$server_name$1 permanent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**/root/fastgpt/docker-compose.yml 核心部署文件**
|
||||||
|
|
||||||
|
环境变量内容和开发时的环境变量基本相同,除了数据库的地址。
|
||||||
|
|
||||||
|
```yml
|
||||||
|
version: '3.3'
|
||||||
|
services:
|
||||||
|
pg:
|
||||||
|
image: ankane/pgvector:v0.4.1
|
||||||
|
container_name: pg
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- 8100:5432
|
||||||
|
environment:
|
||||||
|
# 这里的配置只有首次运行生效。修改后,重启镜像是不会生效的。需要把持久化数据删除再重启,才有效果
|
||||||
|
- POSTGRES_USER=fastgpt
|
||||||
|
- POSTGRES_PASSWORD=1234
|
||||||
|
- POSTGRES_DB=fastgpt
|
||||||
|
volumes:
|
||||||
|
# 刚创建的文件
|
||||||
|
- /root/fastgpt/pg/init.sql:/docker-entrypoint-initdb.d/init.sh
|
||||||
|
- /root/fastgpt/pg/data:/var/lib/postgresql/data
|
||||||
|
- /etc/localtime:/etc/localtime:ro
|
||||||
|
mongodb:
|
||||||
|
image: mongo:6.0.4
|
||||||
|
container_name: mongo
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- 27017:27017
|
||||||
|
environment:
|
||||||
|
# 这里的配置只有首次运行生效。修改后,重启镜像是不会生效的。需要把持久化数据删除再重启,才有效果
|
||||||
|
- MONGO_INITDB_ROOT_USERNAME=username
|
||||||
|
- MONGO_INITDB_ROOT_PASSWORD=password
|
||||||
|
volumes:
|
||||||
|
- /root/fastgpt/mongo/data:/data/db
|
||||||
|
- /root/fastgpt/mongo/logs:/var/log/mongodb
|
||||||
|
- /etc/localtime:/etc/localtime:ro
|
||||||
|
fastgpt:
|
||||||
|
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:latest
|
||||||
|
network_mode: host
|
||||||
|
restart: always
|
||||||
|
container_name: fastgpt
|
||||||
|
environment:
|
||||||
|
# proxy(可选)
|
||||||
|
- AXIOS_PROXY_HOST=127.0.0.1
|
||||||
|
- AXIOS_PROXY_PORT=7890
|
||||||
|
# 是否开启队列任务。 1-开启,0-关闭(请求 parentUrl 去执行任务,单机时直接填1)
|
||||||
|
- queueTask=1
|
||||||
|
- parentUrl=https://hostname/api/openapi/startEvents
|
||||||
|
# 发送邮箱验证码配置。用的是QQ邮箱。参考 nodeMail 获取MAILE_CODE,自行百度。
|
||||||
|
- MY_MAIL=xxxx@qq.com
|
||||||
|
- MAILE_CODE=xxxx
|
||||||
|
# 阿里短信服务(邮箱和短信至少二选一)
|
||||||
|
- aliAccessKeyId=xxxx
|
||||||
|
- aliAccessKeySecret=xxxx
|
||||||
|
- aliSignName=xxxxx
|
||||||
|
- aliTemplateCode=SMS_xxxx
|
||||||
|
# token加密凭证(随便填,作为登录凭证)
|
||||||
|
- TOKEN_KEY=xxxx
|
||||||
|
# 和上方mongo镜像的username,password对应
|
||||||
|
- MONGODB_URI=mongodb://username:password@0.0.0.0:27017/?authSource=admin
|
||||||
|
- MONGODB_NAME=fastgpt
|
||||||
|
- PG_HOST=0.0.0.0
|
||||||
|
- PG_PORT=8100
|
||||||
|
# 和上方PG镜像对应.
|
||||||
|
- PG_USER=fastgpt # POSTGRES_USER
|
||||||
|
- PG_PASSWORD=1234 # POSTGRES_PASSWORD
|
||||||
|
- PG_DB_NAME=fastgpt # POSTGRES_DB
|
||||||
|
# openai
|
||||||
|
- OPENAIKEY=sk-xxxxx
|
||||||
|
- GPT4KEY=sk-xxx
|
||||||
|
- OPENAI_BASE_URL=https://api.openai.com/v1
|
||||||
|
- OPENAI_BASE_URL_AUTH=可选的安全凭证
|
||||||
|
# claude
|
||||||
|
- CLAUDE_BASE_URL=calude模型请求地址
|
||||||
|
- CLAUDE_KEY=CLAUDE_KEY
|
||||||
|
nginx:
|
||||||
|
image: nginx:alpine3.17
|
||||||
|
container_name: nginx
|
||||||
|
restart: always
|
||||||
|
network_mode: host
|
||||||
|
volumes:
|
||||||
|
# 刚创建的文件
|
||||||
|
- /root/fastgpt/nginx/nginx.conf:/etc/nginx/nginx.conf:ro
|
||||||
|
- /root/fastgpt/nginx/logs:/var/log/nginx
|
||||||
|
# https证书,没有的话不填,对应的nginx.conf也要修改
|
||||||
|
- /root/fastgpt/nginx/ssl/docgpt.key:/ssl/docgpt.key
|
||||||
|
- /root/fastgpt/nginx/ssl/docgpt.pem:/ssl/docgpt.pem
|
||||||
|
```
|
||||||
|
|
||||||
### 3. 运行 docker-compose
|
### 3. 运行 docker-compose
|
||||||
|
|
||||||
下面是一个辅助脚本,也可以直接 docker-compose up -d
|
下面是一个辅助脚本,也可以直接 docker-compose up -d
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
version: '3.3'
|
version: '3.3'
|
||||||
services:
|
services:
|
||||||
pg:
|
pg:
|
||||||
image: ankane/pgvector:v0.4.2
|
image: ankane/pgvector:v0.4.1
|
||||||
container_name: pg
|
container_name: pg
|
||||||
restart: always
|
restart: always
|
||||||
ports:
|
ports:
|
||||||
@@ -52,10 +52,6 @@ services:
|
|||||||
- aliTemplateCode=SMS_xxxx
|
- aliTemplateCode=SMS_xxxx
|
||||||
# token加密凭证(随便填,作为登录凭证)
|
# token加密凭证(随便填,作为登录凭证)
|
||||||
- TOKEN_KEY=xxxx
|
- TOKEN_KEY=xxxx
|
||||||
# root key, 最高权限,可以内部接口互相调用
|
|
||||||
- ROOT_KEY=xxx
|
|
||||||
# 是否进行安全校验(1: 开启,0: 关闭)
|
|
||||||
- SENSITIVE_CHECK=1
|
|
||||||
# 和上方mongo镜像的username,password对应
|
# 和上方mongo镜像的username,password对应
|
||||||
- MONGODB_URI=mongodb://username:password@0.0.0.0:27017/?authSource=admin
|
- MONGODB_URI=mongodb://username:password@0.0.0.0:27017/?authSource=admin
|
||||||
- MONGODB_NAME=fastgpt
|
- MONGODB_NAME=fastgpt
|
||||||
|
|||||||
@@ -8,14 +8,12 @@ CREATE TABLE modelData (
|
|||||||
vector VECTOR(1536),
|
vector VECTOR(1536),
|
||||||
status VARCHAR(50) NOT NULL,
|
status VARCHAR(50) NOT NULL,
|
||||||
user_id VARCHAR(50) NOT NULL,
|
user_id VARCHAR(50) NOT NULL,
|
||||||
model_id VARCHAR(50),
|
model_id VARCHAR(50) NOT NULL,
|
||||||
kb_id VARCHAR(50),
|
|
||||||
q TEXT NOT NULL,
|
q TEXT NOT NULL,
|
||||||
a TEXT NOT NULL
|
a TEXT NOT NULL
|
||||||
);
|
);
|
||||||
-- create index
|
-- create index
|
||||||
CREATE INDEX modelData_status_index ON modelData USING HASH (status);
|
CREATE INDEX modelData_status_index ON modelData USING HASH (status);
|
||||||
CREATE INDEX modelData_userId_index ON modelData USING HASH (user_id);
|
CREATE INDEX modelData_userId_index ON modelData USING HASH (user_id);
|
||||||
CREATE INDEX modelData_userId_index ON modelData USING HASH (model_id);
|
CREATE INDEX modelData_modelId_index ON modelData USING HASH (model_id);
|
||||||
CREATE INDEX modelData_kbId_index ON modelData USING HASH (kb_id);
|
|
||||||
EOSQL
|
EOSQL
|
||||||
@@ -67,7 +67,7 @@ docker run --name mongo -p 27017:27017 -e MONGO_INITDB_ROOT_USERNAME=username -e
|
|||||||
**3、部署 pgsql**
|
**3、部署 pgsql**
|
||||||
|
|
||||||
```
|
```
|
||||||
docker run -it --name pg -e "POSTGRES_DB=fastgpt" -e "POSTGRES_PASSWORD=xxx" -e POSTGRES_USER=xxx -p 8100:5432 -v ~/fastgpt/pg/data:/var/lib/postgresql/data -d octoberlan/pgvector:v0.4.1
|
docker run -it --name pg -e "POSTGRES_PASSWORD=xxx" -e POSTGRES_USER=xxx -p 8100:5432 -v ~/fastgpt/pg/data:/var/lib/postgresql/data -d octoberlan/pgvector:v0.4.1
|
||||||
```
|
```
|
||||||
|
|
||||||
进 pgsql 容器运行
|
进 pgsql 容器运行
|
||||||
@@ -88,8 +88,8 @@ CREATE TABLE modelData (
|
|||||||
);
|
);
|
||||||
-- create index
|
-- create index
|
||||||
CREATE INDEX modelData_status_index ON modelData (status);
|
CREATE INDEX modelData_status_index ON modelData (status);
|
||||||
CREATE INDEX modelData_modelId_index ON modelData (model_id);
|
CREATE INDEX modelData_modelId_index ON modelData (modelId);
|
||||||
CREATE INDEX modelData_userId_index ON modelData (user_id);
|
CREATE INDEX modelData_userId_index ON modelData (userId);
|
||||||
EOSQL
|
EOSQL
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ http {
|
|||||||
1. 进入刚刚部署应用的详情,复制外网地址
|
1. 进入刚刚部署应用的详情,复制外网地址
|
||||||

|

|
||||||
|
|
||||||
2. 修改环境变量(是 FastGpt 的环境变量,不是 sealos 的):
|
2. 修改环境变量:
|
||||||
|
|
||||||
```
|
```
|
||||||
OPENAI_BASE_URL=https://tgohwtdlrmer.cloud.sealos.io/openai/v1
|
OPENAI_BASE_URL=https://tgohwtdlrmer.cloud.sealos.io/openai/v1
|
||||||
|
|||||||
@@ -10,38 +10,34 @@
|
|||||||
# proxy(可选)
|
# proxy(可选)
|
||||||
AXIOS_PROXY_HOST=127.0.0.1
|
AXIOS_PROXY_HOST=127.0.0.1
|
||||||
AXIOS_PROXY_PORT=7890
|
AXIOS_PROXY_PORT=7890
|
||||||
# 是否开启队列任务。 1-开启,0-关闭(请求parentUrl去执行任务,单机时直接填1)
|
# openai 中转连接(可选)
|
||||||
|
OPENAI_BASE_URL=https://api.openai.com/v1
|
||||||
|
OPENAI_BASE_URL_AUTH=可选的安全凭证
|
||||||
|
# 是否开启队列任务。 1-开启,0-关闭(请求 parentUrl 去执行任务,单机时直接填1)
|
||||||
queueTask=1
|
queueTask=1
|
||||||
parentUrl=https://hostname/api/openapi/startEvents
|
parentUrl=https://hostname/api/openapi/startEvents
|
||||||
# email
|
# 发送邮箱验证码配置。用的是 QQ 邮箱。参考 nodeMail 获取MAILE_CODE,自行百度。
|
||||||
MY_MAIL=xxx@qq.com
|
MY_MAIL=xxxx@qq.com
|
||||||
MAILE_CODE=xxx
|
MAILE_CODE=xxxx
|
||||||
# ali ems
|
# 阿里短信服务(邮箱和短信至少二选一)
|
||||||
aliAccessKeyId=xxx
|
aliAccessKeyId=xxxx
|
||||||
aliAccessKeySecret=xxx
|
aliAccessKeySecret=xxxx
|
||||||
aliSignName=xxx
|
aliSignName=xxxxx
|
||||||
aliTemplateCode=SMS_xxx
|
aliTemplateCode=SMS_xxxx
|
||||||
# token
|
# token加密凭证(随便填,作为登录凭证)
|
||||||
TOKEN_KEY=xxx
|
TOKEN_KEY=xxxx
|
||||||
# root key, 最高权限
|
queueTask=1
|
||||||
ROOT_KEY=xxx
|
parentUrl=https://hostname/api/openapi/startEvents
|
||||||
# 是否进行安全校验(1: 开启,0: 关闭)
|
# 和mongo镜像的username,password对应
|
||||||
SENSITIVE_CHECK=1
|
MONGODB_URI=mongodb://username:passsword@0.0.0.0:27017/?authSource=admin
|
||||||
# openai
|
MONGODB_NAME=xxx
|
||||||
# OPENAI_BASE_URL=https://api.openai.com/v1
|
|
||||||
# OPENAI_BASE_URL_AUTH=可选的安全凭证(不需要的时候,记得去掉)
|
|
||||||
OPENAIKEY=sk-xxx
|
|
||||||
GPT4KEY=sk-xxx
|
|
||||||
# claude
|
|
||||||
CLAUDE_BASE_URL=calude模型请求地址
|
|
||||||
CLAUDE_KEY=CLAUDE_KEY
|
|
||||||
# db
|
|
||||||
MONGODB_URI=mongodb://username:password@0.0.0.0:27017/test?authSource=admin
|
|
||||||
PG_HOST=0.0.0.0
|
PG_HOST=0.0.0.0
|
||||||
PG_PORT=8100
|
PG_PORT=8100
|
||||||
PG_USER=xxx
|
# 和PG镜像对应.
|
||||||
PG_PASSWORD=xxx
|
PG_USER=fastgpt # POSTGRES_USER
|
||||||
PG_DB_NAME=xxx
|
PG_PASSWORD=1234 # POSTGRES_PASSWORD
|
||||||
|
PG_DB_NAME=fastgpt # POSTGRES_DB
|
||||||
|
OPENAIKEY=sk-xxxxx
|
||||||
```
|
```
|
||||||
|
|
||||||
## 运行
|
## 运行
|
||||||
|
|||||||
BIN
docs/imgs/wx300.jpg
Normal file
|
After Width: | Height: | Size: 53 KiB |
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "fastgpt",
|
"name": "fastgpt",
|
||||||
"version": "3.7",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
@@ -39,7 +39,6 @@
|
|||||||
"mongoose": "^6.10.0",
|
"mongoose": "^6.10.0",
|
||||||
"nanoid": "^4.0.1",
|
"nanoid": "^4.0.1",
|
||||||
"next": "13.1.6",
|
"next": "13.1.6",
|
||||||
"nextjs-cors": "^2.1.2",
|
|
||||||
"nodemailer": "^6.9.1",
|
"nodemailer": "^6.9.1",
|
||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"openai": "^3.2.1",
|
"openai": "^3.2.1",
|
||||||
@@ -83,8 +82,5 @@
|
|||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"./src/**/*.{ts,tsx,scss}": "npm run format"
|
"./src/**/*.{ts,tsx,scss}": "npm run format"
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18.0.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
61
pnpm-lock.yaml
generated
@@ -46,7 +46,6 @@ specifiers:
|
|||||||
mongoose: ^6.10.0
|
mongoose: ^6.10.0
|
||||||
nanoid: ^4.0.1
|
nanoid: ^4.0.1
|
||||||
next: 13.1.6
|
next: 13.1.6
|
||||||
nextjs-cors: ^2.1.2
|
|
||||||
nodemailer: ^6.9.1
|
nodemailer: ^6.9.1
|
||||||
nprogress: ^0.2.0
|
nprogress: ^0.2.0
|
||||||
openai: ^3.2.1
|
openai: ^3.2.1
|
||||||
@@ -98,7 +97,6 @@ dependencies:
|
|||||||
mongoose: registry.npmmirror.com/mongoose/6.10.0
|
mongoose: registry.npmmirror.com/mongoose/6.10.0
|
||||||
nanoid: registry.npmmirror.com/nanoid/4.0.1
|
nanoid: registry.npmmirror.com/nanoid/4.0.1
|
||||||
next: registry.npmmirror.com/next/13.1.6_wiv434v7erz4aedd5whhdwmpv4
|
next: registry.npmmirror.com/next/13.1.6_wiv434v7erz4aedd5whhdwmpv4
|
||||||
nextjs-cors: 2.1.2_next@13.1.6
|
|
||||||
nodemailer: registry.npmmirror.com/nodemailer/6.9.1
|
nodemailer: registry.npmmirror.com/nodemailer/6.9.1
|
||||||
nprogress: registry.npmmirror.com/nprogress/0.2.0
|
nprogress: registry.npmmirror.com/nprogress/0.2.0
|
||||||
openai: registry.npmmirror.com/openai/3.2.1
|
openai: registry.npmmirror.com/openai/3.2.1
|
||||||
@@ -296,29 +294,11 @@ packages:
|
|||||||
resolution: {integrity: sha512-COUnqfB2+ckwXXSFInsFdOAWQzCCx+a5hq2ruyj+Vjund94RJQd4LG2u9hnvJrTgunKAaax7ancBYlDrNYxA0g==}
|
resolution: {integrity: sha512-COUnqfB2+ckwXXSFInsFdOAWQzCCx+a5hq2ruyj+Vjund94RJQd4LG2u9hnvJrTgunKAaax7ancBYlDrNYxA0g==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@types/hast/2.3.4:
|
|
||||||
resolution: {integrity: sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==}
|
|
||||||
dependencies:
|
|
||||||
'@types/unist': 2.0.6
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@types/unist/2.0.6:
|
|
||||||
resolution: {integrity: sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/cookie/0.5.0:
|
/cookie/0.5.0:
|
||||||
resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==}
|
resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/cors/2.8.5:
|
|
||||||
resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==}
|
|
||||||
engines: {node: '>= 0.10'}
|
|
||||||
dependencies:
|
|
||||||
object-assign: 4.1.1
|
|
||||||
vary: 1.1.2
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/fsevents/2.3.2:
|
/fsevents/2.3.2:
|
||||||
resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
|
resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
|
||||||
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||||
@@ -329,24 +309,9 @@ packages:
|
|||||||
|
|
||||||
/graceful-fs/4.2.10:
|
/graceful-fs/4.2.10:
|
||||||
resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==}
|
resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==}
|
||||||
requiresBuild: true
|
|
||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/nextjs-cors/2.1.2_next@13.1.6:
|
|
||||||
resolution: {integrity: sha512-2yOVivaaf2ILe4f/qY32hnj3oC77VCOsUQJQfhVMGsXE/YMEWUY2zy78sH9FKUCM7eG42/l3pDofIzMD781XGA==}
|
|
||||||
peerDependencies:
|
|
||||||
next: ^8.1.1-canary.54 || ^9.0.0 || ^10.0.0-0 || ^11.0.0 || ^12.0.0 || ^13.0.0
|
|
||||||
dependencies:
|
|
||||||
cors: 2.8.5
|
|
||||||
next: registry.npmmirror.com/next/13.1.6_wiv434v7erz4aedd5whhdwmpv4
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/object-assign/4.1.1:
|
|
||||||
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
|
|
||||||
engines: {node: '>=0.10.0'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/saslprep/1.0.3:
|
/saslprep/1.0.3:
|
||||||
resolution: {integrity: sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==}
|
resolution: {integrity: sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
@@ -359,15 +324,9 @@ packages:
|
|||||||
/source-map/0.6.1:
|
/source-map/0.6.1:
|
||||||
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
|
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
requiresBuild: true
|
|
||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/vary/1.1.2:
|
|
||||||
resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
|
|
||||||
engines: {node: '>= 0.8'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
registry.npmmirror.com/@alicloud/credentials/2.2.6:
|
registry.npmmirror.com/@alicloud/credentials/2.2.6:
|
||||||
resolution: {integrity: sha512-jG+msY77dHmAF3x+8VTy7fEgORyXLHmDci8t92HeipBdCHsPptDegA++GEwKgR7f6G4wvafYt+aqMZ1iligdrQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@alicloud/credentials/-/credentials-2.2.6.tgz}
|
resolution: {integrity: sha512-jG+msY77dHmAF3x+8VTy7fEgORyXLHmDci8t92HeipBdCHsPptDegA++GEwKgR7f6G4wvafYt+aqMZ1iligdrQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@alicloud/credentials/-/credentials-2.2.6.tgz}
|
||||||
name: '@alicloud/credentials'
|
name: '@alicloud/credentials'
|
||||||
@@ -7783,7 +7742,7 @@ packages:
|
|||||||
name: hast-util-from-parse5
|
name: hast-util-from-parse5
|
||||||
version: 7.1.2
|
version: 7.1.2
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/hast': 2.3.4
|
'@types/hast': registry.npmmirror.com/@types/hast/2.3.4
|
||||||
'@types/unist': registry.npmmirror.com/@types/unist/2.0.6
|
'@types/unist': registry.npmmirror.com/@types/unist/2.0.6
|
||||||
hastscript: registry.npmmirror.com/hastscript/7.2.0
|
hastscript: registry.npmmirror.com/hastscript/7.2.0
|
||||||
property-information: registry.npmmirror.com/property-information/6.2.0
|
property-information: registry.npmmirror.com/property-information/6.2.0
|
||||||
@@ -7797,7 +7756,7 @@ packages:
|
|||||||
name: hast-util-is-element
|
name: hast-util-is-element
|
||||||
version: 2.1.3
|
version: 2.1.3
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/hast': 2.3.4
|
'@types/hast': registry.npmmirror.com/@types/hast/2.3.4
|
||||||
'@types/unist': registry.npmmirror.com/@types/unist/2.0.6
|
'@types/unist': registry.npmmirror.com/@types/unist/2.0.6
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
@@ -7812,7 +7771,7 @@ packages:
|
|||||||
name: hast-util-parse-selector
|
name: hast-util-parse-selector
|
||||||
version: 3.1.1
|
version: 3.1.1
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/hast': 2.3.4
|
'@types/hast': registry.npmmirror.com/@types/hast/2.3.4
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
registry.npmmirror.com/hast-util-to-text/3.1.2:
|
registry.npmmirror.com/hast-util-to-text/3.1.2:
|
||||||
@@ -7837,7 +7796,7 @@ packages:
|
|||||||
name: hastscript
|
name: hastscript
|
||||||
version: 6.0.0
|
version: 6.0.0
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/hast': 2.3.4
|
'@types/hast': registry.npmmirror.com/@types/hast/2.3.4
|
||||||
comma-separated-tokens: registry.npmmirror.com/comma-separated-tokens/1.0.8
|
comma-separated-tokens: registry.npmmirror.com/comma-separated-tokens/1.0.8
|
||||||
hast-util-parse-selector: registry.npmmirror.com/hast-util-parse-selector/2.2.5
|
hast-util-parse-selector: registry.npmmirror.com/hast-util-parse-selector/2.2.5
|
||||||
property-information: registry.npmmirror.com/property-information/5.6.0
|
property-information: registry.npmmirror.com/property-information/5.6.0
|
||||||
@@ -7849,7 +7808,7 @@ packages:
|
|||||||
name: hastscript
|
name: hastscript
|
||||||
version: 7.2.0
|
version: 7.2.0
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/hast': 2.3.4
|
'@types/hast': registry.npmmirror.com/@types/hast/2.3.4
|
||||||
comma-separated-tokens: registry.npmmirror.com/comma-separated-tokens/2.0.3
|
comma-separated-tokens: registry.npmmirror.com/comma-separated-tokens/2.0.3
|
||||||
hast-util-parse-selector: registry.npmmirror.com/hast-util-parse-selector/3.1.1
|
hast-util-parse-selector: registry.npmmirror.com/hast-util-parse-selector/3.1.1
|
||||||
property-information: registry.npmmirror.com/property-information/6.2.0
|
property-information: registry.npmmirror.com/property-information/6.2.0
|
||||||
@@ -8803,7 +8762,7 @@ packages:
|
|||||||
version: 5.1.2
|
version: 5.1.2
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/mdast': registry.npmmirror.com/@types/mdast/3.0.10
|
'@types/mdast': registry.npmmirror.com/@types/mdast/3.0.10
|
||||||
'@types/unist': 2.0.6
|
'@types/unist': registry.npmmirror.com/@types/unist/2.0.6
|
||||||
unist-util-visit: registry.npmmirror.com/unist-util-visit/4.1.2
|
unist-util-visit: registry.npmmirror.com/unist-util-visit/4.1.2
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
@@ -8931,7 +8890,7 @@ packages:
|
|||||||
name: mdast-util-to-hast
|
name: mdast-util-to-hast
|
||||||
version: 12.3.0
|
version: 12.3.0
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/hast': 2.3.4
|
'@types/hast': registry.npmmirror.com/@types/hast/2.3.4
|
||||||
'@types/mdast': registry.npmmirror.com/@types/mdast/3.0.10
|
'@types/mdast': registry.npmmirror.com/@types/mdast/3.0.10
|
||||||
mdast-util-definitions: registry.npmmirror.com/mdast-util-definitions/5.1.2
|
mdast-util-definitions: registry.npmmirror.com/mdast-util-definitions/5.1.2
|
||||||
micromark-util-sanitize-uri: registry.npmmirror.com/micromark-util-sanitize-uri/1.1.0
|
micromark-util-sanitize-uri: registry.npmmirror.com/micromark-util-sanitize-uri/1.1.0
|
||||||
@@ -9482,7 +9441,7 @@ packages:
|
|||||||
version: 2.7.0
|
version: 2.7.0
|
||||||
dependencies:
|
dependencies:
|
||||||
any-promise: registry.npmmirror.com/any-promise/1.3.0
|
any-promise: registry.npmmirror.com/any-promise/1.3.0
|
||||||
object-assign: 4.1.1
|
object-assign: registry.npmmirror.com/object-assign/4.1.1
|
||||||
thenify-all: registry.npmmirror.com/thenify-all/1.6.0
|
thenify-all: registry.npmmirror.com/thenify-all/1.6.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
@@ -11665,7 +11624,7 @@ packages:
|
|||||||
name: unist-util-position
|
name: unist-util-position
|
||||||
version: 4.0.4
|
version: 4.0.4
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/unist': 2.0.6
|
'@types/unist': registry.npmmirror.com/@types/unist/2.0.6
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
registry.npmmirror.com/unist-util-remove-position/4.0.2:
|
registry.npmmirror.com/unist-util-remove-position/4.0.2:
|
||||||
@@ -11858,7 +11817,7 @@ packages:
|
|||||||
name: vfile-location
|
name: vfile-location
|
||||||
version: 4.1.0
|
version: 4.1.0
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/unist': 2.0.6
|
'@types/unist': registry.npmmirror.com/@types/unist/2.0.6
|
||||||
vfile: registry.npmmirror.com/vfile/5.3.7
|
vfile: registry.npmmirror.com/vfile/5.3.7
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
|||||||
@@ -1,27 +1,16 @@
|
|||||||
### 常见问题
|
### 常见问题
|
||||||
|
|
||||||
**Git 地址**
|
**请求次数太多了**
|
||||||
[项目地址,完全开源,随便用。](https://github.com/c121914yu/FastGPT)
|
一般是因为自己的 openai 账号异常。请先检查自己的账号是否正常使用。
|
||||||
|
**内容长度**
|
||||||
**问题文档**
|
chatgpt 上下文最长 4096 tokens, 会自动截取上下文,超过 4096 部分会被遗忘。
|
||||||
[先看文档,再提问](https://kjqvjse66l.feishu.cn/docx/HtrgdT0pkonP4kxGx8qcu6XDnGh)
|
|
||||||
|
|
||||||
**删除和复制**
|
**删除和复制**
|
||||||
电脑端:聊天内容右侧有复制和删除的图标。
|
电脑端:聊天内容右侧有复制和删除的图标。
|
||||||
移动端:点击对话头像,可以选择复制或删除该条内容。
|
移动端:点击对话头像,可以选择复制或删除该条内容。
|
||||||
|
**代理出错**
|
||||||
**价格表**
|
服务器代理不稳定,可以过一会儿再尝试。 或者可以访问国外服务器: [FastGpt](https://fastgpt.run/)
|
||||||
如果使用了自己的 Api Key,不会计费。可以在账号页,看到详细账单。
|
|
||||||
| 计费项 | 价格: 元/ 1K tokens(包含上下文)|
|
|
||||||
| --- | --- |
|
|
||||||
| claude - 对话 | 免费 |
|
|
||||||
| 知识库 - 索引 | 免费 |
|
|
||||||
| chatgpt - 对话 | 0.025 |
|
|
||||||
| gpt4 - 对话 | 0.5 |
|
|
||||||
| 文件拆分 | 0.025 |
|
|
||||||
|
|
||||||
**其他问题**
|
**其他问题**
|
||||||
请 WX 联系: fastgpt123
|
请 WX 联系: fastgpt123
|
||||||
| 交流群 | 小助手 |
|
| 交流群 | 小助手 |
|
||||||
| ----------------------- | -------------------- |
|
| ----------------------- | -------------------- |
|
||||||
|  |  |
|
|  |  |
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
接受一个 csv 文件,表格头包含 question 和 answer。question 代表问题,answer 代表答案。
|
接受一个 csv 文件,表格头包含 question 和 answer。question 代表问题,answer 代表答案。
|
||||||
导入前会进行去重,如果问题和答案完全相同,则不会被导入,所以最终导入的内容可能会比文件的内容少。但是,对于带有换行的内容,目前无法去重。
|
导入前会进行去重,如果问题和答案完全相同,则不会被导入,所以最终导入的内容可能会比文件的内容少。但是,对于带有换行的内容,目前无法去重。
|
||||||
|
**请保证 csv 文件为 utf-8 编码**
|
||||||
### 请保证 csv 文件为 utf-8 编码
|
| question | answer |
|
||||||
|
| --- | --- |
|
||||||
| question | answer |
|
| 什么是 laf | laf 是一个云函数开发平台…… |
|
||||||
| ------------- | ------------------------------------------------------ |
|
|
||||||
| 什么是 laf | laf 是一个云函数开发平台…… |
|
|
||||||
| 什么是 sealos | Sealos 是以 kubernetes 为内核的云操作系统发行版,可以…… |
|
| 什么是 sealos | Sealos 是以 kubernetes 为内核的云操作系统发行版,可以…… |
|
||||||
|
|||||||
@@ -19,16 +19,16 @@ FastGpt 项目完全开源,可随意私有化部署,去除平台风险忧虑
|
|||||||
| 计费项 | 价格: 元/ 1K tokens(包含上下文)|
|
| 计费项 | 价格: 元/ 1K tokens(包含上下文)|
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| claude - 对话 | 免费 |
|
| claude - 对话 | 免费 |
|
||||||
| 知识库 - 索引 | 免费 |
|
| chatgpt - 对话 | 0.03 |
|
||||||
| chatgpt - 对话 | 0.025 |
|
|
||||||
| gpt4 - 对话 | 0.5 |
|
| gpt4 - 对话 | 0.5 |
|
||||||
| 文件拆分 | 0.025 |
|
| 知识库 - 索引 | 免费 |
|
||||||
|
| 文件拆分 | 0.03 |
|
||||||
|
|
||||||
### 交流群/问题反馈
|
### 交流群/问题反馈
|
||||||
|
|
||||||
如果群满了,可加个小助手,定时拉
|
如果群满了,可加个小助手,定时拉
|
||||||
wx 号: fastgpt123
|
wx 号: fastgpt123
|
||||||
|
|
||||||
| 交流群 | 小助手 |
|
| 交流群 | 小助手 |
|
||||||
| ------------------------------------------------- | ---------------------------------------------- |
|
| ----------------------- | -------------------- |
|
||||||
|  |  |
|
|  |  |
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
### Fast GPT V3.8
|
### Fast GPT V3.6
|
||||||
|
|
||||||
- 新增 - 知识库引用反馈。
|
- 新增 - 分享免登录聊天框。可以直接为模型生成一个分享链接,其他人可以通过这个链接直接使用对话。
|
||||||
- 新增 - 知识库与 AI 助手对多对关系,一个知识库可以被多个 AI 助手关联,一个 AI 助手可以关联多个知识库。
|
- 优化 - UI 细节。
|
||||||
|
|||||||
BIN
public/imgs/wx300.jpg
Normal file
|
After Width: | Height: | Size: 53 KiB |
@@ -1,11 +1,10 @@
|
|||||||
import { GET, POST, DELETE } from './request';
|
import { GET, POST, DELETE } from './request';
|
||||||
import type { HistoryItemType } from '@/types/chat';
|
import type { ChatItemType, HistoryItemType } from '@/types/chat';
|
||||||
import type { InitChatResponse, InitShareChatResponse } from './response/chat';
|
import type { InitChatResponse, InitShareChatResponse } from './response/chat';
|
||||||
import { RequestPaging } from '../types/index';
|
import { RequestPaging } from '../types/index';
|
||||||
import type { ShareChatSchema } from '@/types/mongoSchema';
|
import type { ShareChatSchema } from '@/types/mongoSchema';
|
||||||
import type { ShareChatEditType } from '@/types/model';
|
import type { ShareChatEditType } from '@/types/model';
|
||||||
import { Obj2Query } from '@/utils/tools';
|
import { Obj2Query } from '@/utils/tools';
|
||||||
import { QuoteItemType } from '@/pages/api/openapi/kb/appKbSearch';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取初始化聊天内容
|
* 获取初始化聊天内容
|
||||||
@@ -25,19 +24,14 @@ export const getChatHistory = (data: RequestPaging) =>
|
|||||||
export const delChatHistoryById = (id: string) => GET(`/chat/removeHistory?id=${id}`);
|
export const delChatHistoryById = (id: string) => GET(`/chat/removeHistory?id=${id}`);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get history quotes
|
* 存储一轮对话
|
||||||
*/
|
*/
|
||||||
export const getHistoryQuote = (params: { chatId: string; historyId: string }) =>
|
export const postSaveChat = (data: {
|
||||||
GET<(QuoteItemType & { _id: string })[]>(`/chat/getHistoryQuote`, params);
|
modelId: string;
|
||||||
|
newChatId: '' | string;
|
||||||
/**
|
chatId: '' | string;
|
||||||
* update history quote status
|
prompts: [ChatItemType, ChatItemType];
|
||||||
*/
|
}) => POST<string>('/chat/saveChat', data);
|
||||||
export const updateHistoryQuote = (params: {
|
|
||||||
chatId: string;
|
|
||||||
historyId: string;
|
|
||||||
quoteId: string;
|
|
||||||
}) => GET(`/chat/updateHistoryQuote`, params);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除一句对话
|
* 删除一句对话
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { GUIDE_PROMPT_HEADER, NEW_CHATID_HEADER, QUOTE_LEN_HEADER } from '@/constants/chat';
|
import { SYSTEM_PROMPT_HEADER, NEW_CHATID_HEADER } from '@/constants/chat';
|
||||||
|
|
||||||
interface StreamFetchProps {
|
interface StreamFetchProps {
|
||||||
url: string;
|
url: string;
|
||||||
@@ -7,7 +7,7 @@ interface StreamFetchProps {
|
|||||||
abortSignal: AbortController;
|
abortSignal: AbortController;
|
||||||
}
|
}
|
||||||
export const streamFetch = ({ url, data, onMessage, abortSignal }: StreamFetchProps) =>
|
export const streamFetch = ({ url, data, onMessage, abortSignal }: StreamFetchProps) =>
|
||||||
new Promise<{ responseText: string; newChatId: string; systemPrompt: string; quoteLen: number }>(
|
new Promise<{ responseText: string; systemPrompt: string; newChatId: string }>(
|
||||||
async (resolve, reject) => {
|
async (resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(url, {
|
const res = await fetch(url, {
|
||||||
@@ -23,11 +23,8 @@ export const streamFetch = ({ url, data, onMessage, abortSignal }: StreamFetchPr
|
|||||||
|
|
||||||
const decoder = new TextDecoder();
|
const decoder = new TextDecoder();
|
||||||
|
|
||||||
|
const systemPrompt = decodeURIComponent(res.headers.get(SYSTEM_PROMPT_HEADER) || '').trim();
|
||||||
const newChatId = decodeURIComponent(res.headers.get(NEW_CHATID_HEADER) || '');
|
const newChatId = decodeURIComponent(res.headers.get(NEW_CHATID_HEADER) || '');
|
||||||
const systemPrompt = decodeURIComponent(res.headers.get(GUIDE_PROMPT_HEADER) || '').trim();
|
|
||||||
const quoteLen = res.headers.get(QUOTE_LEN_HEADER)
|
|
||||||
? Number(res.headers.get(QUOTE_LEN_HEADER))
|
|
||||||
: 0;
|
|
||||||
|
|
||||||
let responseText = '';
|
let responseText = '';
|
||||||
|
|
||||||
@@ -36,7 +33,7 @@ export const streamFetch = ({ url, data, onMessage, abortSignal }: StreamFetchPr
|
|||||||
const { done, value } = await reader?.read();
|
const { done, value } = await reader?.read();
|
||||||
if (done) {
|
if (done) {
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
resolve({ responseText, newChatId, quoteLen, systemPrompt });
|
resolve({ responseText, systemPrompt, newChatId });
|
||||||
} else {
|
} else {
|
||||||
const parseError = JSON.parse(responseText);
|
const parseError = JSON.parse(responseText);
|
||||||
reject(parseError?.message || '请求异常');
|
reject(parseError?.message || '请求异常');
|
||||||
@@ -44,13 +41,13 @@ export const streamFetch = ({ url, data, onMessage, abortSignal }: StreamFetchPr
|
|||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const text = decoder.decode(value);
|
const text = decoder.decode(value).replace(/<br\/>/g, '\n');
|
||||||
responseText += text;
|
responseText += text;
|
||||||
onMessage(text);
|
onMessage(text);
|
||||||
read();
|
read();
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
if (err?.message === 'The user aborted a request.') {
|
if (err?.message === 'The user aborted a request.') {
|
||||||
return resolve({ responseText, newChatId, quoteLen: 0, systemPrompt: '' });
|
return resolve({ responseText, systemPrompt, newChatId });
|
||||||
}
|
}
|
||||||
reject(typeof err === 'string' ? err : err?.message || '请求异常');
|
reject(typeof err === 'string' ? err : err?.message || '请求异常');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { GET, POST, DELETE, PUT } from './request';
|
import { GET, POST, DELETE, PUT } from './request';
|
||||||
import type { ModelSchema } from '@/types/mongoSchema';
|
import type { ModelSchema, ModelDataSchema } from '@/types/mongoSchema';
|
||||||
import type { ModelUpdateParams } from '@/types/model';
|
import type { ModelUpdateParams, ShareModelItem } from '@/types/model';
|
||||||
import { RequestPaging } from '../types/index';
|
import { RequestPaging } from '../types/index';
|
||||||
|
import { Obj2Query } from '@/utils/tools';
|
||||||
import type { ModelListResponse } from './response/model';
|
import type { ModelListResponse } from './response/model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -30,13 +31,82 @@ export const getModelById = (id: string) => GET<ModelSchema>(`/model/detail?mode
|
|||||||
export const putModelById = (id: string, data: ModelUpdateParams) =>
|
export const putModelById = (id: string, data: ModelUpdateParams) =>
|
||||||
PUT(`/model/update?modelId=${id}`, data);
|
PUT(`/model/update?modelId=${id}`, data);
|
||||||
|
|
||||||
|
/* 模型 data */
|
||||||
|
type GetModelDataListProps = RequestPaging & {
|
||||||
|
modelId: string;
|
||||||
|
searchText: string;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* 获取模型的知识库数据
|
||||||
|
*/
|
||||||
|
export const getModelDataList = (props: GetModelDataListProps) =>
|
||||||
|
GET(`/model/data/getModelData?${Obj2Query(props)}`);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取导出数据(不分页)
|
||||||
|
*/
|
||||||
|
export const getExportDataList = (modelId: string) =>
|
||||||
|
GET<[string, string][]>(`/model/data/exportModelData?modelId=${modelId}`);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取模型正在拆分数据的数量
|
||||||
|
*/
|
||||||
|
export const getModelSplitDataListLen = (modelId: string) =>
|
||||||
|
GET<{
|
||||||
|
splitDataQueue: number;
|
||||||
|
embeddingQueue: number;
|
||||||
|
}>(`/model/data/getTrainingData?modelId=${modelId}`);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 web 页面内容
|
||||||
|
*/
|
||||||
|
export const getWebContent = (url: string) => POST<string>(`/model/data/fetchingUrlData`, { url });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 手动输入数据
|
||||||
|
*/
|
||||||
|
export const postModelDataInput = (data: {
|
||||||
|
modelId: string;
|
||||||
|
data: { a: ModelDataSchema['a']; q: ModelDataSchema['q'] }[];
|
||||||
|
}) => POST<number>(`/model/data/pushModelDataInput`, data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拆分数据
|
||||||
|
*/
|
||||||
|
export const postModelDataSplitData = (data: {
|
||||||
|
modelId: string;
|
||||||
|
chunks: string[];
|
||||||
|
prompt: string;
|
||||||
|
mode: 'qa' | 'subsection';
|
||||||
|
}) => POST(`/model/data/splitData`, data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* json导入数据
|
||||||
|
*/
|
||||||
|
export const postModelDataCsvData = (modelId: string, data: string[][]) =>
|
||||||
|
POST<number>(`/model/data/pushModelDataCsv`, { modelId, data: data });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新模型数据
|
||||||
|
*/
|
||||||
|
export const putModelDataById = (data: { dataId: string; a: string; q?: string }) =>
|
||||||
|
PUT('/model/data/putModelData', data);
|
||||||
|
/**
|
||||||
|
* 删除一条模型数据
|
||||||
|
*/
|
||||||
|
export const delOneModelData = (dataId: string) =>
|
||||||
|
DELETE(`/model/data/delModelDataById?dataId=${dataId}`);
|
||||||
|
|
||||||
/* 共享市场 */
|
/* 共享市场 */
|
||||||
/**
|
/**
|
||||||
* 获取共享市场模型
|
* 获取共享市场模型
|
||||||
*/
|
*/
|
||||||
export const getShareModelList = (data: { searchText?: string } & RequestPaging) =>
|
export const getShareModelList = (data: { searchText?: string } & RequestPaging) =>
|
||||||
POST(`/model/share/getModels`, data);
|
POST(`/model/share/getModels`, data);
|
||||||
|
/**
|
||||||
|
* 获取我收藏的模型
|
||||||
|
*/
|
||||||
|
export const getCollectionModels = () => GET<ShareModelItem[]>(`/model/share/getCollection`);
|
||||||
/**
|
/**
|
||||||
* 收藏/取消收藏模型
|
* 收藏/取消收藏模型
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,73 +0,0 @@
|
|||||||
import { GET, POST, PUT, DELETE } from '../request';
|
|
||||||
import type { KbItemType } from '@/types/plugin';
|
|
||||||
import { RequestPaging } from '@/types/index';
|
|
||||||
import { SplitTextTypEnum } from '@/constants/plugin';
|
|
||||||
import { KbDataItemType } from '@/types/plugin';
|
|
||||||
|
|
||||||
export type KbUpdateParams = { id: string; name: string; tags: string; avatar: string };
|
|
||||||
|
|
||||||
/* knowledge base */
|
|
||||||
export const getKbList = () => GET<KbItemType[]>(`/plugins/kb/list`);
|
|
||||||
|
|
||||||
export const getKbById = (id: string) => GET<KbItemType>(`/plugins/kb/detail?id=${id}`);
|
|
||||||
|
|
||||||
export const postCreateKb = (data: { name: string }) => POST<string>(`/plugins/kb/create`, data);
|
|
||||||
|
|
||||||
export const putKbById = (data: KbUpdateParams) => PUT(`/plugins/kb/update`, data);
|
|
||||||
|
|
||||||
export const delKbById = (id: string) => DELETE(`/plugins/kb/delete?id=${id}`);
|
|
||||||
|
|
||||||
/* kb data */
|
|
||||||
type GetKbDataListProps = RequestPaging & {
|
|
||||||
kbId: string;
|
|
||||||
searchText: string;
|
|
||||||
};
|
|
||||||
export const getKbDataList = (data: GetKbDataListProps) =>
|
|
||||||
POST(`/plugins/kb/data/getDataList`, data);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取导出数据(不分页)
|
|
||||||
*/
|
|
||||||
export const getExportDataList = (kbId: string) =>
|
|
||||||
GET<[string, string][]>(`/plugins/kb/data/exportModelData?kbId=${kbId}`);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取模型正在拆分数据的数量
|
|
||||||
*/
|
|
||||||
export const getTrainingData = (kbId: string) =>
|
|
||||||
GET<{
|
|
||||||
splitDataQueue: number;
|
|
||||||
embeddingQueue: number;
|
|
||||||
}>(`/plugins/kb/data/getTrainingData?kbId=${kbId}`);
|
|
||||||
|
|
||||||
export const getKbDataItemById = (dataId: string) =>
|
|
||||||
GET(`/plugins/kb/data/getDataById`, { dataId });
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 直接push数据
|
|
||||||
*/
|
|
||||||
export const postKbDataFromList = (data: {
|
|
||||||
kbId: string;
|
|
||||||
data: { a: KbDataItemType['a']; q: KbDataItemType['q'] }[];
|
|
||||||
}) => POST(`/openapi/kb/pushData`, data);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新一条数据
|
|
||||||
*/
|
|
||||||
export const putKbDataById = (data: { dataId: string; a: string; q?: string }) =>
|
|
||||||
PUT('/openapi/kb/updateData', data);
|
|
||||||
/**
|
|
||||||
* 删除一条知识库数据
|
|
||||||
*/
|
|
||||||
export const delOneKbDataByDataId = (dataId: string) =>
|
|
||||||
DELETE(`/openapi/kb/delDataById?dataId=${dataId}`);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 拆分数据
|
|
||||||
*/
|
|
||||||
export const postSplitData = (data: {
|
|
||||||
kbId: string;
|
|
||||||
chunks: string[];
|
|
||||||
prompt: string;
|
|
||||||
mode: `${SplitTextTypEnum}`;
|
|
||||||
}) => POST(`/openapi/text/splitText`, data);
|
|
||||||
@@ -54,13 +54,13 @@ function responseError(err: any) {
|
|||||||
if (typeof err === 'string') {
|
if (typeof err === 'string') {
|
||||||
return Promise.reject({ message: err });
|
return Promise.reject({ message: err });
|
||||||
}
|
}
|
||||||
// 有报错响应
|
if (err.response) {
|
||||||
if (err?.code in TOKEN_ERROR_CODE) {
|
// 有报错响应
|
||||||
clearCookie();
|
const res = err.response;
|
||||||
window.location.replace(
|
if (res.data.code in TOKEN_ERROR_CODE) {
|
||||||
`/login?lastRoute=${encodeURIComponent(location.pathname + location.search)}`
|
clearCookie();
|
||||||
);
|
return Promise.reject({ message: 'token过期,重新登录' });
|
||||||
return Promise.reject({ message: 'token过期,重新登录' });
|
}
|
||||||
}
|
}
|
||||||
return Promise.reject(err);
|
return Promise.reject(err);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,10 @@ import { GET, POST, PUT } from './request';
|
|||||||
import { createHashPassword, Obj2Query } from '@/utils/tools';
|
import { createHashPassword, Obj2Query } from '@/utils/tools';
|
||||||
import { ResLogin, PromotionRecordType } from './response/user';
|
import { ResLogin, PromotionRecordType } from './response/user';
|
||||||
import { UserAuthTypeEnum } from '@/constants/common';
|
import { UserAuthTypeEnum } from '@/constants/common';
|
||||||
import { UserBillType, UserType, UserUpdateParams } from '@/types/user';
|
import { UserType, UserUpdateParams } from '@/types/user';
|
||||||
import type { PagingData, RequestPaging } from '@/types';
|
import type { PagingData, RequestPaging } from '@/types';
|
||||||
import { PaySchema } from '@/types/mongoSchema';
|
import { BillSchema, PaySchema } from '@/types/mongoSchema';
|
||||||
|
import { adaptBill } from '@/utils/adapt';
|
||||||
|
|
||||||
export const sendAuthCode = ({
|
export const sendAuthCode = ({
|
||||||
username,
|
username,
|
||||||
@@ -68,7 +69,10 @@ export const loginOut = () => GET('/user/loginout');
|
|||||||
export const putUserInfo = (data: UserUpdateParams) => PUT('/user/update', data);
|
export const putUserInfo = (data: UserUpdateParams) => PUT('/user/update', data);
|
||||||
|
|
||||||
export const getUserBills = (data: RequestPaging) =>
|
export const getUserBills = (data: RequestPaging) =>
|
||||||
GET<PagingData<UserBillType>>(`/user/getBill?${Obj2Query(data)}`);
|
GET<PagingData<BillSchema>>(`/user/getBill?${Obj2Query(data)}`).then((res) => ({
|
||||||
|
...res,
|
||||||
|
data: res.data.map((bill) => adaptBill(bill))
|
||||||
|
}));
|
||||||
|
|
||||||
export const getPayOrders = () => GET<PaySchema[]>(`/user/getPayOrders`);
|
export const getPayOrders = () => GET<PaySchema[]>(`/user/getPayOrders`);
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { Image } from '@chakra-ui/react';
|
|
||||||
import type { ImageProps } from '@chakra-ui/react';
|
|
||||||
|
|
||||||
const Avatar = ({ w = '30px', ...props }: ImageProps) => {
|
|
||||||
return (
|
|
||||||
<Image
|
|
||||||
fallbackSrc="/icon/logo.png"
|
|
||||||
borderRadius={'50%'}
|
|
||||||
objectFit={'contain'}
|
|
||||||
alt=""
|
|
||||||
w={w}
|
|
||||||
h={w}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Avatar;
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
<?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="1684739031957" class="icon" viewBox="0 0 1026 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4988" xmlns:xlink="http://www.w3.org/1999/xlink" width="64.125" height="64"><path d="M371.732817 94.172314q25.773475 0 44.112294 17.843175t18.338819 43.616651l0 247.821878q0 25.773475-18.338819 44.112294t-44.112294 18.338819l-247.821878 0q-25.773475 0-43.616651-18.338819t-17.843175-44.112294l0-247.821878q0-25.773475 17.843175-43.616651t43.616651-17.843175l247.821878 0zM371.732817 589.81607q25.773475 0 44.112294 17.843175t18.338819 43.616651l0 248.813166q0 25.773475-18.338819 43.616651t-44.112294 17.843175l-247.821878 0q-25.773475 0-43.616651-17.843175t-17.843175-43.616651l0-248.813166q0-25.773475 17.843175-43.616651t43.616651-17.843175l247.821878 0zM868.367861 589.81607q25.773475 0 43.616651 17.843175t17.843175 43.616651l0 248.813166q0 25.773475-17.843175 43.616651t-43.616651 17.843175l-247.821878 0q-25.773475 0-44.112294-17.843175t-18.338819-43.616651l0-248.813166q0-25.773475 18.338819-43.616651t44.112294-17.843175l247.821878 0zM1006.156825 203.21394q19.82575 19.82575 19.82575 46.590513t-19.82575 45.599226l-184.379477 184.379477q-19.82575 19.82575-46.094869 19.82575t-46.094869-19.82575l-184.379477-184.379477q-18.834463-18.834463-18.834463-45.599226t18.834463-46.590513l184.379477-184.379477q19.82575-18.834463 46.094869-18.834463t46.094869 18.834463z" p-id="4989"></path></svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.5 KiB |
@@ -1 +0,0 @@
|
|||||||
<?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="1684826302600" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2244" xmlns:xlink="http://www.w3.org/1999/xlink" ><path d="M904 512h-56c-4.4 0-8 3.6-8 8v320H184V184h320c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H144c-17.7 0-32 14.3-32 32v736c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V520c0-4.4-3.6-8-8-8z" p-id="2245"></path><path d="M355.9 534.9L354 653.8c-0.1 8.9 7.1 16.2 16 16.2h0.4l118-2.9c2-0.1 4-0.9 5.4-2.3l415.9-415c3.1-3.1 3.1-8.2 0-11.3L785.4 114.3c-1.6-1.6-3.6-2.3-5.7-2.3s-4.1 0.8-5.7 2.3l-415.8 415c-1.4 1.5-2.3 3.5-2.3 5.6z m63.5 23.6L779.7 199l45.2 45.1-360.5 359.7-45.7 1.1 0.7-46.4z" p-id="2246"></path></svg>
|
|
||||||
|
Before Width: | Height: | Size: 810 B |
@@ -1 +0,0 @@
|
|||||||
<?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="1684122143852" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2364" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M511.6 76.3C264.3 76.2 64 276.4 64 523.5 64 718.9 189.3 885 363.8 946c23.5 5.9 19.9-10.8 19.9-22.2v-77.5c-135.7 15.9-141.2-73.9-150.3-88.9C215 726 171.5 718 184.5 703c30.9-15.9 62.4 4 98.9 57.9 26.4 39.1 77.9 32.5 104 26 5.7-23.5 17.9-44.5 34.7-60.8-140.6-25.2-199.2-111-199.2-213 0-49.5 16.3-95 48.3-131.7-20.4-60.5 1.9-112.3 4.9-120 58.1-5.2 118.5 41.6 123.2 45.3 33-8.9 70.7-13.6 112.9-13.6 42.4 0 80.2 4.9 113.5 13.9 11.3-8.6 67.3-48.8 121.3-43.9 2.9 7.7 24.7 58.3 5.5 118 32.4 36.8 48.9 82.7 48.9 132.3 0 102.2-59 188.1-200 212.9 23.5 23.2 38.1 55.4 38.1 91v112.5c0.8 9 0 17.9 15 17.9 177.1-59.7 304.6-227 304.6-424.1 0-247.2-200.4-447.3-447.5-447.3z" p-id="2365"></path></svg>
|
|
||||||
|
Before Width: | Height: | Size: 1013 B |
@@ -1 +0,0 @@
|
|||||||
<?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="1684163814302" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3451" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M512 384c-229.8 0-416-57.3-416-128v256c0 70.7 186.2 128 416 128s416-57.3 416-128V256c0 70.7-186.2 128-416 128z" p-id="3452"></path><path d="M512 704c-229.8 0-416-57.3-416-128v256c0 70.7 186.2 128 416 128s416-57.3 416-128V576c0 70.7-186.2 128-416 128zM512 320c229.8 0 416-57.3 416-128S741.8 64 512 64 96 121.3 96 192s186.2 128 416 128z" p-id="3453"></path></svg>
|
|
||||||
|
Before Width: | Height: | Size: 694 B |
@@ -1 +0,0 @@
|
|||||||
<?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="1684745011703" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1481" xmlns:xlink="http://www.w3.org/1999/xlink" ><path d="M110.025 252.249c0 13.741 5.456 27.374 14.899 37.112s22.663 15.364 35.987 15.364 26.544-5.626 35.987-15.364c9.441-9.738 14.899-23.371 14.899-37.112s-5.456-27.375-14.899-37.111c-9.442-9.738-22.663-15.364-35.987-15.364s-26.544 5.626-35.987 15.364c-9.338 9.736-14.899 23.37-14.899 37.111m0 0zM103.625 512.575c0 13.741 5.455 27.482 14.899 37.22 9.442 9.738 22.663 15.364 36.091 15.364 13.324 0 26.649-5.626 36.091-15.364s14.899-23.371 14.899-37.22c0-13.741-5.455-27.482-14.899-37.22-9.442-9.738-22.662-15.364-36.091-15.364-13.324 0-26.649 5.626-36.091 15.364-9.444 9.737-14.899 23.37-14.899 37.22m0 0zM103.625 774.089c0 13.741 5.455 27.482 14.899 37.22 9.442 9.738 22.663 15.364 36.091 15.364 13.324 0 26.649-5.626 36.091-15.364s14.899-23.37 14.899-37.22c0-13.741-5.455-27.482-14.899-37.22-9.442-9.737-22.662-15.364-36.091-15.364-13.324 0-26.649 5.627-36.091 15.364-9.444 9.847-14.899 23.479-14.899 37.22m0 0zM919.041 249.869c0 27.699-19.935 50.095-44.59 50.095H345.88c-24.655 0-44.59-22.397-44.59-50.095 0-27.699 19.935-50.095 44.59-50.095h528.571c24.656-0.001 44.59 22.396 44.59 50.095m0 0zM919.041 510.195c0 27.59-19.935 50.095-44.59 50.095H345.88c-24.655 0-44.59-22.398-44.59-50.096 0-27.699 19.935-50.096 44.59-50.096h528.571c24.656-0.109 44.59 22.397 44.59 50.097m0 0zM919.041 771.601c0 27.699-19.935 50.096-44.59 50.096H345.88c-24.655 0-44.59-22.397-44.59-50.096 0-27.591 19.935-49.988 44.59-49.988h528.571c24.656-0.108 44.59 22.397 44.59 49.988m0 0z" p-id="1482"></path></svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.7 KiB |
@@ -1 +1 @@
|
|||||||
<?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="1684739068105" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7879" xmlns:xlink="http://www.w3.org/1999/xlink" ><path d="M817.87 556.31h-63.58v-66.24A42.27 42.27 0 0 0 712 447.8h-84.81a42.27 42.27 0 0 0-42.27 42.27v66.24H436.57v-66.24a42.27 42.27 0 0 0-42.27-42.27h-84.83a42.27 42.27 0 0 0-42.27 42.27v66.24h-61.83A22.39 22.39 0 0 0 183 578.7a22.39 22.39 0 0 0 22.39 22.39h61.81v65.55a42.27 42.27 0 0 0 42.27 42.27h84.83a42.27 42.27 0 0 0 42.27-42.27v-65.55h148.36v65.55a42.27 42.27 0 0 0 42.27 42.27H712a42.27 42.27 0 0 0 42.27-42.27v-65.55h63.58a22.39 22.39 0 0 0 22.39-22.39 22.39 22.39 0 0 0-22.37-22.39z m-438.64 95.26h-54.69V505.14h54.69z m317.72 0h-54.69V505.14H697z" p-id="7880"></path><path d="M823 202.58h-90.81v-63.09a71.88 71.88 0 0 0-71.88-71.88H363.19a71.88 71.88 0 0 0-71.88 71.88v63.08h-90.12A137.17 137.17 0 0 0 64 339.75v479a137.17 137.17 0 0 0 137.19 137.14H823a137.17 137.17 0 0 0 137.19-137.17v-479A137.17 137.17 0 0 0 823 202.58z m-474.36-54.1A23.52 23.52 0 0 1 372.17 125h279.16a23.52 23.52 0 0 1 23.52 23.52v54.1h-326.2z m554.23 673.31a76.76 76.76 0 0 1-76.76 76.76h-628a76.76 76.76 0 0 1-76.76-76.76V336.67a76.76 76.76 0 0 1 76.76-76.76h628a76.76 76.76 0 0 1 76.76 76.76z" p-id="7881"></path></svg>
|
<?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="1683450447995" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2005" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M728.99015111 121.90378667h107.92732444V40.96h-107.92732444v80.94378667z m175.38161778 67.45429333v107.92732445H985.31555555v-107.92732445h-80.94378666z m-67.45429334 175.38161778h-107.92732444v80.94378667h107.92732444v-80.94378667z m-175.38161777-67.45429333v-107.92732445h-80.94378667v107.92732445h80.94378667z m67.45429333 67.45429333c-37.23491555 0-67.45429333-30.21937778-67.45429333-67.45429333h-80.94378667c0 81.97006222 66.42801778 148.39808 148.39808 148.39808v-80.94378667z m175.38161778-67.45429333c0 37.23491555-30.21937778 67.45429333-67.45429334 67.45429333v80.94378667c81.97006222 0 148.39808-66.42801778 148.39808-148.39808h-80.94378666zM836.91747555 121.90151111c37.23491555 0 67.45429333 30.21937778 67.45429334 67.45429334H985.31555555C985.31555555 107.38801778 918.88753778 40.96 836.91747555 40.96v80.94378667zM728.99015111 40.96c-81.97006222 0-148.39808 66.42801778-148.39808 148.39808h80.94378667c0-37.23491555 30.21937778-67.45429333 67.45429333-67.45429333V40.96zM189.35808 661.53585778h107.92732445v-80.94378667h-107.92732445v80.94378667z m175.38161778 67.45429333v107.92732444h80.94378667v-107.92732444h-80.94378667z m-67.45429333 175.38161778h-107.92732445V985.31555555h107.92732445v-80.94378666zM121.90151111 836.91747555v-107.92732444H40.96v107.92732444h80.94378667z m67.45429334 67.45429334c-37.23491555 0-67.45429333-30.21937778-67.45429334-67.45429334H40.96C40.96 918.88753778 107.38801778 985.31555555 189.35808 985.31555555v-80.94378666z m175.38161777-67.45429334c0 37.23491555-30.21937778 67.45429333-67.45429333 67.45429334V985.31555555c81.97006222 0 148.39808-66.42801778 148.39808-148.39808h-80.94378667z m-67.45429333-175.38161777c37.23491555 0 67.45429333 30.21937778 67.45429333 67.45429333h80.94378667c0-81.97006222-66.42801778-148.39808-148.39808-148.39808v80.94378667z m-107.92732444-80.94378667C107.38801778 580.59207111 40.96 647.02008889 40.96 728.99015111h80.94378667c0-37.23491555 30.21937778-67.45429333 67.45429333-67.45429333v-80.94378667z m0-458.68828444h107.92732444V40.96h-107.92732444v80.94378667z m175.38161777 67.45429333v107.92732445h80.94378667v-107.92732445h-80.94378667z m-67.45429333 175.38161778h-107.92732444v80.94378667h107.92732444v-80.94378667zM121.90151111 297.28540445v-107.92732445H40.96v107.92732445h80.94378667z m67.45429334 67.45429333c-37.23491555 0-67.45429333-30.21937778-67.45429334-67.45429333H40.96c0 81.97006222 66.42801778 148.39808 148.39808 148.39808v-80.94378667z m175.38161777-67.45429333c0 37.23491555-30.21937778 67.45429333-67.45429333 67.45429333v80.94378667c81.97006222 0 148.39808-66.42801778 148.39808-148.39808h-80.94378667zM297.28540445 121.90151111c37.23491555 0 67.45429333 30.21937778 67.45429333 67.45429334h80.94378667c0-81.97006222-66.42801778-148.39808-148.39808-148.39808v80.94378666zM189.35808 40.96C107.38801778 40.96 40.96 107.38801778 40.96 189.35808h80.94378667c0-37.23491555 30.21937778-67.45429333 67.45429333-67.45429333V40.96z m539.63207111 620.57585778h107.92732444v-80.94378667h-107.92732444v80.94378667z m175.38161778 67.45429333v107.92732444H985.31555555v-107.92732444h-80.94378666z m-67.45429334 175.38161778h-107.92732444V985.31555555h107.92732444v-80.94378666z m-175.38161777-67.45429334v-107.92732444h-80.94378667v107.92732444h80.94378667z m67.45429333 67.45429334c-37.23491555 0-67.45429333-30.21937778-67.45429333-67.45429334h-80.94378667c0 81.97006222 66.42801778 148.39808 148.39808 148.39808v-80.94378666z m175.38161778-67.45429334c0 37.23491555-30.21937778 67.45429333-67.45429334 67.45429334V985.31555555C918.88753778 985.31555555 985.31555555 918.88753778 985.31555555 836.91747555h-80.94378666z m-67.45429334-175.38161777c37.23491555 0 67.45429333 30.21937778 67.45429334 67.45429333H985.31555555c0-81.97006222-66.42801778-148.39808-148.39808-148.39808v80.94378667z m-107.92732444-80.94378667c-81.97006222 0-148.39808 66.42801778-148.39808 148.39808h80.94378667c0-37.23491555 30.21937778-67.45429333 67.45429333-67.45429333v-80.94378667z" p-id="2006"></path></svg>
|
||||||
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 4.2 KiB |
1
src/components/Icon/icons/shareMarket.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?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="1683254591061" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1213" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M389.5296 650.78613333a204.8 204.8 0 1 1-10.82026667-288.49493333L557.43146667 245.76a153.6 153.6 0 1 1 39.25333333 55.9104l-176.46933333 115.02933333c15.01866667 28.50133333 23.48373333 60.928 23.48373333 95.3344a204.11733333 204.11733333 0 0 1-16.86186667 81.47626667l257.1264 144.62293333a153.6 153.6 0 1 1-30.9248 60.928l-263.54346666-148.24106666z" p-id="1214"></path></svg>
|
||||||
|
After Width: | Height: | Size: 710 B |
@@ -12,6 +12,7 @@ const map = {
|
|||||||
delete: require('./icons/delete.svg').default,
|
delete: require('./icons/delete.svg').default,
|
||||||
withdraw: require('./icons/withdraw.svg').default,
|
withdraw: require('./icons/withdraw.svg').default,
|
||||||
stop: require('./icons/stop.svg').default,
|
stop: require('./icons/stop.svg').default,
|
||||||
|
shareMarket: require('./icons/shareMarket.svg').default,
|
||||||
collectionLight: require('./icons/collectionLight.svg').default,
|
collectionLight: require('./icons/collectionLight.svg').default,
|
||||||
collectionSolid: require('./icons/collectionSolid.svg').default,
|
collectionSolid: require('./icons/collectionSolid.svg').default,
|
||||||
chat: require('./icons/chat.svg').default,
|
chat: require('./icons/chat.svg').default,
|
||||||
@@ -24,12 +25,7 @@ const map = {
|
|||||||
tabbarMe: require('./icons/phoneTabbar/me.svg').default,
|
tabbarMe: require('./icons/phoneTabbar/me.svg').default,
|
||||||
closeSolid: require('./icons/closeSolid.svg').default,
|
closeSolid: require('./icons/closeSolid.svg').default,
|
||||||
wx: require('./icons/wx.svg').default,
|
wx: require('./icons/wx.svg').default,
|
||||||
out: require('./icons/out.svg').default,
|
out: require('./icons/out.svg').default
|
||||||
git: require('./icons/git.svg').default,
|
|
||||||
kb: require('./icons/kb.svg').default,
|
|
||||||
appStore: require('./icons/appStore.svg').default,
|
|
||||||
menu: require('./icons/menu.svg').default,
|
|
||||||
edit: require('./icons/edit.svg').default
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type IconName = keyof typeof map;
|
export type IconName = keyof typeof map;
|
||||||
|
|||||||
@@ -7,8 +7,7 @@ import { useQuery } from '@tanstack/react-query';
|
|||||||
const unAuthPage: { [key: string]: boolean } = {
|
const unAuthPage: { [key: string]: boolean } = {
|
||||||
'/': true,
|
'/': true,
|
||||||
'/login': true,
|
'/login': true,
|
||||||
'/model/share': true,
|
'/model/share': true
|
||||||
'/chat/share': true
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const Auth = ({ children }: { children: JSX.Element }) => {
|
const Auth = ({ children }: { children: JSX.Element }) => {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import React, { useEffect, useMemo } from 'react';
|
import React, { useEffect, useMemo } from 'react';
|
||||||
import { Box, useColorMode, Flex } from '@chakra-ui/react';
|
import { Box, useColorMode, Flex } from '@chakra-ui/react';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
|
import { useScreen } from '@/hooks/useScreen';
|
||||||
import { useLoading } from '@/hooks/useLoading';
|
import { useLoading } from '@/hooks/useLoading';
|
||||||
import { useGlobalStore } from '@/store/global';
|
import { useGlobalStore } from '@/store/global';
|
||||||
import { throttle } from 'lodash';
|
|
||||||
import Auth from './auth';
|
import Auth from './auth';
|
||||||
import Navbar from './navbar';
|
import Navbar from './navbar';
|
||||||
import NavbarPhone from './navbarPhone';
|
import NavbarPhone from './navbarPhone';
|
||||||
@@ -19,11 +19,12 @@ const phoneUnShowLayoutRoute: Record<string, boolean> = {
|
|||||||
'/chat/share': true
|
'/chat/share': true
|
||||||
};
|
};
|
||||||
|
|
||||||
const Layout = ({ children }: { children: JSX.Element }) => {
|
const Layout = ({ children, isPcDevice }: { children: JSX.Element; isPcDevice: boolean }) => {
|
||||||
|
const { isPc } = useScreen({ defaultIsPc: isPcDevice });
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { colorMode, setColorMode } = useColorMode();
|
const { colorMode, setColorMode } = useColorMode();
|
||||||
const { Loading } = useLoading();
|
const { Loading } = useLoading();
|
||||||
const { loading, setScreenWidth, isPc } = useGlobalStore();
|
const { loading } = useGlobalStore();
|
||||||
|
|
||||||
const isChatPage = useMemo(
|
const isChatPage = useMemo(
|
||||||
() => router.pathname === '/chat' && Object.values(router.query).join('').length !== 0,
|
() => router.pathname === '/chat' && Object.values(router.query).join('').length !== 0,
|
||||||
@@ -36,19 +37,6 @@ const Layout = ({ children }: { children: JSX.Element }) => {
|
|||||||
}
|
}
|
||||||
}, [colorMode, router.pathname, setColorMode]);
|
}, [colorMode, router.pathname, setColorMode]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const resize = throttle(() => {
|
|
||||||
setScreenWidth(document.documentElement.clientWidth);
|
|
||||||
}, 300);
|
|
||||||
resize();
|
|
||||||
|
|
||||||
window.addEventListener('resize', resize);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener('resize', resize);
|
|
||||||
};
|
|
||||||
}, [setScreenWidth]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Box
|
<Box
|
||||||
@@ -87,3 +75,9 @@ const Layout = ({ children }: { children: JSX.Element }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default Layout;
|
export default Layout;
|
||||||
|
|
||||||
|
Layout.getInitialProps = ({ req }: any) => {
|
||||||
|
return {
|
||||||
|
isPcDevice: !/Mobile/.test(req ? req.headers['user-agent'] : navigator.userAgent)
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { Box, Flex, Tooltip } from '@chakra-ui/react';
|
import { Box, Flex, Image, Tooltip } from '@chakra-ui/react';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import MyIcon from '../Icon';
|
import MyIcon from '../Icon';
|
||||||
import { useUserStore } from '@/store/user';
|
import { useUserStore } from '@/store/user';
|
||||||
import { useChatStore } from '@/store/chat';
|
import { useChatStore } from '@/store/chat';
|
||||||
import Avatar from '../Avatar';
|
|
||||||
|
|
||||||
export enum NavbarTypeEnum {
|
export enum NavbarTypeEnum {
|
||||||
normal = 'normal',
|
normal = 'normal',
|
||||||
@@ -23,21 +22,16 @@ const Navbar = () => {
|
|||||||
link: `/chat?modelId=${lastChatModelId}&chatId=${lastChatId}`,
|
link: `/chat?modelId=${lastChatModelId}&chatId=${lastChatId}`,
|
||||||
activeLink: ['/chat']
|
activeLink: ['/chat']
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
label: '我的应用',
|
label: 'AI助手',
|
||||||
icon: 'model',
|
icon: 'model',
|
||||||
link: `/model?modelId=${lastModelId}`,
|
link: `/model?modelId=${lastModelId}`,
|
||||||
activeLink: ['/model']
|
activeLink: ['/model']
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '知识库',
|
label: '共享',
|
||||||
icon: 'kb',
|
icon: 'shareMarket',
|
||||||
link: `/kb`,
|
|
||||||
activeLink: ['/kb']
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '应用市场',
|
|
||||||
icon: 'appStore',
|
|
||||||
link: '/model/share',
|
link: '/model/share',
|
||||||
activeLink: ['/model/share']
|
activeLink: ['/model/share']
|
||||||
},
|
},
|
||||||
@@ -83,7 +77,13 @@ const Navbar = () => {
|
|||||||
cursor={'pointer'}
|
cursor={'pointer'}
|
||||||
onClick={() => router.push('/number')}
|
onClick={() => router.push('/number')}
|
||||||
>
|
>
|
||||||
<Avatar w={'36px'} h={'36px'} src={userInfo?.avatar} fallbackSrc={'/icon/human.png'} />
|
<Image
|
||||||
|
src={userInfo?.avatar || '/icon/human.png'}
|
||||||
|
objectFit={'contain'}
|
||||||
|
w={'36px'}
|
||||||
|
h={'36px'}
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
{/* 导航列表 */}
|
{/* 导航列表 */}
|
||||||
<Box flex={1}>
|
<Box flex={1}>
|
||||||
@@ -125,24 +125,6 @@ const Navbar = () => {
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
))}
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
|
||||||
<Flex
|
|
||||||
mb={3}
|
|
||||||
flexDirection={'column'}
|
|
||||||
alignItems={'center'}
|
|
||||||
justifyContent={'center'}
|
|
||||||
cursor={'pointer'}
|
|
||||||
w={'60px'}
|
|
||||||
h={'45px'}
|
|
||||||
color={'#9096a5'}
|
|
||||||
_hover={{
|
|
||||||
color: '#ffffff'
|
|
||||||
}}
|
|
||||||
onClick={() => window.open('https://github.com/c121914yu/FastGPT')}
|
|
||||||
>
|
|
||||||
<MyIcon name={'git'} width={'22px'} height={'22px'} />
|
|
||||||
</Flex>
|
|
||||||
</Box>
|
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,21 +10,25 @@ const NavbarPhone = () => {
|
|||||||
const navbarList = useMemo(
|
const navbarList = useMemo(
|
||||||
() => [
|
() => [
|
||||||
{
|
{
|
||||||
|
label: '聊天',
|
||||||
icon: 'tabbarChat',
|
icon: 'tabbarChat',
|
||||||
link: `/chat?modelId=${lastChatModelId}&chatId=${lastChatId}`,
|
link: `/chat?modelId=${lastChatModelId}&chatId=${lastChatId}`,
|
||||||
activeLink: ['/chat']
|
activeLink: ['/chat']
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
label: 'AI助手',
|
||||||
icon: 'tabbarModel',
|
icon: 'tabbarModel',
|
||||||
link: `/model`,
|
link: `/model`,
|
||||||
activeLink: ['/model']
|
activeLink: ['/model']
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
label: '发现',
|
||||||
icon: 'tabbarMore',
|
icon: 'tabbarMore',
|
||||||
link: '/tools',
|
link: '/tools',
|
||||||
activeLink: ['/tools']
|
activeLink: ['/tools']
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
label: '我',
|
||||||
icon: 'tabbarMe',
|
icon: 'tabbarMe',
|
||||||
link: '/number',
|
link: '/number',
|
||||||
activeLink: ['/number']
|
activeLink: ['/number']
|
||||||
|
|||||||
@@ -339,12 +339,9 @@
|
|||||||
text-align: justify;
|
text-align: justify;
|
||||||
tab-size: 4;
|
tab-size: 4;
|
||||||
word-spacing: normal;
|
word-spacing: normal;
|
||||||
|
word-break: break-all;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
* {
|
|
||||||
word-break: break-all;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
p {
|
||||||
white-space: pre-line;
|
white-space: pre-line;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import React, { memo, useMemo } from 'react';
|
import React, { memo } from 'react';
|
||||||
import ReactMarkdown from 'react-markdown';
|
import ReactMarkdown from 'react-markdown';
|
||||||
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
|
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
|
||||||
import { Box, Flex, useColorModeValue } from '@chakra-ui/react';
|
import { Box, Flex, useColorModeValue } from '@chakra-ui/react';
|
||||||
import { useCopyData, formatLinkText } from '@/utils/tools';
|
import { useCopyData } from '@/utils/tools';
|
||||||
import Icon from '@/components/Icon';
|
import Icon from '@/components/Icon';
|
||||||
import remarkGfm from 'remark-gfm';
|
import remarkGfm from 'remark-gfm';
|
||||||
import remarkMath from 'remark-math';
|
import remarkMath from 'remark-math';
|
||||||
@@ -12,21 +12,9 @@ import 'katex/dist/katex.min.css';
|
|||||||
import styles from './index.module.scss';
|
import styles from './index.module.scss';
|
||||||
import { codeLight } from './codeLight';
|
import { codeLight } from './codeLight';
|
||||||
|
|
||||||
const Markdown = ({
|
const Markdown = ({ source, isChatting = false }: { source: string; isChatting?: boolean }) => {
|
||||||
source,
|
|
||||||
isChatting = false,
|
|
||||||
formatLink
|
|
||||||
}: {
|
|
||||||
source: string;
|
|
||||||
formatLink?: boolean;
|
|
||||||
isChatting?: boolean;
|
|
||||||
}) => {
|
|
||||||
const { copyData } = useCopyData();
|
const { copyData } = useCopyData();
|
||||||
|
|
||||||
const formatSource = useMemo(() => {
|
|
||||||
return formatLink ? formatLinkText(source) : source;
|
|
||||||
}, [source, formatLink]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ReactMarkdown
|
<ReactMarkdown
|
||||||
className={`markdown ${styles.markdown} ${
|
className={`markdown ${styles.markdown} ${
|
||||||
@@ -75,7 +63,7 @@ const Markdown = ({
|
|||||||
}}
|
}}
|
||||||
linkTarget="_blank"
|
linkTarget="_blank"
|
||||||
>
|
>
|
||||||
{formatSource}
|
{source}
|
||||||
</ReactMarkdown>
|
</ReactMarkdown>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,67 +0,0 @@
|
|||||||
import React, { useState } from 'react';
|
|
||||||
import { Box, Flex } from '@chakra-ui/react';
|
|
||||||
import type { BoxProps } from '@chakra-ui/react';
|
|
||||||
import MyIcon from '../Icon';
|
|
||||||
|
|
||||||
interface Props extends BoxProps {}
|
|
||||||
|
|
||||||
const SideBar = (e?: Props) => {
|
|
||||||
const {
|
|
||||||
w = ['100%', '0 0 250px', '0 0 280px', '0 0 310px', '0 0 340px'],
|
|
||||||
children,
|
|
||||||
...props
|
|
||||||
} = e || {};
|
|
||||||
|
|
||||||
const [foldSideBar, setFoldSideBar] = useState(false);
|
|
||||||
return (
|
|
||||||
<Box
|
|
||||||
position={'relative'}
|
|
||||||
flex={foldSideBar ? '0 0 0' : w}
|
|
||||||
w={['100%', 0]}
|
|
||||||
h={'100%'}
|
|
||||||
zIndex={1}
|
|
||||||
transition={'0.2s'}
|
|
||||||
_hover={{
|
|
||||||
'& > div': { visibility: 'visible', opacity: 1 }
|
|
||||||
}}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
<Flex
|
|
||||||
position={'absolute'}
|
|
||||||
right={0}
|
|
||||||
top={'50%'}
|
|
||||||
transform={'translate(50%,-50%)'}
|
|
||||||
alignItems={'center'}
|
|
||||||
justifyContent={'flex-end'}
|
|
||||||
pr={1}
|
|
||||||
w={'36px'}
|
|
||||||
h={'50px'}
|
|
||||||
borderRadius={'10px'}
|
|
||||||
bg={'rgba(0,0,0,0.5)'}
|
|
||||||
cursor={'pointer'}
|
|
||||||
transition={'0.2s'}
|
|
||||||
{...(foldSideBar
|
|
||||||
? {
|
|
||||||
opacity: 0.6
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
visibility: 'hidden',
|
|
||||||
opacity: 0
|
|
||||||
})}
|
|
||||||
onClick={() => setFoldSideBar(!foldSideBar)}
|
|
||||||
>
|
|
||||||
<MyIcon
|
|
||||||
name={'back'}
|
|
||||||
transform={foldSideBar ? 'rotate(180deg)' : ''}
|
|
||||||
w={'14px'}
|
|
||||||
color={'white'}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
<Box position={'relative'} h={'100%'} overflow={foldSideBar ? 'hidden' : 'visible'}>
|
|
||||||
{children}
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default SideBar;
|
|
||||||
@@ -23,7 +23,7 @@ const WxConcat = ({ onClose }: { onClose: () => void }) => {
|
|||||||
<ModalBody textAlign={'center'}>
|
<ModalBody textAlign={'center'}>
|
||||||
<Image
|
<Image
|
||||||
style={{ margin: 'auto' }}
|
style={{ margin: 'auto' }}
|
||||||
src={'https://otnvvf-imgs.oss.laf.run/wx300.jpg'}
|
src={'/imgs/wx300.jpg'}
|
||||||
width={'200px'}
|
width={'200px'}
|
||||||
height={'200px'}
|
height={'200px'}
|
||||||
alt=""
|
alt=""
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
export const NEW_CHATID_HEADER = 'response-new-chat-id';
|
export const SYSTEM_PROMPT_HEADER = 'System-Prompt-Header';
|
||||||
export const QUOTE_LEN_HEADER = 'response-quote-len';
|
export const NEW_CHATID_HEADER = 'Chat-Id-Header';
|
||||||
export const GUIDE_PROMPT_HEADER = 'response-guide-prompt';
|
|
||||||
|
|
||||||
export enum ChatRoleEnum {
|
export enum ChatRoleEnum {
|
||||||
System = 'System',
|
System = 'System',
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
import type { KbItemType } from '@/types/plugin';
|
|
||||||
|
|
||||||
export const defaultKbDetail: KbItemType = {
|
|
||||||
_id: '',
|
|
||||||
userId: '',
|
|
||||||
updateTime: new Date(),
|
|
||||||
avatar: '/icon/logo.png',
|
|
||||||
name: '',
|
|
||||||
tags: '',
|
|
||||||
totalData: 0
|
|
||||||
};
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { getSystemModelList } from '@/api/system';
|
import { getSystemModelList } from '@/api/system';
|
||||||
import type { ShareChatEditType } from '@/types/model';
|
|
||||||
import type { ModelSchema } from '@/types/mongoSchema';
|
import type { ModelSchema } from '@/types/mongoSchema';
|
||||||
|
import type { ShareChatEditType } from '@/types/model';
|
||||||
|
|
||||||
export const embeddingModel = 'text-embedding-ada-002';
|
export const embeddingModel = 'text-embedding-ada-002';
|
||||||
export type EmbeddingModelType = 'text-embedding-ada-002';
|
export type EmbeddingModelType = 'text-embedding-ada-002';
|
||||||
@@ -32,7 +32,7 @@ export const ChatModelMap = {
|
|||||||
contextMaxToken: 4096,
|
contextMaxToken: 4096,
|
||||||
systemMaxToken: 2400,
|
systemMaxToken: 2400,
|
||||||
maxTemperature: 1.2,
|
maxTemperature: 1.2,
|
||||||
price: 2.5
|
price: 3
|
||||||
},
|
},
|
||||||
[OpenAiChatEnum.GPT4]: {
|
[OpenAiChatEnum.GPT4]: {
|
||||||
chatModel: OpenAiChatEnum.GPT4,
|
chatModel: OpenAiChatEnum.GPT4,
|
||||||
@@ -122,15 +122,15 @@ export const ModelVectorSearchModeMap: Record<
|
|||||||
> = {
|
> = {
|
||||||
[ModelVectorSearchModeEnum.hightSimilarity]: {
|
[ModelVectorSearchModeEnum.hightSimilarity]: {
|
||||||
text: '高相似度, 无匹配时拒绝回复',
|
text: '高相似度, 无匹配时拒绝回复',
|
||||||
similarity: 0.18
|
similarity: 0.2
|
||||||
},
|
},
|
||||||
[ModelVectorSearchModeEnum.noContext]: {
|
[ModelVectorSearchModeEnum.noContext]: {
|
||||||
text: '高相似度,无匹配时直接回复',
|
text: '高相似度,无匹配时直接回复',
|
||||||
similarity: 0.18
|
similarity: 0.2
|
||||||
},
|
},
|
||||||
[ModelVectorSearchModeEnum.lowSimilarity]: {
|
[ModelVectorSearchModeEnum.lowSimilarity]: {
|
||||||
text: '低相似度匹配',
|
text: '低相似度匹配',
|
||||||
similarity: 0.7
|
similarity: 0.8
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -142,7 +142,7 @@ export const defaultModel: ModelSchema = {
|
|||||||
status: ModelStatusEnum.pending,
|
status: ModelStatusEnum.pending,
|
||||||
updateTime: Date.now(),
|
updateTime: Date.now(),
|
||||||
chat: {
|
chat: {
|
||||||
relatedKbs: [],
|
useKb: false,
|
||||||
searchMode: ModelVectorSearchModeEnum.hightSimilarity,
|
searchMode: ModelVectorSearchModeEnum.hightSimilarity,
|
||||||
systemPrompt: '',
|
systemPrompt: '',
|
||||||
temperature: 0,
|
temperature: 0,
|
||||||
@@ -153,6 +153,13 @@ export const defaultModel: ModelSchema = {
|
|||||||
isShareDetail: false,
|
isShareDetail: false,
|
||||||
intro: '',
|
intro: '',
|
||||||
collection: 0
|
collection: 0
|
||||||
|
},
|
||||||
|
security: {
|
||||||
|
domain: ['*'],
|
||||||
|
contextMaxLen: 1,
|
||||||
|
contentMaxLen: 1,
|
||||||
|
expiredTime: 9999,
|
||||||
|
maxLoadAmount: 1
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
export enum SplitTextTypEnum {
|
|
||||||
'qa' = 'qa',
|
|
||||||
'subsection' = 'subsection'
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum PluginTypeEnum {
|
|
||||||
LLM = 'LLM',
|
|
||||||
Text = 'Text',
|
|
||||||
Function = 'Function'
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum PluginParamsTypeEnum {
|
|
||||||
'Text' = 'text'
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { extendTheme, defineStyleConfig, ComponentStyleConfig } from '@chakra-ui/react';
|
import { extendTheme, defineStyleConfig, ComponentStyleConfig } from '@chakra-ui/react';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { modalAnatomy, switchAnatomy, selectAnatomy, checkboxAnatomy } from '@chakra-ui/anatomy';
|
import { modalAnatomy, switchAnatomy, selectAnatomy } from '@chakra-ui/anatomy';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { createMultiStyleConfigHelpers } from '@chakra-ui/styled-system';
|
import { createMultiStyleConfigHelpers } from '@chakra-ui/styled-system';
|
||||||
|
|
||||||
@@ -11,8 +11,6 @@ const { definePartsStyle: switchPart, defineMultiStyleConfig: switchMultiStyle }
|
|||||||
createMultiStyleConfigHelpers(switchAnatomy.keys);
|
createMultiStyleConfigHelpers(switchAnatomy.keys);
|
||||||
const { definePartsStyle: selectPart, defineMultiStyleConfig: selectMultiStyle } =
|
const { definePartsStyle: selectPart, defineMultiStyleConfig: selectMultiStyle } =
|
||||||
createMultiStyleConfigHelpers(selectAnatomy.keys);
|
createMultiStyleConfigHelpers(selectAnatomy.keys);
|
||||||
const { definePartsStyle: checkboxPart, defineMultiStyleConfig: checkboxMultiStyle } =
|
|
||||||
createMultiStyleConfigHelpers(checkboxAnatomy.keys);
|
|
||||||
|
|
||||||
// modal 弹窗
|
// modal 弹窗
|
||||||
const ModalTheme = defineMultiStyleConfig({
|
const ModalTheme = defineMultiStyleConfig({
|
||||||
@@ -173,9 +171,6 @@ export const theme = extendTheme({
|
|||||||
fontWeight: 400,
|
fontWeight: 400,
|
||||||
height: '100%',
|
height: '100%',
|
||||||
overflow: 'hidden'
|
overflow: 'hidden'
|
||||||
},
|
|
||||||
a: {
|
|
||||||
color: 'myBlue.700'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
export enum BillTypeEnum {
|
export enum BillTypeEnum {
|
||||||
chat = 'chat',
|
chat = 'chat',
|
||||||
openapiChat = 'openapiChat',
|
splitData = 'splitData',
|
||||||
QA = 'QA',
|
QA = 'QA',
|
||||||
|
abstract = 'abstract',
|
||||||
vector = 'vector',
|
vector = 'vector',
|
||||||
return = 'return'
|
return = 'return'
|
||||||
}
|
}
|
||||||
@@ -13,8 +14,9 @@ export enum PageTypeEnum {
|
|||||||
|
|
||||||
export const BillTypeMap: Record<`${BillTypeEnum}`, string> = {
|
export const BillTypeMap: Record<`${BillTypeEnum}`, string> = {
|
||||||
[BillTypeEnum.chat]: '对话',
|
[BillTypeEnum.chat]: '对话',
|
||||||
[BillTypeEnum.openapiChat]: 'api 对话',
|
[BillTypeEnum.splitData]: 'QA拆分',
|
||||||
[BillTypeEnum.QA]: 'QA拆分',
|
[BillTypeEnum.QA]: 'QA拆分',
|
||||||
|
[BillTypeEnum.abstract]: '摘要总结',
|
||||||
[BillTypeEnum.vector]: '索引生成',
|
[BillTypeEnum.vector]: '索引生成',
|
||||||
[BillTypeEnum.return]: '退款'
|
[BillTypeEnum.return]: '退款'
|
||||||
};
|
};
|
||||||
@@ -27,6 +29,6 @@ export enum PromotionEnum {
|
|||||||
|
|
||||||
export const PromotionTypeMap = {
|
export const PromotionTypeMap = {
|
||||||
[PromotionEnum.invite]: '好友充值',
|
[PromotionEnum.invite]: '好友充值',
|
||||||
[PromotionEnum.shareModel]: '应用分享',
|
[PromotionEnum.shareModel]: 'AI助手分享',
|
||||||
[PromotionEnum.withdraw]: '提现'
|
[PromotionEnum.withdraw]: '提现'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { IconButton, Flex, Box, Input } from '@chakra-ui/react';
|
|||||||
import { ArrowBackIcon, ArrowForwardIcon } from '@chakra-ui/icons';
|
import { ArrowBackIcon, ArrowForwardIcon } from '@chakra-ui/icons';
|
||||||
import { useMutation } from '@tanstack/react-query';
|
import { useMutation } from '@tanstack/react-query';
|
||||||
import { useToast } from './useToast';
|
import { useToast } from './useToast';
|
||||||
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
|
||||||
export const usePagination = <T = any,>({
|
export const usePagination = <T = any,>({
|
||||||
api,
|
api,
|
||||||
@@ -40,7 +41,6 @@ export const usePagination = <T = any,>({
|
|||||||
});
|
});
|
||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
18
src/hooks/useTabs.tsx
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import React, { useState, useCallback, useRef } from 'react';
|
||||||
|
|
||||||
|
export const useTabs = ({
|
||||||
|
tabs = []
|
||||||
|
}: {
|
||||||
|
tabs: {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
}[];
|
||||||
|
}) => {
|
||||||
|
const [activeTab, setActiveTab] = useState(tabs[0].id);
|
||||||
|
|
||||||
|
return {
|
||||||
|
tabs,
|
||||||
|
activeTab,
|
||||||
|
setActiveTab
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -55,7 +55,7 @@ export default function App({ Component, pageProps }: AppProps) {
|
|||||||
<Script src="/js/qrcode.min.js" strategy="lazyOnload"></Script>
|
<Script src="/js/qrcode.min.js" strategy="lazyOnload"></Script>
|
||||||
<Script src="/js/pdf.js" strategy="lazyOnload"></Script>
|
<Script src="/js/pdf.js" strategy="lazyOnload"></Script>
|
||||||
<Script src="/js/html2pdf.bundle.min.js" strategy="lazyOnload"></Script>
|
<Script src="/js/html2pdf.bundle.min.js" strategy="lazyOnload"></Script>
|
||||||
<Script src="/js/particles.js"></Script>
|
<Script src="/js/particles.js" strategy="lazyOnload"></Script>
|
||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
<ChakraProvider theme={theme}>
|
<ChakraProvider theme={theme}>
|
||||||
<ColorModeScript initialColorMode={theme.config.initialColorMode} />
|
<ColorModeScript initialColorMode={theme.config.initialColorMode} />
|
||||||
|
|||||||
@@ -4,9 +4,7 @@ function Error({ errStr }: { errStr: string }) {
|
|||||||
|
|
||||||
Error.getInitialProps = ({ res, err }: { res: any; err: any }) => {
|
Error.getInitialProps = ({ res, err }: { res: any; err: any }) => {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
return {
|
return { errStr: JSON.stringify(err) };
|
||||||
errStr: `部分系统不兼容,导致页面崩溃。如果可以,请联系作者,反馈下具体操作和页面。大部分是 苹果 的 safari 浏览器导致,可以尝试更换 chrome 浏览器。`
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Error;
|
export default Error;
|
||||||
|
|||||||
@@ -2,34 +2,36 @@ import type { NextApiRequest, NextApiResponse } from 'next';
|
|||||||
import { connectToDatabase } from '@/service/mongo';
|
import { connectToDatabase } from '@/service/mongo';
|
||||||
import { authChat } from '@/service/utils/auth';
|
import { authChat } from '@/service/utils/auth';
|
||||||
import { modelServiceToolMap } from '@/service/utils/chat';
|
import { modelServiceToolMap } from '@/service/utils/chat';
|
||||||
import { ChatItemType } from '@/types/chat';
|
import { ChatItemSimpleType } from '@/types/chat';
|
||||||
import { jsonRes } from '@/service/response';
|
import { jsonRes } from '@/service/response';
|
||||||
|
import { PassThrough } from 'stream';
|
||||||
import { ChatModelMap, ModelVectorSearchModeMap } from '@/constants/model';
|
import { ChatModelMap, ModelVectorSearchModeMap } from '@/constants/model';
|
||||||
import { pushChatBill } from '@/service/events/pushBill';
|
import { pushChatBill } from '@/service/events/pushBill';
|
||||||
import { resStreamResponse } from '@/service/utils/chat';
|
import { resStreamResponse } from '@/service/utils/chat';
|
||||||
import { appKbSearch } from '../openapi/kb/appKbSearch';
|
import { searchKb } from '@/service/plugins/searchKb';
|
||||||
import { ChatRoleEnum, QUOTE_LEN_HEADER, GUIDE_PROMPT_HEADER } from '@/constants/chat';
|
import { ChatRoleEnum } from '@/constants/chat';
|
||||||
import { BillTypeEnum } from '@/constants/user';
|
|
||||||
import { sensitiveCheck } from '@/service/api/text';
|
|
||||||
import { NEW_CHATID_HEADER } from '@/constants/chat';
|
|
||||||
import { saveChat } from './saveChat';
|
|
||||||
import { Types } from 'mongoose';
|
|
||||||
|
|
||||||
/* 发送提示词 */
|
/* 发送提示词 */
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
|
let step = 0; // step=1时,表示开始了流响应
|
||||||
|
const stream = new PassThrough();
|
||||||
|
stream.on('error', () => {
|
||||||
|
console.log('error: ', 'stream error');
|
||||||
|
stream.destroy();
|
||||||
|
});
|
||||||
res.on('close', () => {
|
res.on('close', () => {
|
||||||
res.end();
|
stream.destroy();
|
||||||
});
|
});
|
||||||
res.on('error', () => {
|
res.on('error', () => {
|
||||||
console.log('error: ', 'request error');
|
console.log('error: ', 'request error');
|
||||||
res.end();
|
stream.destroy();
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { chatId, prompt, modelId } = req.body as {
|
const { chatId, prompt, modelId } = req.body as {
|
||||||
prompt: [ChatItemType, ChatItemType];
|
prompt: ChatItemSimpleType;
|
||||||
modelId: string;
|
modelId: string;
|
||||||
chatId?: string;
|
chatId: '' | string;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!modelId || !prompt) {
|
if (!modelId || !prompt) {
|
||||||
@@ -49,143 +51,86 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
const modelConstantsData = ChatModelMap[model.chat.chatModel];
|
const modelConstantsData = ChatModelMap[model.chat.chatModel];
|
||||||
|
|
||||||
// 读取对话内容
|
// 读取对话内容
|
||||||
const prompts = [...content, prompt[0]];
|
const prompts = [...content, prompt];
|
||||||
const {
|
|
||||||
code = 200,
|
|
||||||
systemPrompts = [],
|
|
||||||
quote = [],
|
|
||||||
guidePrompt = ''
|
|
||||||
} = await (async () => {
|
|
||||||
// 使用了知识库搜索
|
|
||||||
if (model.chat.relatedKbs.length > 0) {
|
|
||||||
const { code, searchPrompts, rawSearch, guidePrompt } = await appKbSearch({
|
|
||||||
model,
|
|
||||||
userId,
|
|
||||||
prompts,
|
|
||||||
similarity: ModelVectorSearchModeMap[model.chat.searchMode]?.similarity
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
// 使用了知识库搜索
|
||||||
code,
|
if (model.chat.useKb) {
|
||||||
quote: rawSearch,
|
const { code, searchPrompts } = await searchKb({
|
||||||
systemPrompts: searchPrompts,
|
userOpenAiKey,
|
||||||
guidePrompt
|
prompts,
|
||||||
};
|
similarity: ModelVectorSearchModeMap[model.chat.searchMode]?.similarity,
|
||||||
}
|
model,
|
||||||
if (model.chat.systemPrompt) {
|
|
||||||
return {
|
|
||||||
guidePrompt: model.chat.systemPrompt,
|
|
||||||
systemPrompts: [
|
|
||||||
{
|
|
||||||
obj: ChatRoleEnum.System,
|
|
||||||
value: model.chat.systemPrompt
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
})();
|
|
||||||
|
|
||||||
// get conversationId. create a newId if it is null
|
|
||||||
const conversationId = chatId || String(new Types.ObjectId());
|
|
||||||
!chatId && res.setHeader(NEW_CHATID_HEADER, conversationId);
|
|
||||||
if (showModelDetail) {
|
|
||||||
guidePrompt && res.setHeader(GUIDE_PROMPT_HEADER, encodeURIComponent(guidePrompt));
|
|
||||||
res.setHeader(QUOTE_LEN_HEADER, quote.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
// search result is empty
|
|
||||||
if (code === 201) {
|
|
||||||
const response = systemPrompts[0]?.value;
|
|
||||||
await saveChat({
|
|
||||||
chatId,
|
|
||||||
newChatId: conversationId,
|
|
||||||
modelId,
|
|
||||||
prompts: [
|
|
||||||
prompt[0],
|
|
||||||
{
|
|
||||||
...prompt[1],
|
|
||||||
quote: [],
|
|
||||||
value: response
|
|
||||||
}
|
|
||||||
],
|
|
||||||
userId
|
userId
|
||||||
});
|
});
|
||||||
return res.end(response);
|
|
||||||
|
// search result is empty
|
||||||
|
if (code === 201) {
|
||||||
|
return res.send(searchPrompts[0]?.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
prompts.splice(prompts.length - 3, 0, ...searchPrompts);
|
||||||
|
} else {
|
||||||
|
// 没有用知识库搜索,仅用系统提示词
|
||||||
|
model.chat.systemPrompt &&
|
||||||
|
prompts.splice(prompts.length - 3, 0, {
|
||||||
|
obj: ChatRoleEnum.System,
|
||||||
|
value: model.chat.systemPrompt
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
prompts.splice(prompts.length - 3, 0, ...systemPrompts);
|
|
||||||
|
|
||||||
// content check
|
|
||||||
await sensitiveCheck({
|
|
||||||
input: [...systemPrompts, prompt[0]].map((item) => item.value).join('')
|
|
||||||
});
|
|
||||||
|
|
||||||
// 计算温度
|
// 计算温度
|
||||||
const temperature = (modelConstantsData.maxTemperature * (model.chat.temperature / 10)).toFixed(
|
const temperature = (modelConstantsData.maxTemperature * (model.chat.temperature / 10)).toFixed(
|
||||||
2
|
2
|
||||||
);
|
);
|
||||||
|
|
||||||
// 发出 chat 请求
|
// 发出请求
|
||||||
const { streamResponse } = await modelServiceToolMap[model.chat.chatModel].chatCompletion({
|
const { streamResponse } = await modelServiceToolMap[model.chat.chatModel].chatCompletion({
|
||||||
apiKey: userOpenAiKey || systemAuthKey,
|
apiKey: userOpenAiKey || systemAuthKey,
|
||||||
temperature: +temperature,
|
temperature: +temperature,
|
||||||
messages: prompts,
|
messages: prompts,
|
||||||
stream: true,
|
stream: true,
|
||||||
res,
|
res,
|
||||||
chatId: conversationId
|
chatId
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('api response time:', `${(Date.now() - startTime) / 1000}s`);
|
console.log('api response time:', `${(Date.now() - startTime) / 1000}s`);
|
||||||
|
|
||||||
if (res.closed) return res.end();
|
step = 1;
|
||||||
|
|
||||||
try {
|
const { totalTokens, finishMessages } = await resStreamResponse({
|
||||||
const { totalTokens, finishMessages, responseContent } = await resStreamResponse({
|
model: model.chat.chatModel,
|
||||||
model: model.chat.chatModel,
|
res,
|
||||||
res,
|
stream,
|
||||||
chatResponse: streamResponse,
|
chatResponse: streamResponse,
|
||||||
prompts
|
prompts,
|
||||||
});
|
systemPrompt: showModelDetail
|
||||||
|
? prompts
|
||||||
// save chat
|
.filter((item) => item.obj === ChatRoleEnum.System)
|
||||||
await saveChat({
|
.map((item) => item.value)
|
||||||
chatId,
|
.join('\n')
|
||||||
newChatId: conversationId,
|
: ''
|
||||||
modelId,
|
|
||||||
prompts: [
|
|
||||||
prompt[0],
|
|
||||||
{
|
|
||||||
...prompt[1],
|
|
||||||
value: responseContent,
|
|
||||||
quote: showModelDetail ? quote : [],
|
|
||||||
systemPrompt: showModelDetail ? guidePrompt : ''
|
|
||||||
}
|
|
||||||
],
|
|
||||||
userId
|
|
||||||
});
|
|
||||||
|
|
||||||
res.end();
|
|
||||||
|
|
||||||
// 只有使用平台的 key 才计费
|
|
||||||
pushChatBill({
|
|
||||||
isPay: !userOpenAiKey,
|
|
||||||
chatModel: model.chat.chatModel,
|
|
||||||
userId,
|
|
||||||
chatId: conversationId,
|
|
||||||
textLen: finishMessages.map((item) => item.value).join('').length,
|
|
||||||
tokens: totalTokens,
|
|
||||||
type: BillTypeEnum.chat
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
res.end();
|
|
||||||
console.log('error,结束', error);
|
|
||||||
}
|
|
||||||
} catch (err: any) {
|
|
||||||
res.status(500);
|
|
||||||
jsonRes(res, {
|
|
||||||
code: 500,
|
|
||||||
error: err
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 只有使用平台的 key 才计费
|
||||||
|
pushChatBill({
|
||||||
|
isPay: !userOpenAiKey,
|
||||||
|
chatModel: model.chat.chatModel,
|
||||||
|
userId,
|
||||||
|
chatId,
|
||||||
|
textLen: finishMessages.map((item) => item.value).join('').length,
|
||||||
|
tokens: totalTokens
|
||||||
|
});
|
||||||
|
} catch (err: any) {
|
||||||
|
if (step === 1) {
|
||||||
|
// 直接结束流
|
||||||
|
console.log('error,结束');
|
||||||
|
stream.destroy();
|
||||||
|
} else {
|
||||||
|
res.status(500);
|
||||||
|
jsonRes(res, {
|
||||||
|
code: 500,
|
||||||
|
error: err
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import { jsonRes } from '@/service/response';
|
import { jsonRes } from '@/service/response';
|
||||||
import { connectToDatabase, Chat } from '@/service/mongo';
|
import { connectToDatabase, Chat } from '@/service/mongo';
|
||||||
import { authUser } from '@/service/utils/auth';
|
import { authToken } from '@/service/utils/auth';
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
try {
|
try {
|
||||||
@@ -14,7 +14,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
await connectToDatabase();
|
await connectToDatabase();
|
||||||
|
|
||||||
// 凭证校验
|
// 凭证校验
|
||||||
const { userId } = await authUser({ req, authToken: true });
|
const userId = await authToken(req);
|
||||||
|
|
||||||
const chatRecord = await Chat.findById(chatId);
|
const chatRecord = await Chat.findById(chatId);
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import { jsonRes } from '@/service/response';
|
import { jsonRes } from '@/service/response';
|
||||||
import { connectToDatabase, Chat } from '@/service/mongo';
|
import { connectToDatabase, Chat } from '@/service/mongo';
|
||||||
import { authUser } from '@/service/utils/auth';
|
import { authToken } from '@/service/utils/auth';
|
||||||
|
|
||||||
/* 获取历史记录 */
|
/* 获取历史记录 */
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
try {
|
try {
|
||||||
const { userId } = await authUser({ req, authToken: true });
|
const userId = await authToken(req);
|
||||||
|
|
||||||
await connectToDatabase();
|
await connectToDatabase();
|
||||||
|
|
||||||
|
|||||||
@@ -1,49 +0,0 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
|
||||||
import { jsonRes } from '@/service/response';
|
|
||||||
import { connectToDatabase, Chat } from '@/service/mongo';
|
|
||||||
import { authUser } from '@/service/utils/auth';
|
|
||||||
import { Types } from 'mongoose';
|
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|
||||||
try {
|
|
||||||
const { chatId, historyId } = req.query as { chatId: string; historyId: string };
|
|
||||||
await connectToDatabase();
|
|
||||||
|
|
||||||
const { userId } = await authUser({ req, authToken: true });
|
|
||||||
|
|
||||||
if (!chatId || !historyId) {
|
|
||||||
throw new Error('params is error');
|
|
||||||
}
|
|
||||||
|
|
||||||
const history = await Chat.aggregate([
|
|
||||||
{
|
|
||||||
$match: {
|
|
||||||
_id: new Types.ObjectId(chatId),
|
|
||||||
userId: new Types.ObjectId(userId)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
$unwind: '$content'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
$match: {
|
|
||||||
'content._id': new Types.ObjectId(historyId)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
$project: {
|
|
||||||
quote: '$content.quote'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
jsonRes(res, {
|
|
||||||
data: history[0]?.quote || []
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
jsonRes(res, {
|
|
||||||
code: 500,
|
|
||||||
error: err
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,7 @@ import type { NextApiRequest, NextApiResponse } from 'next';
|
|||||||
import { jsonRes } from '@/service/response';
|
import { jsonRes } from '@/service/response';
|
||||||
import { connectToDatabase, Chat, Model } from '@/service/mongo';
|
import { connectToDatabase, Chat, Model } from '@/service/mongo';
|
||||||
import type { InitChatResponse } from '@/api/response/chat';
|
import type { InitChatResponse } from '@/api/response/chat';
|
||||||
import { authUser } from '@/service/utils/auth';
|
import { authToken } from '@/service/utils/auth';
|
||||||
import { ChatItemType } from '@/types/chat';
|
import { ChatItemType } from '@/types/chat';
|
||||||
import { authModel } from '@/service/utils/auth';
|
import { authModel } from '@/service/utils/auth';
|
||||||
import mongoose from 'mongoose';
|
import mongoose from 'mongoose';
|
||||||
@@ -12,7 +12,7 @@ import type { ModelSchema } from '@/types/mongoSchema';
|
|||||||
/* 初始化我的聊天框,需要身份验证 */
|
/* 初始化我的聊天框,需要身份验证 */
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
try {
|
try {
|
||||||
const { userId } = await authUser({ req, authToken: true });
|
const userId = await authToken(req);
|
||||||
|
|
||||||
let { modelId, chatId } = req.query as { modelId: '' | string; chatId: '' | string };
|
let { modelId, chatId } = req.query as { modelId: '' | string; chatId: '' | string };
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
const myModel = await Model.findOne({ userId });
|
const myModel = await Model.findOne({ userId });
|
||||||
if (!myModel) {
|
if (!myModel) {
|
||||||
const { _id } = await Model.create({
|
const { _id } = await Model.create({
|
||||||
name: '应用1',
|
name: 'AI助手1',
|
||||||
userId,
|
userId,
|
||||||
status: ModelStatusEnum.running
|
status: ModelStatusEnum.running
|
||||||
});
|
});
|
||||||
@@ -73,8 +73,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
_id: '$content._id',
|
_id: '$content._id',
|
||||||
obj: '$content.obj',
|
obj: '$content.obj',
|
||||||
value: '$content.value',
|
value: '$content.value',
|
||||||
systemPrompt: '$content.systemPrompt',
|
systemPrompt: '$content.systemPrompt'
|
||||||
quoteLen: { $size: { $ifNull: ['$content.quote', []] } }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import { jsonRes } from '@/service/response';
|
import { jsonRes } from '@/service/response';
|
||||||
import { connectToDatabase, Chat } from '@/service/mongo';
|
import { connectToDatabase, Chat } from '@/service/mongo';
|
||||||
import { authUser } from '@/service/utils/auth';
|
import { authToken } from '@/service/utils/auth';
|
||||||
|
|
||||||
/* 获取历史记录 */
|
/* 获取历史记录 */
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
try {
|
try {
|
||||||
const { id } = req.query;
|
const { id } = req.query;
|
||||||
const { userId } = await authUser({ req, authToken: true });
|
const userId = await authToken(req);
|
||||||
|
|
||||||
await connectToDatabase();
|
await connectToDatabase();
|
||||||
|
|
||||||
|
|||||||
@@ -3,38 +3,63 @@ import { jsonRes } from '@/service/response';
|
|||||||
import { ChatItemType } from '@/types/chat';
|
import { ChatItemType } from '@/types/chat';
|
||||||
import { connectToDatabase, Chat } from '@/service/mongo';
|
import { connectToDatabase, Chat } from '@/service/mongo';
|
||||||
import { authModel } from '@/service/utils/auth';
|
import { authModel } from '@/service/utils/auth';
|
||||||
import { authUser } from '@/service/utils/auth';
|
import { authToken } from '@/service/utils/auth';
|
||||||
import mongoose from 'mongoose';
|
import mongoose from 'mongoose';
|
||||||
|
|
||||||
type Props = {
|
|
||||||
newChatId?: string;
|
|
||||||
chatId?: string;
|
|
||||||
modelId: string;
|
|
||||||
prompts: [ChatItemType, ChatItemType];
|
|
||||||
};
|
|
||||||
|
|
||||||
/* 聊天内容存存储 */
|
/* 聊天内容存存储 */
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
try {
|
try {
|
||||||
const { chatId, modelId, prompts, newChatId } = req.body as Props;
|
const { chatId, modelId, prompts, newChatId } = req.body as {
|
||||||
|
newChatId: '' | string;
|
||||||
|
chatId: '' | string;
|
||||||
|
modelId: string;
|
||||||
|
prompts: [ChatItemType, ChatItemType];
|
||||||
|
};
|
||||||
|
|
||||||
if (!prompts) {
|
if (!prompts) {
|
||||||
throw new Error('缺少参数');
|
throw new Error('缺少参数');
|
||||||
}
|
}
|
||||||
|
|
||||||
const { userId } = await authUser({ req, authToken: true });
|
const userId = await authToken(req);
|
||||||
|
|
||||||
const nId = await saveChat({
|
await connectToDatabase();
|
||||||
chatId,
|
|
||||||
modelId,
|
|
||||||
prompts,
|
|
||||||
newChatId,
|
|
||||||
userId
|
|
||||||
});
|
|
||||||
|
|
||||||
jsonRes(res, {
|
const content = prompts.map((item) => ({
|
||||||
data: nId
|
_id: new mongoose.Types.ObjectId(item._id),
|
||||||
});
|
obj: item.obj,
|
||||||
|
value: item.value,
|
||||||
|
systemPrompt: item.systemPrompt
|
||||||
|
}));
|
||||||
|
|
||||||
|
await authModel({ modelId, userId, authOwner: false });
|
||||||
|
|
||||||
|
// 没有 chatId, 创建一个对话
|
||||||
|
if (!chatId) {
|
||||||
|
const { _id } = await Chat.create({
|
||||||
|
_id: newChatId ? new mongoose.Types.ObjectId(newChatId) : undefined,
|
||||||
|
userId,
|
||||||
|
modelId,
|
||||||
|
content,
|
||||||
|
title: content[0].value.slice(0, 20),
|
||||||
|
latestChat: content[1].value
|
||||||
|
});
|
||||||
|
return jsonRes(res, {
|
||||||
|
data: _id
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 已经有记录,追加入库
|
||||||
|
await Chat.findByIdAndUpdate(chatId, {
|
||||||
|
$push: {
|
||||||
|
content: {
|
||||||
|
$each: content
|
||||||
|
}
|
||||||
|
},
|
||||||
|
title: content[0].value.slice(0, 20),
|
||||||
|
latestChat: content[1].value,
|
||||||
|
updateTime: new Date()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
jsonRes(res);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
jsonRes(res, {
|
jsonRes(res, {
|
||||||
code: 500,
|
code: 500,
|
||||||
@@ -42,47 +67,3 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function saveChat({
|
|
||||||
chatId,
|
|
||||||
newChatId,
|
|
||||||
modelId,
|
|
||||||
prompts,
|
|
||||||
userId
|
|
||||||
}: Props & { userId: string }) {
|
|
||||||
await connectToDatabase();
|
|
||||||
await authModel({ modelId, userId, authOwner: false });
|
|
||||||
|
|
||||||
const content = prompts.map((item) => ({
|
|
||||||
_id: item._id ? new mongoose.Types.ObjectId(item._id) : undefined,
|
|
||||||
obj: item.obj,
|
|
||||||
value: item.value,
|
|
||||||
systemPrompt: item.systemPrompt,
|
|
||||||
quote: item.quote || []
|
|
||||||
}));
|
|
||||||
|
|
||||||
// 没有 chatId, 创建一个对话
|
|
||||||
if (!chatId) {
|
|
||||||
const { _id } = await Chat.create({
|
|
||||||
_id: newChatId ? new mongoose.Types.ObjectId(newChatId) : undefined,
|
|
||||||
userId,
|
|
||||||
modelId,
|
|
||||||
content,
|
|
||||||
title: content[0].value.slice(0, 20),
|
|
||||||
latestChat: content[1].value
|
|
||||||
});
|
|
||||||
return _id;
|
|
||||||
} else {
|
|
||||||
// 已经有记录,追加入库
|
|
||||||
await Chat.findByIdAndUpdate(chatId, {
|
|
||||||
$push: {
|
|
||||||
content: {
|
|
||||||
$each: content
|
|
||||||
}
|
|
||||||
},
|
|
||||||
title: content[0].value.slice(0, 20),
|
|
||||||
latestChat: content[1].value,
|
|
||||||
updateTime: new Date()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -4,19 +4,27 @@ import { authShareChat } from '@/service/utils/auth';
|
|||||||
import { modelServiceToolMap } from '@/service/utils/chat';
|
import { modelServiceToolMap } from '@/service/utils/chat';
|
||||||
import { ChatItemSimpleType } from '@/types/chat';
|
import { ChatItemSimpleType } from '@/types/chat';
|
||||||
import { jsonRes } from '@/service/response';
|
import { jsonRes } from '@/service/response';
|
||||||
|
import { PassThrough } from 'stream';
|
||||||
import { ChatModelMap, ModelVectorSearchModeMap } from '@/constants/model';
|
import { ChatModelMap, ModelVectorSearchModeMap } from '@/constants/model';
|
||||||
import { pushChatBill, updateShareChatBill } from '@/service/events/pushBill';
|
import { pushChatBill, updateShareChatBill } from '@/service/events/pushBill';
|
||||||
import { resStreamResponse } from '@/service/utils/chat';
|
import { resStreamResponse } from '@/service/utils/chat';
|
||||||
|
import { searchKb } from '@/service/plugins/searchKb';
|
||||||
import { ChatRoleEnum } from '@/constants/chat';
|
import { ChatRoleEnum } from '@/constants/chat';
|
||||||
import { BillTypeEnum } from '@/constants/user';
|
|
||||||
import { sensitiveCheck } from '@/service/api/text';
|
|
||||||
import { appKbSearch } from '../../openapi/kb/appKbSearch';
|
|
||||||
|
|
||||||
/* 发送提示词 */
|
/* 发送提示词 */
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
|
let step = 0; // step=1 时,表示开始了流响应
|
||||||
|
const stream = new PassThrough();
|
||||||
|
stream.on('error', () => {
|
||||||
|
console.log('error: ', 'stream error');
|
||||||
|
stream.destroy();
|
||||||
|
});
|
||||||
|
res.on('close', () => {
|
||||||
|
stream.destroy();
|
||||||
|
});
|
||||||
res.on('error', () => {
|
res.on('error', () => {
|
||||||
console.log('error: ', 'request error');
|
console.log('error: ', 'request error');
|
||||||
res.end();
|
stream.destroy();
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -34,53 +42,38 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
await connectToDatabase();
|
await connectToDatabase();
|
||||||
let startTime = Date.now();
|
let startTime = Date.now();
|
||||||
|
|
||||||
const { model, userOpenAiKey, systemAuthKey, userId } = await authShareChat({
|
const { model, showModelDetail, userOpenAiKey, systemAuthKey, userId } = await authShareChat({
|
||||||
shareId,
|
shareId,
|
||||||
password
|
password
|
||||||
});
|
});
|
||||||
|
|
||||||
const modelConstantsData = ChatModelMap[model.chat.chatModel];
|
const modelConstantsData = ChatModelMap[model.chat.chatModel];
|
||||||
|
|
||||||
const { code = 200, systemPrompts = [] } = await (async () => {
|
// 使用了知识库搜索
|
||||||
// 使用了知识库搜索
|
if (model.chat.useKb) {
|
||||||
if (model.chat.relatedKbs.length > 0) {
|
const { code, searchPrompts } = await searchKb({
|
||||||
const { code, searchPrompts } = await appKbSearch({
|
userOpenAiKey,
|
||||||
model,
|
prompts,
|
||||||
userId,
|
similarity: ModelVectorSearchModeMap[model.chat.searchMode]?.similarity,
|
||||||
prompts,
|
model,
|
||||||
similarity: ModelVectorSearchModeMap[model.chat.searchMode]?.similarity
|
userId
|
||||||
|
});
|
||||||
|
|
||||||
|
// search result is empty
|
||||||
|
if (code === 201) {
|
||||||
|
return res.send(searchPrompts[0]?.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
prompts.splice(prompts.length - 3, 0, ...searchPrompts);
|
||||||
|
} else {
|
||||||
|
// 没有用知识库搜索,仅用系统提示词
|
||||||
|
model.chat.systemPrompt &&
|
||||||
|
prompts.splice(prompts.length - 3, 0, {
|
||||||
|
obj: ChatRoleEnum.System,
|
||||||
|
value: model.chat.systemPrompt
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
|
||||||
code,
|
|
||||||
systemPrompts: searchPrompts
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (model.chat.systemPrompt) {
|
|
||||||
return {
|
|
||||||
systemPrompts: [
|
|
||||||
{
|
|
||||||
obj: ChatRoleEnum.System,
|
|
||||||
value: model.chat.systemPrompt
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
})();
|
|
||||||
|
|
||||||
// search result is empty
|
|
||||||
if (code === 201) {
|
|
||||||
return res.send(systemPrompts[0]?.value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
prompts.splice(prompts.length - 3, 0, ...systemPrompts);
|
|
||||||
|
|
||||||
// content check
|
|
||||||
await sensitiveCheck({
|
|
||||||
input: [...systemPrompts, prompts[prompts.length - 1]].map((item) => item.value).join('')
|
|
||||||
});
|
|
||||||
|
|
||||||
// 计算温度
|
// 计算温度
|
||||||
const temperature = (modelConstantsData.maxTemperature * (model.chat.temperature / 10)).toFixed(
|
const temperature = (modelConstantsData.maxTemperature * (model.chat.temperature / 10)).toFixed(
|
||||||
2
|
2
|
||||||
@@ -98,40 +91,40 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
|
|
||||||
console.log('api response time:', `${(Date.now() - startTime) / 1000}s`);
|
console.log('api response time:', `${(Date.now() - startTime) / 1000}s`);
|
||||||
|
|
||||||
if (res.closed) return res.end();
|
step = 1;
|
||||||
|
|
||||||
try {
|
const { totalTokens, finishMessages } = await resStreamResponse({
|
||||||
const { totalTokens, finishMessages } = await resStreamResponse({
|
model: model.chat.chatModel,
|
||||||
model: model.chat.chatModel,
|
res,
|
||||||
res,
|
stream,
|
||||||
chatResponse: streamResponse,
|
chatResponse: streamResponse,
|
||||||
prompts
|
prompts,
|
||||||
});
|
systemPrompt: ''
|
||||||
|
|
||||||
res.end();
|
|
||||||
|
|
||||||
/* bill */
|
|
||||||
pushChatBill({
|
|
||||||
isPay: !userOpenAiKey,
|
|
||||||
chatModel: model.chat.chatModel,
|
|
||||||
userId,
|
|
||||||
textLen: finishMessages.map((item) => item.value).join('').length,
|
|
||||||
tokens: totalTokens,
|
|
||||||
type: BillTypeEnum.chat
|
|
||||||
});
|
|
||||||
updateShareChatBill({
|
|
||||||
shareId,
|
|
||||||
tokens: totalTokens
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
res.end();
|
|
||||||
console.log('error,结束', error);
|
|
||||||
}
|
|
||||||
} catch (err: any) {
|
|
||||||
res.status(500);
|
|
||||||
jsonRes(res, {
|
|
||||||
code: 500,
|
|
||||||
error: err
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/* bill */
|
||||||
|
pushChatBill({
|
||||||
|
isPay: !userOpenAiKey,
|
||||||
|
chatModel: model.chat.chatModel,
|
||||||
|
userId,
|
||||||
|
textLen: finishMessages.map((item) => item.value).join('').length,
|
||||||
|
tokens: totalTokens
|
||||||
|
});
|
||||||
|
updateShareChatBill({
|
||||||
|
shareId,
|
||||||
|
tokens: totalTokens
|
||||||
|
});
|
||||||
|
} catch (err: any) {
|
||||||
|
if (step === 1) {
|
||||||
|
// 直接结束流
|
||||||
|
console.log('error,结束');
|
||||||
|
stream.destroy();
|
||||||
|
} else {
|
||||||
|
res.status(500);
|
||||||
|
jsonRes(res, {
|
||||||
|
code: 500,
|
||||||
|
error: err
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import { jsonRes } from '@/service/response';
|
import { jsonRes } from '@/service/response';
|
||||||
import { connectToDatabase, ShareChat } from '@/service/mongo';
|
import { connectToDatabase, ShareChat } from '@/service/mongo';
|
||||||
import { authModel, authUser } from '@/service/utils/auth';
|
import { authModel, authToken } from '@/service/utils/auth';
|
||||||
import type { ShareChatEditType } from '@/types/model';
|
import type { ShareChatEditType } from '@/types/model';
|
||||||
|
|
||||||
/* create a shareChat */
|
/* create a shareChat */
|
||||||
@@ -13,11 +13,10 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
|
|
||||||
await connectToDatabase();
|
await connectToDatabase();
|
||||||
|
|
||||||
const { userId } = await authUser({ req, authToken: true });
|
const userId = await authToken(req);
|
||||||
await authModel({
|
await authModel({
|
||||||
modelId,
|
modelId,
|
||||||
userId,
|
userId
|
||||||
authOwner: false
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const { _id } = await ShareChat.create({
|
const { _id } = await ShareChat.create({
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import { jsonRes } from '@/service/response';
|
import { jsonRes } from '@/service/response';
|
||||||
import { connectToDatabase, ShareChat } from '@/service/mongo';
|
import { connectToDatabase, ShareChat } from '@/service/mongo';
|
||||||
import { authUser } from '@/service/utils/auth';
|
import { authToken } from '@/service/utils/auth';
|
||||||
|
|
||||||
/* delete a shareChat by shareChatId */
|
/* delete a shareChat by shareChatId */
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
@@ -12,7 +12,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
|
|
||||||
await connectToDatabase();
|
await connectToDatabase();
|
||||||
|
|
||||||
const { userId } = await authUser({ req, authToken: true });
|
const userId = await authToken(req);
|
||||||
|
|
||||||
await ShareChat.findOneAndRemove({
|
await ShareChat.findOneAndRemove({
|
||||||
_id: id,
|
_id: id,
|
||||||
|
|||||||
@@ -36,8 +36,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
// 校验使用权限
|
// 校验使用权限
|
||||||
const { model } = await authModel({
|
const { model } = await authModel({
|
||||||
modelId: shareChat.modelId,
|
modelId: shareChat.modelId,
|
||||||
userId: String(shareChat.userId),
|
userId: String(shareChat.userId)
|
||||||
authOwner: false
|
|
||||||
});
|
});
|
||||||
|
|
||||||
jsonRes<InitShareChatResponse>(res, {
|
jsonRes<InitShareChatResponse>(res, {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import { jsonRes } from '@/service/response';
|
import { jsonRes } from '@/service/response';
|
||||||
import { connectToDatabase, ShareChat } from '@/service/mongo';
|
import { connectToDatabase, ShareChat } from '@/service/mongo';
|
||||||
import { authUser } from '@/service/utils/auth';
|
import { authToken } from '@/service/utils/auth';
|
||||||
import { hashPassword } from '@/service/utils/tools';
|
import { hashPassword } from '@/service/utils/tools';
|
||||||
|
|
||||||
/* get shareChat list by modelId */
|
/* get shareChat list by modelId */
|
||||||
@@ -13,7 +13,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
|
|
||||||
await connectToDatabase();
|
await connectToDatabase();
|
||||||
|
|
||||||
const { userId } = await authUser({ req, authToken: true });
|
const userId = await authToken(req);
|
||||||
|
|
||||||
const data = await ShareChat.find({
|
const data = await ShareChat.find({
|
||||||
modelId,
|
modelId,
|
||||||
|
|||||||
@@ -1,51 +0,0 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
|
||||||
import { jsonRes } from '@/service/response';
|
|
||||||
import { connectToDatabase, Chat } from '@/service/mongo';
|
|
||||||
import { authUser } from '@/service/utils/auth';
|
|
||||||
import { Types } from 'mongoose';
|
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|
||||||
try {
|
|
||||||
let { chatId, historyId, quoteId } = req.query as {
|
|
||||||
chatId: string;
|
|
||||||
historyId: string;
|
|
||||||
quoteId: string;
|
|
||||||
};
|
|
||||||
await connectToDatabase();
|
|
||||||
|
|
||||||
const { userId } = await authUser({ req, authToken: true });
|
|
||||||
|
|
||||||
if (!chatId || !historyId || !quoteId) {
|
|
||||||
throw new Error('params is error');
|
|
||||||
}
|
|
||||||
|
|
||||||
await Chat.updateOne(
|
|
||||||
{
|
|
||||||
_id: new Types.ObjectId(chatId),
|
|
||||||
userId: new Types.ObjectId(userId),
|
|
||||||
'content._id': new Types.ObjectId(historyId)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
$set: {
|
|
||||||
'content.$.quote.$[quoteElem].isEdit': true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
arrayFilters: [
|
|
||||||
{
|
|
||||||
'quoteElem.id': quoteId
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
jsonRes(res, {
|
|
||||||
data: ''
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
jsonRes(res, {
|
|
||||||
code: 500,
|
|
||||||
error: err
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import { jsonRes } from '@/service/response';
|
import { jsonRes } from '@/service/response';
|
||||||
import { connectToDatabase } from '@/service/mongo';
|
import { connectToDatabase } from '@/service/mongo';
|
||||||
import { authUser } from '@/service/utils/auth';
|
import { authToken } from '@/service/utils/auth';
|
||||||
import { ModelStatusEnum } from '@/constants/model';
|
import { ModelStatusEnum } from '@/constants/model';
|
||||||
import { Model } from '@/service/models/model';
|
import { Model } from '@/service/models/model';
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 凭证校验
|
// 凭证校验
|
||||||
const { userId } = await authUser({ req, authToken: true });
|
const userId = await authToken(req);
|
||||||
|
|
||||||
await connectToDatabase();
|
await connectToDatabase();
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import { jsonRes } from '@/service/response';
|
import { jsonRes } from '@/service/response';
|
||||||
import { authUser } from '@/service/utils/auth';
|
import { authToken } from '@/service/utils/auth';
|
||||||
import { PgClient } from '@/service/pg';
|
import { PgClient } from '@/service/pg';
|
||||||
import { withNextCors } from '@/service/utils/tools';
|
|
||||||
|
|
||||||
export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||||
try {
|
try {
|
||||||
let { dataId } = req.query as {
|
let { dataId } = req.query as {
|
||||||
dataId: string;
|
dataId: string;
|
||||||
@@ -15,7 +14,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 凭证校验
|
// 凭证校验
|
||||||
const { userId } = await authUser({ req });
|
const userId = await authToken(req);
|
||||||
|
|
||||||
await PgClient.delete('modelData', {
|
await PgClient.delete('modelData', {
|
||||||
where: [['user_id', userId], 'AND', ['id', dataId]]
|
where: [['user_id', userId], 'AND', ['id', dataId]]
|
||||||
@@ -29,4 +28,4 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
|||||||
error: err
|
error: err
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
48
src/pages/api/model/data/exportModelData.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
|
import { jsonRes } from '@/service/response';
|
||||||
|
import { connectToDatabase } from '@/service/mongo';
|
||||||
|
import { authToken } from '@/service/utils/auth';
|
||||||
|
import { PgClient } from '@/service/pg';
|
||||||
|
|
||||||
|
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||||
|
try {
|
||||||
|
let { modelId } = req.query as {
|
||||||
|
modelId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!modelId) {
|
||||||
|
throw new Error('缺少参数');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 凭证校验
|
||||||
|
const userId = await authToken(req);
|
||||||
|
|
||||||
|
await connectToDatabase();
|
||||||
|
|
||||||
|
// 统计数据
|
||||||
|
const count = await PgClient.count('modelData', {
|
||||||
|
where: [['model_id', modelId], 'AND', ['user_id', userId]]
|
||||||
|
});
|
||||||
|
// 从 pg 中获取所有数据
|
||||||
|
const pgData = await PgClient.select<{ q: string; a: string }>('modelData', {
|
||||||
|
where: [['model_id', modelId], 'AND', ['user_id', userId]],
|
||||||
|
fields: ['q', 'a'],
|
||||||
|
order: [{ field: 'id', mode: 'DESC' }],
|
||||||
|
limit: count
|
||||||
|
});
|
||||||
|
|
||||||
|
const data: [string, string][] = pgData.rows.map((item) => [
|
||||||
|
item.q.replace(/\n/g, '\\n'),
|
||||||
|
item.a.replace(/\n/g, '\\n')
|
||||||
|
]);
|
||||||
|
|
||||||
|
jsonRes(res, {
|
||||||
|
data
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
jsonRes(res, {
|
||||||
|
code: 500,
|
||||||
|
error: err
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
34
src/pages/api/model/data/fetchingUrlData.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
|
import { jsonRes } from '@/service/response';
|
||||||
|
import { connectToDatabase } from '@/service/mongo';
|
||||||
|
import { authToken } from '@/service/utils/auth';
|
||||||
|
import axios from 'axios';
|
||||||
|
import { axiosConfig } from '@/service/utils/tools';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取网站的内容
|
||||||
|
*/
|
||||||
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
|
try {
|
||||||
|
const { url } = req.body as { url: string };
|
||||||
|
if (!url) {
|
||||||
|
throw new Error('缺少 url');
|
||||||
|
}
|
||||||
|
await connectToDatabase();
|
||||||
|
|
||||||
|
await authToken(req);
|
||||||
|
|
||||||
|
const data = await axios
|
||||||
|
.get(url, {
|
||||||
|
httpsAgent: axiosConfig().httpsAgent
|
||||||
|
})
|
||||||
|
.then((res) => res.data as string);
|
||||||
|
|
||||||
|
jsonRes(res, { data });
|
||||||
|
} catch (err) {
|
||||||
|
jsonRes(res, {
|
||||||
|
code: 500,
|
||||||
|
error: err
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,40 +1,50 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import { jsonRes } from '@/service/response';
|
import { jsonRes } from '@/service/response';
|
||||||
import { connectToDatabase } from '@/service/mongo';
|
import { connectToDatabase } from '@/service/mongo';
|
||||||
import { authUser } from '@/service/utils/auth';
|
import { authToken } from '@/service/utils/auth';
|
||||||
import { PgClient } from '@/service/pg';
|
import { PgClient } from '@/service/pg';
|
||||||
import type { PgKBDataItemType } from '@/types/pg';
|
import type { PgModelDataItemType } from '@/types/pg';
|
||||||
|
import { authModel } from '@/service/utils/auth';
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||||
try {
|
try {
|
||||||
let {
|
let {
|
||||||
kbId,
|
modelId,
|
||||||
pageNum = 1,
|
pageNum = 1,
|
||||||
pageSize = 10,
|
pageSize = 10,
|
||||||
searchText = ''
|
searchText = ''
|
||||||
} = req.body as {
|
} = req.query as {
|
||||||
kbId: string;
|
modelId: string;
|
||||||
pageNum: number;
|
pageNum: string;
|
||||||
pageSize: number;
|
pageSize: string;
|
||||||
searchText: string;
|
searchText: string;
|
||||||
};
|
};
|
||||||
if (!kbId) {
|
|
||||||
|
pageNum = +pageNum;
|
||||||
|
pageSize = +pageSize;
|
||||||
|
|
||||||
|
if (!modelId) {
|
||||||
throw new Error('缺少参数');
|
throw new Error('缺少参数');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 凭证校验
|
// 凭证校验
|
||||||
const { userId } = await authUser({ req, authToken: true });
|
const userId = await authToken(req);
|
||||||
|
|
||||||
await connectToDatabase();
|
await connectToDatabase();
|
||||||
|
|
||||||
|
const { model } = await authModel({
|
||||||
|
userId,
|
||||||
|
modelId,
|
||||||
|
authOwner: false
|
||||||
|
});
|
||||||
|
|
||||||
const where: any = [
|
const where: any = [
|
||||||
['user_id', userId],
|
...(model.share.isShareDetail ? [] : [['user_id', userId], 'AND']),
|
||||||
'AND',
|
['model_id', modelId],
|
||||||
['kb_id', kbId],
|
|
||||||
...(searchText ? ['AND', `(q LIKE '%${searchText}%' OR a LIKE '%${searchText}%')`] : [])
|
...(searchText ? ['AND', `(q LIKE '%${searchText}%' OR a LIKE '%${searchText}%')`] : [])
|
||||||
];
|
];
|
||||||
|
|
||||||
const searchRes = await PgClient.select<PgKBDataItemType>('modelData', {
|
const searchRes = await PgClient.select<PgModelDataItemType>('modelData', {
|
||||||
fields: ['id', 'q', 'a', 'status'],
|
fields: ['id', 'q', 'a', 'status'],
|
||||||
where,
|
where,
|
||||||
order: [{ field: 'id', mode: 'DESC' }],
|
order: [{ field: 'id', mode: 'DESC' }],
|
||||||
@@ -1,43 +1,43 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import { jsonRes } from '@/service/response';
|
import { jsonRes } from '@/service/response';
|
||||||
import { connectToDatabase, SplitData, Model } from '@/service/mongo';
|
import { connectToDatabase, SplitData, Model } from '@/service/mongo';
|
||||||
import { authUser } from '@/service/utils/auth';
|
import { authToken } from '@/service/utils/auth';
|
||||||
import { ModelDataStatusEnum } from '@/constants/model';
|
import { ModelDataStatusEnum } from '@/constants/model';
|
||||||
import { PgClient } from '@/service/pg';
|
import { PgClient } from '@/service/pg';
|
||||||
|
|
||||||
/* 拆分数据成QA */
|
/* 拆分数据成QA */
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
try {
|
try {
|
||||||
const { kbId } = req.query as { kbId: string };
|
const { modelId } = req.query as { modelId: string };
|
||||||
if (!kbId) {
|
if (!modelId) {
|
||||||
throw new Error('参数错误');
|
throw new Error('参数错误');
|
||||||
}
|
}
|
||||||
await connectToDatabase();
|
await connectToDatabase();
|
||||||
|
|
||||||
const { userId } = await authUser({ req, authToken: true });
|
const userId = await authToken(req);
|
||||||
|
|
||||||
// split queue data
|
// split queue data
|
||||||
const data = await SplitData.find({
|
const data = await SplitData.find({
|
||||||
userId,
|
userId,
|
||||||
kbId,
|
modelId,
|
||||||
textList: { $exists: true, $not: { $size: 0 } }
|
textList: { $exists: true, $not: { $size: 0 } }
|
||||||
});
|
});
|
||||||
|
|
||||||
// embedding queue data
|
// embedding queue data
|
||||||
const embeddingData = await PgClient.count('modelData', {
|
const where: any = [
|
||||||
where: [
|
['user_id', userId],
|
||||||
['user_id', userId],
|
'AND',
|
||||||
'AND',
|
['model_id', modelId],
|
||||||
['kb_id', kbId],
|
'AND',
|
||||||
'AND',
|
['status', ModelDataStatusEnum.waiting]
|
||||||
['status', ModelDataStatusEnum.waiting]
|
];
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
jsonRes(res, {
|
jsonRes(res, {
|
||||||
data: {
|
data: {
|
||||||
splitDataQueue: data.map((item) => item.textList).flat().length,
|
splitDataQueue: data.map((item) => item.textList).flat().length,
|
||||||
embeddingQueue: embeddingData
|
embeddingQueue: await PgClient.count('modelData', {
|
||||||
|
where
|
||||||
|
})
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -1,55 +1,53 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import type { KbDataItemType } from '@/types/plugin';
|
|
||||||
import { jsonRes } from '@/service/response';
|
import { jsonRes } from '@/service/response';
|
||||||
import { connectToDatabase } from '@/service/mongo';
|
import { connectToDatabase } from '@/service/mongo';
|
||||||
import { authUser } from '@/service/utils/auth';
|
import { authToken } from '@/service/utils/auth';
|
||||||
import { generateVector } from '@/service/events/generateVector';
|
import { generateVector } from '@/service/events/generateVector';
|
||||||
import { PgClient, insertKbItem } from '@/service/pg';
|
import { ModelDataStatusEnum } from '@/constants/model';
|
||||||
import { authKb } from '@/service/utils/auth';
|
import { PgClient } from '@/service/pg';
|
||||||
import { withNextCors } from '@/service/utils/tools';
|
import { authModel } from '@/service/utils/auth';
|
||||||
|
|
||||||
export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||||
try {
|
try {
|
||||||
const {
|
const { modelId, data } = req.body as {
|
||||||
kbId,
|
modelId: string;
|
||||||
data,
|
data: string[][];
|
||||||
formatLineBreak = true
|
|
||||||
} = req.body as {
|
|
||||||
kbId: string;
|
|
||||||
formatLineBreak?: boolean;
|
|
||||||
data: { a: KbDataItemType['a']; q: KbDataItemType['q'] }[];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!kbId || !Array.isArray(data)) {
|
if (!modelId || !Array.isArray(data)) {
|
||||||
throw new Error('缺少参数');
|
throw new Error('缺少参数');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 凭证校验
|
||||||
|
const userId = await authToken(req);
|
||||||
|
|
||||||
await connectToDatabase();
|
await connectToDatabase();
|
||||||
|
|
||||||
// 凭证校验
|
// 验证是否是该用户的 model
|
||||||
const { userId } = await authUser({ req });
|
await authModel({
|
||||||
|
|
||||||
await authKb({
|
|
||||||
userId,
|
userId,
|
||||||
kbId
|
modelId
|
||||||
});
|
});
|
||||||
|
|
||||||
// 过滤重复的内容
|
// 去重
|
||||||
const searchRes = await Promise.allSettled(
|
const searchRes = await Promise.allSettled(
|
||||||
data.map(async ({ q, a = '' }) => {
|
data.map(async ([q, a = '']) => {
|
||||||
if (!q) {
|
if (!q) {
|
||||||
return Promise.reject('q为空');
|
return Promise.reject('q为空');
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
if (formatLineBreak) {
|
|
||||||
q = q.replace(/\\n/g, '\n');
|
q = q.replace(/\\n/g, '\n');
|
||||||
a = a.replace(/\\n/g, '\n');
|
a = a.replace(/\\n/g, '\n');
|
||||||
}
|
|
||||||
|
|
||||||
// Exactly the same data, not push
|
|
||||||
try {
|
|
||||||
const count = await PgClient.count('modelData', {
|
const count = await PgClient.count('modelData', {
|
||||||
where: [['user_id', userId], 'AND', ['kb_id', kbId], 'AND', ['q', q], 'AND', ['a', a]]
|
where: [
|
||||||
|
['user_id', userId],
|
||||||
|
'AND',
|
||||||
|
['model_id', modelId],
|
||||||
|
'AND',
|
||||||
|
['q', q],
|
||||||
|
'AND',
|
||||||
|
['a', a]
|
||||||
|
]
|
||||||
});
|
});
|
||||||
if (count > 0) {
|
if (count > 0) {
|
||||||
return Promise.reject('已经存在');
|
return Promise.reject('已经存在');
|
||||||
@@ -63,21 +61,25 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
// 过滤重复的内容
|
||||||
const filterData = searchRes
|
const filterData = searchRes
|
||||||
.filter((item) => item.status === 'fulfilled')
|
.filter((item) => item.status === 'fulfilled')
|
||||||
.map<{ q: string; a: string }>((item: any) => item.value);
|
.map<{ q: string; a: string }>((item: any) => item.value);
|
||||||
|
|
||||||
// 插入记录
|
// 插入 pg
|
||||||
const insertRes = await insertKbItem({
|
const insertRes = await PgClient.insert('modelData', {
|
||||||
userId,
|
values: filterData.map((item) => [
|
||||||
kbId,
|
{ key: 'user_id', value: userId },
|
||||||
data: filterData
|
{ key: 'model_id', value: modelId },
|
||||||
|
{ key: 'q', value: item.q },
|
||||||
|
{ key: 'a', value: item.a },
|
||||||
|
{ key: 'status', value: ModelDataStatusEnum.waiting }
|
||||||
|
])
|
||||||
});
|
});
|
||||||
|
|
||||||
generateVector();
|
generateVector();
|
||||||
|
|
||||||
jsonRes(res, {
|
jsonRes(res, {
|
||||||
message: `共插入 ${insertRes.rowCount} 条数据`,
|
|
||||||
data: insertRes.rowCount
|
data: insertRes.rowCount
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -86,12 +88,4 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
|||||||
error: err
|
error: err
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
export const config = {
|
|
||||||
api: {
|
|
||||||
bodyParser: {
|
|
||||||
sizeLimit: '100mb'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
54
src/pages/api/model/data/pushModelDataInput.ts
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
|
import { jsonRes } from '@/service/response';
|
||||||
|
import { connectToDatabase } from '@/service/mongo';
|
||||||
|
import { authToken } from '@/service/utils/auth';
|
||||||
|
import { ModelDataSchema } from '@/types/mongoSchema';
|
||||||
|
import { generateVector } from '@/service/events/generateVector';
|
||||||
|
import { PgClient } from '@/service/pg';
|
||||||
|
import { authModel } from '@/service/utils/auth';
|
||||||
|
|
||||||
|
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||||
|
try {
|
||||||
|
const { modelId, data } = req.body as {
|
||||||
|
modelId: string;
|
||||||
|
data: { a: ModelDataSchema['a']; q: ModelDataSchema['q'] }[];
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!modelId || !Array.isArray(data)) {
|
||||||
|
throw new Error('缺少参数');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 凭证校验
|
||||||
|
const userId = await authToken(req);
|
||||||
|
|
||||||
|
await connectToDatabase();
|
||||||
|
|
||||||
|
// 验证是否是该用户的 model
|
||||||
|
await authModel({
|
||||||
|
userId,
|
||||||
|
modelId
|
||||||
|
});
|
||||||
|
|
||||||
|
// 插入记录
|
||||||
|
await PgClient.insert('modelData', {
|
||||||
|
values: data.map((item) => [
|
||||||
|
{ key: 'user_id', value: userId },
|
||||||
|
{ key: 'model_id', value: modelId },
|
||||||
|
{ key: 'q', value: item.q },
|
||||||
|
{ key: 'a', value: item.a },
|
||||||
|
{ key: 'status', value: 'waiting' }
|
||||||
|
])
|
||||||
|
});
|
||||||
|
|
||||||
|
generateVector();
|
||||||
|
|
||||||
|
jsonRes(res, {
|
||||||
|
data: 0
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
jsonRes(res, {
|
||||||
|
code: 500,
|
||||||
|
error: err
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +1,11 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import { jsonRes } from '@/service/response';
|
import { jsonRes } from '@/service/response';
|
||||||
import { authUser } from '@/service/utils/auth';
|
import { authToken } from '@/service/utils/auth';
|
||||||
import { ModelDataStatusEnum } from '@/constants/model';
|
import { ModelDataStatusEnum } from '@/constants/model';
|
||||||
import { generateVector } from '@/service/events/generateVector';
|
import { generateVector } from '@/service/events/generateVector';
|
||||||
import { PgClient } from '@/service/pg';
|
import { PgClient } from '@/service/pg';
|
||||||
import { withNextCors } from '@/service/utils/tools';
|
|
||||||
|
|
||||||
export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||||
try {
|
try {
|
||||||
const { dataId, a, q } = req.body as { dataId: string; a: string; q?: string };
|
const { dataId, a, q } = req.body as { dataId: string; a: string; q?: string };
|
||||||
|
|
||||||
@@ -15,9 +14,9 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 凭证校验
|
// 凭证校验
|
||||||
const { userId } = await authUser({ req });
|
const userId = await authToken(req);
|
||||||
|
|
||||||
// 更新 pg 内容.仅修改a,不需要更新向量。
|
// 更新 pg 内容
|
||||||
await PgClient.update('modelData', {
|
await PgClient.update('modelData', {
|
||||||
where: [['id', dataId], 'AND', ['user_id', userId]],
|
where: [['id', dataId], 'AND', ['user_id', userId]],
|
||||||
values: [
|
values: [
|
||||||
@@ -40,4 +39,4 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
|||||||
error: err
|
error: err
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
@@ -1,55 +1,53 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import { jsonRes } from '@/service/response';
|
import { jsonRes } from '@/service/response';
|
||||||
import { connectToDatabase, SplitData } from '@/service/mongo';
|
import { connectToDatabase, SplitData } from '@/service/mongo';
|
||||||
import { authKb, authUser } from '@/service/utils/auth';
|
import { authModel, authToken } from '@/service/utils/auth';
|
||||||
import { generateVector } from '@/service/events/generateVector';
|
import { generateVector } from '@/service/events/generateVector';
|
||||||
import { generateQA } from '@/service/events/generateQA';
|
import { generateQA } from '@/service/events/generateQA';
|
||||||
import { insertKbItem } from '@/service/pg';
|
import { PgClient } from '@/service/pg';
|
||||||
import { SplitTextTypEnum } from '@/constants/plugin';
|
|
||||||
import { withNextCors } from '@/service/utils/tools';
|
|
||||||
|
|
||||||
/* split text */
|
/* 拆分数据成QA */
|
||||||
export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
try {
|
try {
|
||||||
const { chunks, kbId, prompt, mode } = req.body as {
|
const { chunks, modelId, prompt, mode } = req.body as {
|
||||||
kbId: string;
|
modelId: string;
|
||||||
chunks: string[];
|
chunks: string[];
|
||||||
prompt: string;
|
prompt: string;
|
||||||
mode: `${SplitTextTypEnum}`;
|
mode: 'qa' | 'subsection';
|
||||||
};
|
};
|
||||||
if (!chunks || !kbId || !prompt) {
|
if (!chunks || !modelId || !prompt) {
|
||||||
throw new Error('参数错误');
|
throw new Error('参数错误');
|
||||||
}
|
}
|
||||||
await connectToDatabase();
|
await connectToDatabase();
|
||||||
|
|
||||||
const { userId } = await authUser({ req });
|
const userId = await authToken(req);
|
||||||
|
|
||||||
// 验证是否是该用户的 model
|
// 验证是否是该用户的 model
|
||||||
await authKb({
|
await authModel({
|
||||||
kbId,
|
modelId,
|
||||||
userId
|
userId
|
||||||
});
|
});
|
||||||
|
|
||||||
if (mode === SplitTextTypEnum.qa) {
|
if (mode === 'qa') {
|
||||||
// 批量QA拆分插入数据
|
// 批量QA拆分插入数据
|
||||||
await SplitData.create({
|
await SplitData.create({
|
||||||
userId,
|
userId,
|
||||||
kbId,
|
modelId,
|
||||||
textList: chunks,
|
textList: chunks,
|
||||||
prompt
|
prompt
|
||||||
});
|
});
|
||||||
|
|
||||||
generateQA();
|
generateQA();
|
||||||
} else if (mode === SplitTextTypEnum.subsection) {
|
} else if (mode === 'subsection') {
|
||||||
// 待优化,直接调用另一个接口
|
|
||||||
// 插入记录
|
// 插入记录
|
||||||
await insertKbItem({
|
await PgClient.insert('modelData', {
|
||||||
userId,
|
values: chunks.map((item) => [
|
||||||
kbId,
|
{ key: 'user_id', value: userId },
|
||||||
data: chunks.map((item) => ({
|
{ key: 'model_id', value: modelId },
|
||||||
q: item,
|
{ key: 'q', value: item },
|
||||||
a: ''
|
{ key: 'a', value: '' },
|
||||||
}))
|
{ key: 'status', value: 'waiting' }
|
||||||
|
])
|
||||||
});
|
});
|
||||||
|
|
||||||
generateVector();
|
generateVector();
|
||||||
@@ -62,7 +60,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
|||||||
error: err
|
error: err
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
api: {
|
api: {
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import { jsonRes } from '@/service/response';
|
import { jsonRes } from '@/service/response';
|
||||||
import { Chat, Model, connectToDatabase, Collection, ShareChat } from '@/service/mongo';
|
import { Chat, Model, connectToDatabase, Collection, ShareChat } from '@/service/mongo';
|
||||||
import { authUser } from '@/service/utils/auth';
|
import { authToken } from '@/service/utils/auth';
|
||||||
|
import { PgClient } from '@/service/pg';
|
||||||
import { authModel } from '@/service/utils/auth';
|
import { authModel } from '@/service/utils/auth';
|
||||||
|
|
||||||
/* 获取我的模型 */
|
/* 获取我的模型 */
|
||||||
@@ -14,7 +15,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 凭证校验
|
// 凭证校验
|
||||||
const { userId } = await authUser({ req, authToken: true });
|
const userId = await authToken(req);
|
||||||
|
|
||||||
await connectToDatabase();
|
await connectToDatabase();
|
||||||
|
|
||||||
@@ -24,6 +25,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
|||||||
userId
|
userId
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 删除 pg 中所有该模型的数据
|
||||||
|
await PgClient.delete('modelData', {
|
||||||
|
where: [['user_id', userId], 'AND', ['model_id', modelId]]
|
||||||
|
});
|
||||||
|
|
||||||
// 删除对应的聊天
|
// 删除对应的聊天
|
||||||
await Chat.deleteMany({
|
await Chat.deleteMany({
|
||||||
modelId
|
modelId
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import { jsonRes } from '@/service/response';
|
import { jsonRes } from '@/service/response';
|
||||||
import { connectToDatabase } from '@/service/mongo';
|
import { connectToDatabase } from '@/service/mongo';
|
||||||
import { authUser } from '@/service/utils/auth';
|
import { authToken } from '@/service/utils/auth';
|
||||||
import { authModel } from '@/service/utils/auth';
|
import { authModel } from '@/service/utils/auth';
|
||||||
|
|
||||||
/* 获取我的模型 */
|
/* 获取我的模型 */
|
||||||
@@ -14,7 +14,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 凭证校验
|
// 凭证校验
|
||||||
const { userId } = await authUser({ req, authToken: true });
|
const userId = await authToken(req);
|
||||||
|
|
||||||
await connectToDatabase();
|
await connectToDatabase();
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import { jsonRes } from '@/service/response';
|
import { jsonRes } from '@/service/response';
|
||||||
import { connectToDatabase, Collection, Model } from '@/service/mongo';
|
import { connectToDatabase, Collection, Model } from '@/service/mongo';
|
||||||
import { authUser } from '@/service/utils/auth';
|
import { authToken } from '@/service/utils/auth';
|
||||||
import type { ModelListResponse } from '@/api/response/model';
|
import type { ModelListResponse } from '@/api/response/model';
|
||||||
|
|
||||||
/* 获取模型列表 */
|
/* 获取模型列表 */
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||||
try {
|
try {
|
||||||
// 凭证校验
|
// 凭证校验
|
||||||
const { userId } = await authUser({ req, authToken: true });
|
const userId = await authToken(req);
|
||||||
|
|
||||||
await connectToDatabase();
|
await connectToDatabase();
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import { jsonRes } from '@/service/response';
|
import { jsonRes } from '@/service/response';
|
||||||
import { connectToDatabase, Collection, Model } from '@/service/mongo';
|
import { connectToDatabase, Collection, Model } from '@/service/mongo';
|
||||||
import { authUser } from '@/service/utils/auth';
|
import { authToken } from '@/service/utils/auth';
|
||||||
|
|
||||||
/* 模型收藏切换 */
|
/* 模型收藏切换 */
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||||
@@ -12,7 +12,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
|||||||
throw new Error('缺少参数');
|
throw new Error('缺少参数');
|
||||||
}
|
}
|
||||||
// 凭证校验
|
// 凭证校验
|
||||||
const { userId } = await authUser({ req, authToken: true });
|
const userId = await authToken(req);
|
||||||
|
|
||||||
await connectToDatabase();
|
await connectToDatabase();
|
||||||
|
|
||||||
|
|||||||
37
src/pages/api/model/share/getCollection.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
|
import { jsonRes } from '@/service/response';
|
||||||
|
import { connectToDatabase, Collection } from '@/service/mongo';
|
||||||
|
import { authToken } from '@/service/utils/auth';
|
||||||
|
import type { ShareModelItem } from '@/types/model';
|
||||||
|
|
||||||
|
/* 获取模型列表 */
|
||||||
|
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||||
|
try {
|
||||||
|
// 凭证校验
|
||||||
|
const userId = await authToken(req);
|
||||||
|
|
||||||
|
await connectToDatabase();
|
||||||
|
|
||||||
|
// get my collections
|
||||||
|
const collections = await Collection.find({
|
||||||
|
userId
|
||||||
|
}).populate('modelId', '_id avatar name userId share');
|
||||||
|
|
||||||
|
jsonRes<ShareModelItem[]>(res, {
|
||||||
|
data: collections
|
||||||
|
.map((item: any) => ({
|
||||||
|
_id: item.modelId?._id,
|
||||||
|
avatar: item.modelId?.avatar || '/icon/logo.png',
|
||||||
|
name: item.modelId?.name || '',
|
||||||
|
userId: item.modelId?.userId || '',
|
||||||
|
share: item.modelId?.share || {},
|
||||||
|
isCollection: true
|
||||||
|
}))
|
||||||
|
.filter((item) => item.share.isShare)
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
jsonRes(res, {
|
||||||
|
data: []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,8 +3,6 @@ import { jsonRes } from '@/service/response';
|
|||||||
import { connectToDatabase, Collection, Model } from '@/service/mongo';
|
import { connectToDatabase, Collection, Model } from '@/service/mongo';
|
||||||
import type { PagingData } from '@/types';
|
import type { PagingData } from '@/types';
|
||||||
import type { ShareModelItem } from '@/types/model';
|
import type { ShareModelItem } from '@/types/model';
|
||||||
import { parseCookie } from '@/service/utils/auth';
|
|
||||||
import { Types } from 'mongoose';
|
|
||||||
|
|
||||||
/* 获取模型列表 */
|
/* 获取模型列表 */
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||||
@@ -17,14 +15,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
|||||||
|
|
||||||
await connectToDatabase();
|
await connectToDatabase();
|
||||||
|
|
||||||
let userId = '';
|
|
||||||
|
|
||||||
try {
|
|
||||||
userId = await parseCookie(req.headers.cookie);
|
|
||||||
} catch (error) {
|
|
||||||
error;
|
|
||||||
}
|
|
||||||
|
|
||||||
const regex = new RegExp(searchText, 'i');
|
const regex = new RegExp(searchText, 'i');
|
||||||
|
|
||||||
const where = {
|
const where = {
|
||||||
@@ -33,58 +23,15 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
|||||||
{ $or: [{ name: { $regex: regex } }, { 'share.intro': { $regex: regex } }] }
|
{ $or: [{ name: { $regex: regex } }, { 'share.intro': { $regex: regex } }] }
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
const pipeline = [
|
|
||||||
{
|
|
||||||
$match: where
|
|
||||||
},
|
|
||||||
{
|
|
||||||
$lookup: {
|
|
||||||
from: 'collections',
|
|
||||||
let: { modelId: '$_id' },
|
|
||||||
pipeline: [
|
|
||||||
{
|
|
||||||
$match: {
|
|
||||||
$expr: {
|
|
||||||
$and: [
|
|
||||||
{ $eq: ['$modelId', '$$modelId'] },
|
|
||||||
{
|
|
||||||
$eq: ['$userId', userId ? new Types.ObjectId(userId) : new Types.ObjectId()]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
as: 'collections'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
$project: {
|
|
||||||
_id: 1,
|
|
||||||
avatar: { $ifNull: ['$avatar', '/icon/logo.png'] },
|
|
||||||
name: 1,
|
|
||||||
userId: 1,
|
|
||||||
share: 1,
|
|
||||||
isCollection: {
|
|
||||||
$cond: { if: { $gt: [{ $size: '$collections' }, 0] }, then: true, else: false }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
$sort: { 'share.collection': -1 }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
$skip: (pageNum - 1) * pageSize
|
|
||||||
},
|
|
||||||
{
|
|
||||||
$limit: pageSize
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
// 获取被分享的模型
|
// 获取被分享的模型
|
||||||
const [models, total] = await Promise.all([
|
const [models, total] = await Promise.all([
|
||||||
// @ts-ignore
|
Model.find(where, '_id avatar name userId share')
|
||||||
Model.aggregate(pipeline),
|
.sort({
|
||||||
|
'share.collection': -1
|
||||||
|
})
|
||||||
|
.limit(pageSize)
|
||||||
|
.skip((pageNum - 1) * pageSize),
|
||||||
Model.countDocuments(where)
|
Model.countDocuments(where)
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -92,7 +39,14 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
|||||||
data: {
|
data: {
|
||||||
pageNum,
|
pageNum,
|
||||||
pageSize,
|
pageSize,
|
||||||
data: models,
|
data: models.map((item) => ({
|
||||||
|
_id: item._id,
|
||||||
|
avatar: item.avatar || '/icon/logo.png',
|
||||||
|
name: item.name,
|
||||||
|
userId: item.userId,
|
||||||
|
share: item.share,
|
||||||
|
isCollection: false
|
||||||
|
})),
|
||||||
total
|
total
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import { jsonRes } from '@/service/response';
|
import { jsonRes } from '@/service/response';
|
||||||
import { connectToDatabase } from '@/service/mongo';
|
import { connectToDatabase } from '@/service/mongo';
|
||||||
import { authUser } from '@/service/utils/auth';
|
import { authToken } from '@/service/utils/auth';
|
||||||
import { Model } from '@/service/models/model';
|
import { Model } from '@/service/models/model';
|
||||||
import type { ModelUpdateParams } from '@/types/model';
|
import type { ModelUpdateParams } from '@/types/model';
|
||||||
import { authModel } from '@/service/utils/auth';
|
import { authModel } from '@/service/utils/auth';
|
||||||
@@ -9,15 +9,15 @@ import { authModel } from '@/service/utils/auth';
|
|||||||
/* 获取我的模型 */
|
/* 获取我的模型 */
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||||
try {
|
try {
|
||||||
const { name, avatar, chat, share } = req.body as ModelUpdateParams;
|
const { name, avatar, chat, share, security } = req.body as ModelUpdateParams;
|
||||||
const { modelId } = req.query as { modelId: string };
|
const { modelId } = req.query as { modelId: string };
|
||||||
|
|
||||||
if (!name || !chat || !modelId) {
|
if (!name || !chat || !security || !modelId) {
|
||||||
throw new Error('参数错误');
|
throw new Error('参数错误');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 凭证校验
|
// 凭证校验
|
||||||
const { userId } = await authUser({ req, authToken: true });
|
const userId = await authToken(req);
|
||||||
|
|
||||||
await connectToDatabase();
|
await connectToDatabase();
|
||||||
|
|
||||||
@@ -38,7 +38,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
|||||||
chat,
|
chat,
|
||||||
'share.isShare': share.isShare,
|
'share.isShare': share.isShare,
|
||||||
'share.isShareDetail': share.isShareDetail,
|
'share.isShareDetail': share.isShareDetail,
|
||||||
'share.intro': share.intro
|
'share.intro': share.intro,
|
||||||
|
security
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,27 +1,29 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import { connectToDatabase } from '@/service/mongo';
|
import { connectToDatabase } from '@/service/mongo';
|
||||||
import { authUser, authModel, getApiKey } from '@/service/utils/auth';
|
import { authOpenApiKey, authModel, getApiKey } from '@/service/utils/auth';
|
||||||
import { modelServiceToolMap, resStreamResponse } from '@/service/utils/chat';
|
import { modelServiceToolMap, resStreamResponse } from '@/service/utils/chat';
|
||||||
import { ChatItemSimpleType } from '@/types/chat';
|
import { ChatItemSimpleType } from '@/types/chat';
|
||||||
import { jsonRes } from '@/service/response';
|
import { jsonRes } from '@/service/response';
|
||||||
|
import { PassThrough } from 'stream';
|
||||||
import { ChatModelMap, ModelVectorSearchModeMap } from '@/constants/model';
|
import { ChatModelMap, ModelVectorSearchModeMap } from '@/constants/model';
|
||||||
import { pushChatBill } from '@/service/events/pushBill';
|
import { pushChatBill } from '@/service/events/pushBill';
|
||||||
|
import { searchKb } from '@/service/plugins/searchKb';
|
||||||
import { ChatRoleEnum } from '@/constants/chat';
|
import { ChatRoleEnum } from '@/constants/chat';
|
||||||
import { withNextCors } from '@/service/utils/tools';
|
|
||||||
import { BillTypeEnum } from '@/constants/user';
|
|
||||||
import { sensitiveCheck } from '@/service/api/text';
|
|
||||||
import { NEW_CHATID_HEADER } from '@/constants/chat';
|
|
||||||
import { Types } from 'mongoose';
|
|
||||||
import { appKbSearch } from '../kb/appKbSearch';
|
|
||||||
|
|
||||||
/* 发送提示词 */
|
/* 发送提示词 */
|
||||||
export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
|
let step = 0; // step=1时,表示开始了流响应
|
||||||
|
const stream = new PassThrough();
|
||||||
|
stream.on('error', () => {
|
||||||
|
console.log('error: ', 'stream error');
|
||||||
|
stream.destroy();
|
||||||
|
});
|
||||||
res.on('close', () => {
|
res.on('close', () => {
|
||||||
res.end();
|
stream.destroy();
|
||||||
});
|
});
|
||||||
res.on('error', () => {
|
res.on('error', () => {
|
||||||
console.log('error: ', 'request error');
|
console.log('error: ', 'request error');
|
||||||
res.end();
|
stream.destroy();
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -51,7 +53,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
|||||||
let startTime = Date.now();
|
let startTime = Date.now();
|
||||||
|
|
||||||
/* 凭证校验 */
|
/* 凭证校验 */
|
||||||
const { userId } = await authUser({ req });
|
const { userId } = await authOpenApiKey(req);
|
||||||
|
|
||||||
const { model } = await authModel({
|
const { model } = await authModel({
|
||||||
userId,
|
userId,
|
||||||
@@ -67,56 +69,36 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
|||||||
|
|
||||||
const modelConstantsData = ChatModelMap[model.chat.chatModel];
|
const modelConstantsData = ChatModelMap[model.chat.chatModel];
|
||||||
|
|
||||||
let systemPrompts: {
|
|
||||||
obj: ChatRoleEnum;
|
|
||||||
value: string;
|
|
||||||
}[] = [];
|
|
||||||
|
|
||||||
// 使用了知识库搜索
|
// 使用了知识库搜索
|
||||||
if (model.chat.relatedKbs.length > 0) {
|
if (model.chat.useKb) {
|
||||||
const { code, searchPrompts } = await appKbSearch({
|
const similarity = ModelVectorSearchModeMap[model.chat.searchMode]?.similarity || 0.22;
|
||||||
|
|
||||||
|
const { code, searchPrompts } = await searchKb({
|
||||||
prompts,
|
prompts,
|
||||||
similarity: ModelVectorSearchModeMap[model.chat.searchMode]?.similarity,
|
similarity,
|
||||||
model,
|
model,
|
||||||
userId
|
userId
|
||||||
});
|
});
|
||||||
|
|
||||||
// search result is empty
|
// search result is empty
|
||||||
if (code === 201) {
|
if (code === 201) {
|
||||||
return isStream
|
return res.send(searchPrompts[0]?.value);
|
||||||
? res.send(searchPrompts[0]?.value)
|
|
||||||
: jsonRes(res, {
|
|
||||||
data: searchPrompts[0]?.value,
|
|
||||||
message: searchPrompts[0]?.value
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
prompts.splice(prompts.length - 1, 0, ...searchPrompts);
|
||||||
systemPrompts = searchPrompts;
|
} else {
|
||||||
} else if (model.chat.systemPrompt) {
|
// 没有用知识库搜索,仅用系统提示词
|
||||||
systemPrompts = [
|
model.chat.systemPrompt &&
|
||||||
{
|
prompts.splice(prompts.length - 1, 0, {
|
||||||
obj: ChatRoleEnum.System,
|
obj: ChatRoleEnum.System,
|
||||||
value: model.chat.systemPrompt
|
value: model.chat.systemPrompt
|
||||||
}
|
});
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
prompts.splice(prompts.length - 3, 0, ...systemPrompts);
|
|
||||||
|
|
||||||
// content check
|
|
||||||
await sensitiveCheck({
|
|
||||||
input: [...systemPrompts, prompts[prompts.length - 1]].map((item) => item.value).join('')
|
|
||||||
});
|
|
||||||
|
|
||||||
// 计算温度
|
// 计算温度
|
||||||
const temperature = (modelConstantsData.maxTemperature * (model.chat.temperature / 10)).toFixed(
|
const temperature = (modelConstantsData.maxTemperature * (model.chat.temperature / 10)).toFixed(
|
||||||
2
|
2
|
||||||
);
|
);
|
||||||
|
|
||||||
// get conversationId. create a newId if it is null
|
|
||||||
const conversationId = chatId || String(new Types.ObjectId());
|
|
||||||
!chatId && res?.setHeader(NEW_CHATID_HEADER, conversationId);
|
|
||||||
|
|
||||||
// 发出请求
|
// 发出请求
|
||||||
const { streamResponse, responseMessages, responseText, totalTokens } =
|
const { streamResponse, responseMessages, responseText, totalTokens } =
|
||||||
await modelServiceToolMap[model.chat.chatModel].chatCompletion({
|
await modelServiceToolMap[model.chat.chatModel].chatCompletion({
|
||||||
@@ -125,55 +107,50 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
|||||||
messages: prompts,
|
messages: prompts,
|
||||||
stream: isStream,
|
stream: isStream,
|
||||||
res,
|
res,
|
||||||
chatId: conversationId
|
chatId
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('api response time:', `${(Date.now() - startTime) / 1000}s`);
|
console.log('api response time:', `${(Date.now() - startTime) / 1000}s`);
|
||||||
|
|
||||||
if (res.closed) return res.end();
|
let textLen = 0;
|
||||||
|
let tokens = totalTokens;
|
||||||
|
|
||||||
const { textLen = 0, tokens = totalTokens } = await (async () => {
|
if (isStream) {
|
||||||
if (isStream) {
|
step = 1;
|
||||||
try {
|
const { finishMessages, totalTokens } = await resStreamResponse({
|
||||||
const { finishMessages, totalTokens } = await resStreamResponse({
|
model: model.chat.chatModel,
|
||||||
model: model.chat.chatModel,
|
res,
|
||||||
res,
|
stream,
|
||||||
chatResponse: streamResponse,
|
chatResponse: streamResponse,
|
||||||
prompts
|
prompts
|
||||||
});
|
});
|
||||||
res.end();
|
textLen = finishMessages.map((item) => item.value).join('').length;
|
||||||
return {
|
tokens = totalTokens;
|
||||||
textLen: finishMessages.map((item) => item.value).join('').length,
|
} else {
|
||||||
tokens: totalTokens
|
textLen = responseMessages.map((item) => item.value).join('').length;
|
||||||
};
|
jsonRes(res, {
|
||||||
} catch (error) {
|
data: responseText
|
||||||
res.end();
|
});
|
||||||
console.log('error,结束', error);
|
}
|
||||||
}
|
|
||||||
} else {
|
|
||||||
jsonRes(res, {
|
|
||||||
data: responseText
|
|
||||||
});
|
|
||||||
return {
|
|
||||||
textLen: responseMessages.map((item) => item.value).join('').length
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
})();
|
|
||||||
|
|
||||||
pushChatBill({
|
pushChatBill({
|
||||||
isPay: true,
|
isPay: true,
|
||||||
chatModel: model.chat.chatModel,
|
chatModel: model.chat.chatModel,
|
||||||
userId,
|
userId,
|
||||||
textLen,
|
textLen,
|
||||||
tokens,
|
tokens
|
||||||
type: BillTypeEnum.openapiChat
|
|
||||||
});
|
});
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
res.status(500);
|
if (step === 1) {
|
||||||
jsonRes(res, {
|
// 直接结束流
|
||||||
code: 500,
|
console.log('error,结束');
|
||||||
error: err
|
stream.destroy();
|
||||||
});
|
} else {
|
||||||
|
res.status(500);
|
||||||
|
jsonRes(res, {
|
||||||
|
code: 500,
|
||||||
|
error: err
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|||||||
194
src/pages/api/openapi/chat/lafGpt.ts
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
|
import { connectToDatabase } from '@/service/mongo';
|
||||||
|
import { authOpenApiKey, authModel, getApiKey } from '@/service/utils/auth';
|
||||||
|
import { resStreamResponse, modelServiceToolMap } from '@/service/utils/chat';
|
||||||
|
import { ChatItemSimpleType } from '@/types/chat';
|
||||||
|
import { jsonRes } from '@/service/response';
|
||||||
|
import { PassThrough } from 'stream';
|
||||||
|
import { ChatModelMap, ModelVectorSearchModeMap } from '@/constants/model';
|
||||||
|
import { pushChatBill } from '@/service/events/pushBill';
|
||||||
|
import { searchKb } from '@/service/plugins/searchKb';
|
||||||
|
import { ChatRoleEnum } from '@/constants/chat';
|
||||||
|
|
||||||
|
/* 发送提示词 */
|
||||||
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
|
let step = 0; // step=1时,表示开始了流响应
|
||||||
|
const stream = new PassThrough();
|
||||||
|
stream.on('error', () => {
|
||||||
|
console.log('error: ', 'stream error');
|
||||||
|
stream.destroy();
|
||||||
|
});
|
||||||
|
res.on('close', () => {
|
||||||
|
stream.destroy();
|
||||||
|
});
|
||||||
|
res.on('error', () => {
|
||||||
|
console.log('error: ', 'request error');
|
||||||
|
stream.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const {
|
||||||
|
prompt,
|
||||||
|
modelId,
|
||||||
|
isStream = true
|
||||||
|
} = req.body as {
|
||||||
|
prompt: ChatItemSimpleType;
|
||||||
|
modelId: string;
|
||||||
|
isStream: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!prompt || !modelId) {
|
||||||
|
throw new Error('缺少参数');
|
||||||
|
}
|
||||||
|
|
||||||
|
await connectToDatabase();
|
||||||
|
let startTime = Date.now();
|
||||||
|
|
||||||
|
/* 凭证校验 */
|
||||||
|
const { userId } = await authOpenApiKey(req);
|
||||||
|
|
||||||
|
/* 查找数据库里的模型信息 */
|
||||||
|
const { model } = await authModel({
|
||||||
|
userId,
|
||||||
|
modelId
|
||||||
|
});
|
||||||
|
|
||||||
|
/* get api key */
|
||||||
|
const { systemAuthKey: apiKey } = await getApiKey({
|
||||||
|
model: model.chat.chatModel,
|
||||||
|
userId,
|
||||||
|
mustPay: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const modelConstantsData = ChatModelMap[model.chat.chatModel];
|
||||||
|
|
||||||
|
console.log('laf gpt start');
|
||||||
|
|
||||||
|
// 请求一次 chatgpt 拆解需求
|
||||||
|
const { responseText: resolveText, totalTokens: resolveTokens } = await modelServiceToolMap[
|
||||||
|
model.chat.chatModel
|
||||||
|
].chatCompletion({
|
||||||
|
apiKey,
|
||||||
|
temperature: 0,
|
||||||
|
messages: [
|
||||||
|
{
|
||||||
|
obj: ChatRoleEnum.System,
|
||||||
|
value: `服务端逻辑生成器.根据用户输入的需求,拆解成 laf 云函数实现的步骤,只返回步骤,按格式返回步骤: 1.\n2.\n3.\n ......
|
||||||
|
下面是一些例子:
|
||||||
|
一个 hello world 例子
|
||||||
|
1. 返回字符串: "hello world"
|
||||||
|
|
||||||
|
计算圆的面积
|
||||||
|
1. 从 body 中获取半径 radius.
|
||||||
|
2. 校验 radius 是否为有效的数字.
|
||||||
|
3. 计算圆的面积.
|
||||||
|
4. 返回圆的面积: {area}
|
||||||
|
|
||||||
|
实现一个手机号发生注册验证码方法.
|
||||||
|
1. 从 query 中获取 phone.
|
||||||
|
2. 校验手机号格式是否正确,不正确则返回错误原因:手机号格式错误.
|
||||||
|
3. 给 phone 发送一个短信验证码,验证码长度为6位字符串,内容为:你正在注册laf,验证码为:code.
|
||||||
|
4. 数据库添加数据,表为"codes",内容为 {phone, code}.
|
||||||
|
|
||||||
|
实现一个云函数,使用手机号注册账号,需要验证手机验证码.
|
||||||
|
1. 从 body 中获取 phone 和 code.
|
||||||
|
2. 校验手机号格式是否正确,不正确则返回错误原因:手机号格式错误.
|
||||||
|
2. 获取数据库数据,表为"codes",查找是否有符合 phone, code 等于body参数的记录,没有的话返回错误原因:验证码不正确.
|
||||||
|
4. 添加数据库数据,表为"users" ,内容为{phone, code, createTime}.
|
||||||
|
5. 删除数据库数据,删除 code 记录.
|
||||||
|
6. 返回新建用户的Id: return {userId}
|
||||||
|
|
||||||
|
更新博客记录。传入blogId,blogText,tags,还需要记录更新的时间.
|
||||||
|
1. 从 body 中获取 blogId,blogText 和 tags.
|
||||||
|
2. 校验 blogId 是否为空,为空则返回错误原因:博客ID不能为空.
|
||||||
|
3. 校验 blogText 是否为空,为空则返回错误原因:博客内容不能为空.
|
||||||
|
4. 校验 tags 是否为数组,不是则返回错误原因:标签必须为数组.
|
||||||
|
5. 获取当前时间,记录为 updateTime.
|
||||||
|
6. 更新数据库数据,表为"blogs",更新符合 blogId 的记录的内容为{blogText, tags, updateTime}.
|
||||||
|
7. 返回结果 "更新博客记录成功"`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
obj: ChatRoleEnum.Human,
|
||||||
|
value: prompt.value
|
||||||
|
}
|
||||||
|
],
|
||||||
|
stream: false
|
||||||
|
});
|
||||||
|
|
||||||
|
prompt.value += ` ${resolveText}`;
|
||||||
|
console.log('prompt resolve success, time:', `${(Date.now() - startTime) / 1000}s`);
|
||||||
|
|
||||||
|
// 读取对话内容
|
||||||
|
const prompts = [prompt];
|
||||||
|
|
||||||
|
// 获取向量匹配到的提示词
|
||||||
|
const { searchPrompts } = await searchKb({
|
||||||
|
similarity: ModelVectorSearchModeMap[model.chat.searchMode]?.similarity,
|
||||||
|
prompts,
|
||||||
|
model,
|
||||||
|
userId
|
||||||
|
});
|
||||||
|
|
||||||
|
prompts.splice(prompts.length - 1, 0, ...searchPrompts);
|
||||||
|
|
||||||
|
// 计算温度
|
||||||
|
const temperature = (modelConstantsData.maxTemperature * (model.chat.temperature / 10)).toFixed(
|
||||||
|
2
|
||||||
|
);
|
||||||
|
|
||||||
|
// 发出请求
|
||||||
|
const { streamResponse, responseMessages, responseText, totalTokens } =
|
||||||
|
await modelServiceToolMap[model.chat.chatModel].chatCompletion({
|
||||||
|
apiKey,
|
||||||
|
temperature: +temperature,
|
||||||
|
messages: prompts,
|
||||||
|
stream: isStream
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('api response time:', `${(Date.now() - startTime) / 1000}s`);
|
||||||
|
|
||||||
|
let textLen = resolveText.length;
|
||||||
|
let tokens = resolveTokens;
|
||||||
|
|
||||||
|
if (isStream) {
|
||||||
|
step = 1;
|
||||||
|
const { finishMessages, totalTokens } = await resStreamResponse({
|
||||||
|
model: model.chat.chatModel,
|
||||||
|
res,
|
||||||
|
stream,
|
||||||
|
chatResponse: streamResponse,
|
||||||
|
prompts
|
||||||
|
});
|
||||||
|
textLen += finishMessages.map((item) => item.value).join('').length;
|
||||||
|
tokens += totalTokens;
|
||||||
|
} else {
|
||||||
|
textLen += responseMessages.map((item) => item.value).join('').length;
|
||||||
|
tokens += totalTokens;
|
||||||
|
jsonRes(res, {
|
||||||
|
data: responseText
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('laf gpt done. time:', `${(Date.now() - startTime) / 1000}s`);
|
||||||
|
|
||||||
|
pushChatBill({
|
||||||
|
isPay: true,
|
||||||
|
chatModel: model.chat.chatModel,
|
||||||
|
userId,
|
||||||
|
textLen,
|
||||||
|
tokens
|
||||||
|
});
|
||||||
|
} catch (err: any) {
|
||||||
|
if (step === 1) {
|
||||||
|
// 直接结束流
|
||||||
|
console.log('error,结束');
|
||||||
|
stream.destroy();
|
||||||
|
} else {
|
||||||
|
res.status(500);
|
||||||
|
jsonRes(res, {
|
||||||
|
code: 500,
|
||||||
|
error: err
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import { jsonRes } from '@/service/response';
|
import { jsonRes } from '@/service/response';
|
||||||
import { connectToDatabase, OpenApi } from '@/service/mongo';
|
import { connectToDatabase, OpenApi } from '@/service/mongo';
|
||||||
import { authUser } from '@/service/utils/auth';
|
import { authToken } from '@/service/utils/auth';
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
try {
|
try {
|
||||||
@@ -12,7 +12,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
throw new Error('缺少参数');
|
throw new Error('缺少参数');
|
||||||
}
|
}
|
||||||
|
|
||||||
const { userId } = await authUser({ req, authToken: true });
|
const userId = await authToken(req);
|
||||||
|
|
||||||
await connectToDatabase();
|
await connectToDatabase();
|
||||||
|
|
||||||
|
|||||||
@@ -2,12 +2,12 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import { jsonRes } from '@/service/response';
|
import { jsonRes } from '@/service/response';
|
||||||
import { connectToDatabase, OpenApi } from '@/service/mongo';
|
import { connectToDatabase, OpenApi } from '@/service/mongo';
|
||||||
import { authUser } from '@/service/utils/auth';
|
import { authToken } from '@/service/utils/auth';
|
||||||
import { UserOpenApiKey } from '@/types/openapi';
|
import { UserOpenApiKey } from '@/types/openapi';
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
try {
|
try {
|
||||||
const { userId } = await authUser({ req, authToken: true });
|
const userId = await authToken(req);
|
||||||
|
|
||||||
await connectToDatabase();
|
await connectToDatabase();
|
||||||
|
|
||||||
|
|||||||
@@ -1,221 +0,0 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
|
||||||
import { jsonRes } from '@/service/response';
|
|
||||||
import { authUser } from '@/service/utils/auth';
|
|
||||||
import { PgClient } from '@/service/pg';
|
|
||||||
import { withNextCors } from '@/service/utils/tools';
|
|
||||||
import type { ChatItemSimpleType } from '@/types/chat';
|
|
||||||
import type { ModelSchema } from '@/types/mongoSchema';
|
|
||||||
import { ModelVectorSearchModeEnum } from '@/constants/model';
|
|
||||||
import { authModel } from '@/service/utils/auth';
|
|
||||||
import { ChatModelMap } from '@/constants/model';
|
|
||||||
import { ChatRoleEnum } from '@/constants/chat';
|
|
||||||
import { openaiEmbedding } from '../plugin/openaiEmbedding';
|
|
||||||
import { ModelDataStatusEnum } from '@/constants/model';
|
|
||||||
import { modelToolMap } from '@/utils/plugin';
|
|
||||||
|
|
||||||
export type QuoteItemType = { id: string; q: string; a: string; isEdit: boolean };
|
|
||||||
type Props = {
|
|
||||||
prompts: ChatItemSimpleType[];
|
|
||||||
similarity: number;
|
|
||||||
appId: string;
|
|
||||||
};
|
|
||||||
type Response = {
|
|
||||||
code: 200 | 201;
|
|
||||||
rawSearch: QuoteItemType[];
|
|
||||||
guidePrompt: string;
|
|
||||||
searchPrompts: {
|
|
||||||
obj: ChatRoleEnum;
|
|
||||||
value: string;
|
|
||||||
}[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
|
||||||
try {
|
|
||||||
const { userId } = await authUser({ req });
|
|
||||||
|
|
||||||
if (!userId) {
|
|
||||||
throw new Error('userId is empty');
|
|
||||||
}
|
|
||||||
|
|
||||||
const { prompts, similarity, appId } = req.body as Props;
|
|
||||||
|
|
||||||
if (!similarity || !Array.isArray(prompts) || !appId) {
|
|
||||||
throw new Error('params is error');
|
|
||||||
}
|
|
||||||
|
|
||||||
// auth model
|
|
||||||
const { model } = await authModel({
|
|
||||||
modelId: appId,
|
|
||||||
userId
|
|
||||||
});
|
|
||||||
|
|
||||||
const result = await appKbSearch({
|
|
||||||
userId,
|
|
||||||
prompts,
|
|
||||||
similarity,
|
|
||||||
model
|
|
||||||
});
|
|
||||||
|
|
||||||
jsonRes<Response>(res, {
|
|
||||||
data: result
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
jsonRes(res, {
|
|
||||||
code: 500,
|
|
||||||
error: err
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export async function appKbSearch({
|
|
||||||
model,
|
|
||||||
userId,
|
|
||||||
prompts,
|
|
||||||
similarity
|
|
||||||
}: {
|
|
||||||
userId: string;
|
|
||||||
prompts: ChatItemSimpleType[];
|
|
||||||
similarity: number;
|
|
||||||
model: ModelSchema;
|
|
||||||
}): Promise<Response> {
|
|
||||||
const modelConstantsData = ChatModelMap[model.chat.chatModel];
|
|
||||||
|
|
||||||
// search two times.
|
|
||||||
const userPrompts = prompts.filter((item) => item.obj === 'Human');
|
|
||||||
|
|
||||||
const input: string[] = [
|
|
||||||
userPrompts[userPrompts.length - 1].value,
|
|
||||||
userPrompts[userPrompts.length - 2]?.value
|
|
||||||
].filter((item) => item);
|
|
||||||
|
|
||||||
// get vector
|
|
||||||
const promptVectors = await openaiEmbedding({
|
|
||||||
userId,
|
|
||||||
input
|
|
||||||
});
|
|
||||||
|
|
||||||
// search kb
|
|
||||||
const searchRes = await Promise.all(
|
|
||||||
promptVectors.map((promptVector) =>
|
|
||||||
PgClient.select<QuoteItemType>('modelData', {
|
|
||||||
fields: ['id', 'q', 'a'],
|
|
||||||
where: [
|
|
||||||
['status', ModelDataStatusEnum.ready],
|
|
||||||
'AND',
|
|
||||||
`kb_id IN (${model.chat.relatedKbs.map((item) => `'${item}'`).join(',')})`,
|
|
||||||
'AND',
|
|
||||||
`vector <=> '[${promptVector}]' < ${similarity}`
|
|
||||||
],
|
|
||||||
order: [{ field: 'vector', mode: `<=> '[${promptVector}]'` }],
|
|
||||||
limit: promptVectors.length === 1 ? 15 : 10
|
|
||||||
}).then((res) => res.rows)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
// filter same search result
|
|
||||||
const idSet = new Set<string>();
|
|
||||||
const filterSearch = searchRes.map((search) =>
|
|
||||||
search.filter((item) => {
|
|
||||||
if (idSet.has(item.id)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
idSet.add(item.id);
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
// slice search result by rate.
|
|
||||||
const sliceRateMap: Record<number, number[]> = {
|
|
||||||
1: [1],
|
|
||||||
2: [0.7, 0.3]
|
|
||||||
};
|
|
||||||
const sliceRate = sliceRateMap[searchRes.length] || sliceRateMap[0];
|
|
||||||
// 计算固定提示词的 token 数量
|
|
||||||
|
|
||||||
const guidePrompt = model.chat.systemPrompt // user system prompt
|
|
||||||
? {
|
|
||||||
obj: ChatRoleEnum.System,
|
|
||||||
value: model.chat.systemPrompt
|
|
||||||
}
|
|
||||||
: model.chat.searchMode === ModelVectorSearchModeEnum.noContext
|
|
||||||
? {
|
|
||||||
obj: ChatRoleEnum.System,
|
|
||||||
value: `知识库是关于"${model.name}"的内容,根据知识库内容回答问题.`
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
obj: ChatRoleEnum.System,
|
|
||||||
value: `玩一个问答游戏,规则为:
|
|
||||||
1.你完全忘记你已有的知识
|
|
||||||
2.你只回答关于"${model.name}"的问题
|
|
||||||
3.你只从知识库中选择内容进行回答
|
|
||||||
4.如果问题不在知识库中,你会回答:"我不知道。"
|
|
||||||
请务必遵守规则`
|
|
||||||
};
|
|
||||||
|
|
||||||
const fixedSystemTokens = modelToolMap[model.chat.chatModel].countTokens({
|
|
||||||
messages: [guidePrompt]
|
|
||||||
});
|
|
||||||
const maxTokens = modelConstantsData.systemMaxToken - fixedSystemTokens;
|
|
||||||
const sliceResult = sliceRate.map((rate, i) =>
|
|
||||||
modelToolMap[model.chat.chatModel]
|
|
||||||
.tokenSlice({
|
|
||||||
maxToken: Math.round(maxTokens * rate),
|
|
||||||
messages: filterSearch[i].map((item) => ({
|
|
||||||
obj: ChatRoleEnum.System,
|
|
||||||
value: `${item.q}\n${item.a}`
|
|
||||||
}))
|
|
||||||
})
|
|
||||||
.map((item) => item.value)
|
|
||||||
);
|
|
||||||
|
|
||||||
// slice filterSearch
|
|
||||||
const sliceSearch = filterSearch.map((item, i) => item.slice(0, sliceResult[i].length)).flat();
|
|
||||||
|
|
||||||
// system prompt
|
|
||||||
const systemPrompt = sliceResult.flat().join('\n').trim();
|
|
||||||
|
|
||||||
/* 高相似度+不回复 */
|
|
||||||
if (!systemPrompt && model.chat.searchMode === ModelVectorSearchModeEnum.hightSimilarity) {
|
|
||||||
return {
|
|
||||||
code: 201,
|
|
||||||
rawSearch: [],
|
|
||||||
guidePrompt: '',
|
|
||||||
searchPrompts: [
|
|
||||||
{
|
|
||||||
obj: ChatRoleEnum.System,
|
|
||||||
value: '对不起,你的问题不在知识库中。'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
/* 高相似度+无上下文,不添加额外知识,仅用系统提示词 */
|
|
||||||
if (!systemPrompt && model.chat.searchMode === ModelVectorSearchModeEnum.noContext) {
|
|
||||||
return {
|
|
||||||
code: 200,
|
|
||||||
rawSearch: [],
|
|
||||||
guidePrompt: model.chat.systemPrompt || '',
|
|
||||||
searchPrompts: model.chat.systemPrompt
|
|
||||||
? [
|
|
||||||
{
|
|
||||||
obj: ChatRoleEnum.System,
|
|
||||||
value: model.chat.systemPrompt
|
|
||||||
}
|
|
||||||
]
|
|
||||||
: []
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
code: 200,
|
|
||||||
rawSearch: sliceSearch,
|
|
||||||
guidePrompt: guidePrompt.value || '',
|
|
||||||
searchPrompts: [
|
|
||||||
{
|
|
||||||
obj: ChatRoleEnum.System,
|
|
||||||
value: `知识库:${systemPrompt}`
|
|
||||||
},
|
|
||||||
guidePrompt
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
|
||||||
import { jsonRes } from '@/service/response';
|
|
||||||
import { authUser } from '@/service/utils/auth';
|
|
||||||
import { PgClient } from '@/service/pg';
|
|
||||||
import { withNextCors } from '@/service/utils/tools';
|
|
||||||
import { getApiKey } from '@/service/utils/auth';
|
|
||||||
import { getOpenAIApi } from '@/service/utils/chat/openai';
|
|
||||||
import { embeddingModel } from '@/constants/model';
|
|
||||||
import { axiosConfig } from '@/service/utils/tools';
|
|
||||||
import { pushGenerateVectorBill } from '@/service/events/pushBill';
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
input: string[];
|
|
||||||
};
|
|
||||||
type Response = number[][];
|
|
||||||
|
|
||||||
export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
|
||||||
try {
|
|
||||||
const { userId } = await authUser({ req });
|
|
||||||
let { input } = req.query as Props;
|
|
||||||
|
|
||||||
if (!Array.isArray(input)) {
|
|
||||||
throw new Error('缺少参数');
|
|
||||||
}
|
|
||||||
|
|
||||||
jsonRes<Response>(res, {
|
|
||||||
data: await openaiEmbedding({ userId, input, mustPay: true })
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
jsonRes(res, {
|
|
||||||
code: 500,
|
|
||||||
error: err
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export async function openaiEmbedding({
|
|
||||||
userId,
|
|
||||||
input,
|
|
||||||
mustPay = false
|
|
||||||
}: { userId: string; mustPay?: boolean } & Props) {
|
|
||||||
const { userOpenAiKey, systemAuthKey } = await getApiKey({
|
|
||||||
model: 'gpt-3.5-turbo',
|
|
||||||
userId,
|
|
||||||
mustPay
|
|
||||||
});
|
|
||||||
|
|
||||||
// 获取 chatAPI
|
|
||||||
const chatAPI = getOpenAIApi();
|
|
||||||
|
|
||||||
// 把输入的内容转成向量
|
|
||||||
const result = await chatAPI
|
|
||||||
.createEmbedding(
|
|
||||||
{
|
|
||||||
model: embeddingModel,
|
|
||||||
input
|
|
||||||
},
|
|
||||||
{
|
|
||||||
timeout: 60000,
|
|
||||||
...axiosConfig(userOpenAiKey || systemAuthKey)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then((res) => ({
|
|
||||||
tokenLen: res.data.usage.total_tokens || 0,
|
|
||||||
vectors: res.data.data.map((item) => item.embedding)
|
|
||||||
}));
|
|
||||||
|
|
||||||
pushGenerateVectorBill({
|
|
||||||
isPay: !userOpenAiKey,
|
|
||||||
userId,
|
|
||||||
text: input.join(''),
|
|
||||||
tokenLen: result.tokenLen
|
|
||||||
});
|
|
||||||
|
|
||||||
return result.vectors;
|
|
||||||
}
|
|
||||||
@@ -2,13 +2,13 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import { jsonRes } from '@/service/response';
|
import { jsonRes } from '@/service/response';
|
||||||
import { connectToDatabase, OpenApi } from '@/service/mongo';
|
import { connectToDatabase, OpenApi } from '@/service/mongo';
|
||||||
import { authUser } from '@/service/utils/auth';
|
import { authToken } from '@/service/utils/auth';
|
||||||
import { customAlphabet } from 'nanoid';
|
import { customAlphabet } from 'nanoid';
|
||||||
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890');
|
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890');
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
try {
|
try {
|
||||||
const { userId } = await authUser({ req, authToken: true });
|
const userId = await authToken(req);
|
||||||
|
|
||||||
await connectToDatabase();
|
await connectToDatabase();
|
||||||
|
|
||||||
|
|||||||
@@ -1,66 +0,0 @@
|
|||||||
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
|
||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
|
||||||
import { jsonRes } from '@/service/response';
|
|
||||||
import { authUser } from '@/service/utils/auth';
|
|
||||||
import type { ChatItemSimpleType } from '@/types/chat';
|
|
||||||
import { countOpenAIToken } from '@/utils/plugin/openai';
|
|
||||||
|
|
||||||
type ModelType = 'gpt-3.5-turbo' | 'gpt-4' | 'gpt-4-32k';
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
messages: ChatItemSimpleType[];
|
|
||||||
model: ModelType;
|
|
||||||
maxLen: number;
|
|
||||||
};
|
|
||||||
type Response = ChatItemSimpleType[];
|
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|
||||||
try {
|
|
||||||
await authUser({ req });
|
|
||||||
|
|
||||||
const { messages, model, maxLen } = req.body as Props;
|
|
||||||
|
|
||||||
if (!Array.isArray(messages) || !model || !maxLen) {
|
|
||||||
throw new Error('params is error');
|
|
||||||
}
|
|
||||||
|
|
||||||
return jsonRes<Response>(res, {
|
|
||||||
data: gpt_chatItemTokenSlice({
|
|
||||||
messages,
|
|
||||||
model,
|
|
||||||
maxToken: maxLen
|
|
||||||
})
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
jsonRes(res, {
|
|
||||||
code: 500,
|
|
||||||
error: err
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function gpt_chatItemTokenSlice({
|
|
||||||
messages,
|
|
||||||
model,
|
|
||||||
maxToken
|
|
||||||
}: {
|
|
||||||
messages: ChatItemSimpleType[];
|
|
||||||
model: ModelType;
|
|
||||||
maxToken: number;
|
|
||||||
}) {
|
|
||||||
let result: ChatItemSimpleType[] = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < messages.length; i++) {
|
|
||||||
const msgs = [...result, messages[i]];
|
|
||||||
|
|
||||||
const tokens = countOpenAIToken({ messages: msgs, model });
|
|
||||||
|
|
||||||
if (tokens < maxToken) {
|
|
||||||
result = msgs;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
|
||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
|
||||||
import { jsonRes } from '@/service/response';
|
|
||||||
import { authUser, getSystemOpenAiKey } from '@/service/utils/auth';
|
|
||||||
import type { TextPluginRequestParams } from '@/types/plugin';
|
|
||||||
import axios from 'axios';
|
|
||||||
import { axiosConfig } from '@/service/utils/tools';
|
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|
||||||
try {
|
|
||||||
if (process.env.SENSITIVE_CHECK !== '1') {
|
|
||||||
return jsonRes(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
await authUser({ req });
|
|
||||||
|
|
||||||
const { input } = req.body as TextPluginRequestParams;
|
|
||||||
|
|
||||||
const response = await axios({
|
|
||||||
...axiosConfig(getSystemOpenAiKey()),
|
|
||||||
method: 'POST',
|
|
||||||
url: `/moderations`,
|
|
||||||
data: {
|
|
||||||
input
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const data = (response.data.results?.[0]?.category_scores as Record<string, number>) || {};
|
|
||||||
|
|
||||||
const values = Object.values(data);
|
|
||||||
|
|
||||||
for (const val of values) {
|
|
||||||
if (val > 0.2) {
|
|
||||||
return jsonRes(res, {
|
|
||||||
code: 500,
|
|
||||||
message: '您的内容不合规'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
jsonRes(res);
|
|
||||||
} catch (err) {
|
|
||||||
jsonRes(res, {
|
|
||||||
code: 500,
|
|
||||||
error: err
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
|
||||||
import { jsonRes } from '@/service/response';
|
|
||||||
import { connectToDatabase, KB } from '@/service/mongo';
|
|
||||||
import { authUser } from '@/service/utils/auth';
|
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
|
||||||
try {
|
|
||||||
const { name, tags } = req.body as {
|
|
||||||
name: string;
|
|
||||||
tags: string[];
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!name) {
|
|
||||||
throw new Error('缺少参数');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 凭证校验
|
|
||||||
const { userId } = await authUser({ req, authToken: true });
|
|
||||||
|
|
||||||
await connectToDatabase();
|
|
||||||
|
|
||||||
const { _id } = await KB.create({
|
|
||||||
name,
|
|
||||||
userId,
|
|
||||||
tags
|
|
||||||
});
|
|
||||||
|
|
||||||
jsonRes(res, { data: _id });
|
|
||||||
} catch (err) {
|
|
||||||
jsonRes(res, {
|
|
||||||
code: 500,
|
|
||||||
error: err
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
|
||||||
import { jsonRes } from '@/service/response';
|
|
||||||
import { connectToDatabase, User } from '@/service/mongo';
|
|
||||||
import { authUser } from '@/service/utils/auth';
|
|
||||||
import { PgClient } from '@/service/pg';
|
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
|
||||||
try {
|
|
||||||
let { kbId } = req.query as {
|
|
||||||
kbId: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!kbId) {
|
|
||||||
throw new Error('缺少参数');
|
|
||||||
}
|
|
||||||
|
|
||||||
await connectToDatabase();
|
|
||||||
|
|
||||||
// 凭证校验
|
|
||||||
const { userId } = await authUser({ req, authToken: true });
|
|
||||||
|
|
||||||
const thirtyMinutesAgo = new Date(Date.now() - 30 * 60 * 1000);
|
|
||||||
|
|
||||||
// auth export times
|
|
||||||
const authTimes = await User.findOne(
|
|
||||||
{
|
|
||||||
_id: userId,
|
|
||||||
$or: [
|
|
||||||
{ 'limit.exportKbTime': { $exists: false } },
|
|
||||||
{ 'limit.exportKbTime': { $lte: thirtyMinutesAgo } }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
'_id limit'
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!authTimes) {
|
|
||||||
throw new Error('上次导出未到半小时,每半小时仅可导出一次。');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 统计数据
|
|
||||||
const count = await PgClient.count('modelData', {
|
|
||||||
where: [['kb_id', kbId], 'AND', ['user_id', userId]]
|
|
||||||
});
|
|
||||||
// 从 pg 中获取所有数据
|
|
||||||
const pgData = await PgClient.select<{ q: string; a: string }>('modelData', {
|
|
||||||
where: [['kb_id', kbId], 'AND', ['user_id', userId]],
|
|
||||||
fields: ['q', 'a'],
|
|
||||||
order: [{ field: 'id', mode: 'DESC' }],
|
|
||||||
limit: count
|
|
||||||
});
|
|
||||||
|
|
||||||
const data: [string, string][] = pgData.rows.map((item) => [
|
|
||||||
item.q.replace(/\n/g, '\\n'),
|
|
||||||
item.a.replace(/\n/g, '\\n')
|
|
||||||
]);
|
|
||||||
|
|
||||||
// update export time
|
|
||||||
await User.findByIdAndUpdate(userId, {
|
|
||||||
'limit.exportKbTime': new Date()
|
|
||||||
});
|
|
||||||
|
|
||||||
jsonRes(res, {
|
|
||||||
data
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
jsonRes(res, {
|
|
||||||
code: 500,
|
|
||||||
error: err
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
|
||||||
import { jsonRes } from '@/service/response';
|
|
||||||
import { connectToDatabase } from '@/service/mongo';
|
|
||||||
import { authUser } from '@/service/utils/auth';
|
|
||||||
import { PgClient } from '@/service/pg';
|
|
||||||
import type { PgKBDataItemType } from '@/types/pg';
|
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
|
||||||
try {
|
|
||||||
let { dataId } = req.query as {
|
|
||||||
dataId: string;
|
|
||||||
};
|
|
||||||
if (!dataId) {
|
|
||||||
throw new Error('缺少参数');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 凭证校验
|
|
||||||
const { userId } = await authUser({ req, authToken: true });
|
|
||||||
|
|
||||||
await connectToDatabase();
|
|
||||||
|
|
||||||
const where: any = [['user_id', userId], 'AND', ['id', dataId]];
|
|
||||||
|
|
||||||
const searchRes = await PgClient.select<PgKBDataItemType>('modelData', {
|
|
||||||
fields: ['id', 'q', 'a', 'status'],
|
|
||||||
where,
|
|
||||||
limit: 1
|
|
||||||
});
|
|
||||||
|
|
||||||
jsonRes(res, {
|
|
||||||
data: searchRes.rows[0]
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
jsonRes(res, {
|
|
||||||
code: 500,
|
|
||||||
error: err
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
|
||||||
import { jsonRes } from '@/service/response';
|
|
||||||
import { connectToDatabase, KB } from '@/service/mongo';
|
|
||||||
import { authUser } from '@/service/utils/auth';
|
|
||||||
import { PgClient } from '@/service/pg';
|
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
|
||||||
try {
|
|
||||||
const { id } = req.query as {
|
|
||||||
id: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!id) {
|
|
||||||
throw new Error('缺少参数');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 凭证校验
|
|
||||||
const { userId } = await authUser({ req, authToken: true });
|
|
||||||
|
|
||||||
await connectToDatabase();
|
|
||||||
|
|
||||||
// delete mongo data
|
|
||||||
await KB.findOneAndDelete({
|
|
||||||
_id: id,
|
|
||||||
userId
|
|
||||||
});
|
|
||||||
|
|
||||||
// delete all pg data
|
|
||||||
// 删除 pg 中所有该模型的数据
|
|
||||||
await PgClient.delete('modelData', {
|
|
||||||
where: [['user_id', userId], 'AND', ['kb_id', id]]
|
|
||||||
});
|
|
||||||
|
|
||||||
// delete related model
|
|
||||||
|
|
||||||
jsonRes(res);
|
|
||||||
} catch (err) {
|
|
||||||
jsonRes(res, {
|
|
||||||
code: 500,
|
|
||||||
error: err
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
|
||||||
import { jsonRes } from '@/service/response';
|
|
||||||
import { connectToDatabase, KB } from '@/service/mongo';
|
|
||||||
import { authUser } from '@/service/utils/auth';
|
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
|
||||||
try {
|
|
||||||
const { id } = req.query as {
|
|
||||||
id: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!id) {
|
|
||||||
throw new Error('缺少参数');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 凭证校验
|
|
||||||
const { userId } = await authUser({ req, authToken: true });
|
|
||||||
|
|
||||||
await connectToDatabase();
|
|
||||||
|
|
||||||
const data = await KB.findOne({
|
|
||||||
_id: id,
|
|
||||||
userId
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!data) {
|
|
||||||
throw new Error('kb is not exist');
|
|
||||||
}
|
|
||||||
|
|
||||||
jsonRes(res, {
|
|
||||||
data: {
|
|
||||||
_id: data._id,
|
|
||||||
avatar: data.avatar,
|
|
||||||
name: data.name,
|
|
||||||
userId: data.userId,
|
|
||||||
updateTime: data.updateTime,
|
|
||||||
tags: data.tags.join(' ')
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
jsonRes(res, {
|
|
||||||
code: 500,
|
|
||||||
error: err
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
|
||||||
import { jsonRes } from '@/service/response';
|
|
||||||
import { connectToDatabase, KB } from '@/service/mongo';
|
|
||||||
import { authUser } from '@/service/utils/auth';
|
|
||||||
import { PgClient } from '@/service/pg';
|
|
||||||
import { KbItemType } from '@/types/plugin';
|
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
|
||||||
try {
|
|
||||||
// 凭证校验
|
|
||||||
const { userId } = await authUser({ req, authToken: true });
|
|
||||||
|
|
||||||
await connectToDatabase();
|
|
||||||
|
|
||||||
const kbList = await KB.find({
|
|
||||||
userId
|
|
||||||
}).sort({ updateTime: -1 });
|
|
||||||
|
|
||||||
const data = await Promise.all(
|
|
||||||
kbList.map(async (item) => ({
|
|
||||||
_id: item._id,
|
|
||||||
avatar: item.avatar,
|
|
||||||
name: item.name,
|
|
||||||
userId: item.userId,
|
|
||||||
updateTime: item.updateTime,
|
|
||||||
tags: item.tags.join(' '),
|
|
||||||
totalData: await PgClient.count('modelData', {
|
|
||||||
where: [['user_id', userId], 'AND', ['kb_id', item._id]]
|
|
||||||
})
|
|
||||||
}))
|
|
||||||
);
|
|
||||||
|
|
||||||
jsonRes<KbItemType[]>(res, {
|
|
||||||
data
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
jsonRes(res, {
|
|
||||||
code: 500,
|
|
||||||
error: err
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
|
||||||
import { jsonRes } from '@/service/response';
|
|
||||||
import { connectToDatabase, KB } from '@/service/mongo';
|
|
||||||
import { authUser } from '@/service/utils/auth';
|
|
||||||
import type { KbUpdateParams } from '@/api/plugins/kb';
|
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
|
||||||
try {
|
|
||||||
const { id, name, tags, avatar } = req.body as KbUpdateParams;
|
|
||||||
|
|
||||||
if (!id || !name) {
|
|
||||||
throw new Error('缺少参数');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 凭证校验
|
|
||||||
const { userId } = await authUser({ req, authToken: true });
|
|
||||||
|
|
||||||
await connectToDatabase();
|
|
||||||
|
|
||||||
await KB.findOneAndUpdate(
|
|
||||||
{
|
|
||||||
_id: id,
|
|
||||||
userId
|
|
||||||
},
|
|
||||||
{
|
|
||||||
avatar,
|
|
||||||
name,
|
|
||||||
tags: tags.split(' ').filter((item) => item)
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
jsonRes(res);
|
|
||||||
} catch (err) {
|
|
||||||
jsonRes(res, {
|
|
||||||
code: 500,
|
|
||||||
error: err
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import { jsonRes } from '@/service/response';
|
import { jsonRes } from '@/service/response';
|
||||||
import { connectToDatabase, User, Pay } from '@/service/mongo';
|
import { connectToDatabase, User, Pay } from '@/service/mongo';
|
||||||
import { authUser } from '@/service/utils/auth';
|
import { authToken } from '@/service/utils/auth';
|
||||||
import { PaySchema, UserModelSchema } from '@/types/mongoSchema';
|
import { PaySchema, UserModelSchema } from '@/types/mongoSchema';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { getPayResult } from '@/service/utils/wxpay';
|
import { getPayResult } from '@/service/utils/wxpay';
|
||||||
@@ -13,7 +13,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
try {
|
try {
|
||||||
let { payId } = req.query as { payId: string };
|
let { payId } = req.query as { payId: string };
|
||||||
|
|
||||||
const { userId } = await authUser({ req, authToken: true });
|
const userId = await authToken(req);
|
||||||
|
|
||||||
await connectToDatabase();
|
await connectToDatabase();
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import { jsonRes } from '@/service/response';
|
import { jsonRes } from '@/service/response';
|
||||||
import { connectToDatabase, Bill } from '@/service/mongo';
|
import { connectToDatabase, Bill } from '@/service/mongo';
|
||||||
import { authUser } from '@/service/utils/auth';
|
import { authToken } from '@/service/utils/auth';
|
||||||
import { adaptBill } from '@/utils/adapt';
|
import type { BillSchema } from '@/types/mongoSchema';
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
try {
|
try {
|
||||||
@@ -12,12 +12,12 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
pageNum = +pageNum;
|
pageNum = +pageNum;
|
||||||
pageSize = +pageSize;
|
pageSize = +pageSize;
|
||||||
|
|
||||||
const { userId } = await authUser({ req, authToken: true });
|
const userId = await authToken(req);
|
||||||
|
|
||||||
await connectToDatabase();
|
await connectToDatabase();
|
||||||
|
|
||||||
// 根据 id 获取用户账单
|
// 根据 id 获取用户账单
|
||||||
const bills = await Bill.find({
|
const bills = await Bill.find<BillSchema>({
|
||||||
userId
|
userId
|
||||||
})
|
})
|
||||||
.sort({ _id: -1 }) // 按照创建时间倒序排列
|
.sort({ _id: -1 }) // 按照创建时间倒序排列
|
||||||
@@ -33,7 +33,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
data: {
|
data: {
|
||||||
pageNum,
|
pageNum,
|
||||||
pageSize,
|
pageSize,
|
||||||
data: bills.map(adaptBill),
|
data: bills,
|
||||||
total
|
total
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import { jsonRes } from '@/service/response';
|
import { jsonRes } from '@/service/response';
|
||||||
import { authUser } from '@/service/utils/auth';
|
import { authToken } from '@/service/utils/auth';
|
||||||
import { customAlphabet } from 'nanoid';
|
import { customAlphabet } from 'nanoid';
|
||||||
import { connectToDatabase, Pay } from '@/service/mongo';
|
import { connectToDatabase, Pay } from '@/service/mongo';
|
||||||
import { PRICE_SCALE } from '@/constants/common';
|
import { PRICE_SCALE } from '@/constants/common';
|
||||||
@@ -14,7 +14,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
let { amount = 0 } = req.query as { amount: string };
|
let { amount = 0 } = req.query as { amount: string };
|
||||||
amount = +amount;
|
amount = +amount;
|
||||||
|
|
||||||
const { userId } = await authUser({ req, authToken: true });
|
const userId = await authToken(req);
|
||||||
|
|
||||||
const id = nanoid();
|
const id = nanoid();
|
||||||
await connectToDatabase();
|
await connectToDatabase();
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import { jsonRes } from '@/service/response';
|
import { jsonRes } from '@/service/response';
|
||||||
import { authUser } from '@/service/utils/auth';
|
import { authToken } from '@/service/utils/auth';
|
||||||
import { connectToDatabase, Pay } from '@/service/mongo';
|
import { connectToDatabase, Pay } from '@/service/mongo';
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
try {
|
try {
|
||||||
const { userId } = await authUser({ req, authToken: true });
|
const userId = await authToken(req);
|
||||||
|
|
||||||
await connectToDatabase();
|
await connectToDatabase();
|
||||||
|
|
||||||
|
|||||||