mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-23 21:13:50 +00:00
temp
This commit is contained in:
@@ -45,8 +45,13 @@ const Button = defineStyleConfig({
|
||||
}
|
||||
},
|
||||
variants: {
|
||||
outline: {
|
||||
borderWidth: '1.5px'
|
||||
white: {
|
||||
color: '#fff',
|
||||
backgroundColor: 'transparent',
|
||||
border: '1px solid #ffffff',
|
||||
_hover: {
|
||||
backgroundColor: 'rgba(255,255,255,0.1)'
|
||||
}
|
||||
}
|
||||
},
|
||||
defaultProps: {
|
||||
|
@@ -103,9 +103,14 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
}
|
||||
};
|
||||
|
||||
for await (const chunk of chatResponse.data as any) {
|
||||
const parser = createParser(onParse);
|
||||
parser.feed(decodeURIComponent(chunk));
|
||||
const decoder = new TextDecoder();
|
||||
try {
|
||||
for await (const chunk of chatResponse.data as any) {
|
||||
const parser = createParser(onParse);
|
||||
parser.feed(decoder.decode(chunk));
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('pipe error', error);
|
||||
}
|
||||
pass.push(null);
|
||||
} catch (err: any) {
|
||||
|
19
src/pages/chat/components/SlideBar.tsx
Normal file
19
src/pages/chat/components/SlideBar.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import React from 'react';
|
||||
import { Box, Button } from '@chakra-ui/react';
|
||||
import { AddIcon } from '@chakra-ui/icons';
|
||||
|
||||
const SlideBar = ({ resetChat }: { resetChat: () => void }) => {
|
||||
return (
|
||||
<Box flex={'0 0 250px'} p={3} backgroundColor={'blackAlpha.800'} color={'white'}>
|
||||
{/* 新对话 */}
|
||||
<Button w={'100%'} variant={'white'} h={'40px'} leftIcon={<AddIcon />} onClick={resetChat}>
|
||||
新对话
|
||||
</Button>
|
||||
{/* 我的模型 */}
|
||||
|
||||
{/* 历史记录 */}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default SlideBar;
|
@@ -12,12 +12,21 @@ import { OpenAiModelEnum } from '@/constants/model';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { useGlobalStore } from '@/store/global';
|
||||
import { streamFetch } from '@/api/fetch';
|
||||
import SlideBar from './components/SlideBar';
|
||||
|
||||
const Markdown = dynamic(() => import('@/components/Markdown'));
|
||||
|
||||
const textareaMinH = '22px';
|
||||
|
||||
const Chat = ({ chatId, windowId }: { chatId: string; windowId?: string }) => {
|
||||
const Chat = ({
|
||||
chatId,
|
||||
windowId,
|
||||
timeStamp
|
||||
}: {
|
||||
chatId: string;
|
||||
windowId?: string;
|
||||
timeStamp: string;
|
||||
}) => {
|
||||
const { toast } = useToast();
|
||||
const router = useRouter();
|
||||
const { isPc, media } = useScreen();
|
||||
@@ -45,7 +54,7 @@ const Chat = ({ chatId, windowId }: { chatId: string; windowId?: string }) => {
|
||||
|
||||
// 初始化聊天框
|
||||
useQuery(
|
||||
['initData'],
|
||||
['initData', timeStamp],
|
||||
() => {
|
||||
setLoading(true);
|
||||
return getInitChatSiteInfo(chatId, windowId);
|
||||
@@ -53,7 +62,7 @@ const Chat = ({ chatId, windowId }: { chatId: string; windowId?: string }) => {
|
||||
{
|
||||
onSuccess(res) {
|
||||
// 可能没有 windowId,给它设置一下
|
||||
router.replace(`/chat?chatId=${chatId}&windowId=${res.windowId}`);
|
||||
router.replace(`/chat?chatId=${chatId}&windowId=${res.windowId}&timeStamp=${timeStamp}`);
|
||||
|
||||
setChatSiteData(res.chatSite);
|
||||
setChatList(
|
||||
@@ -92,8 +101,8 @@ const Chat = ({ chatId, windowId }: { chatId: string; windowId?: string }) => {
|
||||
|
||||
// 重载对话
|
||||
const resetChat = useCallback(() => {
|
||||
window.open(`/chat?chatId=${chatId}`, '_self');
|
||||
}, [chatId]);
|
||||
router.push(`/chat?chatId=${chatId}&timeStamp=${Date.now()}`);
|
||||
}, [chatId, router]);
|
||||
|
||||
// gpt3 方法
|
||||
const gpt3ChatPrompt = useCallback(
|
||||
@@ -270,156 +279,124 @@ const Chat = ({ chatId, windowId }: { chatId: string; windowId?: string }) => {
|
||||
}, [chatList, resetInputVal, windowId]);
|
||||
|
||||
return (
|
||||
<Flex height={'100%'} flexDirection={'column'}>
|
||||
{/* 头部 */}
|
||||
<Flex
|
||||
px={4}
|
||||
h={'50px'}
|
||||
alignItems={'center'}
|
||||
backgroundColor={'white'}
|
||||
boxShadow={'0 5px 10px rgba(0,0,0,0.1)'}
|
||||
zIndex={1}
|
||||
>
|
||||
<Box flex={1}>{chatSiteData?.name}</Box>
|
||||
{/* 滚动到底部按键 */}
|
||||
{ChatBox.current && ChatBox.current.scrollHeight > 2 * ChatBox.current.clientHeight && (
|
||||
<Box mr={10} cursor={'pointer'} onClick={scrollToBottom}>
|
||||
<Icon
|
||||
name={'icon-xiangxiazhankai-xianxingyuankuang'}
|
||||
width={25}
|
||||
height={25}
|
||||
color={'#718096'}
|
||||
></Icon>
|
||||
</Box>
|
||||
)}
|
||||
{/* 重置按键 */}
|
||||
<Button size={'sm'} colorScheme={'gray'} onClick={resetChat}>
|
||||
新对话
|
||||
</Button>
|
||||
</Flex>
|
||||
{/* 聊天内容 */}
|
||||
<Box ref={ChatBox} flex={'1 0 0'} h={0} w={'100%'} px={0} pb={10} overflowY={'auto'}>
|
||||
{chatList.map((item, index) => (
|
||||
<Box
|
||||
key={index}
|
||||
py={media(9, 6)}
|
||||
px={media(4, 2)}
|
||||
backgroundColor={index % 2 === 0 ? 'rgba(247,247,248,1)' : '#fff'}
|
||||
borderBottom={'1px solid rgba(0,0,0,0.1)'}
|
||||
>
|
||||
<Flex maxW={'800px'} m={'auto'} alignItems={'flex-start'}>
|
||||
<Box mr={media(4, 1)}>
|
||||
<Image
|
||||
src={item.obj === 'Human' ? '/icon/human.png' : '/icon/logo.png'}
|
||||
alt="/icon/logo.png"
|
||||
width={media(30, 20)}
|
||||
height={media(30, 20)}
|
||||
/>
|
||||
</Box>
|
||||
<Box flex={'1 0 0'} w={0} overflow={'hidden'}>
|
||||
{item.obj === 'AI' ? (
|
||||
<Markdown
|
||||
source={item.value}
|
||||
isChatting={isChatting && index === chatList.length - 1}
|
||||
<Flex h={'100%'}>
|
||||
<SlideBar resetChat={resetChat} />
|
||||
<Flex flex={1} h={'100%'} flexDirection={'column'}>
|
||||
{/* 聊天内容 */}
|
||||
<Box ref={ChatBox} flex={'1 0 0'} h={0} w={'100%'} overflowY={'auto'}>
|
||||
{chatList.map((item, index) => (
|
||||
<Box
|
||||
key={index}
|
||||
py={media(9, 6)}
|
||||
px={media(4, 2)}
|
||||
backgroundColor={index % 2 === 0 ? 'rgba(247,247,248,1)' : '#fff'}
|
||||
borderBottom={'1px solid rgba(0,0,0,0.1)'}
|
||||
>
|
||||
<Flex maxW={'750px'} m={'auto'} alignItems={'flex-start'}>
|
||||
<Box mr={media(4, 1)}>
|
||||
<Image
|
||||
src={item.obj === 'Human' ? '/icon/human.png' : '/icon/logo.png'}
|
||||
alt="/icon/logo.png"
|
||||
width={media(30, 20)}
|
||||
height={media(30, 20)}
|
||||
/>
|
||||
</Box>
|
||||
<Box flex={'1 0 0'} w={0} overflow={'hidden'}>
|
||||
{item.obj === 'AI' ? (
|
||||
<Markdown
|
||||
source={item.value}
|
||||
isChatting={isChatting && index === chatList.length - 1}
|
||||
/>
|
||||
) : (
|
||||
<Box whiteSpace={'pre-wrap'}>{item.value}</Box>
|
||||
)}
|
||||
</Box>
|
||||
</Flex>
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
{/* 发送区 */}
|
||||
<Box
|
||||
m={media('20px auto', '0 auto')}
|
||||
w={media('100vw', '100%')}
|
||||
maxW={media('750px', 'auto')}
|
||||
boxShadow={'0 -14px 30px rgba(255,255,255,0.6)'}
|
||||
borderTop={media('none', '1px solid rgba(0,0,0,0.1)')}
|
||||
>
|
||||
{lastWordHuman ? (
|
||||
<Box textAlign={'center'}>
|
||||
<Box color={'red'}>对话出现了异常</Box>
|
||||
<Flex py={5} justifyContent={'center'}>
|
||||
<Button mr={20} onClick={resetChat} colorScheme={'green'}>
|
||||
重开对话
|
||||
</Button>
|
||||
<Button onClick={reEdit}>重新编辑最后一句</Button>
|
||||
</Flex>
|
||||
</Box>
|
||||
) : (
|
||||
<Box
|
||||
py={5}
|
||||
position={'relative'}
|
||||
boxShadow={'base'}
|
||||
overflow={'hidden'}
|
||||
borderRadius={media('md', 'none')}
|
||||
>
|
||||
{/* 输入框 */}
|
||||
<Textarea
|
||||
ref={TextareaDom}
|
||||
w={'100%'}
|
||||
pr={'45px'}
|
||||
py={0}
|
||||
border={'none'}
|
||||
_focusVisible={{
|
||||
border: 'none'
|
||||
}}
|
||||
placeholder="提问"
|
||||
resize={'none'}
|
||||
value={inputVal}
|
||||
rows={1}
|
||||
height={'22px'}
|
||||
lineHeight={'22px'}
|
||||
maxHeight={'150px'}
|
||||
maxLength={chatSiteData?.secret.contentMaxLen || -1}
|
||||
overflowY={'auto'}
|
||||
onChange={(e) => {
|
||||
const textarea = e.target;
|
||||
setInputVal(textarea.value);
|
||||
textarea.style.height = textareaMinH;
|
||||
textarea.style.height = `${textarea.scrollHeight}px`;
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
// 触发快捷发送
|
||||
if (isPc && e.keyCode === 13 && !e.shiftKey) {
|
||||
sendPrompt();
|
||||
e.preventDefault();
|
||||
}
|
||||
// 全选内容
|
||||
// @ts-ignore
|
||||
e.key === 'a' && e.ctrlKey && e.target?.select();
|
||||
}}
|
||||
/>
|
||||
{/* 发送和等待按键 */}
|
||||
<Box position={'absolute'} bottom={5} right={media('20px', '10px')}>
|
||||
{isChatting ? (
|
||||
<Image
|
||||
style={{ transform: 'translateY(4px)' }}
|
||||
src={'/icon/chatting.svg'}
|
||||
width={30}
|
||||
height={30}
|
||||
alt={''}
|
||||
/>
|
||||
) : (
|
||||
<Box whiteSpace={'pre-wrap'}>{item.value}</Box>
|
||||
<Box cursor={'pointer'} onClick={sendPrompt}>
|
||||
<Icon name={'icon-fasong'} width={20} height={20} color={'#718096'}></Icon>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
</Flex>
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
{/* 空内容提示 */}
|
||||
{/* {
|
||||
chatList.length === 0 && (
|
||||
<>
|
||||
<Card>
|
||||
内容太长
|
||||
</Card>
|
||||
</>
|
||||
)
|
||||
} */}
|
||||
<Box
|
||||
m={media('20px auto', '0 auto')}
|
||||
w={media('100vw', '100%')}
|
||||
maxW={media('800px', 'auto')}
|
||||
boxShadow={'0 -14px 30px rgba(255,255,255,0.6)'}
|
||||
borderTop={media('none', '1px solid rgba(0,0,0,0.1)')}
|
||||
>
|
||||
{lastWordHuman ? (
|
||||
<Box textAlign={'center'}>
|
||||
<Box color={'red'}>对话出现了异常</Box>
|
||||
<Flex py={5} justifyContent={'center'}>
|
||||
<Button mr={20} onClick={resetChat} colorScheme={'green'}>
|
||||
重开对话
|
||||
</Button>
|
||||
<Button onClick={reEdit}>重新编辑最后一句</Button>
|
||||
</Flex>
|
||||
</Box>
|
||||
) : (
|
||||
<Box
|
||||
py={5}
|
||||
position={'relative'}
|
||||
boxShadow={'base'}
|
||||
overflow={'hidden'}
|
||||
borderRadius={media('md', 'none')}
|
||||
>
|
||||
{/* 输入框 */}
|
||||
<Textarea
|
||||
ref={TextareaDom}
|
||||
w={'100%'}
|
||||
pr={'45px'}
|
||||
py={0}
|
||||
border={'none'}
|
||||
_focusVisible={{
|
||||
border: 'none'
|
||||
}}
|
||||
placeholder="提问"
|
||||
resize={'none'}
|
||||
value={inputVal}
|
||||
rows={1}
|
||||
height={'22px'}
|
||||
lineHeight={'22px'}
|
||||
maxHeight={'150px'}
|
||||
maxLength={chatSiteData?.secret.contentMaxLen || -1}
|
||||
overflowY={'auto'}
|
||||
onChange={(e) => {
|
||||
const textarea = e.target;
|
||||
setInputVal(textarea.value);
|
||||
textarea.style.height = textareaMinH;
|
||||
textarea.style.height = `${textarea.scrollHeight}px`;
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
// 触发快捷发送
|
||||
if (isPc && e.keyCode === 13 && !e.shiftKey) {
|
||||
sendPrompt();
|
||||
e.preventDefault();
|
||||
}
|
||||
// 全选内容
|
||||
// @ts-ignore
|
||||
e.key === 'a' && e.ctrlKey && e.target?.select();
|
||||
}}
|
||||
/>
|
||||
{/* 发送和等待按键 */}
|
||||
<Box position={'absolute'} bottom={5} right={media('20px', '10px')}>
|
||||
{isChatting ? (
|
||||
<Image
|
||||
style={{ transform: 'translateY(4px)' }}
|
||||
src={'/icon/chatting.svg'}
|
||||
width={30}
|
||||
height={30}
|
||||
alt={''}
|
||||
/>
|
||||
) : (
|
||||
<Box cursor={'pointer'} onClick={sendPrompt}>
|
||||
<Icon name={'icon-fasong'} width={20} height={20} color={'#718096'}></Icon>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
@@ -429,8 +406,9 @@ export default Chat;
|
||||
export async function getServerSideProps(context: any) {
|
||||
const chatId = context.query?.chatId || '';
|
||||
const windowId = context.query?.windowId || '';
|
||||
const timeStamp = context.query?.timeStamp || `${Date.now()}`;
|
||||
|
||||
return {
|
||||
props: { chatId, windowId }
|
||||
props: { chatId, windowId, timeStamp }
|
||||
};
|
||||
}
|
||||
|
@@ -1,6 +1,5 @@
|
||||
import React, { useState, useCallback } from 'react';
|
||||
import { Box, Button, Flex, Card } from '@chakra-ui/react';
|
||||
import { getMyModels } from '@/api/model';
|
||||
import { getChatSiteId } from '@/api/chat';
|
||||
import { ModelType } from '@/types/model';
|
||||
import { useRouter } from 'next/router';
|
||||
@@ -11,6 +10,7 @@ import { useQuery } from '@tanstack/react-query';
|
||||
import { useLoading } from '@/hooks/useLoading';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import { useUserStore } from '@/store/user';
|
||||
|
||||
const CreateModel = dynamic(() => import('./components/CreateModel'));
|
||||
|
||||
@@ -18,22 +18,20 @@ const ModelList = () => {
|
||||
const { toast } = useToast();
|
||||
const { isPc } = useScreen();
|
||||
const router = useRouter();
|
||||
const [models, setModels] = useState<ModelType[]>([]);
|
||||
const { myModels, setMyModels, getMyModels } = useUserStore();
|
||||
const [openCreateModel, setOpenCreateModel] = useState(false);
|
||||
const { Loading, setIsLoading } = useLoading();
|
||||
|
||||
/* 加载模型 */
|
||||
const { isLoading } = useQuery(['loadModels'], () => getMyModels(), {
|
||||
onSuccess(res) {
|
||||
if (!res) return;
|
||||
setModels(res);
|
||||
}
|
||||
});
|
||||
const { isLoading } = useQuery(['loadModels'], getMyModels);
|
||||
|
||||
/* 创建成功回调 */
|
||||
const createModelSuccess = useCallback((data: ModelType) => {
|
||||
setModels((state) => [data, ...state]);
|
||||
}, []);
|
||||
const createModelSuccess = useCallback(
|
||||
(data: ModelType) => {
|
||||
setMyModels([data, ...myModels]);
|
||||
},
|
||||
[myModels, setMyModels]
|
||||
);
|
||||
|
||||
/* 点前往聊天预览页 */
|
||||
const handlePreviewChat = useCallback(
|
||||
@@ -74,9 +72,9 @@ const ModelList = () => {
|
||||
{/* 表单 */}
|
||||
<Box mt={5} position={'relative'}>
|
||||
{isPc ? (
|
||||
<ModelTable models={models} handlePreviewChat={handlePreviewChat} />
|
||||
<ModelTable models={myModels} handlePreviewChat={handlePreviewChat} />
|
||||
) : (
|
||||
<ModelPhoneList models={models} handlePreviewChat={handlePreviewChat} />
|
||||
<ModelPhoneList models={myModels} handlePreviewChat={handlePreviewChat} />
|
||||
)}
|
||||
</Box>
|
||||
{/* 创建弹窗 */}
|
||||
|
@@ -28,7 +28,7 @@ export const jsonRes = (
|
||||
} else if (openaiError[error?.response?.statusText]) {
|
||||
msg = openaiError[error.response.statusText];
|
||||
}
|
||||
|
||||
// console.log(error?.response)
|
||||
console.log('error->', error.code, error?.response?.statusText, msg);
|
||||
}
|
||||
|
||||
|
@@ -2,25 +2,30 @@ import { create } from 'zustand';
|
||||
import { devtools } from 'zustand/middleware';
|
||||
import { immer } from 'zustand/middleware/immer';
|
||||
import type { UserType, UserUpdateParams } from '@/types/user';
|
||||
import type { ModelType } from '@/types/model';
|
||||
import { setToken } from '@/utils/user';
|
||||
import { getMyModels } from '@/api/model';
|
||||
|
||||
type State = {
|
||||
userInfo: UserType | null;
|
||||
setUserInfo: (user: UserType, token?: string) => void;
|
||||
updateUserInfo: (user: UserUpdateParams) => void;
|
||||
myModels: ModelType[];
|
||||
getMyModels: () => void;
|
||||
setMyModels: (data: ModelType[]) => void;
|
||||
};
|
||||
|
||||
export const useUserStore = create<State>()(
|
||||
devtools(
|
||||
immer((set, get) => ({
|
||||
userInfo: null,
|
||||
setUserInfo: (user: UserType, token?: string) => {
|
||||
setUserInfo(user: UserType, token?: string) {
|
||||
set((state) => {
|
||||
state.userInfo = user;
|
||||
});
|
||||
token && setToken(token);
|
||||
},
|
||||
updateUserInfo: (user: UserUpdateParams) => {
|
||||
updateUserInfo(user: UserUpdateParams) {
|
||||
set((state) => {
|
||||
if (!state.userInfo) return;
|
||||
state.userInfo = {
|
||||
@@ -28,6 +33,20 @@ export const useUserStore = create<State>()(
|
||||
...user
|
||||
};
|
||||
});
|
||||
},
|
||||
myModels: [],
|
||||
getMyModels: () =>
|
||||
getMyModels().then((res) => {
|
||||
set((state) => {
|
||||
state.myModels = res;
|
||||
});
|
||||
return res;
|
||||
}),
|
||||
setMyModels(data: ModelType[]) {
|
||||
set((state) => {
|
||||
state.myModels = data;
|
||||
});
|
||||
return null;
|
||||
}
|
||||
}))
|
||||
)
|
||||
|
Reference in New Issue
Block a user