diff --git a/client/src/api/chat.ts b/client/src/api/chat.ts index ad9dd44b1..b1f3cac72 100644 --- a/client/src/api/chat.ts +++ b/client/src/api/chat.ts @@ -10,7 +10,7 @@ import type { Props as UpdateHistoryProps } from '@/pages/api/chat/history/updat /** * 获取初始化聊天内容 */ -export const getInitChatSiteInfo = (data: { appId: string; historyId?: string }) => +export const getInitChatSiteInfo = (data: { appId: string; chatId?: string }) => GET(`/chat/init`, data); /** @@ -27,14 +27,14 @@ export const delChatHistoryById = (id: string) => GET(`/chat/removeHistory?id=${ /** * get history quotes */ -export const getHistoryQuote = (params: { historyId: string; contentId: string }) => +export const getHistoryQuote = (params: { chatId: string; contentId: string }) => GET<(QuoteItemType & { _id: string })[]>(`/chat/history/getHistoryQuote`, params); /** * update history quote status */ export const updateHistoryQuote = (params: { - historyId: string; + chatId: string; contentId: string; quoteId: string; sourceText: string; @@ -43,7 +43,7 @@ export const updateHistoryQuote = (params: { /** * 删除一句对话 */ -export const delChatRecordByIndex = (data: { historyId: string; contentId: string }) => +export const delChatRecordByIndex = (data: { chatId: string; contentId: string }) => DELETE(`/chat/delChatRecordByContentId`, data); /** diff --git a/client/src/api/fetch.ts b/client/src/api/fetch.ts index d0dd4811e..3b0c75b44 100644 --- a/client/src/api/fetch.ts +++ b/client/src/api/fetch.ts @@ -19,7 +19,7 @@ export const streamFetch = ({ new Promise<{ responseText: string; errMsg: string; - newHistoryId: string | null; + newChatId: string | null; [rawSearchKey]: QuoteItemType[]; }>(async (resolve, reject) => { try { @@ -45,7 +45,7 @@ export const streamFetch = ({ let responseText = ''; let rawSearch: QuoteItemType[] = []; let errMsg = ''; - const newHistoryId = response.headers.get('newHistoryId'); + const newChatId = response.headers.get('newChatId'); const read = async () => { try { @@ -55,7 +55,7 @@ export const streamFetch = ({ return resolve({ responseText, errMsg, - newHistoryId, + newChatId, rawSearch }); } else { @@ -95,7 +95,7 @@ export const streamFetch = ({ return resolve({ responseText, errMsg, - newHistoryId, + newChatId, rawSearch }); } diff --git a/client/src/api/response/chat.d.ts b/client/src/api/response/chat.d.ts index acb867144..303b13df4 100644 --- a/client/src/api/response/chat.d.ts +++ b/client/src/api/response/chat.d.ts @@ -13,7 +13,6 @@ export interface InitChatResponse { intro: string; canUse: boolean; }; - customTitle?: string; title: string; variables: Record; history: ChatItemType[]; diff --git a/client/src/components/ChatBox/QuoteModal.tsx b/client/src/components/ChatBox/QuoteModal.tsx index fcc320206..0af5dbd35 100644 --- a/client/src/components/ChatBox/QuoteModal.tsx +++ b/client/src/components/ChatBox/QuoteModal.tsx @@ -20,12 +20,12 @@ import { getErrText } from '@/utils/tools'; import { QuoteItemType } from '@/pages/api/app/modules/kb/search'; const QuoteModal = ({ - historyId, + chatId, contentId, rawSearch = [], onClose }: { - historyId?: string; + chatId?: string; contentId?: string; rawSearch?: QuoteItemType[]; onClose: () => void; @@ -45,8 +45,8 @@ const QuoteModal = ({ refetch, isLoading } = useQuery(['getHistoryQuote'], () => { - if (historyId && contentId) { - return getHistoryQuote({ historyId, contentId }); + if (chatId && contentId) { + return getHistoryQuote({ chatId, contentId }); } if (rawSearch.length > 0) { return rawSearch; @@ -59,12 +59,12 @@ const QuoteModal = ({ */ const updateQuoteStatus = useCallback( async (quoteId: string, sourceText: string) => { - if (!historyId || !contentId) return; + if (!chatId || !contentId) return; setIsLoading(true); try { await updateHistoryQuote({ contentId, - historyId, + chatId, quoteId, sourceText }); @@ -78,7 +78,7 @@ const QuoteModal = ({ } setIsLoading(false); }, - [contentId, historyId, refetch, setIsLoading, toast] + [contentId, chatId, refetch, setIsLoading, toast] ); /** diff --git a/client/src/components/ChatBox/index.tsx b/client/src/components/ChatBox/index.tsx index 4f6128793..686610b14 100644 --- a/client/src/components/ChatBox/index.tsx +++ b/client/src/components/ChatBox/index.tsx @@ -49,7 +49,7 @@ export type StartChatFnProps = { export type ComponentRef = { getChatHistory: () => ChatSiteItemType[]; resetVariables: (data?: Record) => void; - resetHistory: (history: ChatSiteItemType[]) => void; + resetHistory: (chatId: ChatSiteItemType[]) => void; scrollToBottom: (behavior?: 'smooth' | 'auto') => void; }; @@ -76,11 +76,10 @@ const Empty = () => { return ( @@ -110,7 +109,7 @@ const ChatAvatar = ({ const ChatBox = ( { showEmptyIntro = false, - historyId, + chatId, appAvatar, variableModules, welcomeText, @@ -119,7 +118,7 @@ const ChatBox = ( onDelMessage }: { showEmptyIntro?: boolean; - historyId?: string; + chatId?: string; appAvatar: string; variableModules?: VariableItemType[]; welcomeText?: string; @@ -389,14 +388,16 @@ const ChatBox = ( }; const showEmpty = useMemo( - () => showEmptyIntro && chatHistory.length === 0 && !(variableModules || welcomeText), + () => showEmptyIntro && chatHistory.length === 0 && !variableModules?.length && !welcomeText, [chatHistory.length, showEmptyIntro, variableModules, welcomeText] ); return ( - + + {showEmpty && } + {!!welcomeText && ( {/* avatar */} @@ -410,7 +411,7 @@ const ChatBox = ( )} {/* variable input */} - {variableModules && ( + {!!variableModules?.length && ( {/* avatar */} @@ -467,7 +468,7 @@ const ChatBox = ( )} {/* chat history */} - + {chatHistory.map((item, index) => ( ))} - - {showEmpty && } {/* input */} {variableIsFinish ? ( - + setQuoteModalData(undefined)} /> )} - {/* quote modal */} ); }; diff --git a/client/src/components/Layout/navbar.tsx b/client/src/components/Layout/navbar.tsx index 7f81a5ea9..01fcd2b89 100644 --- a/client/src/components/Layout/navbar.tsx +++ b/client/src/components/Layout/navbar.tsx @@ -17,14 +17,14 @@ export enum NavbarTypeEnum { const Navbar = ({ unread }: { unread: number }) => { const router = useRouter(); const { userInfo } = useUserStore(); - const { lastChatAppId, lastHistoryId } = useChatStore(); + const { lastChatAppId, lastChatId } = useChatStore(); const navbarList = useMemo( () => [ { label: '聊天', icon: 'chatLight', activeIcon: 'chatFill', - link: `/chat?appId=${lastChatAppId}&historyId=${lastHistoryId}`, + link: `/chat?appId=${lastChatAppId}&chatId=${lastChatId}`, activeLink: ['/chat'] }, { @@ -56,7 +56,7 @@ const Navbar = ({ unread }: { unread: number }) => { activeLink: ['/number'] } ], - [lastHistoryId, lastChatAppId] + [lastChatId, lastChatAppId] ); const itemStyles: any = { diff --git a/client/src/components/Layout/navbarPhone.tsx b/client/src/components/Layout/navbarPhone.tsx index eb925a0ed..af502f74f 100644 --- a/client/src/components/Layout/navbarPhone.tsx +++ b/client/src/components/Layout/navbarPhone.tsx @@ -7,13 +7,13 @@ import Badge from '../Badge'; const NavbarPhone = ({ unread }: { unread: number }) => { const router = useRouter(); - const { lastChatAppId, lastHistoryId } = useChatStore(); + const { lastChatAppId, lastChatId } = useChatStore(); const navbarList = useMemo( () => [ { label: '聊天', icon: 'chatLight', - link: `/chat?appId=${lastChatAppId}&historyId=${lastHistoryId}`, + link: `/chat?appId=${lastChatAppId}&chatId=${lastChatId}`, activeLink: ['/chat'], unread: 0 }, @@ -39,7 +39,7 @@ const NavbarPhone = ({ unread }: { unread: number }) => { unread } ], - [lastHistoryId, lastChatAppId, unread] + [lastChatId, lastChatAppId, unread] ); return ( diff --git a/client/src/components/Markdown/index.tsx b/client/src/components/Markdown/index.tsx index e5885972b..c8fed70d8 100644 --- a/client/src/components/Markdown/index.tsx +++ b/client/src/components/Markdown/index.tsx @@ -43,6 +43,7 @@ const Markdown = ({ source, isChatting = false }: { source: string; isChatting?: a: Link, img: Image, pre: 'div', + p: 'div', code: Code }} > diff --git a/client/src/pages/api/chat/delChatRecordByContentId.ts b/client/src/pages/api/chat/delChatRecordByContentId.ts index b39c1aaa2..40a4ca329 100644 --- a/client/src/pages/api/chat/delChatRecordByContentId.ts +++ b/client/src/pages/api/chat/delChatRecordByContentId.ts @@ -5,10 +5,9 @@ import { authUser } from '@/service/utils/auth'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { - const { historyId, contentId } = req.query as { historyId: string; contentId: string }; - console.log(historyId, contentId); + const { chatId, contentId } = req.query as { chatId: string; contentId: string }; - if (!historyId || !contentId) { + if (!chatId || !contentId) { throw new Error('缺少参数'); } @@ -17,7 +16,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) // 凭证校验 const { userId } = await authUser({ req, authToken: true }); - const chatRecord = await Chat.findById(historyId); + const chatRecord = await Chat.findById(chatId); if (!chatRecord) { throw new Error('找不到对话'); @@ -26,7 +25,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) // 删除一条数据库记录 await Chat.updateOne( { - _id: historyId, + _id: chatId, userId }, { $pull: { content: { _id: contentId } } } diff --git a/client/src/pages/api/chat/history/getHistory.ts b/client/src/pages/api/chat/history/getHistory.ts index d40d66b14..90ec90a34 100644 --- a/client/src/pages/api/chat/history/getHistory.ts +++ b/client/src/pages/api/chat/history/getHistory.ts @@ -27,7 +27,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) _id: item._id, updateTime: item.updateTime, appId: item.appId, - title: item.customTitle || item.title, + customTitle: item.customTitle, + title: item.title, top: item.top })) }); diff --git a/client/src/pages/api/chat/history/getHistoryQuote.ts b/client/src/pages/api/chat/history/getHistoryQuote.ts index 34b37f26e..18571a071 100644 --- a/client/src/pages/api/chat/history/getHistoryQuote.ts +++ b/client/src/pages/api/chat/history/getHistoryQuote.ts @@ -7,22 +7,22 @@ import { rawSearchKey } from '@/constants/chat'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { - const { historyId, contentId } = req.query as { - historyId: string; + const { chatId, contentId } = req.query as { + chatId: string; contentId: string; }; await connectToDatabase(); const { userId } = await authUser({ req, authToken: true }); - if (!historyId || !contentId) { + if (!chatId || !contentId) { throw new Error('params is error'); } const history = await Chat.aggregate([ { $match: { - _id: new Types.ObjectId(historyId), + _id: new Types.ObjectId(chatId), userId: new Types.ObjectId(userId) } }, diff --git a/client/src/pages/api/chat/history/updateChatHistory.ts b/client/src/pages/api/chat/history/updateChatHistory.ts index 9eac2bfaf..7c5595314 100644 --- a/client/src/pages/api/chat/history/updateChatHistory.ts +++ b/client/src/pages/api/chat/history/updateChatHistory.ts @@ -4,7 +4,7 @@ import { connectToDatabase, Chat } from '@/service/mongo'; import { authUser } from '@/service/utils/auth'; export type Props = { - historyId: string; + chatId: string; customTitle?: string; top?: boolean; }; @@ -12,7 +12,7 @@ export type Props = { /* 更新聊天标题 */ export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { - const { historyId, customTitle, top } = req.body as Props; + const { chatId, customTitle, top } = req.body as Props; const { userId } = await authUser({ req, authToken: true }); @@ -20,7 +20,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) await Chat.findOneAndUpdate( { - _id: historyId, + _id: chatId, userId }, { diff --git a/client/src/pages/api/chat/history/updateHistoryQuote.ts b/client/src/pages/api/chat/history/updateHistoryQuote.ts index a8580e91d..65dcc9ae3 100644 --- a/client/src/pages/api/chat/history/updateHistoryQuote.ts +++ b/client/src/pages/api/chat/history/updateHistoryQuote.ts @@ -7,12 +7,12 @@ import { Types } from 'mongoose'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { let { - historyId, + chatId, contentId, quoteId, sourceText = '' } = req.body as { - historyId: string; + chatId: string; contentId: string; quoteId: string; sourceText: string; @@ -21,13 +21,13 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) const { userId } = await authUser({ req, authToken: true }); - if (!contentId || !historyId || !quoteId) { + if (!contentId || !chatId || !quoteId) { throw new Error('params is error'); } await Chat.updateOne( { - _id: new Types.ObjectId(historyId), + _id: new Types.ObjectId(chatId), userId: new Types.ObjectId(userId), 'content._id': new Types.ObjectId(contentId) }, diff --git a/client/src/pages/api/chat/init.ts b/client/src/pages/api/chat/init.ts index 261c8a8fe..fa9b8e8aa 100644 --- a/client/src/pages/api/chat/init.ts +++ b/client/src/pages/api/chat/init.ts @@ -53,7 +53,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) const { chat, history = [] }: { chat?: ChatSchema; history?: ChatItemType[] } = await (async () => { if (chatId) { - // auth historyId + // auth chatId const chat = await Chat.findOne({ _id: chatId, userId @@ -104,7 +104,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) intro: app.intro, canUse: app.share.isShare || isOwner }, - customTitle: chat?.customTitle, title: chat?.title || '新对话', variables: chat?.variables || {}, history diff --git a/client/src/pages/api/chat/saveChat.ts b/client/src/pages/api/chat/saveChat.ts index cc7f9d1f4..35e94957d 100644 --- a/client/src/pages/api/chat/saveChat.ts +++ b/client/src/pages/api/chat/saveChat.ts @@ -7,7 +7,7 @@ import { authUser } from '@/service/utils/auth'; import { Types } from 'mongoose'; type Props = { - historyId?: string; + chatId?: string; appId: string; variables?: Record; prompts: [ChatItemType, ChatItemType]; @@ -16,7 +16,7 @@ type Props = { /* 聊天内容存存储 */ export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { - const { historyId, appId, prompts } = req.body as Props; + const { chatId, appId, prompts } = req.body as Props; if (!prompts) { throw new Error('缺少参数'); @@ -25,7 +25,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) const { userId } = await authUser({ req, authToken: true }); const response = await saveChat({ - historyId, + chatId, appId, prompts, userId @@ -43,13 +43,13 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) } export async function saveChat({ - newHistoryId, - historyId, + newChatId, + chatId, appId, prompts, variables, userId -}: Props & { newHistoryId?: Types.ObjectId; userId: string }): Promise<{ newHistoryId: string }> { +}: Props & { newChatId?: Types.ObjectId; userId: string }): Promise<{ newChatId: string }> { await connectToDatabase(); const { app } = await authApp({ appId, userId, authOwner: false }); @@ -60,9 +60,9 @@ export async function saveChat({ } const [response] = await Promise.all([ - ...(historyId + ...(chatId ? [ - Chat.findByIdAndUpdate(historyId, { + Chat.findByIdAndUpdate(chatId, { $push: { content: { $each: prompts @@ -72,19 +72,19 @@ export async function saveChat({ title: prompts[0].value.slice(0, 20), updateTime: new Date() }).then(() => ({ - newHistoryId: '' + newChatId: '' })) ] : [ Chat.create({ - _id: newHistoryId, + _id: newChatId, userId, appId, variables, content: prompts, title: prompts[0].value.slice(0, 20) }).then((res) => ({ - newHistoryId: String(res._id) + newChatId: String(res._id) })) ]), // update app @@ -99,6 +99,6 @@ export async function saveChat({ return { // @ts-ignore - newHistoryId: response?.newHistoryId || '' + newChatId: response?.newChatId || '' }; } diff --git a/client/src/pages/api/openapi/v1/chat/completions.ts b/client/src/pages/api/openapi/v1/chat/completions.ts index daec3aa84..74b5ac703 100644 --- a/client/src/pages/api/openapi/v1/chat/completions.ts +++ b/client/src/pages/api/openapi/v1/chat/completions.ts @@ -20,7 +20,7 @@ import { BillSourceEnum } from '@/constants/user'; export type MessageItemType = ChatCompletionRequestMessage & { _id?: string }; type FastGptWebChatProps = { - historyId?: string; // undefined: nonuse history, '': new chat, 'xxxxx': use history + chatId?: string; // undefined: nonuse history, '': new chat, 'xxxxx': use history appId?: string; }; type FastGptShareChatProps = { @@ -47,14 +47,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex res.end(); }); - let { - historyId, - appId, - shareId, - stream = false, - messages = [], - variables = {} - } = req.body as Props; + let { chatId, appId, shareId, stream = false, messages = [], variables = {} } = req.body as Props; let billId = ''; @@ -91,7 +84,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex appId, userId }), - getChatHistory({ historyId, userId }) + getChatHistory({ chatId, userId }) ]); const isOwner = !shareId && userId === String(app.userId); @@ -107,9 +100,9 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex throw new Error('Question is empty'); } - const newHistoryId = historyId === '' ? new Types.ObjectId() : undefined; - if (stream && newHistoryId) { - res.setHeader('newHistoryId', String(newHistoryId)); + const newChatId = chatId === '' ? new Types.ObjectId() : undefined; + if (stream && newChatId) { + res.setHeader('newChatId', String(newChatId)); } billId = await createTaskBill({ @@ -133,10 +126,10 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex }); // save chat - if (typeof historyId === 'string') { + if (typeof chatId === 'string') { await saveChat({ - historyId, - newHistoryId, + chatId, + newChatId, appId, variables, prompts: [ @@ -173,10 +166,10 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex } else { res.json({ data: { - newHistoryId, + newChatId, ...responseData }, - id: historyId || '', + id: chatId || '', model: '', usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 }, choices: [ diff --git a/client/src/pages/api/openapi/v1/chat/getHistory.ts b/client/src/pages/api/openapi/v1/chat/getHistory.ts index ceeaba3ef..2531f4b8b 100644 --- a/client/src/pages/api/openapi/v1/chat/getHistory.ts +++ b/client/src/pages/api/openapi/v1/chat/getHistory.ts @@ -7,7 +7,7 @@ import { Types } from 'mongoose'; import type { ChatItemType } from '@/types/chat'; export type Props = { - historyId?: string; + chatId?: string; limit?: number; }; export type Response = { history: ChatItemType[] }; @@ -16,11 +16,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) try { await connectToDatabase(); const { userId } = await authUser({ req }); - const { historyId, limit } = req.body as Props; + const { chatId, limit } = req.body as Props; jsonRes(res, { data: await getChatHistory({ - historyId, + chatId, userId, limit }) @@ -34,16 +34,16 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) } export async function getChatHistory({ - historyId, + chatId, userId, limit = 50 }: Props & { userId: string }): Promise { - if (!historyId) { + if (!chatId) { return { history: [] }; } const history = await Chat.aggregate([ - { $match: { _id: new Types.ObjectId(historyId), userId: new Types.ObjectId(userId) } }, + { $match: { _id: new Types.ObjectId(chatId), userId: new Types.ObjectId(userId) } }, { $project: { content: { diff --git a/client/src/pages/chat/components/ChatHistorySlider.tsx b/client/src/pages/chat/components/ChatHistorySlider.tsx index f145bc58d..81123c2f2 100644 --- a/client/src/pages/chat/components/ChatHistorySlider.tsx +++ b/client/src/pages/chat/components/ChatHistorySlider.tsx @@ -1,4 +1,4 @@ -import React, { useMemo } from 'react'; +import React, { useMemo, useState } from 'react'; import { Box, Button, @@ -11,6 +11,7 @@ import { IconButton } from '@chakra-ui/react'; import { useGlobalStore } from '@/store/global'; +import { useEditInfo } from '@/hooks/useEditInfo'; import { useRouter } from 'next/router'; import Avatar from '@/components/Avatar'; import MyTooltip from '@/components/MyTooltip'; @@ -19,6 +20,7 @@ import MyIcon from '@/components/Icon'; type HistoryItemType = { id: string; title: string; + customTitle?: string; top?: boolean; }; @@ -27,29 +29,34 @@ const ChatHistorySlider = ({ appName, appAvatar, history, - activeHistoryId, + activeChatId, onChangeChat, onDelHistory, onSetHistoryTop, - onUpdateTitle + onSetCustomTitle }: { appId?: string; appName: string; appAvatar: string; history: HistoryItemType[]; - activeHistoryId: string; - onChangeChat: (historyId?: string) => void; - onDelHistory: (historyId: string) => void; - onSetHistoryTop?: (e: { historyId: string; top: boolean }) => void; - onUpdateTitle?: (e: { historyId: string; title: string }) => void; + activeChatId: string; + onChangeChat: (chatId?: string) => void; + onDelHistory: (chatId: string) => void; + onSetHistoryTop?: (e: { chatId: string; top: boolean }) => void; + onSetCustomTitle?: (e: { chatId: string; title: string }) => void; }) => { const theme = useTheme(); const router = useRouter(); const { isPc } = useGlobalStore(); + // custom title edit + const { onOpenModal, EditModal: EditTitleModal } = useEditInfo({ + title: '自定义历史记录标题', + placeholder: '如果设置为空,会自动跟随聊天记录。' + }); const concatHistory = useMemo( - () => (!activeHistoryId ? [{ id: activeHistoryId, title: '新对话' }].concat(history) : history), - [activeHistoryId, history] + () => (!activeChatId ? [{ id: activeChatId, title: '新对话' }].concat(history) : history), + [activeChatId, history] ); return ( @@ -121,7 +128,7 @@ const ChatHistorySlider = ({ } }} bg={item.top ? '#E6F6F6 !important' : ''} - {...(item.id === activeHistoryId + {...(item.id === activeChatId ? { backgroundColor: 'myBlue.100 !important', color: 'myBlue.700' @@ -132,9 +139,9 @@ const ChatHistorySlider = ({ } })} > - + - {item.title} + {item.customTitle || item.title} {!!item.id && ( @@ -154,20 +161,24 @@ const ChatHistorySlider = ({ { e.stopPropagation(); - onSetHistoryTop({ historyId: item.id, top: !item.top }); + onSetHistoryTop({ chatId: item.id, top: !item.top }); }} > {item.top ? '取消置顶' : '置顶'} )} - {onUpdateTitle && ( + {onSetCustomTitle && ( { e.stopPropagation(); - onUpdateTitle({ - historyId: item.id, - title: '是是是' + onOpenModal({ + defaultVal: item.customTitle || item.title, + onSuccess: (e) => + onSetCustomTitle({ + chatId: item.id, + title: e + }) }); }} > @@ -180,7 +191,7 @@ const ChatHistorySlider = ({ onClick={(e) => { e.stopPropagation(); onDelHistory(item.id); - if (item.id === activeHistoryId) { + if (item.id === activeChatId) { onChangeChat(); } }} @@ -218,6 +229,7 @@ const ChatHistorySlider = ({ 切换应用 )} + ); }; diff --git a/client/src/pages/chat/index.tsx b/client/src/pages/chat/index.tsx index eb064326e..a6b5af224 100644 --- a/client/src/pages/chat/index.tsx +++ b/client/src/pages/chat/index.tsx @@ -26,7 +26,7 @@ import ChatHeader from './components/ChatHeader'; const Chat = () => { const router = useRouter(); - const { appId = '', historyId = '' } = router.query as { appId: string; historyId: string }; + const { appId = '', chatId = '' } = router.query as { appId: string; chatId: string }; const theme = useTheme(); const ChatBoxRef = useRef(null); @@ -35,8 +35,8 @@ const Chat = () => { const { lastChatAppId, setLastChatAppId, - lastHistoryId, - setLastHistoryId, + lastChatId, + setLastChatId, history, loadHistory, updateHistory, @@ -52,12 +52,12 @@ const Chat = () => { const startChat = useCallback( async ({ messages, controller, generatingMessage, variables }: StartChatFnProps) => { const prompts = messages.slice(-2); - const { responseText, newHistoryId, rawSearch } = await streamFetch({ + const { responseText, newChatId, rawSearch } = await streamFetch({ data: { messages: prompts, variables, appId, - historyId + chatId }, onMessage: generatingMessage, abortSignal: controller @@ -66,27 +66,27 @@ const Chat = () => { const newTitle = prompts[0].content?.slice(0, 20) || '新对话'; // update history - if (newHistoryId) { + if (newChatId) { forbidRefresh.current = true; - router.replace({ - query: { - historyId: newHistoryId, - appId - } - }); const newHistory: ChatHistoryItemType = { - _id: newHistoryId, + _id: newChatId, updateTime: new Date(), title: newTitle, appId, top: false }; updateHistory(newHistory); + router.replace({ + query: { + chatId: newChatId, + appId + } + }); } else { - const currentHistory = history.find((item) => item._id === historyId); - currentHistory && + const currentChat = history.find((item) => item._id === chatId); + currentChat && updateHistory({ - ...currentHistory, + ...currentChat, updateTime: new Date(), title: newTitle }); @@ -100,41 +100,41 @@ const Chat = () => { return { responseText, rawSearch }; }, - [appId, history, historyId, router, setChatData, updateHistory] + [appId, chatId, history, router, setChatData, updateHistory] ); // 删除一句话 const delOneHistoryItem = useCallback( async ({ contentId, index }: { contentId?: string; index: number }) => { - if (!historyId || !contentId) return; + if (!chatId || !contentId) return; try { setChatData((state) => ({ ...state, history: state.history.filter((_, i) => i !== index) })); - await delChatRecordByIndex({ historyId, contentId }); + await delChatRecordByIndex({ chatId, contentId }); } catch (err) { console.log(err); } }, - [historyId, setChatData] + [chatId, setChatData] ); // get chat app info const loadChatInfo = useCallback( async ({ appId, - historyId, + chatId, loading = false }: { appId: string; - historyId: string; + chatId: string; loading?: boolean; }) => { try { loading && setIsLoading(true); - const res = await getInitChatSiteInfo({ appId, historyId }); + const res = await getInitChatSiteInfo({ appId, chatId }); const history = res.history.map((item) => ({ ...item, status: 'finish' as any @@ -166,22 +166,22 @@ const Chat = () => { } catch (e: any) { // reset all chat tore setLastChatAppId(''); - setLastHistoryId(''); + setLastChatId(''); router.replace('/chat'); } setIsLoading(false); return null; }, - [setIsLoading, setChatData, router, setLastChatAppId, setLastHistoryId] + [setIsLoading, setChatData, router, setLastChatAppId, setLastChatId] ); // 初始化聊天框 - useQuery(['init', appId, historyId], () => { + useQuery(['init', appId, chatId], () => { // pc: redirect to latest model chat if (!appId && lastChatAppId) { router.replace({ query: { appId: lastChatAppId, - historyId: lastHistoryId + chatId: lastChatId } }); return null; @@ -189,7 +189,7 @@ const Chat = () => { // store id appId && setLastChatAppId(appId); - setLastHistoryId(historyId); + setLastChatId(chatId); if (forbidRefresh.current) { forbidRefresh.current = false; @@ -198,7 +198,7 @@ const Chat = () => { return loadChatInfo({ appId, - historyId, + chatId, loading: appId !== chatData.appId }); }); @@ -231,16 +231,17 @@ const Chat = () => { appId={appId} appName={chatData.app.name} appAvatar={chatData.app.avatar} - activeHistoryId={historyId} + activeChatId={chatId} history={history.map((item) => ({ id: item._id, title: item.title, + customTitle: item.customTitle, top: item.top }))} - onChangeChat={(historyId) => { + onChangeChat={(chatId) => { router.push({ query: { - historyId: historyId || '', + chatId: chatId || '', appId } }); @@ -252,7 +253,7 @@ const Chat = () => { onSetHistoryTop={async (e) => { try { await putChatHistory(e); - const historyItem = history.find((item) => item._id === e.historyId); + const historyItem = history.find((item) => item._id === e.chatId); if (!historyItem) return; updateHistory({ ...historyItem, @@ -260,17 +261,17 @@ const Chat = () => { }); } catch (error) {} }} - onUpdateTitle={async (e) => { + onSetCustomTitle={async (e) => { try { await putChatHistory({ - historyId: e.historyId, + chatId: e.chatId, customTitle: e.title }); - const historyItem = history.find((item) => item._id === e.historyId); + const historyItem = history.find((item) => item._id === e.chatId); if (!historyItem) return; updateHistory({ ...historyItem, - title: e.title + customTitle: e.title }); } catch (error) {} }} @@ -297,7 +298,7 @@ const Chat = () => { import('./components/ChatHistorySlider') ssr: false }); -const ShareChat = ({ shareId, historyId }: { shareId: string; historyId: string }) => { +const ShareChat = ({ shareId, chatId }: { shareId: string; chatId: string }) => { const router = useRouter(); const { toast } = useToast(); const { isOpen: isOpenSlider, onClose: onCloseSlider, onOpen: onOpenSlider } = useDisclosure(); @@ -33,7 +33,7 @@ const ShareChat = ({ shareId, historyId }: { shareId: string; historyId: string shareChatHistory, saveChatResponse, delShareChatHistoryItemById, - delOneShareHistoryByHistoryId, + delOneShareHistoryByChatId, delManyShareChatHistoryByShareId } = useShareChatStore(); @@ -60,7 +60,7 @@ const ShareChat = ({ shareId, historyId }: { shareId: string; historyId: string /* save chat */ const { newChatId } = saveChatResponse({ - historyId, + chatId, prompts: gptMessage2ChatType(prompts).map((item) => ({ ...item, status: 'finish' @@ -73,7 +73,7 @@ const ShareChat = ({ shareId, historyId }: { shareId: string; historyId: string router.replace({ query: { shareId, - historyId: newChatId + chatId: newChatId } }); } @@ -88,15 +88,15 @@ const ShareChat = ({ shareId, historyId }: { shareId: string; historyId: string return { responseText }; }, - [historyId, router, saveChatResponse, shareChatData.maxContext, shareId] + [chatId, router, saveChatResponse, shareChatData.maxContext, shareId] ); const loadAppInfo = useCallback( - async (shareId: string, historyId: string) => { - console.log(shareId, historyId); + async (shareId: string, chatId: string) => { + console.log(shareId, chatId); if (!shareId) return null; - const history = shareChatHistory.find((item) => item._id === historyId) || defaultHistory; + const history = shareChatHistory.find((item) => item._id === chatId) || defaultHistory; ChatBoxRef.current?.resetHistory(history.chats); ChatBoxRef.current?.resetVariables(history.variables); @@ -134,8 +134,8 @@ const ShareChat = ({ shareId, historyId }: { shareId: string; historyId: string [delManyShareChatHistoryByShareId, setShareChatData, shareChatData, shareChatHistory, toast] ); - useQuery(['init', shareId, historyId], () => { - return loadAppInfo(shareId, historyId); + useQuery(['init', shareId, chatId], () => { + return loadAppInfo(shareId, chatId); }); return ( @@ -156,15 +156,15 @@ const ShareChat = ({ shareId, historyId }: { shareId: string; historyId: string ({ id: item._id, title: item.title }))} - onChangeChat={(historyId) => { + onChangeChat={(chatId) => { router.push({ query: { - historyId: historyId || '', + chatId: chatId || '', shareId } }); @@ -172,7 +172,7 @@ const ShareChat = ({ shareId, historyId }: { shareId: string; historyId: string onCloseSlider(); } }} - onDelHistory={delOneShareHistoryByHistoryId} + onDelHistory={delOneShareHistoryByChatId} /> )} @@ -208,7 +208,7 @@ const ShareChat = ({ shareId, historyId }: { shareId: string; historyId: string })); }} onStartChat={startChat} - onDelMessage={({ index }) => delShareChatHistoryItemById({ historyId, index })} + onDelMessage={({ index }) => delShareChatHistoryItemById({ chatId, index })} /> @@ -219,10 +219,10 @@ const ShareChat = ({ shareId, historyId }: { shareId: string; historyId: string export async function getServerSideProps(context: any) { const shareId = context?.query?.shareId || ''; - const historyId = context?.query?.historyId || ''; + const chatId = context?.query?.chatId || ''; return { - props: { shareId, historyId } + props: { shareId, chatId } }; } diff --git a/client/src/pages/kb/detail/components/DataCard.tsx b/client/src/pages/kb/detail/components/DataCard.tsx index 69c3f1456..7bc0e7278 100644 --- a/client/src/pages/kb/detail/components/DataCard.tsx +++ b/client/src/pages/kb/detail/components/DataCard.tsx @@ -253,7 +253,7 @@ const DataCard = ({ kbId }: { kbId: string }) => { )} {total === 0 && ( - + 知识库空空如也 diff --git a/client/src/pages/login/index.tsx b/client/src/pages/login/index.tsx index c253e1f4a..36feb66e4 100644 --- a/client/src/pages/login/index.tsx +++ b/client/src/pages/login/index.tsx @@ -18,12 +18,12 @@ const Login = () => { const { isPc } = useGlobalStore(); const [pageType, setPageType] = useState<`${PageTypeEnum}`>(PageTypeEnum.login); const { setUserInfo } = useUserStore(); - const { setLastHistoryId, setLastChatAppId } = useChatStore(); + const { setLastChatId, setLastChatAppId } = useChatStore(); const loginSuccess = useCallback( (res: ResLogin) => { // init store - setLastHistoryId(''); + setLastChatId(''); setLastChatAppId(''); setUserInfo(res.user); @@ -31,7 +31,7 @@ const Login = () => { router.push(lastRoute ? decodeURIComponent(lastRoute) : '/app/list'); }, 100); }, - [lastRoute, router, setLastHistoryId, setLastChatAppId, setUserInfo] + [lastRoute, router, setLastChatId, setLastChatAppId, setUserInfo] ); function DynamicComponent({ type }: { type: `${PageTypeEnum}` }) { diff --git a/client/src/service/utils/chat/index.ts b/client/src/service/utils/chat/index.ts index 04f262cae..928e140c4 100644 --- a/client/src/service/utils/chat/index.ts +++ b/client/src/service/utils/chat/index.ts @@ -1,17 +1,10 @@ import { ChatItemType } from '@/types/chat'; import { modelToolMap } from '@/utils/plugin'; -import { ChatRoleEnum } from '@/constants/chat'; +import { ChatRoleEnum, sseResponseEventEnum } from '@/constants/chat'; +import { sseResponse } from '../tools'; import { OpenAiChatEnum } from '@/constants/model'; import type { NextApiResponse } from 'next'; -export type ChatCompletionType = { - apiKey: string; - temperature: number; - maxToken?: number; - messages: ChatItemType[]; - historyId?: string; - [key: string]: any; -}; export type ChatCompletionResponseType = { streamResponse: any; responseMessages: ChatItemType[]; diff --git a/client/src/store/chat.ts b/client/src/store/chat.ts index 8ebf16680..872a766fc 100644 --- a/client/src/store/chat.ts +++ b/client/src/store/chat.ts @@ -15,12 +15,12 @@ type State = { setChatData: (e: InitChatResponse | ((e: InitChatResponse) => InitChatResponse)) => void; lastChatAppId: string; setLastChatAppId: (id: string) => void; - lastHistoryId: string; - setLastHistoryId: (id: string) => void; + lastChatId: string; + setLastChatId: (id: string) => void; }; const defaultChatData: InitChatResponse = { - historyId: '', + chatId: '', appId: '', app: { name: '', @@ -43,10 +43,10 @@ export const useChatStore = create()( state.lastChatAppId = id; }); }, - lastHistoryId: '', - setLastHistoryId(id: string) { + lastChatId: '', + setLastChatId(id: string) { set((state) => { - state.lastHistoryId = id; + state.lastChatId = id; }); }, history: [], @@ -63,25 +63,36 @@ export const useChatStore = create()( }); return null; }, - async delHistory(historyId) { + async delHistory(chatId) { set((state) => { - state.history = state.history.filter((item) => item._id !== historyId); + state.history = state.history.filter((item) => item._id !== chatId); }); - await delChatHistoryById(historyId); + await delChatHistoryById(chatId); }, updateHistory(history) { const index = get().history.findIndex((item) => item._id === history._id); set((state) => { - if (index > -1) { - const newHistory = [ - history, - ...get().history.slice(0, index), - ...get().history.slice(index + 1) - ]; - state.history = newHistory; - } else { - state.history = [history, ...state.history]; - } + const newHistory = (() => { + if (index > -1) { + return [ + history, + ...get().history.slice(0, index), + ...get().history.slice(index + 1) + ]; + } else { + return [history, ...state.history]; + } + })(); + // newHistory.sort(function (a, b) { + // if (a.top === true && b.top === false) { + // return -1; + // } else if (a.top === false && b.top === true) { + // return 1; + // } else { + // return 0; + // } + // }); + state.history = newHistory; }); }, chatData: defaultChatData, @@ -101,7 +112,7 @@ export const useChatStore = create()( name: 'chatStore', partialize: (state) => ({ lastChatAppId: state.lastChatAppId, - lastHistoryId: state.lastHistoryId + lastChatId: state.lastChatId }) } ) diff --git a/client/src/store/shareChat.ts b/client/src/store/shareChat.ts index ff33b0493..dc0caaf33 100644 --- a/client/src/store/shareChat.ts +++ b/client/src/store/shareChat.ts @@ -12,13 +12,13 @@ type State = { setShareChatData: (e: ShareChatType | ((e: ShareChatType) => ShareChatType)) => void; shareChatHistory: ShareChatHistoryItemType[]; saveChatResponse: (e: { - historyId: string; + chatId: string; prompts: ChatSiteItemType[]; variables: Record; shareId: string; }) => { newChatId: string }; - delOneShareHistoryByHistoryId: (historyId: string) => void; - delShareChatHistoryItemById: (e: { historyId: string; index: number }) => void; + delOneShareHistoryByChatId: (chatId: string) => void; + delShareChatHistoryItemById: (e: { chatId: string; index: number }) => void; delManyShareChatHistoryByShareId: (shareId?: string) => void; }; @@ -62,15 +62,15 @@ export const useShareChatStore = create()( }); }, shareChatHistory: [], - saveChatResponse({ historyId, prompts, variables, shareId }) { - const history = get().shareChatHistory.find((item) => item._id === historyId); + saveChatResponse({ chatId, prompts, variables, shareId }) { + const history = get().shareChatHistory.find((item) => item._id === chatId); const newChatId = history ? '' : nanoid(); const historyList = (() => { if (history) { return get().shareChatHistory.map((item) => - item._id === historyId + item._id === chatId ? { ...item, title: prompts[prompts.length - 2]?.value, @@ -102,18 +102,16 @@ export const useShareChatStore = create()( newChatId }; }, - delOneShareHistoryByHistoryId(historyId: string) { + delOneShareHistoryByChatId(chatId: string) { set((state) => { - state.shareChatHistory = state.shareChatHistory.filter( - (item) => item._id !== historyId - ); + state.shareChatHistory = state.shareChatHistory.filter((item) => item._id !== chatId); }); }, - delShareChatHistoryItemById({ historyId, index }) { + delShareChatHistoryItemById({ chatId, index }) { set((state) => { // update history store const newHistoryList = state.shareChatHistory.map((item) => - item._id === historyId + item._id === chatId ? { ...item, chats: [...item.chats.slice(0, index), ...item.chats.slice(index + 1)] diff --git a/client/src/types/chat.d.ts b/client/src/types/chat.d.ts index b6c270c2f..6e7a0e679 100644 --- a/client/src/types/chat.d.ts +++ b/client/src/types/chat.d.ts @@ -20,6 +20,7 @@ export type ChatSiteItemType = { export type HistoryItemType = { _id: string; updateTime: Date; + customTitle?: string; title: string; }; export type ChatHistoryItemType = HistoryItemType & {