fix: ui bug
This commit is contained in:
parent
5bf95bd846
commit
5d4dd4a18c
@ -35,185 +35,10 @@ 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.1
|
image: ankane/pgvector:v0.4.2
|
||||||
container_name: pg
|
container_name: pg
|
||||||
restart: always
|
restart: always
|
||||||
ports:
|
ports:
|
||||||
|
|||||||
@ -7,7 +7,7 @@ interface Props extends BoxProps {}
|
|||||||
|
|
||||||
const SideBar = (e?: Props) => {
|
const SideBar = (e?: Props) => {
|
||||||
const {
|
const {
|
||||||
w = [1, '0 0 250px', '0 0 280px', '0 0 310px', '0 0 340px'],
|
w = ['100%', '0 0 250px', '0 0 280px', '0 0 310px', '0 0 340px'],
|
||||||
children,
|
children,
|
||||||
...props
|
...props
|
||||||
} = e || {};
|
} = e || {};
|
||||||
|
|||||||
@ -146,7 +146,7 @@ const DataCard = ({ kbId }: { kbId: string }) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box position={'relative'} w={'100%'}>
|
<Box position={'relative'}>
|
||||||
<Flex>
|
<Flex>
|
||||||
<Box fontWeight={'bold'} fontSize={'lg'} flex={1} mr={2}>
|
<Box fontWeight={'bold'} fontSize={'lg'} flex={1} mr={2}>
|
||||||
知识库数据: {total}组
|
知识库数据: {total}组
|
||||||
@ -155,7 +155,7 @@ const DataCard = ({ kbId }: { kbId: string }) => {
|
|||||||
icon={<RepeatIcon />}
|
icon={<RepeatIcon />}
|
||||||
aria-label={'refresh'}
|
aria-label={'refresh'}
|
||||||
variant={'outline'}
|
variant={'outline'}
|
||||||
mr={4}
|
mr={[2, 4]}
|
||||||
size={'sm'}
|
size={'sm'}
|
||||||
onClick={() => refetchData(pageNum)}
|
onClick={() => refetchData(pageNum)}
|
||||||
/>
|
/>
|
||||||
@ -218,73 +218,70 @@ const DataCard = ({ kbId }: { kbId: string }) => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
<TableContainer mt={4} minH={'200px'}>
|
||||||
<Box mt={4}>
|
<Table>
|
||||||
<TableContainer>
|
<Thead>
|
||||||
<Table variant={'simple'} w={'100%'}>
|
<Tr>
|
||||||
<Thead>
|
<Th>
|
||||||
<Tr>
|
匹配的知识点
|
||||||
<Th>
|
<Tooltip
|
||||||
匹配的知识点
|
label={
|
||||||
<Tooltip
|
'对话时,会将用户的问题和知识库的 "匹配知识点" 进行比较,找到最相似的前 n 条记录,将这些记录的 "匹配知识点"+"补充知识点" 作为 chatgpt 的系统提示词。'
|
||||||
label={
|
}
|
||||||
'对话时,会将用户的问题和知识库的 "匹配知识点" 进行比较,找到最相似的前 n 条记录,将这些记录的 "匹配知识点"+"补充知识点" 作为 chatgpt 的系统提示词。'
|
>
|
||||||
|
<QuestionOutlineIcon ml={1} />
|
||||||
|
</Tooltip>
|
||||||
|
</Th>
|
||||||
|
<Th>补充知识</Th>
|
||||||
|
<Th>状态</Th>
|
||||||
|
<Th>操作</Th>
|
||||||
|
</Tr>
|
||||||
|
</Thead>
|
||||||
|
<Tbody>
|
||||||
|
{modelDataList.map((item) => (
|
||||||
|
<Tr key={item.id}>
|
||||||
|
<Td>
|
||||||
|
<Box {...tdStyles.current}>{item.q}</Box>
|
||||||
|
</Td>
|
||||||
|
<Td>
|
||||||
|
<Box {...tdStyles.current}>{item.a || '-'}</Box>
|
||||||
|
</Td>
|
||||||
|
<Td>{ModelDataStatusMap[item.status]}</Td>
|
||||||
|
<Td>
|
||||||
|
<IconButton
|
||||||
|
mr={5}
|
||||||
|
icon={<EditIcon />}
|
||||||
|
variant={'outline'}
|
||||||
|
aria-label={'delete'}
|
||||||
|
size={'sm'}
|
||||||
|
onClick={() =>
|
||||||
|
setEditInputData({
|
||||||
|
dataId: item.id,
|
||||||
|
q: item.q,
|
||||||
|
a: item.a
|
||||||
|
})
|
||||||
}
|
}
|
||||||
>
|
/>
|
||||||
<QuestionOutlineIcon ml={1} />
|
<IconButton
|
||||||
</Tooltip>
|
icon={<DeleteIcon />}
|
||||||
</Th>
|
variant={'outline'}
|
||||||
<Th>补充知识</Th>
|
colorScheme={'gray'}
|
||||||
<Th>状态</Th>
|
aria-label={'delete'}
|
||||||
<Th>操作</Th>
|
size={'sm'}
|
||||||
|
onClick={async () => {
|
||||||
|
await delOneKbDataByDataId(item.id);
|
||||||
|
refetchData(pageNum);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Td>
|
||||||
</Tr>
|
</Tr>
|
||||||
</Thead>
|
))}
|
||||||
<Tbody>
|
</Tbody>
|
||||||
{modelDataList.map((item) => (
|
</Table>
|
||||||
<Tr key={item.id}>
|
</TableContainer>
|
||||||
<Td>
|
<Flex mt={2} justifyContent={'flex-end'}>
|
||||||
<Box {...tdStyles.current}>{item.q}</Box>
|
<Pagination />
|
||||||
</Td>
|
</Flex>
|
||||||
<Td>
|
|
||||||
<Box {...tdStyles.current}>{item.a || '-'}</Box>
|
|
||||||
</Td>
|
|
||||||
<Td>{ModelDataStatusMap[item.status]}</Td>
|
|
||||||
<Td>
|
|
||||||
<IconButton
|
|
||||||
mr={5}
|
|
||||||
icon={<EditIcon />}
|
|
||||||
variant={'outline'}
|
|
||||||
aria-label={'delete'}
|
|
||||||
size={'sm'}
|
|
||||||
onClick={() =>
|
|
||||||
setEditInputData({
|
|
||||||
dataId: item.id,
|
|
||||||
q: item.q,
|
|
||||||
a: item.a
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<IconButton
|
|
||||||
icon={<DeleteIcon />}
|
|
||||||
variant={'outline'}
|
|
||||||
colorScheme={'gray'}
|
|
||||||
aria-label={'delete'}
|
|
||||||
size={'sm'}
|
|
||||||
onClick={async () => {
|
|
||||||
await delOneKbDataByDataId(item.id);
|
|
||||||
refetchData(pageNum);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Td>
|
|
||||||
</Tr>
|
|
||||||
))}
|
|
||||||
</Tbody>
|
|
||||||
</Table>
|
|
||||||
</TableContainer>
|
|
||||||
<Flex mt={2} justifyContent={'flex-end'}>
|
|
||||||
<Pagination />
|
|
||||||
</Flex>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Loading loading={isLoading} fixed={false} />
|
<Loading loading={isLoading} fixed={false} />
|
||||||
{editInputData !== undefined && (
|
{editInputData !== undefined && (
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import React, { useCallback, useState } from 'react';
|
import React, { useCallback, useState, useMemo } from 'react';
|
||||||
import { Box, Flex, useTheme, Input, IconButton, Tooltip, Image, Tag } from '@chakra-ui/react';
|
import { Box, Flex, useTheme, Input, IconButton, Tooltip, Image, Tag } from '@chakra-ui/react';
|
||||||
import { AddIcon } from '@chakra-ui/icons';
|
import { AddIcon } from '@chakra-ui/icons';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
@ -17,6 +17,11 @@ const KbList = ({ kbId }: { kbId: string }) => {
|
|||||||
const { myKbList, loadKbList } = useUserStore();
|
const { myKbList, loadKbList } = useUserStore();
|
||||||
const [searchText, setSearchText] = useState('');
|
const [searchText, setSearchText] = useState('');
|
||||||
|
|
||||||
|
const kbs = useMemo(
|
||||||
|
() => myKbList.filter((item) => new RegExp(searchText, 'ig').test(item.name + item.tags)),
|
||||||
|
[myKbList, searchText]
|
||||||
|
);
|
||||||
|
|
||||||
/* 加载模型 */
|
/* 加载模型 */
|
||||||
const { isLoading } = useQuery(['loadModels'], () => loadKbList(false));
|
const { isLoading } = useQuery(['loadModels'], () => loadKbList(false));
|
||||||
|
|
||||||
@ -82,7 +87,7 @@ const KbList = ({ kbId }: { kbId: string }) => {
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Box flex={'1 0 0'} h={0} overflow={'overlay'}>
|
<Box flex={'1 0 0'} h={0} overflow={'overlay'}>
|
||||||
{myKbList.map((item) => (
|
{kbs.map((item) => (
|
||||||
<Flex
|
<Flex
|
||||||
key={item._id}
|
key={item._id}
|
||||||
position={'relative'}
|
position={'relative'}
|
||||||
|
|||||||
@ -23,11 +23,11 @@ const Kb = ({ kbId }: { kbId: string }) => {
|
|||||||
<Flex h={'100%'} position={'relative'} overflow={'hidden'}>
|
<Flex h={'100%'} position={'relative'} overflow={'hidden'}>
|
||||||
{/* 模型列表 */}
|
{/* 模型列表 */}
|
||||||
{(isPc || !kbId) && (
|
{(isPc || !kbId) && (
|
||||||
<SideBar w={[1, '0 0 250px', '0 0 270px', '0 0 290px']}>
|
<SideBar w={['100%', '0 0 250px', '0 0 270px', '0 0 290px']}>
|
||||||
<KbList kbId={kbId} />
|
<KbList kbId={kbId} />
|
||||||
</SideBar>
|
</SideBar>
|
||||||
)}
|
)}
|
||||||
<Box flex={1} h={'100%'} position={'relative'}>
|
<Box flex={'1 0 0'} w={0} h={'100%'} position={'relative'}>
|
||||||
{kbId && <KbDetail kbId={kbId} />}
|
{kbId && <KbDetail kbId={kbId} />}
|
||||||
</Box>
|
</Box>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|||||||
@ -29,7 +29,7 @@ const Model = ({ modelId }: { modelId: string }) => {
|
|||||||
<Flex h={'100%'} position={'relative'} overflow={'hidden'}>
|
<Flex h={'100%'} position={'relative'} overflow={'hidden'}>
|
||||||
{/* 模型列表 */}
|
{/* 模型列表 */}
|
||||||
{(isPc || !modelId) && (
|
{(isPc || !modelId) && (
|
||||||
<SideBar w={[1, '0 0 250px', '0 0 270px', '0 0 290px']}>
|
<SideBar w={['100%', '0 0 250px', '0 0 270px', '0 0 290px']}>
|
||||||
<ModelList modelId={modelId} />
|
<ModelList modelId={modelId} />
|
||||||
</SideBar>
|
</SideBar>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -18,6 +18,7 @@ const ShareModelList = ({
|
|||||||
<>
|
<>
|
||||||
{models.map((model) => (
|
{models.map((model) => (
|
||||||
<Flex
|
<Flex
|
||||||
|
w={'100%'}
|
||||||
flexDirection={'column'}
|
flexDirection={'column'}
|
||||||
key={model._id}
|
key={model._id}
|
||||||
p={4}
|
p={4}
|
||||||
|
|||||||
@ -5,6 +5,11 @@ import MyIcon from '@/components/Icon';
|
|||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
|
|
||||||
const list = [
|
const list = [
|
||||||
|
{
|
||||||
|
icon: 'kb',
|
||||||
|
label: '我的知识库',
|
||||||
|
link: '/kb'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
icon: 'shareMarket',
|
icon: 'shareMarket',
|
||||||
label: 'AI助手市场',
|
label: 'AI助手市场',
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user