mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-23 21:13:50 +00:00
fix: chat 页有些手机无法加载
This commit is contained in:
87
README.md
87
README.md
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
Fast GPT 允许你使用自己的 openai API KEY 来快速的调用 openai 接口,包括 GPT3 及其微调方法,以及最新的 gpt3.5 接口。
|
Fast GPT 允许你使用自己的 openai API KEY 来快速的调用 openai 接口,包括 GPT3 及其微调方法,以及最新的 gpt3.5 接口。
|
||||||
|
|
||||||
## 初始化
|
## 开发
|
||||||
复制 .env.template 成 .env.local ,填写核心参数
|
复制 .env.template 成 .env.local ,填写核心参数
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -15,7 +15,6 @@ TOKEN_KEY=随便填一个,用于生成和校验 token
|
|||||||
OPENAIKEY=openai的key
|
OPENAIKEY=openai的key
|
||||||
REDIS_URL=redis的地址
|
REDIS_URL=redis的地址
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pnpm dev
|
pnpm dev
|
||||||
```
|
```
|
||||||
@@ -32,48 +31,6 @@ docker push imageName:tag
|
|||||||
# 或者直接拉镜像,见下方
|
# 或者直接拉镜像,见下方
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 服务器拉取镜像和运行
|
|
||||||
```yml
|
|
||||||
# docker-compose
|
|
||||||
version: "3.3"
|
|
||||||
services:
|
|
||||||
fast-gpt:
|
|
||||||
image: c121914yu/fast-gpt:latest
|
|
||||||
environment:
|
|
||||||
AXIOS_PROXY_HOST: 127.0.0.1
|
|
||||||
AXIOS_PROXY_PORT: 7890
|
|
||||||
MY_MAIL:
|
|
||||||
MAILE_CODE:
|
|
||||||
TOKEN_KEY:
|
|
||||||
MONGODB_URI:
|
|
||||||
OPENAIKEY:
|
|
||||||
REDIS_URL:
|
|
||||||
network_mode: host
|
|
||||||
restart: always
|
|
||||||
container_name: fast-gpt
|
|
||||||
```
|
|
||||||
```bash
|
|
||||||
#!/bin/bash
|
|
||||||
# 拉取最新镜像
|
|
||||||
docker-compose pull
|
|
||||||
docker-compose up -d
|
|
||||||
|
|
||||||
echo "Docker Compose 重新拉取镜像完成!"
|
|
||||||
|
|
||||||
# 删除本地旧镜像
|
|
||||||
images=$(docker images --format "{{.ID}} {{.Repository}}" | grep fast-gpt)
|
|
||||||
|
|
||||||
# 将镜像 ID 和名称放入数组中
|
|
||||||
IFS=$'\n' read -rd '' -a image_array <<<"$images"
|
|
||||||
|
|
||||||
# 遍历数组并删除所有旧的镜像
|
|
||||||
for ((i=1; i<${#image_array[@]}; i++))
|
|
||||||
do
|
|
||||||
image=${image_array[$i]}
|
|
||||||
image_id=${image%% *}
|
|
||||||
docker rmi $image_id
|
|
||||||
done
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 软件教程:docker 安装
|
#### 软件教程:docker 安装
|
||||||
```bash
|
```bash
|
||||||
@@ -163,3 +120,45 @@ appendfsync everysec
|
|||||||
# 添加索引
|
# 添加索引
|
||||||
FT.CREATE idx:model:data:hash ON HASH PREFIX 1 model:data: SCHEMA modelId TAG userId TAG status TAG q TEXT text TEXT vector VECTOR FLAT 6 DIM 1536 DISTANCE_METRIC COSINE TYPE FLOAT32
|
FT.CREATE idx:model:data:hash ON HASH PREFIX 1 model:data: SCHEMA modelId TAG userId TAG status TAG q TEXT text TEXT vector VECTOR FLAT 6 DIM 1536 DISTANCE_METRIC COSINE TYPE FLOAT32
|
||||||
```
|
```
|
||||||
|
#### 服务器拉取镜像和运行
|
||||||
|
```yml
|
||||||
|
# docker-compose
|
||||||
|
version: "3.3"
|
||||||
|
services:
|
||||||
|
fast-gpt:
|
||||||
|
image: c121914yu/fast-gpt:latest
|
||||||
|
environment:
|
||||||
|
AXIOS_PROXY_HOST: 127.0.0.1
|
||||||
|
AXIOS_PROXY_PORT: 7890
|
||||||
|
MY_MAIL:
|
||||||
|
MAILE_CODE:
|
||||||
|
TOKEN_KEY:
|
||||||
|
MONGODB_URI:
|
||||||
|
OPENAIKEY:
|
||||||
|
REDIS_URL:
|
||||||
|
network_mode: host
|
||||||
|
restart: always
|
||||||
|
container_name: fast-gpt
|
||||||
|
```
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
# 拉取最新镜像
|
||||||
|
docker-compose pull
|
||||||
|
docker-compose up -d
|
||||||
|
|
||||||
|
echo "Docker Compose 重新拉取镜像完成!"
|
||||||
|
|
||||||
|
# 删除本地旧镜像
|
||||||
|
images=$(docker images --format "{{.ID}} {{.Repository}}" | grep fast-gpt)
|
||||||
|
|
||||||
|
# 将镜像 ID 和名称放入数组中
|
||||||
|
IFS=$'\n' read -rd '' -a image_array <<<"$images"
|
||||||
|
|
||||||
|
# 遍历数组并删除所有旧的镜像
|
||||||
|
for ((i=1; i<${#image_array[@]}; i++))
|
||||||
|
do
|
||||||
|
image=${image_array[$i]}
|
||||||
|
image_id=${image%% *}
|
||||||
|
docker rmi $image_id
|
||||||
|
done
|
||||||
|
```
|
||||||
|
@@ -47,7 +47,7 @@ export default function App({ Component, pageProps }: AppProps) {
|
|||||||
<meta name="description" content="Generated by Fast GPT" />
|
<meta name="description" content="Generated by Fast GPT" />
|
||||||
<meta
|
<meta
|
||||||
name="viewport"
|
name="viewport"
|
||||||
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0;"
|
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"
|
||||||
/>
|
/>
|
||||||
<link rel="icon" href="/favicon.ico" />
|
<link rel="icon" href="/favicon.ico" />
|
||||||
</Head>
|
</Head>
|
||||||
|
@@ -1,13 +1,7 @@
|
|||||||
import React, { useCallback, useState, useRef, useMemo, useEffect } from 'react';
|
import React, { useCallback, useState, useRef, useMemo, useEffect } from 'react';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import {
|
import { getInitChatSiteInfo, getChatSiteId, delChatRecordByIndex, postSaveChat } from '@/api/chat';
|
||||||
getInitChatSiteInfo,
|
|
||||||
getChatSiteId,
|
|
||||||
postGPT3SendPrompt,
|
|
||||||
delChatRecordByIndex,
|
|
||||||
postSaveChat
|
|
||||||
} from '@/api/chat';
|
|
||||||
import type { InitChatResponse } from '@/api/response/chat';
|
import type { InitChatResponse } from '@/api/response/chat';
|
||||||
import { ChatSiteItemType } from '@/types/chat';
|
import { ChatSiteItemType } from '@/types/chat';
|
||||||
import {
|
import {
|
||||||
@@ -33,12 +27,11 @@ import { useGlobalStore } from '@/store/global';
|
|||||||
import { useChatStore } from '@/store/chat';
|
import { useChatStore } from '@/store/chat';
|
||||||
import { useCopyData } from '@/utils/tools';
|
import { useCopyData } from '@/utils/tools';
|
||||||
import { streamFetch } from '@/api/fetch';
|
import { streamFetch } from '@/api/fetch';
|
||||||
import SlideBar from './components/SlideBar';
|
|
||||||
import Empty from './components/Empty';
|
|
||||||
import Icon from '@/components/Icon';
|
import Icon from '@/components/Icon';
|
||||||
import { encode } from 'gpt-token-utils';
|
|
||||||
import { modelList } from '@/constants/model';
|
import { modelList } from '@/constants/model';
|
||||||
|
|
||||||
|
const SlideBar = dynamic(() => import('./components/SlideBar'));
|
||||||
|
const Empty = dynamic(() => import('./components/Empty'));
|
||||||
const Markdown = dynamic(() => import('@/components/Markdown'));
|
const Markdown = dynamic(() => import('@/components/Markdown'));
|
||||||
|
|
||||||
const textareaMinH = '22px';
|
const textareaMinH = '22px';
|
||||||
@@ -48,10 +41,12 @@ interface ChatType extends InitChatResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Chat = ({ chatId }: { chatId: string }) => {
|
const Chat = ({ chatId }: { chatId: string }) => {
|
||||||
const { toast } = useToast();
|
|
||||||
const router = useRouter();
|
|
||||||
const ChatBox = useRef<HTMLDivElement>(null);
|
const ChatBox = useRef<HTMLDivElement>(null);
|
||||||
const TextareaDom = useRef<HTMLTextAreaElement>(null);
|
const TextareaDom = useRef<HTMLTextAreaElement>(null);
|
||||||
|
|
||||||
|
const { toast } = useToast();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
// 中断请求
|
// 中断请求
|
||||||
const controller = useRef(new AbortController());
|
const controller = useRef(new AbortController());
|
||||||
const [chatData, setChatData] = useState<ChatType>({
|
const [chatData, setChatData] = useState<ChatType>({
|
||||||
@@ -70,11 +65,11 @@ const Chat = ({ chatId }: { chatId: string }) => {
|
|||||||
() => chatData.history[chatData.history.length - 1]?.status === 'loading',
|
() => chatData.history[chatData.history.length - 1]?.status === 'loading',
|
||||||
[chatData.history]
|
[chatData.history]
|
||||||
);
|
);
|
||||||
|
const { isOpen: isOpenSlider, onClose: onCloseSlider, onOpen: onOpenSlider } = useDisclosure();
|
||||||
|
|
||||||
const { copyData } = useCopyData();
|
const { copyData } = useCopyData();
|
||||||
const { isPc, media } = useScreen();
|
const { isPc, media } = useScreen();
|
||||||
const { setLoading } = useGlobalStore();
|
const { setLoading } = useGlobalStore();
|
||||||
|
|
||||||
const { isOpen: isOpenSlider, onClose: onCloseSlider, onOpen: onOpenSlider } = useDisclosure();
|
|
||||||
const { pushChatHistory } = useChatStore();
|
const { pushChatHistory } = useChatStore();
|
||||||
|
|
||||||
// 滚动到底部
|
// 滚动到底部
|
||||||
@@ -211,12 +206,11 @@ const Chat = ({ chatId }: { chatId: string }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 长度校验
|
// 长度校验
|
||||||
const tokens = encode(val).length;
|
|
||||||
const model = modelList.find((item) => item.model === chatData.modelName);
|
const model = modelList.find((item) => item.model === chatData.modelName);
|
||||||
|
|
||||||
if (model && tokens >= model.maxToken) {
|
if (model && val.length >= model.maxToken) {
|
||||||
toast({
|
toast({
|
||||||
title: '单次输入超出 4000 tokens',
|
title: '单次输入超出 4000 字符',
|
||||||
status: 'warning'
|
status: 'warning'
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
@@ -314,14 +308,6 @@ const Chat = ({ chatId }: { chatId: string }) => {
|
|||||||
[copyData]
|
[copyData]
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
controller.current = new AbortController();
|
|
||||||
return () => {
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
controller.current?.abort();
|
|
||||||
};
|
|
||||||
}, [chatId]);
|
|
||||||
|
|
||||||
// 初始化聊天框
|
// 初始化聊天框
|
||||||
useQuery(
|
useQuery(
|
||||||
['init', chatId],
|
['init', chatId],
|
||||||
@@ -359,6 +345,14 @@ const Chat = ({ chatId }: { chatId: string }) => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 更新流中断对象
|
||||||
|
useEffect(() => {
|
||||||
|
controller.current = new AbortController();
|
||||||
|
return () => {
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
controller.current?.abort();
|
||||||
|
};
|
||||||
|
}, [chatId]);
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Flex
|
||||||
h={'100%'}
|
h={'100%'}
|
||||||
@@ -546,7 +540,7 @@ const Chat = ({ chatId }: { chatId: string }) => {
|
|||||||
export default Chat;
|
export default Chat;
|
||||||
|
|
||||||
export async function getServerSideProps(context: any) {
|
export async function getServerSideProps(context: any) {
|
||||||
const chatId = context.query?.chatId || '';
|
const chatId = context?.query?.chatId || 'noid';
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: { chatId }
|
props: { chatId }
|
||||||
|
@@ -127,7 +127,7 @@ const SelectFileModal = ({
|
|||||||
tokens,账号余额不足时,未拆分的数据会被删除。
|
tokens,账号余额不足时,未拆分的数据会被删除。
|
||||||
</Box>
|
</Box>
|
||||||
<Box mt={2}>
|
<Box mt={2}>
|
||||||
一共 {encode(fileText).length} 个tokens,大约 {formatPrice(encode(fileText).length * 4)}
|
一共 {encode(fileText).length} 个tokens,大约 {formatPrice(encode(fileText).length * 3)}
|
||||||
元
|
元
|
||||||
</Box>
|
</Box>
|
||||||
<Flex w={'100%'} alignItems={'center'} my={4}>
|
<Flex w={'100%'} alignItems={'center'} my={4}>
|
||||||
|
@@ -106,7 +106,7 @@ const SelectUrlModal = ({
|
|||||||
QA 拆分,需要较长训练时间,拆分需要消耗 tokens,账号余额不足时,未拆分的数据会被删除。
|
QA 拆分,需要较长训练时间,拆分需要消耗 tokens,账号余额不足时,未拆分的数据会被删除。
|
||||||
</Box>
|
</Box>
|
||||||
<Box mt={2}>
|
<Box mt={2}>
|
||||||
一共 {encode(webText).length} 个tokens,大约 {formatPrice(encode(webText).length * 4)}元
|
一共 {encode(webText).length} 个tokens,大约 {formatPrice(encode(webText).length * 3)}元
|
||||||
</Box>
|
</Box>
|
||||||
<Flex w={'100%'} alignItems={'center'} my={4}>
|
<Flex w={'100%'} alignItems={'center'} my={4}>
|
||||||
<Box flex={'0 0 70px'}>网站地址</Box>
|
<Box flex={'0 0 70px'}>网站地址</Box>
|
||||||
|
Reference in New Issue
Block a user