add model test log (#4272)

* sync collection

* remove lock

* add model test log

* update ui

* update log

* fix: channel test

* preview chunk ui

* test model ux

* test model log

* perf: dataset selector

* fix: system plugin auth

* update nextjs
This commit is contained in:
Archer
2025-03-24 13:49:43 +08:00
committed by archer
parent a680b565ea
commit 2fcf421672
24 changed files with 210 additions and 153 deletions

View File

@@ -42,7 +42,7 @@
"lodash": "^4.17.21",
"mermaid": "^10.2.3",
"nanoid": "^5.1.3",
"next": "14.2.24",
"next": "14.2.25",
"next-i18next": "15.4.2",
"nprogress": "^0.2.0",
"qrcode": "^1.5.4",

View File

@@ -69,31 +69,37 @@ export const DatasetSelectModal = ({
{selectedDatasets.map((item) =>
(() => {
return (
<Card
key={item.datasetId}
p={3}
border={theme.borders.base}
boxShadow={'sm'}
bg={'primary.200'}
>
<Flex alignItems={'center'} h={'38px'}>
<Avatar src={item.avatar} w={['1.25rem', '1.75rem']}></Avatar>
<Box flex={'1 0 0'} w={0} className="textEllipsis" mx={3}>
{item.name}
</Box>
<MyIcon
name={'delete'}
w={'14px'}
cursor={'pointer'}
_hover={{ color: 'red.500' }}
onClick={() => {
setSelectedDatasets((state) =>
state.filter((dataset) => dataset.datasetId !== item.datasetId)
);
}}
/>
</Flex>
</Card>
<MyTooltip label={item.name}>
<Card
key={item.datasetId}
p={3}
border={'base'}
boxShadow={'sm'}
bg={'primary.200'}
>
<Flex alignItems={'center'} h={'38px'}>
<Avatar
src={item.avatar}
w={['1.25rem', '1.75rem']}
borderRadius={'sm'}
></Avatar>
<Box flex={'1 0 0'} w={0} className="textEllipsis" mx={3} fontSize={'sm'}>
{item.name}
</Box>
<MyIcon
name={'delete'}
w={'14px'}
cursor={'pointer'}
_hover={{ color: 'red.500' }}
onClick={() => {
setSelectedDatasets((state) =>
state.filter((dataset) => dataset.datasetId !== item.datasetId)
);
}}
/>
</Flex>
</Card>
</MyTooltip>
);
})()
)}
@@ -117,7 +123,7 @@ export const DatasetSelectModal = ({
label={
item.type === DatasetTypeEnum.folder
? t('common:dataset.Select Folder')
: t('common:dataset.Select Dataset')
: item.name
}
>
<Card
@@ -152,14 +158,18 @@ export const DatasetSelectModal = ({
}}
>
<Flex alignItems={'center'} h={'38px'}>
<Avatar src={item.avatar} w={['24px', '28px']}></Avatar>
<Avatar
src={item.avatar}
w={['1.25rem', '1.75rem']}
borderRadius={'sm'}
></Avatar>
<Box
flex={'1 0 0'}
w={0}
className="textEllipsis"
ml={3}
fontSize={'md'}
color={'myGray.900'}
fontSize={'sm'}
>
{item.name}
</Box>

View File

@@ -268,12 +268,10 @@ const RenderUserFormInteractive = React.memo(function RenderFormInput({
{interactive.params.description && <Markdown source={interactive.params.description} />}
{interactive.params.inputForm?.map((input) => (
<Box key={input.label}>
<Flex mb={1} alignItems={'center'} w={'full'}>
<FormLabel required={input.required} w={'full'} whiteSpace={'pre-wrap'}>
{input.label}
{input.description && <QuestionTip ml={1} label={input.description} />}
</FormLabel>
</Flex>
<FormLabel mb={1} required={input.required} whiteSpace={'pre-wrap'}>
{input.label}
{input.description && <QuestionTip ml={1} label={input.description} />}
</FormLabel>
{input.type === FlowNodeInputTypeEnum.input && (
<MyTextarea
isDisabled={interactive.params.submitted}

View File

@@ -250,22 +250,24 @@ export const WholeResponseContent = ({
value={activeModule?.similarity}
/>
<Row label={t('common:core.chat.response.module limit')} value={activeModule?.limit} />
<Row
label={t('common:core.chat.response.search using reRank')}
rawDom={
<Box border={'base'} borderRadius={'md'} p={2}>
{activeModule?.searchUsingReRank ? (
activeModule?.rerankModel ? (
<Box>{`${activeModule.rerankModel}: ${activeModule.rerankWeight}`}</Box>
{activeModule?.searchUsingReRank !== undefined && (
<Row
label={t('common:core.chat.response.search using reRank')}
rawDom={
<Box border={'base'} borderRadius={'md'} p={2}>
{activeModule?.searchUsingReRank ? (
activeModule?.rerankModel ? (
<Box>{`${activeModule.rerankModel}: ${activeModule.rerankWeight}`}</Box>
) : (
'True'
)
) : (
'True'
)
) : (
`False`
)}
</Box>
}
/>
`False`
)}
</Box>
}
/>
)}
{activeModule.queryExtensionResult && (
<>
<Row

View File

@@ -38,6 +38,7 @@ import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
import MyNumberInput from '@fastgpt/web/components/common/Input/NumberInput';
import { getModelProvider } from '@fastgpt/global/core/ai/provider';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
const EditChannelModal = dynamic(() => import('./EditChannelModal'), { ssr: false });
const ModelTest = dynamic(() => import('./ModelTest'), { ssr: false });
@@ -77,6 +78,9 @@ const ChannelTable = ({ Tab }: { Tab: React.ReactNode }) => {
}
);
const { openConfirm, ConfirmModal } = useConfirm({
type: 'delete'
});
const { runAsync: onDeleteChannel, loading: loadingDeleteChannel } = useRequest2(deleteChannel, {
manual: true,
onSuccess: () => {
@@ -212,7 +216,14 @@ const ChannelTable = ({ Tab }: { Tab: React.ReactNode }) => {
type: 'danger',
icon: 'delete',
label: t('common:common.Delete'),
onClick: () => onDeleteChannel(item.id)
onClick: () =>
openConfirm(
() => onDeleteChannel(item.id),
undefined,
t('account_model:confirm_delete_channel', {
name: item.name
})
)()
}
]
}
@@ -238,6 +249,7 @@ const ChannelTable = ({ Tab }: { Tab: React.ReactNode }) => {
{!!modelTestData && (
<ModelTest {...modelTestData} onClose={() => setTestModelData(undefined)} />
)}
<ConfirmModal />
</>
);
};

View File

@@ -197,7 +197,7 @@ const ChannelLog = ({ Tab }: { Tab: React.ReactNode }) => {
/>
</Box>
</HStack>
<HStack flex={'0 0 200px'}>
<HStack>
<FormLabel>{t('account_model:channel_name')}</FormLabel>
<Box flex={'1 0 0'}>
<MySelect<string>
@@ -210,7 +210,7 @@ const ChannelLog = ({ Tab }: { Tab: React.ReactNode }) => {
/>
</Box>
</HStack>
<HStack flex={'0 0 200px'}>
<HStack>
<FormLabel>{t('account_model:model_name')}</FormLabel>
<Box flex={'1 0 0'}>
<MySelect<string>

View File

@@ -34,9 +34,9 @@ const PreviewData = () => {
const [previewFile, setPreviewFile] = useState<ImportSourceItemType>();
const { data = [], loading: isLoading } = useRequest2(
const { data = { chunks: [], total: 0 }, loading: isLoading } = useRequest2(
async () => {
if (!previewFile) return;
if (!previewFile) return { chunks: [], total: 0 };
if (importSource === ImportDataSourceEnum.fileCustom) {
const chunkSplitter = processParamsForm.getValues('chunkSplitter');
const { chunks } = splitText2Chunks({
@@ -46,10 +46,13 @@ const PreviewData = () => {
overlapRatio: chunkOverlapRatio,
customReg: chunkSplitter ? [chunkSplitter] : []
});
return chunks.map((chunk) => ({
q: chunk,
a: ''
}));
return {
chunks: chunks.map((chunk) => ({
q: chunk,
a: ''
})),
total: chunks.length
};
}
return getPreviewChunks({
@@ -81,7 +84,7 @@ const PreviewData = () => {
manual: false,
onSuccess(result) {
if (!previewFile) return;
if (!result || result.length === 0) {
if (!result || result.total === 0) {
toast({
title: t('dataset:preview_chunk_empty'),
status: 'error'
@@ -130,14 +133,14 @@ const PreviewData = () => {
<Flex py={4} px={5} borderBottom={'base'} justifyContent={'space-between'}>
<FormLabel fontSize={'md'}>{t('dataset:preview_chunk')}</FormLabel>
<Box fontSize={'xs'} color={'myGray.500'}>
{t('dataset:preview_chunk_intro')}
{t('dataset:preview_chunk_intro', { total: data.total })}
</Box>
</Flex>
<MyBox isLoading={isLoading} flex={'1 0 0'} h={0}>
<Box h={'100%'} overflowY={'auto'} px={5} py={3}>
{previewFile ? (
<>
{data.map((item, index) => (
{data.chunks.map((item, index) => (
<Box
key={index}
fontSize={'sm'}

View File

@@ -35,11 +35,17 @@ async function handler(
if (!modelData) return Promise.reject('Model not found');
if (channelId) {
delete modelData.requestUrl;
delete modelData.requestAuth;
}
const headers: Record<string, string> = channelId
? {
'Aiproxy-Channel': String(channelId)
}
: {};
addLog.debug(`Test model`, modelData);
if (modelData.type === 'llm') {
return testLLMModel(modelData, headers);
@@ -63,10 +69,6 @@ async function handler(
export default NextAPI(handler);
const testLLMModel = async (model: LLMModelItemType, headers: Record<string, string>) => {
const ai = getAIApi({
timeout: 10000
});
const requestBody = llmCompletionsBodyFormat(
{
model: model.model,
@@ -75,6 +77,7 @@ const testLLMModel = async (model: LLMModelItemType, headers: Record<string, str
},
model
);
const { response, isStreamResponse } = await createChatCompletion({
body: requestBody,
options: {
@@ -144,7 +147,7 @@ const testTTSModel = async (model: TTSModelType, headers: Record<string, string>
const testSTTModel = async (model: STTModelType, headers: Record<string, string>) => {
const path = isProduction ? '/app/data/test.mp3' : 'data/test.mp3';
const { text } = await aiTranscriptions({
model: model.model,
model,
fileStream: fs.createReadStream(path),
headers
});

View File

@@ -43,9 +43,12 @@ export type PostPreviewFilesChunksProps = {
externalFileId?: string;
};
export type PreviewChunksResponse = {
q: string;
a: string;
}[];
chunks: {
q: string;
a: string;
}[];
total: number;
};
async function handler(
req: ApiRequestProps<PostPreviewFilesChunksProps>
@@ -123,13 +126,17 @@ async function handler(
customPdfParse
});
return rawText2Chunks({
const chunks = rawText2Chunks({
rawText,
chunkSize,
maxSize: getLLMMaxChunkSize(getLLMModel(dataset.agentModel)),
overlapRatio,
customReg: chunkSplitter ? [chunkSplitter] : [],
isQAImport: isQAImport
}).slice(0, 10);
});
return {
chunks: chunks.slice(0, 10),
total: chunks.length
};
}
export default NextAPI(handler);

View File

@@ -66,7 +66,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
// }
const result = await aiTranscriptions({
model: getDefaultSTTModel().model,
model: getDefaultSTTModel(),
fileStream: fs.createReadStream(file.path)
});