4.8.21 feature (#3720)

* agent search demo

* edit form force close image select

* feat: llm params and doubao1.5

* perf: model error tip

* fix: template register path

* package
This commit is contained in:
Archer
2025-02-08 10:44:33 +08:00
committed by GitHub
parent bb82b515e0
commit 42b2046f96
45 changed files with 896 additions and 109 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "app",
"version": "4.8.20",
"version": "4.8.21",
"private": false,
"scripts": {
"dev": "next dev",

View File

@@ -18,7 +18,8 @@ import {
Thead,
Tr,
Table,
FlexProps
FlexProps,
Input
} from '@chakra-ui/react';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
@@ -31,13 +32,15 @@ import { getWebLLMModel } from '@/web/common/system/utils';
import MyIcon from '@fastgpt/web/components/common/Icon';
import dynamic from 'next/dynamic';
import InputSlider from '@fastgpt/web/components/common/MySlider/InputSlider';
import MySelect from '@fastgpt/web/components/common/MySelect';
import JsonEditor from '@fastgpt/web/components/common/Textarea/JsonEditor';
const ModelPriceModal = dynamic(() =>
import('@/components/core/ai/ModelTable').then((mod) => mod.ModelPriceModal)
);
const FlexItemStyles: FlexProps = {
mt: 5,
mt: 4,
alignItems: 'center',
h: '35px'
};
@@ -68,7 +71,7 @@ const AIChatSettingsModal = ({
const [refresh, setRefresh] = useState(false);
const { feConfigs } = useSystemStore();
const { handleSubmit, getValues, setValue, watch } = useForm({
const { handleSubmit, getValues, setValue, watch, register } = useForm({
defaultValues: defaultData
});
const model = watch('model');
@@ -88,6 +91,17 @@ const AIChatSettingsModal = ({
const llmSupportTemperature = typeof selectedModel?.maxTemperature === 'number';
const llmSupportReasoning = !!selectedModel?.reasoning;
const topP = watch(NodeInputKeyEnum.aiChatTopP);
const llmSupportTopP = !!selectedModel?.showTopP;
const stopSign = watch(NodeInputKeyEnum.aiChatStopSign);
const llmSupportStopSign = !!selectedModel?.showStopSign;
const responseFormat = watch(NodeInputKeyEnum.aiChatResponseFormat);
const jsonSchema = watch(NodeInputKeyEnum.aiChatJsonSchema);
const llmSupportResponseFormat =
!!selectedModel?.responseFormatList && selectedModel?.responseFormatList.length > 0;
const tokenLimit = useMemo(() => {
return selectedModel?.maxResponse || 4096;
}, [selectedModel?.maxResponse]);
@@ -146,7 +160,7 @@ const AIChatSettingsModal = ({
</Flex>
<TableContainer
my={5}
my={4}
bg={'primary.50'}
borderRadius={'lg'}
borderWidth={'1px'}
@@ -291,6 +305,110 @@ const AIChatSettingsModal = ({
</Box>
</Flex>
)}
{llmSupportTopP && (
<Flex {...FlexItemStyles}>
<Box {...LabelStyles}>
<Flex alignItems={'center'}>
<Box mr={0.5}>Top_p</Box>
<QuestionTip label={t('app:show_top_p_tip')} />
</Flex>
<Switch
isChecked={topP !== undefined}
size={'sm'}
onChange={(e) => {
setValue(NodeInputKeyEnum.aiChatTopP, e.target.checked ? 1 : undefined);
}}
/>
</Box>
<Box flex={'1 0 0'}>
<InputSlider
min={0}
max={1}
step={0.1}
value={topP}
isDisabled={topP === undefined}
onChange={(e) => {
setValue(NodeInputKeyEnum.aiChatTopP, e);
setRefresh(!refresh);
}}
/>
</Box>
</Flex>
)}
{llmSupportStopSign && (
<Flex {...FlexItemStyles}>
<Box {...LabelStyles}>
<Flex alignItems={'center'}>
<Box mr={0.5}>{t('app:stop_sign')}</Box>
</Flex>
<Switch
isChecked={stopSign !== undefined}
size={'sm'}
onChange={(e) => {
setValue(NodeInputKeyEnum.aiChatStopSign, e.target.checked ? '' : undefined);
}}
/>
</Box>
<Box flex={'1 0 0'}>
<Input
isDisabled={stopSign === undefined}
size={'sm'}
{...register(NodeInputKeyEnum.aiChatStopSign)}
placeholder={t('app:stop_sign_placeholder')}
bg={'myGray.25'}
/>
</Box>
</Flex>
)}
{llmSupportResponseFormat && selectedModel?.responseFormatList && (
<Flex {...FlexItemStyles}>
<Box {...LabelStyles}>
<Flex alignItems={'center'}>{t('app:response_format')}</Flex>
<Switch
isChecked={responseFormat !== undefined}
size={'sm'}
onChange={(e) => {
setValue(
NodeInputKeyEnum.aiChatResponseFormat,
e.target.checked ? selectedModel?.responseFormatList?.[0] : undefined
);
}}
/>
</Box>
<Box flex={'1 0 0'}>
<MySelect<string>
isDisabled={responseFormat === undefined}
size={'sm'}
bg={'myGray.25'}
list={selectedModel.responseFormatList.map((item) => ({
value: item,
label: item
}))}
value={responseFormat}
onchange={(e) => {
setValue(NodeInputKeyEnum.aiChatResponseFormat, e);
}}
/>
</Box>
</Flex>
)}
{/* Json schema */}
{responseFormat === 'json_schema' && (
<Flex {...FlexItemStyles} h="auto">
<Box {...LabelStyles}>
<Flex alignItems={'center'}>JSON Schema</Flex>
</Box>
<Box flex={'1 0 0'}>
<JsonEditor
value={jsonSchema || ''}
onChange={(e) => {
setValue(NodeInputKeyEnum.aiChatJsonSchema, e);
}}
bg={'myGray.25'}
/>
</Box>
</Flex>
)}
{llmSupportReasoning && (
<Flex {...FlexItemStyles} h={'25px'}>
<Box {...LabelStyles}>
@@ -306,25 +424,6 @@ const AIChatSettingsModal = ({
</Box>
</Flex>
)}
{showResponseAnswerText && (
<Flex {...FlexItemStyles} h={'25px'}>
<Box {...LabelStyles}>
<Flex alignItems={'center'}>
{t('app:stream_response')}
<QuestionTip ml={1} label={t('app:stream_response_tip')}></QuestionTip>
</Flex>
<Switch
isChecked={getValues(NodeInputKeyEnum.aiChatIsResponseText)}
size={'sm'}
onChange={(e) => {
const value = e.target.checked;
setValue(NodeInputKeyEnum.aiChatIsResponseText, value);
setRefresh((state) => !state);
}}
/>
</Box>
</Flex>
)}
{showVisionSwitch && (
<Flex {...FlexItemStyles} h={'25px'}>
<Box {...LabelStyles} w={llmSupportVision ? '9rem' : 'auto'}>
@@ -349,6 +448,25 @@ const AIChatSettingsModal = ({
</Box>
</Flex>
)}
{showResponseAnswerText && (
<Flex {...FlexItemStyles} h={'25px'}>
<Box {...LabelStyles}>
<Flex alignItems={'center'}>
{t('app:stream_response')}
<QuestionTip ml={1} label={t('app:stream_response_tip')}></QuestionTip>
</Flex>
<Switch
isChecked={getValues(NodeInputKeyEnum.aiChatIsResponseText)}
size={'sm'}
onChange={(e) => {
const value = e.target.checked;
setValue(NodeInputKeyEnum.aiChatIsResponseText, value);
setRefresh((state) => !state);
}}
/>
</Box>
</Flex>
)}
</ModalBody>
<ModalFooter>
<Button variant={'whiteBase'} onClick={onClose}>

View File

@@ -1,4 +1,4 @@
import type { UserTypee } from '@fastgpt/global/support/user/type.d';
import type { UserType } from '@fastgpt/global/support/user/type.d';
import type { PromotionRecordSchema } from '@fastgpt/global/support/activity/type.d';
export interface ResLogin {
user: UserType;

View File

@@ -767,6 +767,51 @@ const ModelEditModal = ({
</Flex>
</Td>
</Tr>
<Tr>
<Td>
<HStack spacing={1}>
<Box>{t('account:model.show_top_p')}</Box>
</HStack>
</Td>
<Td textAlign={'right'}>
<Flex justifyContent={'flex-end'}>
<Switch {...register('showTopP')} />
</Flex>
</Td>
</Tr>
<Tr>
<Td>
<HStack spacing={1}>
<Box>{t('account:model.show_stop_sign')}</Box>
</HStack>
</Td>
<Td textAlign={'right'}>
<Flex justifyContent={'flex-end'}>
<Switch {...register('showStopSign')} />
</Flex>
</Td>
</Tr>
<Tr>
<Td>{t('account:model.response_format')}</Td>
<Td textAlign={'right'}>
<JsonEditor
value={JSON.stringify(getValues('responseFormatList'), null, 2)}
resize
onChange={(e) => {
if (!e) {
setValue('responseFormatList', []);
return;
}
try {
setValue('responseFormatList', JSON.parse(e));
} catch (error) {
console.error(error);
}
}}
{...InputStyles}
/>
</Td>
</Tr>
</>
)}
{isEmbeddingModel && (

View File

@@ -1,4 +1,4 @@
import React, { useMemo, useTransition } from 'react';
import React, { useEffect, useMemo, useTransition } from 'react';
import {
Box,
Flex,
@@ -116,6 +116,25 @@ const EditForm = ({
const tokenLimit = useMemo(() => {
return selectedModel?.quoteMaxToken || 3000;
}, [selectedModel?.quoteMaxToken]);
// Force close image select when model not support vision
useEffect(() => {
if (!selectedModel.vision) {
setAppForm((state) => ({
...state,
chatConfig: {
...state.chatConfig,
...(state.chatConfig.fileSelectConfig
? {
fileSelectConfig: {
...state.chatConfig.fileSelectConfig,
canSelectImg: false
}
}
: {})
}
}));
}
}, [selectedModel]);
return (
<>
@@ -139,24 +158,18 @@ const EditForm = ({
temperature: appForm.aiSettings.temperature,
maxToken: appForm.aiSettings.maxToken,
maxHistories: appForm.aiSettings.maxHistories,
aiChatReasoning: appForm.aiSettings.aiChatReasoning ?? true
aiChatReasoning: appForm.aiSettings.aiChatReasoning ?? true,
aiChatTopP: appForm.aiSettings.aiChatTopP,
aiChatStopSign: appForm.aiSettings.aiChatStopSign,
aiChatResponseFormat: appForm.aiSettings.aiChatResponseFormat,
aiChatJsonSchema: appForm.aiSettings.aiChatJsonSchema
}}
onChange={({
model,
temperature,
maxToken,
maxHistories,
aiChatReasoning = false
}) => {
onChange={({ maxHistories = 6, aiChatReasoning = true, ...data }) => {
setAppForm((state) => ({
...state,
aiSettings: {
...state.aiSettings,
model,
temperature,
maxToken,
maxHistories: maxHistories ?? 6,
aiChatReasoning
...data
}
}));
}}

View File

@@ -40,7 +40,14 @@ const SelectAiModelRender = ({ item, inputs = [], nodeId }: RenderInputProps) =>
aiChatVision:
inputs.find((input) => input.key === NodeInputKeyEnum.aiChatVision)?.value ?? true,
aiChatReasoning:
inputs.find((input) => input.key === NodeInputKeyEnum.aiChatReasoning)?.value ?? true
inputs.find((input) => input.key === NodeInputKeyEnum.aiChatReasoning)?.value ?? true,
aiChatTopP: inputs.find((input) => input.key === NodeInputKeyEnum.aiChatTopP)?.value,
aiChatStopSign: inputs.find((input) => input.key === NodeInputKeyEnum.aiChatStopSign)?.value,
aiChatResponseFormat: inputs.find(
(input) => input.key === NodeInputKeyEnum.aiChatResponseFormat
)?.value,
aiChatJsonSchema: inputs.find((input) => input.key === NodeInputKeyEnum.aiChatJsonSchema)
?.value
}),
[inputs]
);

View File

@@ -28,12 +28,12 @@ function Error() {
return (
<Box whiteSpace={'pre-wrap'}>
{`出现未捕获的异常。
1. 私有部署用户90%由于配置文件不正确导致。
1. 私有部署用户90%由于配置文件不正确/模型未启用导致。
2. 部分系统不兼容相关API。大部分是苹果的safari 浏览器导致,可以尝试更换 chrome。
3. 请关闭浏览器翻译功能,部分翻译导致页面崩溃。
排除3后打开控制台的 console 查看具体报错信息。
如果提示 xxx undefined 的话,就是配置文件有错误。
如果提示 xxx undefined 的话,就是配置文件有错误,或者是缺少可用模型,请确保系统内每个系列模型至少有一个可用
`}
</Box>
);

View File

@@ -16,6 +16,7 @@ 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';
import { llmCompletionsBodyFormat } from '@fastgpt/service/core/ai/utils';
export type testQuery = { model: string };
@@ -57,22 +58,23 @@ export default NextAPI(handler);
const testLLMModel = async (model: LLMModelItemType) => {
const ai = getAIApi({});
const response = await ai.chat.completions.create(
const requestBody = llmCompletionsBodyFormat(
{
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}`
}
: undefined
}
model
);
const response = await ai.chat.completions.create(requestBody, {
...(model.requestUrl ? { path: model.requestUrl } : {}),
headers: model.requestAuth
? {
Authorization: `Bearer ${model.requestAuth}`
}
: undefined
});
const responseText = response.choices?.[0]?.message?.content;

View File

@@ -15,6 +15,7 @@ import { NextAPI } from '@/service/middleware/entry';
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
import { useIPFrequencyLimit } from '@fastgpt/service/common/middle/reqFrequencyLimit';
import { agentSearchDatasetData } from '@fastgpt/service/core/dataset/search/agent';
async function handler(req: NextApiRequest) {
const {
@@ -59,6 +60,7 @@ async function handler(req: NextApiRequest) {
});
const { searchRes, tokens, ...result } = await searchDatasetData({
histories: [],
teamId,
reRankQuery: rewriteQuery,
queries: concatQueries,

View File

@@ -29,6 +29,7 @@ import { GET } from '@/web/common/api/request';
import { getDocPath } from '@/web/common/system/doc';
import { getWebReqUrl } from '@fastgpt/web/common/system/utils';
import LoginForm from '@/pageComponents/login/LoginForm/LoginForm';
import { useToast } from '@fastgpt/web/hooks/useToast';
const RegisterForm = dynamic(() => import('@/pageComponents/login/RegisterForm'));
const ForgetPasswordForm = dynamic(() => import('@/pageComponents/login/ForgetPasswordForm'));
@@ -41,12 +42,13 @@ const Login = ({ ChineseRedirectUrl }: { ChineseRedirectUrl: string }) => {
const router = useRouter();
const { t } = useTranslation();
const { lastRoute = '' } = router.query as { lastRoute: string };
const { feConfigs } = useSystemStore();
const { feConfigs, llmModelList } = useSystemStore();
const [pageType, setPageType] = useState<`${LoginPageTypeEnum}`>(LoginPageTypeEnum.passwordLogin);
const { setUserInfo } = useUserStore();
const { setLastChatAppId } = useChatStore();
const { isOpen, onOpen, onClose } = useDisclosure();
const { isPc } = useSystem();
const { toast } = useToast();
const {
isOpen: isOpenCookiesDrawer,
@@ -61,6 +63,16 @@ const Login = ({ ChineseRedirectUrl }: { ChineseRedirectUrl: string }) => {
(res: ResLogin) => {
setUserInfo(res.user);
// Check that the model is available
if (res.user.username === 'root' && llmModelList?.length === 0) {
toast({
status: 'warning',
title: t('login:model_not_config')
});
router.push('/account/model');
return;
}
const decodeLastRoute = decodeURIComponent(lastRoute);
// 检查是否是当前的 route
const navigateTo =

View File

@@ -106,14 +106,14 @@ export function form2AppWorkflow(
version: AiChatModule.version,
inputs: [
{
key: 'model',
key: NodeInputKeyEnum.aiModel,
renderTypeList: [FlowNodeInputTypeEnum.settingLLMModel, FlowNodeInputTypeEnum.reference],
label: '',
valueType: WorkflowIOValueTypeEnum.string,
value: formData.aiSettings.model
},
{
key: 'temperature',
key: NodeInputKeyEnum.aiChatTemperature,
renderTypeList: [FlowNodeInputTypeEnum.hidden],
label: '',
value: formData.aiSettings.temperature,
@@ -123,7 +123,7 @@ export function form2AppWorkflow(
step: 1
},
{
key: 'maxToken',
key: NodeInputKeyEnum.aiChatMaxToken,
renderTypeList: [FlowNodeInputTypeEnum.hidden],
label: '',
value: formData.aiSettings.maxToken,
@@ -133,7 +133,7 @@ export function form2AppWorkflow(
step: 50
},
{
key: 'isResponseAnswerText',
key: NodeInputKeyEnum.aiChatIsResponseText,
renderTypeList: [FlowNodeInputTypeEnum.hidden],
label: '',
value: true,
@@ -143,7 +143,7 @@ export function form2AppWorkflow(
AiChatQuoteTemplate,
AiChatQuotePrompt,
{
key: 'systemPrompt',
key: NodeInputKeyEnum.aiSystemPrompt,
renderTypeList: [FlowNodeInputTypeEnum.textarea, FlowNodeInputTypeEnum.reference],
max: 3000,
valueType: WorkflowIOValueTypeEnum.string,
@@ -153,7 +153,7 @@ export function form2AppWorkflow(
value: formData.aiSettings.systemPrompt
},
{
key: 'history',
key: NodeInputKeyEnum.history,
renderTypeList: [FlowNodeInputTypeEnum.numberInput, FlowNodeInputTypeEnum.reference],
valueType: WorkflowIOValueTypeEnum.chatHistory,
label: 'core.module.input.label.chat history',
@@ -163,16 +163,16 @@ export function form2AppWorkflow(
value: formData.aiSettings.maxHistories
},
{
key: 'userChatInput',
key: NodeInputKeyEnum.userChatInput,
renderTypeList: [FlowNodeInputTypeEnum.reference, FlowNodeInputTypeEnum.textarea],
valueType: WorkflowIOValueTypeEnum.string,
label: i18nT('common:core.module.input.label.user question'),
required: true,
toolDescription: i18nT('common:core.module.input.label.user question'),
value: [workflowStartNodeId, 'userChatInput']
value: [workflowStartNodeId, NodeInputKeyEnum.userChatInput]
},
{
key: 'quoteQA',
key: NodeInputKeyEnum.aiChatDatasetQuote,
renderTypeList: [FlowNodeInputTypeEnum.settingDatasetQuotePrompt],
label: '',
debugLabel: i18nT('common:core.module.Dataset quote.label'),
@@ -197,6 +197,34 @@ export function form2AppWorkflow(
label: '',
valueType: WorkflowIOValueTypeEnum.boolean,
value: formData.aiSettings.aiChatReasoning
},
{
key: NodeInputKeyEnum.aiChatTopP,
renderTypeList: [FlowNodeInputTypeEnum.hidden],
label: '',
valueType: WorkflowIOValueTypeEnum.number,
value: formData.aiSettings.aiChatTopP
},
{
key: NodeInputKeyEnum.aiChatStopSign,
renderTypeList: [FlowNodeInputTypeEnum.hidden],
label: '',
valueType: WorkflowIOValueTypeEnum.string,
value: formData.aiSettings.aiChatStopSign
},
{
key: NodeInputKeyEnum.aiChatResponseFormat,
renderTypeList: [FlowNodeInputTypeEnum.hidden],
label: '',
valueType: WorkflowIOValueTypeEnum.string,
value: formData.aiSettings.aiChatResponseFormat
},
{
key: NodeInputKeyEnum.aiChatJsonSchema,
renderTypeList: [FlowNodeInputTypeEnum.hidden],
label: '',
valueType: WorkflowIOValueTypeEnum.string,
value: formData.aiSettings.aiChatJsonSchema
}
],
outputs: AiChatModule.outputs