Commit f05c0fff by YeYuheng Committed by GitHub

fix(agent): decouple file upload settings from model capabilities (#7459)

parent fc70c504
...@@ -18,7 +18,6 @@ import type { AppFileSelectConfigType } from '@fastgpt/global/core/app/type/conf ...@@ -18,7 +18,6 @@ import type { AppFileSelectConfigType } from '@fastgpt/global/core/app/type/conf
import MyModal from '@fastgpt/web/components/common/MyModal'; import MyModal from '@fastgpt/web/components/common/MyModal';
import ChatFunctionTip from './Tip'; import ChatFunctionTip from './Tip';
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel'; import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
import { useMount } from 'ahooks';
import { useSystemStore } from '@/web/common/system/useSystemStore'; import { useSystemStore } from '@/web/common/system/useSystemStore';
import { useUserStore } from '@/web/support/user/useUserStore'; import { useUserStore } from '@/web/support/user/useUserStore';
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip'; import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
...@@ -29,12 +28,10 @@ import { FileTypeSelectorPanel } from '@fastgpt/web/components/core/app/FileType ...@@ -29,12 +28,10 @@ import { FileTypeSelectorPanel } from '@fastgpt/web/components/core/app/FileType
import { getUserFileAmountLimit } from '@fastgpt/global/core/workflow/fileLimit'; import { getUserFileAmountLimit } from '@fastgpt/global/core/workflow/fileLimit';
const FileSelect = ({ const FileSelect = ({
forbidVision = false,
value = defaultAppSelectFileConfig, value = defaultAppSelectFileConfig,
onChange, onChange,
...labelStyle ...labelStyle
}: Omit<BoxProps, 'onChange'> & { }: Omit<BoxProps, 'onChange'> & {
forbidVision?: boolean;
value?: AppFileSelectConfigType; value?: AppFileSelectConfigType;
onChange: (e: AppFileSelectConfigType) => void; onChange: (e: AppFileSelectConfigType) => void;
}) => { }) => {
...@@ -61,16 +58,6 @@ const FileSelect = ({ ...@@ -61,16 +58,6 @@ const FileSelect = ({
? t('common:core.app.whisper.Open') ? t('common:core.app.whisper.Open')
: t('common:core.app.whisper.Close'); : t('common:core.app.whisper.Close');
// Close select img switch when vision is forbidden
useMount(() => {
if (forbidVision) {
onChange({
...value,
canSelectImg: false
});
}
});
return ( return (
<Flex alignItems={'center'}> <Flex alignItems={'center'}>
<MyIcon name={'core/app/simpleMode/file'} mr={2} w={'20px'} /> <MyIcon name={'core/app/simpleMode/file'} mr={2} w={'20px'} />
......
import React, { useEffect, useMemo } from 'react'; import React, { useMemo } from 'react';
import { import {
Box, Box,
Flex, Flex,
...@@ -165,26 +165,6 @@ const EditForm = ({ ...@@ -165,26 +165,6 @@ const EditForm = ({
return selectedModel.quoteMaxToken || 3000; return selectedModel.quoteMaxToken || 3000;
}, [selectedModel.quoteMaxToken]); }, [selectedModel.quoteMaxToken]);
// 简易 Agent 不暴露多模态开关,文件选择能力直接跟随模型能力。
useEffect(() => {
setAppForm((state) => ({
...state,
chatConfig: {
...state.chatConfig,
...(state.chatConfig.fileSelectConfig
? {
fileSelectConfig: {
...state.chatConfig.fileSelectConfig,
canSelectImg: !!selectedModel.vision,
canSelectAudio: !!selectedModel.audio,
canSelectVideo: !!selectedModel.video
}
}
: {})
}
}));
}, [selectedModel, setAppForm]);
return ( return (
<> <>
<Box mt={4} {...cardStyles} boxShadow={'3.5'}> <Box mt={4} {...cardStyles} boxShadow={'3.5'}>
...@@ -521,7 +501,6 @@ const EditForm = ({ ...@@ -521,7 +501,6 @@ const EditForm = ({
{/* File select */} {/* File select */}
<Box {...BoxStyles}> <Box {...BoxStyles}>
<FileSelectConfig <FileSelectConfig
forbidVision={!selectedModel?.vision}
value={appForm.chatConfig.fileSelectConfig} value={appForm.chatConfig.fileSelectConfig}
onChange={(e) => { onChange={(e) => {
setAppForm((state) => ({ setAppForm((state) => ({
......
...@@ -159,19 +159,6 @@ export function agentForm2AppWorkflow( ...@@ -159,19 +159,6 @@ export function agentForm2AppWorkflow(
video: !!modelData?.video, video: !!modelData?.video,
extractFiles: !!(modelData?.vision || modelData?.audio || modelData?.video) extractFiles: !!(modelData?.vision || modelData?.audio || modelData?.video)
}; };
const chatConfig: AppChatConfigType = {
...data.chatConfig,
...(data.chatConfig.fileSelectConfig
? {
fileSelectConfig: {
...data.chatConfig.fileSelectConfig,
canSelectImg: modelMultimodal.vision,
canSelectAudio: modelMultimodal.audio,
canSelectVideo: modelMultimodal.video
}
}
: {})
};
function systemConfigTemplate(): StoreNodeItemType { function systemConfigTemplate(): StoreNodeItemType {
return { return {
...@@ -412,7 +399,7 @@ export function agentForm2AppWorkflow( ...@@ -412,7 +399,7 @@ export function agentForm2AppWorkflow(
return { return {
nodes: [systemConfigTemplate(), workflowStartTemplate(), ...workflow.nodes], nodes: [systemConfigTemplate(), workflowStartTemplate(), ...workflow.nodes],
edges: workflow.edges, edges: workflow.edges,
chatConfig chatConfig: data.chatConfig
}; };
} }
......
...@@ -112,26 +112,6 @@ const EditForm = ({ ...@@ -112,26 +112,6 @@ const EditForm = ({
return selectedModel.quoteMaxToken || 3000; return selectedModel.quoteMaxToken || 3000;
}, [selectedModel.quoteMaxToken]); }, [selectedModel.quoteMaxToken]);
// 简易应用不暴露多模态开关,文件选择能力直接跟随模型能力。
useEffect(() => {
setAppForm((state) => ({
...state,
chatConfig: {
...state.chatConfig,
...(state.chatConfig.fileSelectConfig
? {
fileSelectConfig: {
...state.chatConfig.fileSelectConfig,
canSelectImg: !!selectedModel.vision,
canSelectAudio: !!selectedModel.audio,
canSelectVideo: !!selectedModel.video
}
}
: {})
}
}));
}, [selectedModel, setAppForm]);
useEffect(() => { useEffect(() => {
if ( if (
appForm.dataset.datasetSearchUsingExtensionQuery && appForm.dataset.datasetSearchUsingExtensionQuery &&
...@@ -402,7 +382,6 @@ const EditForm = ({ ...@@ -402,7 +382,6 @@ const EditForm = ({
{/* File select */} {/* File select */}
<Box {...BoxStyles}> <Box {...BoxStyles}>
<FileSelectConfig <FileSelectConfig
forbidVision={!selectedModel?.vision}
value={appForm.chatConfig.fileSelectConfig} value={appForm.chatConfig.fileSelectConfig}
onChange={(e) => { onChange={(e) => {
setAppForm((state) => ({ setAppForm((state) => ({
......
...@@ -226,19 +226,6 @@ export function form2AppWorkflow( ...@@ -226,19 +226,6 @@ export function form2AppWorkflow(
video: !!modelData?.video, video: !!modelData?.video,
extractFiles: !!(modelData?.vision || modelData?.audio || modelData?.video) extractFiles: !!(modelData?.vision || modelData?.audio || modelData?.video)
}; };
const chatConfig: AppChatConfigType = {
...data.chatConfig,
...(data.chatConfig.fileSelectConfig
? {
fileSelectConfig: {
...data.chatConfig.fileSelectConfig,
canSelectImg: modelMultimodal.vision,
canSelectAudio: modelMultimodal.audio,
canSelectVideo: modelMultimodal.video
}
}
: {})
};
function systemConfigTemplate(): StoreNodeItemType { function systemConfigTemplate(): StoreNodeItemType {
return { return {
...@@ -860,6 +847,6 @@ export function form2AppWorkflow( ...@@ -860,6 +847,6 @@ export function form2AppWorkflow(
return { return {
nodes: [systemConfigTemplate(), workflowStartTemplate(), ...workflow.nodes], nodes: [systemConfigTemplate(), workflowStartTemplate(), ...workflow.nodes],
edges: workflow.edges, edges: workflow.edges,
chatConfig chatConfig: data.chatConfig
}; };
} }
...@@ -57,6 +57,45 @@ describe('form2AppWorkflow', () => { ...@@ -57,6 +57,45 @@ describe('form2AppWorkflow', () => {
expect(result.edges).toHaveLength(1); expect(result.edges).toHaveLength(1);
}); });
it('should preserve file upload settings independently from model capabilities', () => {
const fileSelectConfigs = [
{
canSelectFile: true,
canSelectImg: true,
canSelectAudio: true,
canSelectVideo: true
},
{
canSelectFile: false,
canSelectImg: false,
canSelectAudio: false,
canSelectVideo: false
}
];
const workflows = fileSelectConfigs.map((fileSelectConfig) => {
const form = getDefaultAppForm();
form.chatConfig.fileSelectConfig = fileSelectConfig;
return form2AppWorkflow(form, mockT);
});
expect(workflows.map((workflow) => workflow.chatConfig.fileSelectConfig)).toEqual(
fileSelectConfigs
);
const getMultimodalInputs = (workflow: (typeof workflows)[number]) => {
const aiNode = workflow.nodes.find((node) => node.flowNodeType === FlowNodeTypeEnum.chatNode);
return [
NodeInputKeyEnum.aiChatVision,
NodeInputKeyEnum.aiChatAudio,
NodeInputKeyEnum.aiChatVideo
].map((key) => aiNode?.inputs.find((input) => input.key === key)?.value);
};
expect(getMultimodalInputs(workflows[0])).toEqual(getMultimodalInputs(workflows[1]));
});
it('roundtrips simple app sandbox entrypoint through the tool call node', () => { it('roundtrips simple app sandbox entrypoint through the tool call node', () => {
const form: AppFormEditFormType = { const form: AppFormEditFormType = {
aiSettings: { aiSettings: {
...@@ -395,6 +434,45 @@ describe('getAppQGuideCustomURL', () => { ...@@ -395,6 +434,45 @@ describe('getAppQGuideCustomURL', () => {
describe('appWorkflow2AgentForm', () => { describe('appWorkflow2AgentForm', () => {
const mockT = (str: string) => str; const mockT = (str: string) => str;
it('should preserve agent file upload settings independently from model capabilities', () => {
const fileSelectConfigs = [
{
canSelectFile: true,
canSelectImg: true,
canSelectAudio: true,
canSelectVideo: true
},
{
canSelectFile: false,
canSelectImg: false,
canSelectAudio: false,
canSelectVideo: false
}
];
const workflows = fileSelectConfigs.map((fileSelectConfig) => {
const form = getDefaultAppForm();
form.chatConfig.fileSelectConfig = fileSelectConfig;
return agentForm2AppWorkflow(form, mockT);
});
expect(workflows.map((workflow) => workflow.chatConfig.fileSelectConfig)).toEqual(
fileSelectConfigs
);
const getMultimodalInputs = (workflow: (typeof workflows)[number]) => {
const agentNode = workflow.nodes.find((node) => node.flowNodeType === FlowNodeTypeEnum.agent);
return [
NodeInputKeyEnum.aiChatVision,
NodeInputKeyEnum.aiChatAudio,
NodeInputKeyEnum.aiChatVideo
].map((key) => agentNode?.inputs.find((input) => input.key === key)?.value);
};
expect(getMultimodalInputs(workflows[0])).toEqual(getMultimodalInputs(workflows[1]));
});
it('should normalize dataset rerank fields from partial datasetParams', () => { it('should normalize dataset rerank fields from partial datasetParams', () => {
const result = appWorkflow2AgentForm({ const result = appWorkflow2AgentForm({
nodes: [ nodes: [
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment