model perf (#3657)
* fix: model * dataset quote * perf: model config * model tag * doubao model config * perf: config model * feat: model test
This commit is contained in:
105
projects/app/src/components/Markdown/A.tsx
Normal file
105
projects/app/src/components/Markdown/A.tsx
Normal file
@@ -0,0 +1,105 @@
|
||||
import { eventBus, EventNameEnum } from '@/web/common/utils/eventbus';
|
||||
import {
|
||||
Button,
|
||||
Link,
|
||||
Popover,
|
||||
PopoverTrigger,
|
||||
PopoverContent,
|
||||
PopoverHeader,
|
||||
PopoverBody,
|
||||
PopoverArrow,
|
||||
PopoverCloseButton
|
||||
} from '@chakra-ui/react';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import React, { useMemo } from 'react';
|
||||
import { getQuoteData } from '@/web/core/dataset/api';
|
||||
import MyBox from '@fastgpt/web/components/common/MyBox';
|
||||
import RawSourceBox from '../core/dataset/RawSourceBox';
|
||||
import { getCollectionSourceData } from '@fastgpt/global/core/dataset/collection/utils';
|
||||
import Markdown from '.';
|
||||
|
||||
const A = ({ children, ...props }: any) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const {
|
||||
data: quoteData,
|
||||
loading,
|
||||
runAsync
|
||||
} = useRequest2(getQuoteData, {
|
||||
manual: true
|
||||
});
|
||||
|
||||
// empty href link
|
||||
if (!props.href && typeof children?.[0] === 'string') {
|
||||
const text = useMemo(() => String(children), [children]);
|
||||
|
||||
return (
|
||||
<MyTooltip label={t('common:core.chat.markdown.Quick Question')}>
|
||||
<Button
|
||||
variant={'whitePrimary'}
|
||||
size={'xs'}
|
||||
borderRadius={'md'}
|
||||
my={1}
|
||||
onClick={() => eventBus.emit(EventNameEnum.sendQuestion, { text })}
|
||||
>
|
||||
{text}
|
||||
</Button>
|
||||
</MyTooltip>
|
||||
);
|
||||
}
|
||||
|
||||
// Quote
|
||||
if (props.href === 'QUOTE' && typeof children?.[0] === 'string') {
|
||||
return (
|
||||
<Popover
|
||||
direction="rtl"
|
||||
isLazy
|
||||
placement="auto"
|
||||
strategy={'fixed'}
|
||||
onOpen={() => runAsync(String(children))}
|
||||
>
|
||||
<PopoverTrigger>
|
||||
<Button variant={'unstyled'} minH={0} minW={0} h={'auto'}>
|
||||
<MyTooltip label={t('common:read_quote')}>
|
||||
<MyIcon
|
||||
name={'core/chat/quoteSign'}
|
||||
w={'1rem'}
|
||||
color={'primary.700'}
|
||||
cursor={'pointer'}
|
||||
transform={'translateY(-3px)'}
|
||||
/>
|
||||
</MyTooltip>
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent boxShadow={'lg'} w={'400px'}>
|
||||
<MyBox isLoading={loading} minH={'300px'}>
|
||||
<PopoverArrow />
|
||||
<PopoverHeader h={'40px'} display={'flex'} alignItems={'center'}>
|
||||
{quoteData?.collection && (
|
||||
<RawSourceBox
|
||||
collectionId={quoteData?.collection._id}
|
||||
{...getCollectionSourceData(quoteData?.collection)}
|
||||
fontSize={'sm'}
|
||||
color={'black'}
|
||||
textDecoration={'none'}
|
||||
/>
|
||||
)}
|
||||
<PopoverCloseButton />
|
||||
</PopoverHeader>
|
||||
<PopoverBody fontSize={'sm'} maxH={'400px'} overflow={'auto'}>
|
||||
<Markdown source={quoteData?.q} />
|
||||
<Markdown source={quoteData?.a} />
|
||||
</PopoverBody>
|
||||
</MyBox>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
|
||||
return <Link {...props}>{children}</Link>;
|
||||
};
|
||||
|
||||
export default A;
|
||||
@@ -10,12 +10,7 @@ import RehypeExternalLinks from 'rehype-external-links';
|
||||
import styles from './index.module.scss';
|
||||
import dynamic from 'next/dynamic';
|
||||
|
||||
import { Link, Button, Box } from '@chakra-ui/react';
|
||||
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { EventNameEnum, eventBus } from '@/web/common/utils/eventbus';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { MARKDOWN_QUOTE_SIGN } from '@fastgpt/global/core/chat/constants';
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import { CodeClassNameEnum } from './utils';
|
||||
|
||||
const CodeLight = dynamic(() => import('./CodeLight'), { ssr: false });
|
||||
@@ -27,6 +22,7 @@ const IframeHtmlCodeBlock = dynamic(() => import('./codeBlock/iframe-html'), { s
|
||||
|
||||
const ChatGuide = dynamic(() => import('./chat/Guide'), { ssr: false });
|
||||
const QuestionGuide = dynamic(() => import('./chat/QuestionGuide'), { ssr: false });
|
||||
const A = dynamic(() => import('./A'), { ssr: false });
|
||||
|
||||
type Props = {
|
||||
source?: string;
|
||||
@@ -74,7 +70,10 @@ const MarkdownRender = ({ source = '', showAnimation, isDisabled, forbidZhFormat
|
||||
'$1$3 $2$4'
|
||||
)
|
||||
// 处理引用标记
|
||||
.replace(/\n*(\[QUOTE SIGN\]\(.*\))/g, '$1');
|
||||
.replace(/\n*(\[QUOTE SIGN\]\(.*\))/g, '$1')
|
||||
// 处理 [quote:id] 格式引用,将 [quote:675934a198f46329dfc6d05a] 转换为 [675934a198f46329dfc6d05a]()
|
||||
.replace(/\[quote:?\s*([a-f0-9]{24})\](?!\()/gi, '[$1](QUOTE)')
|
||||
.replace(/\[([a-f0-9]{24})\](?!\()/g, '[$1](QUOTE)');
|
||||
|
||||
// 还原 URL
|
||||
const finalText = textWithSpaces.replace(
|
||||
@@ -155,53 +154,6 @@ function Image({ src }: { src?: string }) {
|
||||
return <MdImage src={src} />;
|
||||
}
|
||||
|
||||
function A({ children, ...props }: any) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
// empty href link
|
||||
if (!props.href && typeof children?.[0] === 'string') {
|
||||
const text = useMemo(() => String(children), [children]);
|
||||
|
||||
return (
|
||||
<MyTooltip label={t('common:core.chat.markdown.Quick Question')}>
|
||||
<Button
|
||||
variant={'whitePrimary'}
|
||||
size={'xs'}
|
||||
borderRadius={'md'}
|
||||
my={1}
|
||||
onClick={() => eventBus.emit(EventNameEnum.sendQuestion, { text })}
|
||||
>
|
||||
{text}
|
||||
</Button>
|
||||
</MyTooltip>
|
||||
);
|
||||
}
|
||||
|
||||
// quote link(未使用)
|
||||
if (children?.length === 1 && typeof children?.[0] === 'string') {
|
||||
const text = String(children);
|
||||
if (text === MARKDOWN_QUOTE_SIGN && props.href) {
|
||||
return (
|
||||
<MyTooltip label={props.href}>
|
||||
<MyIcon
|
||||
name={'core/chat/quoteSign'}
|
||||
transform={'translateY(-2px)'}
|
||||
w={'18px'}
|
||||
color={'primary.500'}
|
||||
cursor={'pointer'}
|
||||
_hover={{
|
||||
color: 'primary.700'
|
||||
}}
|
||||
// onClick={() => getCollectionSourceAndOpen(props.href)}
|
||||
/>
|
||||
</MyTooltip>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return <Link {...props}>{children}</Link>;
|
||||
}
|
||||
|
||||
function RewritePre({ children }: any) {
|
||||
const modifiedChildren = React.Children.map(children, (child) => {
|
||||
if (React.isValidElement(child)) {
|
||||
|
||||
@@ -307,12 +307,11 @@ const AIChatSettingsModal = ({
|
||||
)}
|
||||
{showVisionSwitch && (
|
||||
<Flex {...FlexItemStyles} h={'25px'}>
|
||||
<Box {...LabelStyles}>
|
||||
<Box {...LabelStyles} w={llmSupportVision ? '9rem' : 'auto'}>
|
||||
<Flex alignItems={'center'}>
|
||||
{t('app:llm_use_vision')}
|
||||
<QuestionTip ml={1} label={t('app:llm_use_vision_tip')}></QuestionTip>
|
||||
</Flex>
|
||||
|
||||
{llmSupportVision ? (
|
||||
<Switch
|
||||
isChecked={useVision}
|
||||
@@ -323,7 +322,7 @@ const AIChatSettingsModal = ({
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<Box fontSize={'sm'} color={'myGray.500'}>
|
||||
<Box ml={3} fontSize={'sm'} color={'myGray.500'}>
|
||||
{t('app:llm_not_support_vision')}
|
||||
</Box>
|
||||
)}
|
||||
|
||||
@@ -82,7 +82,7 @@ const ModelTable = () => {
|
||||
) : (
|
||||
<Flex color={'myGray.700'}>
|
||||
<Box fontWeight={'bold'} color={'myGray.900'} mr={0.5}>
|
||||
{item.charsPointsPrice}
|
||||
{item.charsPointsPrice || 0}
|
||||
</Box>
|
||||
{`${t('common:support.wallet.subscription.point')} / 1K Tokens`}
|
||||
</Flex>
|
||||
@@ -95,7 +95,7 @@ const ModelTable = () => {
|
||||
priceLabel: (
|
||||
<Flex color={'myGray.700'}>
|
||||
<Box fontWeight={'bold'} color={'myGray.900'} mr={0.5}>
|
||||
{item.charsPointsPrice}
|
||||
{item.charsPointsPrice || 0}
|
||||
</Box>
|
||||
{` ${t('common:support.wallet.subscription.point')} / 1K Tokens`}
|
||||
</Flex>
|
||||
@@ -108,7 +108,7 @@ const ModelTable = () => {
|
||||
priceLabel: (
|
||||
<Flex color={'myGray.700'}>
|
||||
<Box fontWeight={'bold'} color={'myGray.900'} mr={0.5}>
|
||||
{item.charsPointsPrice}
|
||||
{item.charsPointsPrice || 0}
|
||||
</Box>
|
||||
{` ${t('common:support.wallet.subscription.point')} / 1K ${t('common:unit.character')}`}
|
||||
</Flex>
|
||||
|
||||
@@ -35,6 +35,7 @@ import {
|
||||
getModelConfigJson,
|
||||
getSystemModelDetail,
|
||||
getSystemModelList,
|
||||
getTestModel,
|
||||
putSystemModel
|
||||
} from '@/web/core/ai/config';
|
||||
import MyBox from '@fastgpt/web/components/common/MyBox';
|
||||
@@ -52,14 +53,21 @@ import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
||||
import { putUpdateWithJson } from '@/web/core/ai/config';
|
||||
import CopyBox from '@fastgpt/web/components/common/String/CopyBox';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
|
||||
const MyModal = dynamic(() => import('@fastgpt/web/components/common/MyModal'));
|
||||
|
||||
const ModelTable = ({ Tab }: { Tab: React.ReactNode }) => {
|
||||
const { t } = useTranslation();
|
||||
const { userInfo } = useUserStore();
|
||||
const { llmModelList, embeddingModelList, ttsModelList, sttModelList, reRankModelList } =
|
||||
useSystemStore();
|
||||
const {
|
||||
llmModelList,
|
||||
embeddingModelList,
|
||||
ttsModelList,
|
||||
sttModelList,
|
||||
reRankModelList,
|
||||
feConfigs
|
||||
} = useSystemStore();
|
||||
|
||||
const isRoot = userInfo?.username === 'root';
|
||||
|
||||
@@ -125,7 +133,7 @@ const ModelTable = ({ Tab }: { Tab: React.ReactNode }) => {
|
||||
) : (
|
||||
<Flex color={'myGray.700'}>
|
||||
<Box fontWeight={'bold'} color={'myGray.900'} mr={0.5}>
|
||||
{item.charsPointsPrice}
|
||||
{item.charsPointsPrice || 0}
|
||||
</Box>
|
||||
{`${t('common:support.wallet.subscription.point')} / 1K Tokens`}
|
||||
</Flex>
|
||||
@@ -140,7 +148,7 @@ const ModelTable = ({ Tab }: { Tab: React.ReactNode }) => {
|
||||
priceLabel: (
|
||||
<Flex color={'myGray.700'}>
|
||||
<Box fontWeight={'bold'} color={'myGray.900'} mr={0.5}>
|
||||
{item.charsPointsPrice}
|
||||
{item.charsPointsPrice || 0}
|
||||
</Box>
|
||||
{` ${t('common:support.wallet.subscription.point')} / 1K Tokens`}
|
||||
</Flex>
|
||||
@@ -155,7 +163,7 @@ const ModelTable = ({ Tab }: { Tab: React.ReactNode }) => {
|
||||
priceLabel: (
|
||||
<Flex color={'myGray.700'}>
|
||||
<Box fontWeight={'bold'} color={'myGray.900'} mr={0.5}>
|
||||
{item.charsPointsPrice}
|
||||
{item.charsPointsPrice || 0}
|
||||
</Box>
|
||||
{` ${t('common:support.wallet.subscription.point')} / 1K ${t('common:unit.character')}`}
|
||||
</Flex>
|
||||
@@ -239,6 +247,10 @@ const ModelTable = ({ Tab }: { Tab: React.ReactNode }) => {
|
||||
);
|
||||
}, [systemModelList]);
|
||||
|
||||
const { runAsync: onTestModel, loading: testingModel } = useRequest2(getTestModel, {
|
||||
manual: true,
|
||||
successToast: t('common:common.Success')
|
||||
});
|
||||
const { runAsync: updateModel, loading: updatingModel } = useRequest2(putSystemModel, {
|
||||
onSuccess: refreshModels
|
||||
});
|
||||
@@ -275,8 +287,8 @@ const ModelTable = ({ Tab }: { Tab: React.ReactNode }) => {
|
||||
model: '',
|
||||
name: '',
|
||||
charsPointsPrice: 0,
|
||||
inputPrice: 0,
|
||||
outputPrice: 0,
|
||||
inputPrice: undefined,
|
||||
outputPrice: undefined,
|
||||
|
||||
isCustom: true,
|
||||
isActive: true,
|
||||
@@ -291,7 +303,9 @@ const ModelTable = ({ Tab }: { Tab: React.ReactNode }) => {
|
||||
onClose: onCloseJsonConfig
|
||||
} = useDisclosure();
|
||||
|
||||
const isLoading = loadingModels || loadingData || updatingModel;
|
||||
const isLoading = loadingModels || loadingData || updatingModel || testingModel;
|
||||
|
||||
const [showModelId, setShowModelId] = useState(true);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -376,9 +390,20 @@ const ModelTable = ({ Tab }: { Tab: React.ReactNode }) => {
|
||||
<Table>
|
||||
<Thead>
|
||||
<Tr color={'myGray.600'}>
|
||||
<Th fontSize={'xs'}>{t('common:model.name')}</Th>
|
||||
<Th fontSize={'xs'}>
|
||||
<HStack
|
||||
spacing={1}
|
||||
cursor={'pointer'}
|
||||
onClick={() => setShowModelId(!showModelId)}
|
||||
>
|
||||
<Box>
|
||||
{showModelId ? t('account:model.model_id') : t('common:model.name')}
|
||||
</Box>
|
||||
<MyIcon name={'modal/changePer'} w={'1rem'} />
|
||||
</HStack>
|
||||
</Th>
|
||||
<Th fontSize={'xs'}>{t('common:model.model_type')}</Th>
|
||||
<Th fontSize={'xs'}>{t('common:model.billing')}</Th>
|
||||
{feConfigs?.isPlus && <Th fontSize={'xs'}>{t('common:model.billing')}</Th>}
|
||||
<Th fontSize={'xs'}>
|
||||
<Box
|
||||
cursor={'pointer'}
|
||||
@@ -396,16 +421,37 @@ const ModelTable = ({ Tab }: { Tab: React.ReactNode }) => {
|
||||
<Tr key={item.model} _hover={{ bg: 'myGray.50' }}>
|
||||
<Td fontSize={'sm'}>
|
||||
<HStack>
|
||||
<Avatar src={item.avatar} w={'1.2rem'} />
|
||||
<CopyBox value={item.name} color={'myGray.900'}>
|
||||
{item.name}
|
||||
<Avatar src={item.avatar} w={'1.2rem'} borderRadius={'50%'} />
|
||||
<CopyBox
|
||||
value={showModelId ? item.model : item.name}
|
||||
color={'myGray.900'}
|
||||
fontWeight={'500'}
|
||||
>
|
||||
{showModelId ? item.model : item.name}
|
||||
</CopyBox>
|
||||
</HStack>
|
||||
<HStack mt={2}>
|
||||
{item.contextToken && (
|
||||
<MyTag type="borderFill" colorSchema="blue" py={0.5}>
|
||||
{Math.floor(item.contextToken / 1000)}k
|
||||
</MyTag>
|
||||
)}
|
||||
{item.vision && (
|
||||
<MyTag type="borderFill" colorSchema="green" py={0.5}>
|
||||
{t('account:model.vision_tag')}
|
||||
</MyTag>
|
||||
)}
|
||||
{item.toolChoice && (
|
||||
<MyTag type="borderFill" colorSchema="adora" py={0.5}>
|
||||
{t('account:model.tool_choice_tag')}
|
||||
</MyTag>
|
||||
)}
|
||||
</HStack>
|
||||
</Td>
|
||||
<Td>
|
||||
<MyTag colorSchema={item.tagColor as any}>{item.typeLabel}</MyTag>
|
||||
</Td>
|
||||
<Td fontSize={'sm'}>{item.priceLabel}</Td>
|
||||
{feConfigs?.isPlus && <Td fontSize={'sm'}>{item.priceLabel}</Td>}
|
||||
<Td fontSize={'sm'}>
|
||||
<Switch
|
||||
size={'sm'}
|
||||
@@ -421,8 +467,14 @@ const ModelTable = ({ Tab }: { Tab: React.ReactNode }) => {
|
||||
</Td>
|
||||
<Td>
|
||||
<HStack>
|
||||
<MyIconButton
|
||||
icon={'core/chat/sendLight'}
|
||||
tip={t('account:model.test_model')}
|
||||
onClick={() => onTestModel(item.model)}
|
||||
/>
|
||||
<MyIconButton
|
||||
icon={'common/settingLight'}
|
||||
tip={t('account:model.edit_model')}
|
||||
onClick={() => onEditModel(item.model)}
|
||||
/>
|
||||
{item.isCustom && (
|
||||
@@ -545,24 +597,6 @@ const ModelEditModal = ({
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
<Tr>
|
||||
<Td>{t('common:model.provider')}</Td>
|
||||
<Td textAlign={'right'}>
|
||||
{isCustom ? (
|
||||
<MySelect
|
||||
value={provider}
|
||||
onchange={(value) => setValue('provider', value)}
|
||||
list={providerList.current}
|
||||
{...InputStyles}
|
||||
/>
|
||||
) : (
|
||||
<HStack justifyContent={'flex-end'}>
|
||||
<Avatar src={providerData.avatar} w={'1rem'} />
|
||||
<Box>{t(providerData.name)}</Box>
|
||||
</HStack>
|
||||
)}
|
||||
</Td>
|
||||
</Tr>
|
||||
<Tr>
|
||||
<Td>
|
||||
<HStack spacing={1}>
|
||||
@@ -578,6 +612,17 @@ const ModelEditModal = ({
|
||||
)}
|
||||
</Td>
|
||||
</Tr>
|
||||
<Tr>
|
||||
<Td>{t('common:model.provider')}</Td>
|
||||
<Td textAlign={'right'}>
|
||||
<MySelect
|
||||
value={provider}
|
||||
onchange={(value) => setValue('provider', value)}
|
||||
list={providerList.current}
|
||||
{...InputStyles}
|
||||
/>
|
||||
</Td>
|
||||
</Tr>
|
||||
<Tr>
|
||||
<Td>
|
||||
<HStack spacing={1}>
|
||||
|
||||
@@ -79,6 +79,16 @@ const EditModal = ({ onClose, ...props }: RenderInputProps & { onClose: () => vo
|
||||
}>();
|
||||
const quoteTemplateVariables = useMemo(
|
||||
() => [
|
||||
{
|
||||
key: 'id',
|
||||
label: 'id',
|
||||
icon: 'core/app/simpleMode/variable'
|
||||
},
|
||||
{
|
||||
key: 'source',
|
||||
label: t('common:core.dataset.search.Source name'),
|
||||
icon: 'core/app/simpleMode/variable'
|
||||
},
|
||||
{
|
||||
key: 'q',
|
||||
label: 'q',
|
||||
@@ -90,13 +100,8 @@ const EditModal = ({ onClose, ...props }: RenderInputProps & { onClose: () => vo
|
||||
icon: 'core/app/simpleMode/variable'
|
||||
},
|
||||
{
|
||||
key: 'source',
|
||||
label: t('common:core.dataset.search.Source name'),
|
||||
icon: 'core/app/simpleMode/variable'
|
||||
},
|
||||
{
|
||||
key: 'sourceId',
|
||||
label: t('common:core.dataset.search.Source id'),
|
||||
key: 'updateTime',
|
||||
label: t('app:source_updateTime'),
|
||||
icon: 'core/app/simpleMode/variable'
|
||||
},
|
||||
{
|
||||
|
||||
@@ -20,6 +20,11 @@ export type listResponse = {
|
||||
|
||||
isActive: boolean;
|
||||
isCustom: boolean;
|
||||
|
||||
// Tag
|
||||
contextToken?: number;
|
||||
vision?: boolean;
|
||||
toolChoice?: boolean;
|
||||
}[];
|
||||
|
||||
async function handler(
|
||||
@@ -39,7 +44,13 @@ async function handler(
|
||||
inputPrice: model.inputPrice,
|
||||
outputPrice: model.outputPrice,
|
||||
isActive: model.isActive ?? false,
|
||||
isCustom: model.isCustom ?? false
|
||||
isCustom: model.isCustom ?? false,
|
||||
|
||||
// Tag
|
||||
contextToken:
|
||||
'maxContext' in model ? model.maxContext : 'maxToken' in model ? model.maxToken : undefined,
|
||||
vision: 'vision' in model ? model.vision : undefined,
|
||||
toolChoice: 'toolChoice' in model ? model.toolChoice : undefined
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
126
projects/app/src/pages/api/core/ai/model/test.ts
Normal file
126
projects/app/src/pages/api/core/ai/model/test.ts
Normal file
@@ -0,0 +1,126 @@
|
||||
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
|
||||
import { NextAPI } from '@/service/middleware/entry';
|
||||
import { authSystemAdmin } from '@fastgpt/service/support/permission/user/auth';
|
||||
import { findModelFromAlldata } from '@fastgpt/service/core/ai/model';
|
||||
import {
|
||||
EmbeddingModelItemType,
|
||||
LLMModelItemType,
|
||||
ReRankModelItemType,
|
||||
STTModelType,
|
||||
TTSModelType
|
||||
} from '@fastgpt/global/core/ai/model.d';
|
||||
import { getAIApi } from '@fastgpt/service/core/ai/config';
|
||||
import { addLog } from '@fastgpt/service/common/system/log';
|
||||
import { getVectorsByText } from '@fastgpt/service/core/ai/embedding';
|
||||
import { reRankRecall } from '@fastgpt/service/core/ai/rerank';
|
||||
import { aiTranscriptions } from '@fastgpt/service/core/ai/audio/transcriptions';
|
||||
import { isProduction } from '@fastgpt/global/common/system/constants';
|
||||
import * as fs from 'fs';
|
||||
|
||||
export type testQuery = { model: string };
|
||||
|
||||
export type testBody = {};
|
||||
|
||||
export type testResponse = any;
|
||||
|
||||
async function handler(
|
||||
req: ApiRequestProps<testBody, testQuery>,
|
||||
res: ApiResponseType<any>
|
||||
): Promise<testResponse> {
|
||||
await authSystemAdmin({ req });
|
||||
|
||||
const { model } = req.query;
|
||||
const modelData = findModelFromAlldata(model);
|
||||
|
||||
if (!modelData) return Promise.reject('Model not found');
|
||||
|
||||
if (modelData.type === 'llm') {
|
||||
return testLLMModel(modelData);
|
||||
}
|
||||
if (modelData.type === 'embedding') {
|
||||
return testEmbeddingModel(modelData);
|
||||
}
|
||||
if (modelData.type === 'tts') {
|
||||
return testTTSModel(modelData);
|
||||
}
|
||||
if (modelData.type === 'stt') {
|
||||
return testSTTModel(modelData);
|
||||
}
|
||||
if (modelData.type === 'rerank') {
|
||||
return testReRankModel(modelData);
|
||||
}
|
||||
|
||||
return Promise.reject('Model type not supported');
|
||||
}
|
||||
|
||||
export default NextAPI(handler);
|
||||
|
||||
const testLLMModel = async (model: LLMModelItemType) => {
|
||||
const ai = getAIApi({});
|
||||
const response = await ai.chat.completions.create(
|
||||
{
|
||||
model: model.model,
|
||||
messages: [{ role: 'user', content: 'hi' }],
|
||||
stream: false,
|
||||
max_tokens: 10
|
||||
},
|
||||
{
|
||||
...(model.requestUrl ? { path: model.requestUrl } : {}),
|
||||
headers: {
|
||||
...(model.requestAuth ? { Authorization: `Bearer ${model.requestAuth}` } : {})
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const responseText = response.choices?.[0]?.message?.content;
|
||||
|
||||
if (!responseText) {
|
||||
return Promise.reject('Model response empty');
|
||||
}
|
||||
|
||||
addLog.info(`Model test response: ${responseText}`);
|
||||
};
|
||||
|
||||
const testEmbeddingModel = async (model: EmbeddingModelItemType) => {
|
||||
return getVectorsByText({
|
||||
input: 'Hi',
|
||||
model
|
||||
});
|
||||
};
|
||||
|
||||
const testTTSModel = async (model: TTSModelType) => {
|
||||
const ai = getAIApi();
|
||||
await ai.audio.speech.create(
|
||||
{
|
||||
model: model.model,
|
||||
voice: model.voices[0]?.value as any,
|
||||
input: 'Hi',
|
||||
response_format: 'mp3',
|
||||
speed: 1
|
||||
},
|
||||
model.requestUrl && model.requestAuth
|
||||
? {
|
||||
path: model.requestUrl,
|
||||
headers: {
|
||||
Authorization: `Bearer ${model.requestAuth}`
|
||||
}
|
||||
}
|
||||
: {}
|
||||
);
|
||||
};
|
||||
|
||||
const testSTTModel = async (model: STTModelType) => {
|
||||
const path = isProduction ? '/app/data/test.mp3' : 'data/test.mp3';
|
||||
const { text } = await aiTranscriptions({
|
||||
model: model.model,
|
||||
fileStream: fs.createReadStream(path)
|
||||
});
|
||||
addLog.info(`STT result: ${text}`);
|
||||
};
|
||||
|
||||
const testReRankModel = async (model: ReRankModelItemType) => {
|
||||
await reRankRecall({
|
||||
query: 'Hi',
|
||||
documents: [{ id: '1', text: 'Hi' }]
|
||||
});
|
||||
};
|
||||
@@ -39,6 +39,9 @@ async function handler(
|
||||
return Promise.reject(`${item.model} metadata.provider is required`);
|
||||
}
|
||||
item.metadata.model = item.model.trim();
|
||||
if (!item.metadata.name) {
|
||||
item.metadata.name = item.model;
|
||||
}
|
||||
}
|
||||
|
||||
await mongoSessionRun(async (session) => {
|
||||
|
||||
34
projects/app/src/pages/api/core/dataset/data/getQuoteData.ts
Normal file
34
projects/app/src/pages/api/core/dataset/data/getQuoteData.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import type { NextApiRequest } from 'next';
|
||||
import { NextAPI } from '@/service/middleware/entry';
|
||||
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||
import { authDatasetData } from '@fastgpt/service/support/permission/dataset/auth';
|
||||
import { CollectionWithDatasetType } from '@fastgpt/global/core/dataset/type';
|
||||
|
||||
export type GetQuoteDataResponse = {
|
||||
collection: CollectionWithDatasetType;
|
||||
q: string;
|
||||
a: string;
|
||||
};
|
||||
|
||||
async function handler(req: NextApiRequest): Promise<GetQuoteDataResponse> {
|
||||
const { id: dataId } = req.query as {
|
||||
id: string;
|
||||
};
|
||||
|
||||
// 凭证校验
|
||||
const { datasetData, collection } = await authDatasetData({
|
||||
req,
|
||||
authToken: true,
|
||||
authApiKey: true,
|
||||
dataId,
|
||||
per: ReadPermissionVal
|
||||
});
|
||||
|
||||
return {
|
||||
collection,
|
||||
q: datasetData.q,
|
||||
a: datasetData.a
|
||||
};
|
||||
}
|
||||
|
||||
export default NextAPI(handler);
|
||||
@@ -1,14 +1,12 @@
|
||||
import { initHttpAgent } from '@fastgpt/service/common/middle/httpAgent';
|
||||
import fs, { existsSync, readdirSync } from 'fs';
|
||||
import fs, { existsSync } from 'fs';
|
||||
import type { FastGPTFeConfigsType } from '@fastgpt/global/common/system/types/index.d';
|
||||
import type { FastGPTConfigFileType } from '@fastgpt/global/common/system/types/index.d';
|
||||
import { PluginSourceEnum } from '@fastgpt/global/core/plugin/constants';
|
||||
import { getFastGPTConfigFromDB } from '@fastgpt/service/common/system/config/controller';
|
||||
import { FastGPTProUrl } from '@fastgpt/service/common/system/constants';
|
||||
import { isProduction } from '@fastgpt/global/common/system/constants';
|
||||
import { initFastGPTConfig } from '@fastgpt/service/common/system/tools';
|
||||
import json5 from 'json5';
|
||||
import { SystemPluginTemplateItemType } from '@fastgpt/global/core/workflow/type';
|
||||
import { defaultGroup, defaultTemplateTypes } from '@fastgpt/web/core/workflow/constants';
|
||||
import { MongoPluginGroups } from '@fastgpt/service/core/app/plugin/pluginGroupSchema';
|
||||
import { MongoTemplateTypes } from '@fastgpt/service/core/app/templates/templateTypeSchema';
|
||||
@@ -48,14 +46,7 @@ export function initGlobalVariables() {
|
||||
|
||||
/* Init system data(Need to connected db). It only needs to run once */
|
||||
export async function getInitConfig() {
|
||||
return Promise.all([
|
||||
initSystemConfig(),
|
||||
getSystemVersion(),
|
||||
loadSystemModels(),
|
||||
|
||||
// abandon
|
||||
getSystemPlugin()
|
||||
]);
|
||||
return Promise.all([initSystemConfig(), getSystemVersion(), loadSystemModels()]);
|
||||
}
|
||||
|
||||
const defaultFeConfigs: FastGPTFeConfigsType = {
|
||||
@@ -129,34 +120,6 @@ async function getSystemVersion() {
|
||||
}
|
||||
}
|
||||
|
||||
async function getSystemPlugin() {
|
||||
if (global.communityPlugins && global.communityPlugins.length > 0) return;
|
||||
|
||||
const basePath =
|
||||
process.env.NODE_ENV === 'development' ? 'data/pluginTemplates' : '/app/data/pluginTemplates';
|
||||
// read data/pluginTemplates directory, get all json file
|
||||
const files = readdirSync(basePath);
|
||||
// filter json file
|
||||
const filterFiles = files.filter((item) => item.endsWith('.json'));
|
||||
|
||||
// read json file
|
||||
const fileTemplates = await Promise.all(
|
||||
filterFiles.map<Promise<SystemPluginTemplateItemType>>(async (filename) => {
|
||||
const content = await fs.promises.readFile(`${basePath}/${filename}`, 'utf-8');
|
||||
return {
|
||||
...json5.parse(content),
|
||||
originCost: 0,
|
||||
currentCost: 0,
|
||||
id: `${PluginSourceEnum.community}-${filename.replace('.json', '')}`
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
fileTemplates.sort((a, b) => (b.weight || 0) - (a.weight || 0));
|
||||
|
||||
global.communityPlugins = fileTemplates;
|
||||
}
|
||||
|
||||
export async function initSystemPluginGroups() {
|
||||
try {
|
||||
const { groupOrder, ...restDefaultGroup } = defaultGroup;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { GET, PUT, DELETE } from '@/web/common/api/request';
|
||||
import { GET, PUT, DELETE, POST } from '@/web/common/api/request';
|
||||
import type { listResponse } from '@/pages/api/core/ai/model/list';
|
||||
import type { updateBody } from '@/pages/api/core/ai/model/update';
|
||||
import type { deleteQuery } from '@/pages/api/core/ai/model/delete';
|
||||
@@ -16,3 +16,5 @@ export const deleteSystemModel = (data: deleteQuery) => DELETE('/core/ai/model/d
|
||||
export const getModelConfigJson = () => GET<string>('/core/ai/model/getConfigJson');
|
||||
export const putUpdateWithJson = (data: updateWithJsonBody) =>
|
||||
PUT('/core/ai/model/updateWithJson', data);
|
||||
|
||||
export const getTestModel = (model: String) => GET('/core/ai/model/test', { model });
|
||||
|
||||
@@ -65,6 +65,7 @@ import type {
|
||||
listExistIdQuery,
|
||||
listExistIdResponse
|
||||
} from '@/pages/api/core/dataset/apiDataset/listExistId';
|
||||
import { GetQuoteDataResponse } from '@/pages/api/core/dataset/data/getQuoteData';
|
||||
|
||||
/* ======================== dataset ======================= */
|
||||
export const getDatasets = (data: GetDatasetListBody) =>
|
||||
@@ -203,6 +204,10 @@ export const putDatasetDataById = (data: UpdateDatasetDataProps) =>
|
||||
export const delOneDatasetDataById = (id: string) =>
|
||||
DELETE<string>(`/core/dataset/data/delete`, { id });
|
||||
|
||||
// Get quote data
|
||||
export const getQuoteData = (id: string) =>
|
||||
GET<GetQuoteDataResponse>(`/core/dataset/data/getQuoteData`, { id });
|
||||
|
||||
/* ================ training ==================== */
|
||||
export const postRebuildEmbedding = (data: rebuildEmbeddingBody) =>
|
||||
POST(`/core/dataset/training/rebuildEmbedding`, data);
|
||||
|
||||
Reference in New Issue
Block a user