123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337 |
- import { ChatContext } from '@/app/chat-context';
- import { apiInterceptors, getAppInfo, getChatHistory, getDialogueList } from '@/client/api';
- import useChat from '@/hooks/use-chat';
- import ChatContentContainer from '@/new-components/chat/ChatContentContainer';
- import ChatDefault from '@/new-components/chat/content/ChatDefault';
- import ChatInputPanel from '@/new-components/chat/input/ChatInputPanel';
- import ChatSider from '@/new-components/chat/sider/ChatSider';
- import { IApp } from '@/types/app';
- import { ChartData, ChatHistoryResponse, IChatDialogueSchema } from '@/types/chat';
- import { getInitMessage } from '@/utils';
- import { useAsyncEffect, useRequest } from 'ahooks';
- import { Flex, Layout, Spin } from 'antd';
- import dynamic from 'next/dynamic';
- import { useSearchParams } from 'next/navigation';
- import React, { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
- const DbEditor = dynamic(() => import('@/components/chat/db-editor'), {
- ssr: false,
- });
- const ChatContainer = dynamic(() => import('@/components/chat/chat-container'), { ssr: false });
- const { Content } = Layout;
- interface ChatContentProps {
- history: ChatHistoryResponse; // 会话记录列表
- replyLoading: boolean; // 对话回复loading
- scrollRef: React.RefObject<HTMLDivElement>; // 会话内容可滚动dom
- canAbort: boolean; // 是否能中断回复
- chartsData: ChartData[];
- agent: string;
- currentDialogue: IChatDialogueSchema; // 当前选择的会话
- appInfo: IApp;
- temperatureValue: any;
- resourceValue: any;
- modelValue: string;
- setModelValue: React.Dispatch<React.SetStateAction<string>>;
- setTemperatureValue: React.Dispatch<React.SetStateAction<any>>;
- setResourceValue: React.Dispatch<React.SetStateAction<any>>;
- setAppInfo: React.Dispatch<React.SetStateAction<IApp>>;
- setAgent: React.Dispatch<React.SetStateAction<string>>;
- setCanAbort: React.Dispatch<React.SetStateAction<boolean>>;
- setReplyLoading: React.Dispatch<React.SetStateAction<boolean>>;
- handleChat: (content: string, data?: Record<string, any>) => Promise<void>; // 处理会话请求逻辑函数
- refreshDialogList: () => void;
- refreshHistory: () => void;
- refreshAppInfo: () => void;
- setHistory: React.Dispatch<React.SetStateAction<ChatHistoryResponse>>;
- }
- export const ChatContentContext = createContext<ChatContentProps>({
- history: [],
- replyLoading: false,
- scrollRef: { current: null },
- canAbort: false,
- chartsData: [],
- agent: '',
- currentDialogue: {} as any,
- appInfo: {} as any,
- temperatureValue: 0.5,
- resourceValue: {},
- modelValue: '',
- setModelValue: () => {},
- setResourceValue: () => {},
- setTemperatureValue: () => {},
- setAppInfo: () => {},
- setAgent: () => {},
- setCanAbort: () => {},
- setReplyLoading: () => {},
- refreshDialogList: () => {},
- refreshHistory: () => {},
- refreshAppInfo: () => {},
- setHistory: () => {},
- handleChat: () => Promise.resolve(),
- });
- const Chat: React.FC = () => {
- const { model, currentDialogInfo } = useContext(ChatContext);
- const { isContract, setIsContract, setIsMenuExpand } = useContext(ChatContext);
- const { chat, ctrl } = useChat({
- app_code: currentDialogInfo.app_code || '',
- });
- const searchParams = useSearchParams();
- const chatId = searchParams?.get('id') ?? '';
- const scene = searchParams?.get('scene') ?? '';
- const knowledgeId = searchParams?.get('knowledge_id') ?? '';
- const dbName = searchParams?.get('db_name') ?? '';
- const scrollRef = useRef<HTMLDivElement>(null);
- const order = useRef<number>(1);
- const [history, setHistory] = useState<ChatHistoryResponse>([]);
- const [chartsData] = useState<Array<ChartData>>();
- const [replyLoading, setReplyLoading] = useState<boolean>(false);
- const [canAbort, setCanAbort] = useState<boolean>(false);
- const [agent, setAgent] = useState<string>('');
- const [appInfo, setAppInfo] = useState<IApp>({} as IApp);
- const [temperatureValue, setTemperatureValue] = useState();
- const [resourceValue, setResourceValue] = useState<any>();
- const [modelValue, setModelValue] = useState<string>('');
- useEffect(() => {
- setTemperatureValue(appInfo?.param_need?.filter(item => item.type === 'temperature')[0]?.value || 0.5);
- setModelValue(appInfo?.param_need?.filter(item => item.type === 'model')[0]?.value || model);
- setResourceValue(
- knowledgeId || dbName || appInfo?.param_need?.filter(item => item.type === 'resource')[0]?.bind_value,
- );
- }, [appInfo, dbName, knowledgeId, model]);
- useEffect(() => {
- // 仅初始化执行,防止dashboard页面无法切换状态
- setIsMenuExpand(scene !== 'chat_dashboard');
- // 路由变了要取消Editor模式,再进来是默认的Preview模式
- if (chatId && scene) {
- setIsContract(false);
- }
- }, [chatId, scene]);
- // 是否是默认小助手
- const isChatDefault = useMemo(() => {
- return !chatId && !scene;
- }, [chatId, scene]);
- // 获取会话列表
- const {
- data: dialogueList = [],
- refresh: refreshDialogList,
- loading: listLoading,
- } = useRequest(async () => {
- return await apiInterceptors(getDialogueList());
- });
- // 获取应用详情
- const { run: queryAppInfo, refresh: refreshAppInfo } = useRequest(
- async () =>
- await apiInterceptors(
- getAppInfo({
- ...currentDialogInfo,
- }),
- ),
- {
- manual: true,
- onSuccess: data => {
- const [, res] = data;
- setAppInfo(res || ({} as IApp));
- },
- },
- );
- // 列表当前活跃对话
- const currentDialogue = useMemo(() => {
- const [, list] = dialogueList;
- return list?.find(item => item.conv_uid === chatId) || ({} as IChatDialogueSchema);
- }, [chatId, dialogueList]);
- useEffect(() => {
- const initMessage = getInitMessage();
- if (currentDialogInfo.chat_scene === scene && !isChatDefault && !(initMessage && initMessage.message)) {
- queryAppInfo();
- }
- }, [chatId, currentDialogInfo, isChatDefault, queryAppInfo, scene]);
- // 获取会话历史记录
- const {
- run: getHistory,
- loading: historyLoading,
- refresh: refreshHistory,
- } = useRequest(async () => await apiInterceptors(getChatHistory(chatId)), {
- manual: true,
- onSuccess: data => {
- const [, res] = data;
- const viewList = res?.filter(item => item.role === 'view');
- if (viewList && viewList.length > 0) {
- order.current = viewList[viewList.length - 1].order + 1;
- }
- setHistory(res || []);
- },
- });
- // 会话提问
- const handleChat = useCallback(
- (content: string, data?: Record<string, any>) => {
- return new Promise<void>(resolve => {
- const initMessage = getInitMessage();
- const ctrl = new AbortController();
- setReplyLoading(true);
- if (history && history.length > 0) {
- const viewList = history?.filter(item => item.role === 'view');
- const humanList = history?.filter(item => item.role === 'human');
- order.current = (viewList[viewList.length - 1]?.order || humanList[humanList.length - 1]?.order) + 1;
- }
- const tempHistory: ChatHistoryResponse = [
- ...(initMessage && initMessage.id === chatId ? [] : history),
- {
- role: 'human',
- context: content,
- model_name: data?.model_name || modelValue,
- order: order.current,
- time_stamp: 0,
- },
- {
- role: 'view',
- context: '',
- model_name: data?.model_name || modelValue,
- order: order.current,
- time_stamp: 0,
- thinking: true,
- },
- ];
- const index = tempHistory.length - 1;
- setHistory([...tempHistory]);
- chat({
- data: {
- chat_mode: scene,
- model_name: modelValue,
- user_input: content,
- ...data,
- },
- ctrl,
- chatId,
- onMessage: message => {
- setCanAbort(true);
- if (data?.incremental) {
- tempHistory[index].context += message;
- tempHistory[index].thinking = false;
- } else {
- tempHistory[index].context = message;
- tempHistory[index].thinking = false;
- }
- setHistory([...tempHistory]);
- },
- onDone: () => {
- setReplyLoading(false);
- setCanAbort(false);
- resolve();
- },
- onClose: () => {
- setReplyLoading(false);
- setCanAbort(false);
- resolve();
- },
- onError: message => {
- setReplyLoading(false);
- setCanAbort(false);
- tempHistory[index].context = message;
- tempHistory[index].thinking = false;
- setHistory([...tempHistory]);
- resolve();
- },
- });
- });
- },
- [chatId, history, modelValue, chat, scene],
- );
- useAsyncEffect(async () => {
- // 如果是默认小助手,不获取历史记录
- if (isChatDefault) {
- return;
- }
- const initMessage = getInitMessage();
- if (initMessage && initMessage.id === chatId) {
- return;
- }
- await getHistory();
- }, [chatId, scene, getHistory]);
- useEffect(() => {
- if (isChatDefault) {
- order.current = 1;
- setHistory([]);
- }
- }, [isChatDefault]);
- const contentRender = () => {
- if (scene === 'chat_dashboard') {
- return isContract ? <DbEditor /> : <ChatContainer />;
- } else {
- return isChatDefault ? (
- <Content>
- <ChatDefault />
- </Content>
- ) : (
- <Spin spinning={historyLoading} className='w-full h-full m-auto'>
- <Content className='flex flex-col h-screen'>
- <ChatContentContainer ref={scrollRef} />
- <ChatInputPanel ctrl={ctrl} />
- </Content>
- </Spin>
- );
- }
- };
- return (
- <ChatContentContext.Provider
- value={{
- history,
- replyLoading,
- scrollRef,
- canAbort,
- chartsData: chartsData || [],
- agent,
- currentDialogue,
- appInfo,
- temperatureValue,
- resourceValue,
- modelValue,
- setModelValue,
- setResourceValue,
- setTemperatureValue,
- setAppInfo,
- setAgent,
- setCanAbort,
- setReplyLoading,
- handleChat,
- refreshDialogList,
- refreshHistory,
- refreshAppInfo,
- setHistory,
- }}
- >
- <Flex flex={1}>
- <Layout className='bg-gradient-light bg-cover bg-center dark:bg-gradient-dark'>
- <ChatSider
- refresh={refreshDialogList}
- dialogueList={dialogueList}
- listLoading={listLoading}
- historyLoading={historyLoading}
- order={order}
- />
- <Layout className='bg-transparent'>{contentRender()}</Layout>
- </Layout>
- </Flex>
- </ChatContentContext.Provider>
- );
- };
- export default Chat;
|