mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-23 05:12:39 +00:00
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:
@@ -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",
|
||||
|
@@ -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>
|
||||
|
@@ -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}
|
||||
|
@@ -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
|
||||
|
@@ -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 />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@@ -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>
|
||||
|
@@ -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'}
|
||||
|
@@ -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
|
||||
});
|
||||
|
@@ -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);
|
||||
|
@@ -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)
|
||||
});
|
||||
|
||||
|
Reference in New Issue
Block a user