fix: model test channel id;fix: quote reader (#4123)

* fix: model test channel id

* fix: quote reader
This commit is contained in:
Archer 2025-03-12 16:51:00 +08:00 committed by archer
parent 7eda599181
commit 19f0f110e2
No known key found for this signature in database
GPG Key ID: 4446499B846D4A9E
11 changed files with 151 additions and 65 deletions

View File

@ -44,6 +44,7 @@ const ResponseTags = ({
const chatType = useContextSelector(ChatBoxContext, (v) => v.chatType); const chatType = useContextSelector(ChatBoxContext, (v) => v.chatType);
const appId = useContextSelector(ChatBoxContext, (v) => v.appId); const appId = useContextSelector(ChatBoxContext, (v) => v.appId);
const chatId = useContextSelector(ChatBoxContext, (v) => v.chatId); const chatId = useContextSelector(ChatBoxContext, (v) => v.chatId);
const outLinkAuthData = useContextSelector(ChatBoxContext, (v) => v.outLinkAuthData);
const setQuoteData = useContextSelector(ChatItemContext, (v) => v.setQuoteData); const setQuoteData = useContextSelector(ChatItemContext, (v) => v.setQuoteData);
@ -65,6 +66,7 @@ const ResponseTags = ({
? quoteListRef.current.scrollHeight > (isPc ? 50 : 55) ? quoteListRef.current.scrollHeight > (isPc ? 50 : 55)
: true; : true;
const isShowReadRawSource = useContextSelector(ChatItemContext, (v) => v.isShowReadRawSource);
const sourceList = useMemo(() => { const sourceList = useMemo(() => {
return Object.values( return Object.values(
quoteList.reduce((acc: Record<string, SearchDataResponseItemType[]>, cur) => { quoteList.reduce((acc: Record<string, SearchDataResponseItemType[]>, cur) => {
@ -157,18 +159,34 @@ const ResponseTags = ({
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
setQuoteData({ if (isShowReadRawSource) {
rawSearch: quoteList, setQuoteData({
metadata: { rawSearch: quoteList,
appId, metadata: {
chatId, appId,
chatItemDataId: dataId, chatId,
collectionId: item.collectionId, chatItemDataId: dataId,
sourceId: item.sourceId || '', collectionId: item.collectionId,
sourceName: item.sourceName, sourceId: item.sourceId || '',
datasetId: item.datasetId sourceName: item.sourceName,
} datasetId: item.datasetId,
}); outLinkAuthData
}
});
} else {
setQuoteData({
rawSearch: quoteList,
metadata: {
appId,
chatId,
chatItemDataId: dataId,
collectionIdList: [item.collectionId],
sourceId: item.sourceId || '',
sourceName: item.sourceName,
outLinkAuthData
}
});
}
}} }}
height={6} height={6}
> >
@ -230,7 +248,8 @@ const ResponseTags = ({
appId, appId,
chatId, chatId,
chatItemDataId: dataId, chatItemDataId: dataId,
collectionIdList: [...new Set(quoteList.map((item) => item.collectionId))] collectionIdList: [...new Set(quoteList.map((item) => item.collectionId))],
outLinkAuthData
} }
}); });
}} }}

View File

@ -34,7 +34,15 @@ type ModelTestItem = {
duration?: number; duration?: number;
}; };
const ModelTest = ({ models, onClose }: { models: string[]; onClose: () => void }) => { const ModelTest = ({
channelId,
models,
onClose
}: {
channelId: number;
models: string[];
onClose: () => void;
}) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { toast } = useToast(); const { toast } = useToast();
const [testModelList, setTestModelList] = useState<ModelTestItem[]>([]); const [testModelList, setTestModelList] = useState<ModelTestItem[]>([]);
@ -57,6 +65,7 @@ const ModelTest = ({ models, onClose }: { models: string[]; onClose: () => void
colorSchema: 'red' colorSchema: 'red'
} }
}); });
const { loading: loadingModels } = useRequest2(getSystemModelList, { const { loading: loadingModels } = useRequest2(getSystemModelList, {
manual: false, manual: false,
refreshDeps: [models], refreshDeps: [models],
@ -95,7 +104,7 @@ const ModelTest = ({ models, onClose }: { models: string[]; onClose: () => void
); );
const start = Date.now(); const start = Date.now();
try { try {
await getTestModel(model); await getTestModel({ model, channelId });
const duration = Date.now() - start; const duration = Date.now() - start;
setTestModelList((prev) => setTestModelList((prev) =>
prev.map((item) => prev.map((item) =>

View File

@ -74,7 +74,7 @@ const ChannelTable = ({ Tab }: { Tab: React.ReactNode }) => {
} }
}); });
const [testModels, setTestModels] = useState<string[]>(); const [modelTestData, setTestModelData] = useState<{ channelId: number; models: string[] }>();
const isLoading = const isLoading =
loadingChannelList || loadingChannelList ||
@ -165,7 +165,11 @@ const ChannelTable = ({ Tab }: { Tab: React.ReactNode }) => {
{ {
icon: 'core/chat/sendLight', icon: 'core/chat/sendLight',
label: t('account_model:model_test'), label: t('account_model:model_test'),
onClick: () => setTestModels(item.models) onClick: () =>
setTestModelData({
channelId: item.id,
models: item.models
})
}, },
...(item.status === ChannelStatusEnum.ChannelStatusEnabled ...(item.status === ChannelStatusEnum.ChannelStatusEnabled
? [ ? [
@ -222,7 +226,9 @@ const ChannelTable = ({ Tab }: { Tab: React.ReactNode }) => {
onSuccess={refreshChannelList} onSuccess={refreshChannelList}
/> />
)} )}
{!!testModels && <ModelTest models={testModels} onClose={() => setTestModels(undefined)} />} {!!modelTestData && (
<ModelTest {...modelTestData} onClose={() => setTestModelData(undefined)} />
)}
</> </>
); );
}; };

View File

@ -35,9 +35,11 @@ const CollectionReader = ({
const { t } = useTranslation(); const { t } = useTranslation();
const router = useRouter(); const router = useRouter();
const { userInfo } = useUserStore(); const { userInfo } = useUserStore();
const { collectionId, datasetId, chatItemDataId, sourceId, sourceName } = metadata; const { collectionId, datasetId, chatItemDataId, sourceId, sourceName } = metadata;
const [quoteIndex, setQuoteIndex] = useState(0); const [quoteIndex, setQuoteIndex] = useState(0);
// Get dataset permission
const { data: permissionData, loading: isPermissionLoading } = useRequest2( const { data: permissionData, loading: isPermissionLoading } = useRequest2(
async () => await getDatasetDataPermission(datasetId), async () => await getDatasetDataPermission(datasetId),
{ {
@ -56,6 +58,7 @@ const CollectionReader = ({
const currentQuoteItem = filterResults[quoteIndex]; const currentQuoteItem = filterResults[quoteIndex];
// Get quote list
const { const {
dataList: datasetDataList, dataList: datasetDataList,
setDataList: setDatasetDataList, setDataList: setDatasetDataList,

View File

@ -22,10 +22,14 @@ const QuoteReader = ({
}) => { }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const filterRawSearch = useMemo(() => {
return rawSearch.filter((item) => metadata.collectionIdList.includes(item.collectionId));
}, [rawSearch, metadata.collectionIdList]);
const { data: quoteList, loading } = useRequest2( const { data: quoteList, loading } = useRequest2(
async () => async () =>
await getQuoteDataList({ await getQuoteDataList({
datasetDataIdList: rawSearch.map((item) => item.id), datasetDataIdList: filterRawSearch.map((item) => item.id),
collectionIdList: metadata.collectionIdList, collectionIdList: metadata.collectionIdList,
chatItemDataId: metadata.chatItemDataId, chatItemDataId: metadata.chatItemDataId,
appId: metadata.appId, appId: metadata.appId,
@ -33,12 +37,13 @@ const QuoteReader = ({
...metadata.outLinkAuthData ...metadata.outLinkAuthData
}), }),
{ {
refreshDeps: [metadata, filterRawSearch],
manual: false manual: false
} }
); );
const formatedDataList = useMemo(() => { const formatedDataList = useMemo(() => {
return rawSearch return filterRawSearch
.map((searchItem) => { .map((searchItem) => {
const dataItem = quoteList?.find((item) => item._id === searchItem.id); const dataItem = quoteList?.find((item) => item._id === searchItem.id);
@ -57,7 +62,7 @@ const QuoteReader = ({
.sort((a, b) => { .sort((a, b) => {
return (b.score.primaryScore?.value || 0) - (a.score.primaryScore?.value || 0); return (b.score.primaryScore?.value || 0) - (a.score.primaryScore?.value || 0);
}); });
}, [quoteList, rawSearch]); }, [quoteList, filterRawSearch]);
return ( return (
<Flex flexDirection={'column'} h={'full'}> <Flex flexDirection={'column'} h={'full'}>
@ -71,16 +76,48 @@ const QuoteReader = ({
> >
<Box flex={1} py={4}> <Box flex={1} py={4}>
<Flex gap={2} mr={2} mb={1}> <Flex gap={2} mr={2} mb={1}>
<MyIcon name={'core/chat/quoteFill'} w={['1rem', '1.25rem']} color={'primary.600'} /> {metadata.sourceId ? (
<Box <>
maxW={['200px', '300px']} <MyIcon
className={'textEllipsis'} name={
wordBreak={'break-all'} getSourceNameIcon({
color={'myGray.900'} sourceId: metadata.sourceId,
fontWeight={'medium'} sourceName: metadata.sourceName || ''
> }) as any
{t('common:core.chat.Quote Amount', { amount: rawSearch.length })} }
</Box> w={['1rem', '1.25rem']}
color={'primary.600'}
/>
<Box
ml={1}
maxW={['200px', '220px']}
className={'textEllipsis'}
wordBreak={'break-all'}
fontSize={'sm'}
color={'myGray.900'}
fontWeight={'medium'}
>
{metadata.sourceName || t('common:common.UnKnow Source')}
</Box>
</>
) : (
<>
<MyIcon
name={'core/chat/quoteFill'}
w={['1rem', '1.25rem']}
color={'primary.600'}
/>
<Box
maxW={['200px', '300px']}
className={'textEllipsis'}
wordBreak={'break-all'}
color={'myGray.900'}
fontWeight={'medium'}
>
{t('common:core.chat.Quote Amount', { amount: filterRawSearch.length })}
</Box>
</>
)}
</Flex> </Flex>
<Box fontSize={'mini'} color={'myGray.500'}> <Box fontSize={'mini'} color={'myGray.500'}>
{t('common:core.chat.quote.Quote Tip')} {t('common:core.chat.quote.Quote Tip')}

View File

@ -1,9 +1,9 @@
import React from 'react'; import React from 'react';
import { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type'; import { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type';
import { useContextSelector } from 'use-context-selector';
import { ChatItemContext, GetQuoteProps } from '@/web/core/chat/context/chatItemContext'; import { ChatItemContext, GetQuoteProps } from '@/web/core/chat/context/chatItemContext';
import CollectionQuoteReader from './CollectionQuoteReader'; import CollectionQuoteReader from './CollectionQuoteReader';
import QuoteReader from './QuoteReader'; import QuoteReader from './QuoteReader';
import { useContextSelector } from 'use-context-selector';
const ChatQuoteList = ({ const ChatQuoteList = ({
rawSearch = [], rawSearch = [],
@ -18,7 +18,7 @@ const ChatQuoteList = ({
return ( return (
<> <>
{'collectionId' in metadata && isShowReadRawSource && ( {'collectionId' in metadata && (
<CollectionQuoteReader rawSearch={rawSearch} metadata={metadata} onClose={onClose} /> <CollectionQuoteReader rawSearch={rawSearch} metadata={metadata} onClose={onClose} />
)} )}
{'collectionIdList' in metadata && ( {'collectionIdList' in metadata && (

View File

@ -1,7 +1,7 @@
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next'; import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import { authSystemAdmin } from '@fastgpt/service/support/permission/user/auth'; import { authSystemAdmin } from '@fastgpt/service/support/permission/user/auth';
import { findModelFromAlldata, getReRankModel } from '@fastgpt/service/core/ai/model'; import { findModelFromAlldata } from '@fastgpt/service/core/ai/model';
import { import {
EmbeddingModelItemType, EmbeddingModelItemType,
LLMModelItemType, LLMModelItemType,
@ -9,7 +9,7 @@ import {
STTModelType, STTModelType,
TTSModelType TTSModelType
} from '@fastgpt/global/core/ai/model.d'; } from '@fastgpt/global/core/ai/model.d';
import { getAIApi } from '@fastgpt/service/core/ai/config'; import { createChatCompletion, getAIApi } from '@fastgpt/service/core/ai/config';
import { addLog } from '@fastgpt/service/common/system/log'; import { addLog } from '@fastgpt/service/common/system/log';
import { getVectorsByText } from '@fastgpt/service/core/ai/embedding'; import { getVectorsByText } from '@fastgpt/service/core/ai/embedding';
import { reRankRecall } from '@fastgpt/service/core/ai/rerank'; import { reRankRecall } from '@fastgpt/service/core/ai/rerank';
@ -18,7 +18,7 @@ import { isProduction } from '@fastgpt/global/common/system/constants';
import * as fs from 'fs'; import * as fs from 'fs';
import { llmCompletionsBodyFormat } from '@fastgpt/service/core/ai/utils'; import { llmCompletionsBodyFormat } from '@fastgpt/service/core/ai/utils';
export type testQuery = { model: string; channelId?: string }; export type testQuery = { model: string; channelId?: number };
export type testBody = {}; export type testBody = {};
@ -37,7 +37,7 @@ async function handler(
const headers: Record<string, string> = channelId const headers: Record<string, string> = channelId
? { ? {
'Aiproxy-Channel': channelId 'Aiproxy-Channel': String(channelId)
} }
: {}; : {};
@ -75,26 +75,33 @@ const testLLMModel = async (model: LLMModelItemType, headers: Record<string, str
}, },
model model
); );
const response = await ai.chat.completions.create(requestBody, { const { response, isStreamResponse } = await createChatCompletion({
...(model.requestUrl ? { path: model.requestUrl } : {}), body: requestBody,
headers: model.requestAuth options: {
? { headers: {
Authorization: `Bearer ${model.requestAuth}`, Accept: 'application/json, text/plain, */*',
...headers ...headers
} }
: headers }
}); });
for await (const part of response) { if (isStreamResponse) {
const content = part.choices?.[0]?.delta?.content || ''; for await (const part of response) {
// @ts-ignore const content = part.choices?.[0]?.delta?.content || '';
const reasoningContent = part.choices?.[0]?.delta?.reasoning_content || ''; // @ts-ignore
if (content || reasoningContent) { const reasoningContent = part.choices?.[0]?.delta?.reasoning_content || '';
response?.controller?.abort(); if (content || reasoningContent) {
return; response?.controller?.abort();
return;
}
}
} else {
addLog.info(`Model not stream response`);
const answer = response.choices?.[0]?.message?.content || '';
if (answer) {
return answer;
} }
} }
addLog.info(`Model not stream response`);
return Promise.reject('Model response empty'); return Promise.reject('Model response empty');
}; };

View File

@ -52,7 +52,7 @@ async function handler(
const limitedPageSize = Math.min(pageSize, 30); const limitedPageSize = Math.min(pageSize, 30);
const [{ chat }, { chatItem }] = await Promise.all([ const [{ chat, showRawSource }, { chatItem }] = await Promise.all([
authChatCrud({ authChatCrud({
req, req,
authToken: true, authToken: true,
@ -65,6 +65,9 @@ async function handler(
}), }),
authCollectionInChat({ appId, chatId, chatItemDataId, collectionIds: [collectionId] }) authCollectionInChat({ appId, chatId, chatItemDataId, collectionIds: [collectionId] })
]); ]);
if (!showRawSource) {
return Promise.reject(ChatErrEnum.unAuthChat);
}
if (!chat) return Promise.reject(ChatErrEnum.unAuthChat); if (!chat) return Promise.reject(ChatErrEnum.unAuthChat);
const baseMatch: BaseMatchType = { const baseMatch: BaseMatchType = {

View File

@ -245,16 +245,15 @@ const OutLink = (props: Props) => {
desc={props.appIntro || data?.app?.intro} desc={props.appIntro || data?.app?.intro}
icon={props.appAvatar || data?.app?.avatar} icon={props.appAvatar || data?.app?.avatar}
/> />
<Flex h={'full'}> <Flex
h={'full'}
gap={4}
{...(isEmbed
? { p: '0 !important', insertProps: { borderRadius: '0', boxShadow: 'none' } }
: { p: [0, 5] })}
>
{(!quoteData || isPc) && ( {(!quoteData || isPc) && (
<PageContainer <PageContainer flex={'1 0 0'} w={0} isLoading={loading} p={'0 !important'}>
flex={'1 0 0'}
w={0}
isLoading={loading}
{...(isEmbed
? { p: '0 !important', insertProps: { borderRadius: '0', boxShadow: 'none' } }
: { p: [0, 5] })}
>
<Flex h={'100%'} flexDirection={['column', 'row']}> <Flex h={'100%'} flexDirection={['column', 'row']}>
{RenderHistoryList} {RenderHistoryList}
@ -302,7 +301,7 @@ const OutLink = (props: Props) => {
)} )}
{quoteData && ( {quoteData && (
<PageContainer flex={'1 0 0'} w={0} maxW={'560px'}> <PageContainer flex={'1 0 0'} w={0} maxW={'560px'} p={'0 !important'}>
<ChatQuoteList <ChatQuoteList
rawSearch={quoteData.rawSearch} rawSearch={quoteData.rawSearch}
metadata={quoteData.metadata} metadata={quoteData.metadata}

View File

@ -5,6 +5,7 @@ import type { deleteQuery } from '@/pages/api/core/ai/model/delete';
import type { SystemModelItemType } from '@fastgpt/service/core/ai/type'; import type { SystemModelItemType } from '@fastgpt/service/core/ai/type';
import type { updateWithJsonBody } from '@/pages/api/core/ai/model/updateWithJson'; import type { updateWithJsonBody } from '@/pages/api/core/ai/model/updateWithJson';
import type { updateDefaultBody } from '@/pages/api/core/ai/model/updateDefault'; import type { updateDefaultBody } from '@/pages/api/core/ai/model/updateDefault';
import type { testQuery } from '@/pages/api/core/ai/model/test';
export const getSystemModelList = () => GET<listResponse>('/core/ai/model/list'); export const getSystemModelList = () => GET<listResponse>('/core/ai/model/list');
export const getSystemModelDetail = (model: string) => export const getSystemModelDetail = (model: string) =>
@ -21,7 +22,7 @@ export const getModelConfigJson = () => GET<string>('/core/ai/model/getConfigJso
export const putUpdateWithJson = (data: updateWithJsonBody) => export const putUpdateWithJson = (data: updateWithJsonBody) =>
PUT('/core/ai/model/updateWithJson', data); PUT('/core/ai/model/updateWithJson', data);
export const getTestModel = (model: String) => GET('/core/ai/model/test', { model }); export const getTestModel = (data: testQuery) => GET('/core/ai/model/test', data);
export const putUpdateDefaultModels = (data: updateDefaultBody) => export const putUpdateDefaultModels = (data: updateDefaultBody) =>
PUT('/core/ai/model/updateDefault', data); PUT('/core/ai/model/updateDefault', data);

View File

@ -48,6 +48,8 @@ export type GetCollectionQuoteDataProps = GetQuoteDataBasicProps & {
}; };
export type GetAllQuoteDataProps = GetQuoteDataBasicProps & { export type GetAllQuoteDataProps = GetQuoteDataBasicProps & {
collectionIdList: string[]; collectionIdList: string[];
sourceId?: string;
sourceName?: string;
}; };
export type GetQuoteProps = GetAllQuoteDataProps | GetCollectionQuoteDataProps; export type GetQuoteProps = GetAllQuoteDataProps | GetCollectionQuoteDataProps;