perf: quote response

This commit is contained in:
archer
2023-07-17 16:12:51 +08:00
parent 60a9dfb55f
commit 53a4d9db05
20 changed files with 215 additions and 173 deletions

View File

@@ -4,8 +4,7 @@ import type { InitChatResponse, InitShareChatResponse } from './response/chat';
import { RequestPaging } from '../types/index'; import { RequestPaging } from '../types/index';
import type { ShareChatSchema } from '@/types/mongoSchema'; import type { ShareChatSchema } from '@/types/mongoSchema';
import type { ShareChatEditType } from '@/types/app'; import type { ShareChatEditType } from '@/types/app';
import { Obj2Query } from '@/utils/tools'; import type { QuoteItemType } from '@/pages/api/openapi/modules/kb/search';
import type { QuoteItemType } from '@/pages/api/openapi/kb/appKbSearch';
import type { Props as UpdateHistoryProps } from '@/pages/api/chat/history/updateChatHistory'; import type { Props as UpdateHistoryProps } from '@/pages/api/chat/history/updateChatHistory';
/** /**
@@ -39,7 +38,7 @@ export const updateHistoryQuote = (params: {
contentId: string; contentId: string;
quoteId: string; quoteId: string;
sourceText: string; sourceText: string;
}) => GET(`/chat/history/updateHistoryQuote`, params); }) => PUT(`/chat/history/updateHistoryQuote`, params);
/** /**
* 删除一句对话 * 删除一句对话

View File

@@ -1,6 +1,8 @@
import { sseResponseEventEnum } from '@/constants/chat'; import { sseResponseEventEnum } from '@/constants/chat';
import { getErrText } from '@/utils/tools'; import { getErrText } from '@/utils/tools';
import { parseStreamChunk } from '@/utils/adapt'; import { parseStreamChunk } from '@/utils/adapt';
import { QuoteItemType } from '@/pages/api/openapi/modules/kb/search';
import { rawSearchKey } from '@/constants/chat';
interface StreamFetchProps { interface StreamFetchProps {
url?: string; url?: string;
@@ -14,8 +16,12 @@ export const streamFetch = ({
onMessage, onMessage,
abortSignal abortSignal
}: StreamFetchProps) => }: StreamFetchProps) =>
new Promise<{ responseText: string; errMsg: string; newHistoryId: string | null }>( new Promise<{
async (resolve, reject) => { responseText: string;
errMsg: string;
newHistoryId: string | null;
[rawSearchKey]: QuoteItemType[];
}>(async (resolve, reject) => {
try { try {
const response = await window.fetch(url, { const response = await window.fetch(url, {
method: 'POST', method: 'POST',
@@ -29,11 +35,6 @@ export const streamFetch = ({
}) })
}); });
if (response.status !== 200) {
const err = await response.json();
return reject(err);
}
if (!response?.body) { if (!response?.body) {
throw new Error('Request Error'); throw new Error('Request Error');
} }
@@ -42,6 +43,7 @@ export const streamFetch = ({
// response data // response data
let responseText = ''; let responseText = '';
let rawSearch: QuoteItemType[] = [];
let errMsg = ''; let errMsg = '';
const newHistoryId = response.headers.get('newHistoryId'); const newHistoryId = response.headers.get('newHistoryId');
@@ -49,14 +51,18 @@ export const streamFetch = ({
try { try {
const { done, value } = await reader.read(); const { done, value } = await reader.read();
if (done) { if (done) {
if (response.status === 200) { if (response.status === 200 && !errMsg) {
return resolve({ return resolve({
responseText, responseText,
errMsg, errMsg,
newHistoryId newHistoryId,
rawSearch
}); });
} else { } else {
return reject('响应过程出现异常~'); return reject({
message: errMsg || '响应过程出现异常~',
responseText
});
} }
} }
const chunkResponse = parseStreamChunk(value); const chunkResponse = parseStreamChunk(value);
@@ -75,6 +81,8 @@ export const streamFetch = ({
const answer: string = data?.choices?.[0].delta.content || ''; const answer: string = data?.choices?.[0].delta.content || '';
onMessage(answer); onMessage(answer);
responseText += answer; responseText += answer;
} else if (item.event === sseResponseEventEnum.appStreamResponse) {
rawSearch = data?.[rawSearchKey] ? data[rawSearchKey] : rawSearch;
} else if (item.event === sseResponseEventEnum.error) { } else if (item.event === sseResponseEventEnum.error) {
errMsg = getErrText(data, '流响应错误'); errMsg = getErrText(data, '流响应错误');
} }
@@ -85,7 +93,8 @@ export const streamFetch = ({
return resolve({ return resolve({
responseText, responseText,
errMsg, errMsg,
newHistoryId newHistoryId,
rawSearch
}); });
} }
reject(getErrText(err, '请求异常')); reject(getErrText(err, '请求异常'));
@@ -97,5 +106,4 @@ export const streamFetch = ({
reject(getErrText(err, '请求异常')); reject(getErrText(err, '请求异常'));
} }
} });
);

View File

@@ -17,20 +17,24 @@ import { useQuery } from '@tanstack/react-query';
import { getHistoryQuote, updateHistoryQuote } from '@/api/chat'; import { getHistoryQuote, updateHistoryQuote } from '@/api/chat';
import { useToast } from '@/hooks/useToast'; import { useToast } from '@/hooks/useToast';
import { getErrText } from '@/utils/tools'; import { getErrText } from '@/utils/tools';
import { QuoteItemType } from '@/pages/api/openapi/modules/kb/search';
const QuoteModal = ({ const QuoteModal = ({
historyId, historyId,
contentId, contentId,
rawSearch = [],
onClose onClose
}: { }: {
historyId: string; historyId?: string;
contentId: string; contentId?: string;
rawSearch?: QuoteItemType[];
onClose: () => void; onClose: () => void;
}) => { }) => {
const theme = useTheme(); const theme = useTheme();
const { toast } = useToast(); const { toast } = useToast();
const { setIsLoading, Loading } = useLoading(); const { setIsLoading, Loading } = useLoading();
const [editDataItem, setEditDataItem] = useState<{ const [editDataItem, setEditDataItem] = useState<{
kbId: string;
dataId: string; dataId: string;
a: string; a: string;
q: string; q: string;
@@ -40,13 +44,22 @@ const QuoteModal = ({
data: quote = [], data: quote = [],
refetch, refetch,
isLoading isLoading
} = useQuery(['getHistoryQuote'], () => getHistoryQuote({ historyId, contentId })); } = useQuery(['getHistoryQuote'], () => {
if (historyId && contentId) {
return getHistoryQuote({ historyId, contentId });
}
if (rawSearch.length > 0) {
return rawSearch;
}
return [];
});
/** /**
* update kbData, update mongo status and reload quotes * update kbData, update mongo status and reload quotes
*/ */
const updateQuoteStatus = useCallback( const updateQuoteStatus = useCallback(
async (quoteId: string, sourceText: string) => { async (quoteId: string, sourceText: string) => {
if (!historyId || !contentId) return;
setIsLoading(true); setIsLoading(true);
try { try {
await updateHistoryQuote({ await updateHistoryQuote({
@@ -83,6 +96,7 @@ const QuoteModal = ({
} }
setEditDataItem({ setEditDataItem({
kbId: data.kb_id,
dataId: data.id, dataId: data.id,
q: data.q, q: data.q,
a: data.a a: data.a
@@ -166,7 +180,7 @@ const QuoteModal = ({
onClose={() => setEditDataItem(undefined)} onClose={() => setEditDataItem(undefined)}
onSuccess={() => updateQuoteStatus(editDataItem.dataId, '手动修改')} onSuccess={() => updateQuoteStatus(editDataItem.dataId, '手动修改')}
onDelete={() => updateQuoteStatus(editDataItem.dataId, '已删除')} onDelete={() => updateQuoteStatus(editDataItem.dataId, '已删除')}
kbId="" kbId={editDataItem.kbId}
defaultValues={editDataItem} defaultValues={editDataItem}
/> />
)} )}

View File

@@ -15,7 +15,7 @@ import { Box, Card, Flex, Input, Textarea, Button, useTheme } from '@chakra-ui/r
import { useUserStore } from '@/store/user'; import { useUserStore } from '@/store/user';
import { Types } from 'mongoose'; import { Types } from 'mongoose';
import { HUMAN_ICON } from '@/constants/chat'; import { HUMAN_ICON, quoteLenKey, rawSearchKey } from '@/constants/chat';
import Markdown from '@/components/Markdown'; import Markdown from '@/components/Markdown';
import MyIcon from '@/components/Icon'; import MyIcon from '@/components/Icon';
import Avatar from '@/components/Avatar'; import Avatar from '@/components/Avatar';
@@ -26,10 +26,15 @@ import { VariableInputEnum } from '@/constants/app';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import MySelect from '@/components/Select'; import MySelect from '@/components/Select';
import { MessageItemType } from '@/pages/api/openapi/v1/chat/completions'; import { MessageItemType } from '@/pages/api/openapi/v1/chat/completions';
import styles from './index.module.scss';
import MyTooltip from '../MyTooltip'; import MyTooltip from '../MyTooltip';
import { fileDownload } from '@/utils/file'; import { fileDownload } from '@/utils/file';
import { htmlTemplate } from '@/constants/common'; import { htmlTemplate } from '@/constants/common';
import dynamic from 'next/dynamic';
const QuoteModal = dynamic(() => import('./QuoteModal'));
import styles from './index.module.scss';
import { QuoteItemType } from '@/pages/api/openapi/modules/kb/search';
const textareaMinH = '22px'; const textareaMinH = '22px';
export type StartChatFnProps = { export type StartChatFnProps = {
@@ -65,6 +70,7 @@ const VariableLabel = ({
const ChatBox = ( const ChatBox = (
{ {
historyId,
appAvatar, appAvatar,
variableModules, variableModules,
welcomeText, welcomeText,
@@ -72,11 +78,14 @@ const ChatBox = (
onStartChat, onStartChat,
onDelMessage onDelMessage
}: { }: {
historyId?: string;
appAvatar: string; appAvatar: string;
variableModules?: VariableItemType[]; variableModules?: VariableItemType[];
welcomeText?: string; welcomeText?: string;
onUpdateVariable?: (e: Record<string, any>) => void; onUpdateVariable?: (e: Record<string, any>) => void;
onStartChat: (e: StartChatFnProps) => Promise<{ responseText: string }>; onStartChat: (
e: StartChatFnProps
) => Promise<{ responseText?: string; rawSearch?: QuoteItemType[] }>;
onDelMessage?: (e: { contentId?: string; index: number }) => void; onDelMessage?: (e: { contentId?: string; index: number }) => void;
}, },
ref: ForwardedRef<ComponentRef> ref: ForwardedRef<ComponentRef>
@@ -92,6 +101,10 @@ const ChatBox = (
const [refresh, setRefresh] = useState(false); const [refresh, setRefresh] = useState(false);
const [variables, setVariables] = useState<Record<string, any>>({}); const [variables, setVariables] = useState<Record<string, any>>({});
const [chatHistory, setChatHistory] = useState<ChatSiteItemType[]>([]); const [chatHistory, setChatHistory] = useState<ChatSiteItemType[]>([]);
const [quoteModalData, setQuoteModalData] = useState<{
contentId?: string;
rawSearch?: QuoteItemType[];
}>();
const isChatting = useMemo( const isChatting = useMemo(
() => chatHistory[chatHistory.length - 1]?.status === 'loading', () => chatHistory[chatHistory.length - 1]?.status === 'loading',
@@ -235,13 +248,25 @@ const ChatBox = (
const messages = adaptChatItem_openAI({ messages: newChatList, reserveId: true }); const messages = adaptChatItem_openAI({ messages: newChatList, reserveId: true });
await onStartChat({ const { rawSearch } = await onStartChat({
messages, messages,
controller: abortSignal, controller: abortSignal,
generatingMessage, generatingMessage,
variables: data variables: data
}); });
// set finish status
setChatHistory((state) =>
state.map((item, index) => {
if (index !== state.length - 1) return item;
return {
...item,
status: 'finish',
rawSearch
};
})
);
setTimeout(() => { setTimeout(() => {
generatingScroll(); generatingScroll();
TextareaDom.current?.focus(); TextareaDom.current?.focus();
@@ -258,7 +283,6 @@ const ChatBox = (
resetInputVal(value); resetInputVal(value);
setChatHistory(newChatList.slice(0, newChatList.length - 2)); setChatHistory(newChatList.slice(0, newChatList.length - 2));
} }
}
// set finish status // set finish status
setChatHistory((state) => setChatHistory((state) =>
@@ -270,6 +294,7 @@ const ChatBox = (
}; };
}) })
); );
}
}, },
[ [
isChatting, isChatting,
@@ -439,7 +464,24 @@ const ChatBox = (
source={item.value} source={item.value}
isChatting={index === chatHistory.length - 1 && isChatting} isChatting={index === chatHistory.length - 1 && isChatting}
/> />
{(item[quoteLenKey] || item[rawSearchKey]?.length) && (
<Button
size={'xs'}
variant={'base'}
mt={2}
w={'80px'}
onClick={() => {
setQuoteModalData({
contentId: item._id,
rawSearch: item[rawSearchKey]
});
}}
>
{item[quoteLenKey] || item[rawSearchKey]?.length}
</Button>
)}
</Card> </Card>
<Flex {...controlContainerStyle}> <Flex {...controlContainerStyle}>
<MyTooltip label={'复制'}> <MyTooltip label={'复制'}>
<MyIcon <MyIcon
@@ -611,6 +653,15 @@ const ChatBox = (
</Box> </Box>
</Box> </Box>
) : null} ) : null}
{/* quote modal */}
{!!quoteModalData && (
<QuoteModal
historyId={historyId}
{...quoteModalData}
onClose={() => setQuoteModalData(undefined)}
/>
)}
{/* quote modal */}
</Flex> </Flex>
); );
}; };

View File

@@ -1,4 +1,5 @@
import type { AppItemType } from '@/types/app'; import type { AppItemType } from '@/types/app';
import { rawSearchKey } from './chat';
/* app */ /* app */
export enum AppModuleItemTypeEnum { export enum AppModuleItemTypeEnum {
@@ -530,7 +531,7 @@ export const appTemplates: (AppItemType & { avatar: string; intro: string })[] =
], ],
outputs: [ outputs: [
{ {
key: 'rawSearch', key: rawSearchKey,
label: '源搜索数据', label: '源搜索数据',
type: 'hidden', type: 'hidden',
response: true, response: true,
@@ -1165,7 +1166,7 @@ export const appTemplates: (AppItemType & { avatar: string; intro: string })[] =
], ],
outputs: [ outputs: [
{ {
key: 'rawSearch', key: rawSearchKey,
label: '源搜索数据', label: '源搜索数据',
type: 'hidden', type: 'hidden',
response: true, response: true,

View File

@@ -24,5 +24,8 @@ export const ChatRoleMap = {
} }
}; };
export const rawSearchKey = 'rawSearch';
export const quoteLenKey = 'quoteLen';
export const HUMAN_ICON = `https://fastgpt.run/icon/human.png`; export const HUMAN_ICON = `https://fastgpt.run/icon/human.png`;
export const LOGO_ICON = `https://fastgpt.run/icon/logo.png`; export const LOGO_ICON = `https://fastgpt.run/icon/logo.png`;

View File

@@ -7,6 +7,7 @@ import {
Input_Template_TFSwitch, Input_Template_TFSwitch,
Input_Template_UserChatInput Input_Template_UserChatInput
} from './inputTemplate'; } from './inputTemplate';
import { rawSearchKey } from '../chat';
export const VariableInputModule: AppModuleTemplateItemType = { export const VariableInputModule: AppModuleTemplateItemType = {
logo: '/imgs/module/userGuide.png', logo: '/imgs/module/userGuide.png',
@@ -215,7 +216,7 @@ export const KBSearchModule: AppModuleTemplateItemType = {
], ],
outputs: [ outputs: [
{ {
key: 'rawSearch', key: rawSearchKey,
label: '源搜索数据', label: '源搜索数据',
type: FlowOutputItemTypeEnum.hidden, type: FlowOutputItemTypeEnum.hidden,
response: true, response: true,

View File

@@ -3,6 +3,7 @@ import { jsonRes } from '@/service/response';
import { connectToDatabase, Chat } from '@/service/mongo'; import { connectToDatabase, Chat } from '@/service/mongo';
import { authUser } from '@/service/utils/auth'; import { authUser } from '@/service/utils/auth';
import { Types } from 'mongoose'; import { Types } from 'mongoose';
import { rawSearchKey } from '@/constants/chat';
export default async function handler(req: NextApiRequest, res: NextApiResponse) { export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try { try {
@@ -35,13 +36,13 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
}, },
{ {
$project: { $project: {
quote: '$content.quote' [rawSearchKey]: `$content.${rawSearchKey}`
} }
} }
]); ]);
jsonRes(res, { jsonRes(res, {
data: history[0]?.quote || [] data: history[0]?.[rawSearchKey] || []
}); });
} catch (err) { } catch (err) {
jsonRes(res, { jsonRes(res, {

View File

@@ -11,7 +11,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
contentId, contentId,
quoteId, quoteId,
sourceText = '' sourceText = ''
} = req.query as { } = req.body as {
historyId: string; historyId: string;
contentId: string; contentId: string;
quoteId: string; quoteId: string;
@@ -33,7 +33,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
}, },
{ {
$set: { $set: {
'content.$.quote.$[quoteElem].source': sourceText 'content.$.rawSearch.$[quoteElem].source': sourceText
} }
}, },
{ {

View File

@@ -9,6 +9,7 @@ import mongoose from 'mongoose';
import type { AppSchema, ChatSchema } from '@/types/mongoSchema'; import type { AppSchema, ChatSchema } from '@/types/mongoSchema';
import { FlowModuleTypeEnum } from '@/constants/flow'; import { FlowModuleTypeEnum } from '@/constants/flow';
import { SystemInputEnum } from '@/constants/app'; import { SystemInputEnum } from '@/constants/app';
import { quoteLenKey, rawSearchKey } from '@/constants/chat';
/* 初始化我的聊天框,需要身份验证 */ /* 初始化我的聊天框,需要身份验证 */
export default async function handler(req: NextApiRequest, res: NextApiResponse) { export default async function handler(req: NextApiRequest, res: NextApiResponse) {
@@ -82,8 +83,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
_id: '$content._id', _id: '$content._id',
obj: '$content.obj', obj: '$content.obj',
value: '$content.value', value: '$content.value',
systemPrompt: '$content.systemPrompt', [quoteLenKey]: { $size: { $ifNull: [`$content.${rawSearchKey}`, []] } }
quoteLen: { $size: { $ifNull: ['$content.quote', []] } }
} }
} }
]); ]);

View File

@@ -128,7 +128,6 @@ export async function chatCompletion({
const adaptMessages = adaptChatItem_openAI({ messages: filterMessages, reserveId: false }); const adaptMessages = adaptChatItem_openAI({ messages: filterMessages, reserveId: false });
const chatAPI = getOpenAIApi(); const chatAPI = getOpenAIApi();
console.log(adaptMessages);
/* count response max token */ /* count response max token */
const promptsToken = modelToolMap.countTokens({ const promptsToken = modelToolMap.countTokens({

View File

@@ -3,13 +3,14 @@ import { jsonRes } from '@/service/response';
import { PgClient } from '@/service/pg'; import { PgClient } from '@/service/pg';
import { withNextCors } from '@/service/utils/tools'; import { withNextCors } from '@/service/utils/tools';
import type { ChatItemType } from '@/types/chat'; import type { ChatItemType } from '@/types/chat';
import { ChatRoleEnum } from '@/constants/chat'; import { ChatRoleEnum, rawSearchKey } from '@/constants/chat';
import { modelToolMap } from '@/utils/plugin'; import { modelToolMap } from '@/utils/plugin';
import { getVector } from '../../plugin/vector'; import { getVector } from '../../plugin/vector';
import { countModelPrice, pushTaskBillListItem } from '@/service/events/pushBill'; import { countModelPrice, pushTaskBillListItem } from '@/service/events/pushBill';
import { getModel } from '@/service/utils/data'; import { getModel } from '@/service/utils/data';
export type QuoteItemType = { export type QuoteItemType = {
kb_id: string;
id: string; id: string;
q: string; q: string;
a: string; a: string;
@@ -26,7 +27,7 @@ type Props = {
billId?: string; billId?: string;
}; };
type Response = { type Response = {
rawSearch: QuoteItemType[]; [rawSearchKey]: QuoteItemType[];
isEmpty?: boolean; isEmpty?: boolean;
quotePrompt?: string; quotePrompt?: string;
}; };
@@ -85,7 +86,7 @@ export async function kbSearch({
PgClient.query( PgClient.query(
`BEGIN; `BEGIN;
SET LOCAL ivfflat.probes = ${global.systemEnv.pgIvfflatProbe || 10}; SET LOCAL ivfflat.probes = ${global.systemEnv.pgIvfflatProbe || 10};
select id,q,a,source from modelData where kb_id IN (${kb_ids select kb_id,id,q,a,source from modelData where kb_id IN (${kb_ids
.map((item) => `'${item}'`) .map((item) => `'${item}'`)
.join(',')}) AND vector <#> '[${vectors[0]}]' < -${similarity} order by vector <#> '[${ .join(',')}) AND vector <#> '[${vectors[0]}]' < -${similarity} order by vector <#> '[${
vectors[0] vectors[0]

View File

@@ -42,11 +42,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
export function gpt_chatItemTokenSlice({ export function gpt_chatItemTokenSlice({
messages, messages,
model, model = 'gpt-3.5-turbo',
maxToken maxToken
}: { }: {
messages: ChatItemType[]; messages: ChatItemType[];
model: ModelType; model?: ModelType;
maxToken: number; maxToken: number;
}) { }) {
let result: ChatItemType[] = []; let result: ChatItemType[] = [];

View File

@@ -94,6 +94,8 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
getChatHistory({ historyId, userId }) getChatHistory({ historyId, userId })
]); ]);
const isOwner = !shareId && userId === String(app.userId);
const prompts = history.concat(gptMessage2ChatType(messages)); const prompts = history.concat(gptMessage2ChatType(messages));
if (prompts[prompts.length - 1].obj === 'AI') { if (prompts[prompts.length - 1].obj === 'AI') {
prompts.pop(); prompts.pop();
@@ -143,24 +145,30 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
_id: messages[messages.length - 1]._id, _id: messages[messages.length - 1]._id,
obj: ChatRoleEnum.AI, obj: ChatRoleEnum.AI,
value: answerText, value: answerText,
responseData ...responseData
} }
], ],
userId userId
}); });
} }
console.log(`finish time: ${(Date.now() - startTime) / 100}s`);
if (stream) { if (stream) {
sseResponse({ sseResponse({
res, res,
event: sseResponseEventEnum.answer, event: sseResponseEventEnum.answer,
data: '[DONE]' data: '[DONE]'
}); });
if (isOwner) {
sseResponse({ sseResponse({
res, res,
event: sseResponseEventEnum.appStreamResponse, event: sseResponseEventEnum.appStreamResponse,
data: JSON.stringify(responseData) data: JSON.stringify(responseData)
}); });
}
res.end(); res.end();
} else { } else {
res.json({ res.json({
@@ -189,7 +197,6 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
delTaskBill(billId); delTaskBill(billId);
if (stream) { if (stream) {
res.status(500);
sseErrRes(res, err); sseErrRes(res, err);
res.end(); res.end();
} else { } else {

View File

@@ -29,7 +29,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
const where: any = [['user_id', userId], 'AND', ['id', dataId]]; const where: any = [['user_id', userId], 'AND', ['id', dataId]];
const searchRes = await PgClient.select<KbDataItemType>('modelData', { const searchRes = await PgClient.select<KbDataItemType>('modelData', {
fields: ['id', 'q', 'a', 'source'], fields: ['kb_id', 'id', 'q', 'a', 'source'],
where, where,
limit: 1 limit: 1
}); });

View File

@@ -63,7 +63,7 @@ const ChatTest = (
const history = messages.slice(-historyMaxLen - 2, -2); const history = messages.slice(-historyMaxLen - 2, -2);
// 流请求,获取数据 // 流请求,获取数据
const { responseText, errMsg } = await streamFetch({ const { responseText, rawSearch } = await streamFetch({
url: '/api/chat/chatTest', url: '/api/chat/chatTest',
data: { data: {
history, history,
@@ -77,14 +77,7 @@ const ChatTest = (
abortSignal: controller abortSignal: controller
}); });
if (errMsg) { return { responseText, rawSearch };
return Promise.reject({
message: errMsg,
responseText
});
}
return { responseText };
}, },
[app._id, app.name, modules] [app._id, app.name, modules]
); );

View File

@@ -1,28 +1,18 @@
import React, { useCallback, useState, useRef } from 'react'; import React, { useCallback, useRef } from 'react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { getInitChatSiteInfo, delChatRecordByIndex, putChatHistory } from '@/api/chat'; import { getInitChatSiteInfo, delChatRecordByIndex, putChatHistory } from '@/api/chat';
import { import {
Box, Box,
Flex, Flex,
useColorModeValue,
Modal,
ModalOverlay,
ModalContent,
ModalBody,
ModalCloseButton,
ModalHeader,
useDisclosure, useDisclosure,
Drawer, Drawer,
DrawerOverlay, DrawerOverlay,
DrawerContent, DrawerContent,
useTheme useTheme
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { useToast } from '@/hooks/useToast';
import { useGlobalStore } from '@/store/global'; import { useGlobalStore } from '@/store/global';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import dynamic from 'next/dynamic';
import { streamFetch } from '@/api/fetch'; import { streamFetch } from '@/api/fetch';
import MyIcon from '@/components/Icon';
import { useChatStore } from '@/store/chat'; import { useChatStore } from '@/store/chat';
import { useLoading } from '@/hooks/useLoading'; import { useLoading } from '@/hooks/useLoading';
@@ -32,8 +22,6 @@ import PageContainer from '@/components/PageContainer';
import SideBar from '@/components/SideBar'; import SideBar from '@/components/SideBar';
import ChatHistorySlider from './components/ChatHistorySlider'; import ChatHistorySlider from './components/ChatHistorySlider';
import SliderApps from './components/SliderApps'; import SliderApps from './components/SliderApps';
import Tag from '@/components/Tag';
import ToolMenu from './components/ToolMenu';
import ChatHeader from './components/ChatHeader'; import ChatHeader from './components/ChatHeader';
const Chat = () => { const Chat = () => {
@@ -44,9 +32,6 @@ const Chat = () => {
const ChatBoxRef = useRef<ComponentRef>(null); const ChatBoxRef = useRef<ComponentRef>(null);
const forbidRefresh = useRef(false); const forbidRefresh = useRef(false);
const [showHistoryQuote, setShowHistoryQuote] = useState<string>();
const [showSystemPrompt, setShowSystemPrompt] = useState('');
const { const {
lastChatAppId, lastChatAppId,
setLastChatAppId, setLastChatAppId,
@@ -67,7 +52,7 @@ const Chat = () => {
const startChat = useCallback( const startChat = useCallback(
async ({ messages, controller, generatingMessage, variables }: StartChatFnProps) => { async ({ messages, controller, generatingMessage, variables }: StartChatFnProps) => {
const prompts = messages.slice(-2); const prompts = messages.slice(-2);
const { responseText, newHistoryId } = await streamFetch({ const { responseText, newHistoryId, rawSearch } = await streamFetch({
data: { data: {
messages: prompts, messages: prompts,
variables, variables,
@@ -113,7 +98,7 @@ const Chat = () => {
history: ChatBoxRef.current?.getChatHistory() || state.history history: ChatBoxRef.current?.getChatHistory() || state.history
})); }));
return { responseText }; return { responseText, rawSearch };
}, },
[appId, history, historyId, router, setChatData, updateHistory] [appId, history, historyId, router, setChatData, updateHistory]
); );
@@ -297,6 +282,7 @@ const Chat = () => {
<Box flex={1}> <Box flex={1}>
<ChatBox <ChatBox
ref={ChatBoxRef} ref={ChatBoxRef}
historyId={historyId}
appAvatar={chatData.app.avatar} appAvatar={chatData.app.avatar}
variableModules={chatData.app.variableModules} variableModules={chatData.app.variableModules}
welcomeText={chatData.app.welcomeText} welcomeText={chatData.app.welcomeText}
@@ -311,27 +297,6 @@ const Chat = () => {
</Flex> </Flex>
<Loading fixed={false} /> <Loading fixed={false} />
</PageContainer> </PageContainer>
{/* quote modal*/}
{/* {showHistoryQuote && historyId && (
<QuoteModal
historyId={historyId}
onClose={() => setShowHistoryQuote(undefined)}
/>
)} */}
{/* system prompt show modal */}
{
<Modal isOpen={!!showSystemPrompt} onClose={() => setShowSystemPrompt('')}>
<ModalOverlay />
<ModalContent maxW={'min(90vw, 600px)'} maxH={'80vh'} minH={'50vh'} overflow={'overlay'}>
<ModalCloseButton />
<ModalHeader></ModalHeader>
<ModalBody pt={0} whiteSpace={'pre-wrap'} textAlign={'justify'} fontSize={'xs'}>
{showSystemPrompt}
</ModalBody>
</ModalContent>
</Modal>
}
</Flex> </Flex>
); );
}; };

View File

@@ -184,7 +184,7 @@ const ShareChat = ({ shareId, historyId }: { shareId: string; historyId: string
{/* header */} {/* header */}
<ChatHeader <ChatHeader
appAvatar={shareChatData.app.avatar} appAvatar={shareChatData.app.avatar}
appName={shareChatData.history.title} appName={shareChatData.app.name}
history={shareChatData.history.chats} history={shareChatData.history.chats}
onOpenSlider={onOpenSlider} onOpenSlider={onOpenSlider}
/> />

View File

@@ -44,20 +44,17 @@ const ChatSchema = new Schema({
type: String, type: String,
default: '' default: ''
}, },
quote: { rawSearch: {
type: [ type: [
{ {
id: String, id: String,
q: String, q: String,
a: String, a: String,
kb_id: String,
source: String source: String
} }
], ],
default: [] default: []
},
systemPrompt: {
type: String,
default: ''
} }
} }
], ],

View File

@@ -1,4 +1,4 @@
import { ChatRoleEnum } from '@/constants/chat'; import { ChatRoleEnum, rawSearchKey } from '@/constants/chat';
import type { InitChatResponse, InitShareChatResponse } from '@/api/response/chat'; import type { InitChatResponse, InitShareChatResponse } from '@/api/response/chat';
import { QuoteItemType } from '@/pages/api/openapi/kb/appKbSearch'; import { QuoteItemType } from '@/pages/api/openapi/kb/appKbSearch';
@@ -8,6 +8,8 @@ export type ChatItemType = {
_id?: string; _id?: string;
obj: `${ChatRoleEnum}`; obj: `${ChatRoleEnum}`;
value: string; value: string;
[rawSearchKey]?: QuoteItemType[];
quoteLen?: number;
[key: string]: any; [key: string]: any;
}; };