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
import MyModal from '@fastgpt/web/components/common/MyModal';
import ChatFunctionTip from './Tip';
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
import { useMount } from 'ahooks';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { useUserStore } from '@/web/support/user/useUserStore';
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
......@@ -29,12 +28,10 @@ import { FileTypeSelectorPanel } from '@fastgpt/web/components/core/app/FileType
import { getUserFileAmountLimit } from '@fastgpt/global/core/workflow/fileLimit';
const FileSelect = ({
forbidVision = false,
value = defaultAppSelectFileConfig,
onChange,
...labelStyle
}: Omit<BoxProps, 'onChange'> & {
forbidVision?: boolean;
value?: AppFileSelectConfigType;
onChange: (e: AppFileSelectConfigType) => void;
}) => {
......@@ -61,16 +58,6 @@ const FileSelect = ({
? t('common:core.app.whisper.Open')
: t('common:core.app.whisper.Close');
// Close select img switch when vision is forbidden
useMount(() => {
if (forbidVision) {
onChange({
...value,
canSelectImg: false
});
}
});
return (
<Flex alignItems={'center'}>
<MyIcon name={'core/app/simpleMode/file'} mr={2} w={'20px'} />
......
import React, { useEffect, useMemo } from 'react';
import React, { useMemo } from 'react';
import {
Box,
Flex,
......@@ -165,26 +165,6 @@ const EditForm = ({
return selectedModel.quoteMaxToken || 3000;
}, [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 (
<>
<Box mt={4} {...cardStyles} boxShadow={'3.5'}>
......@@ -521,7 +501,6 @@ const EditForm = ({
{/* File select */}
<Box {...BoxStyles}>
<FileSelectConfig
forbidVision={!selectedModel?.vision}
value={appForm.chatConfig.fileSelectConfig}
onChange={(e) => {
setAppForm((state) => ({
......
......@@ -159,19 +159,6 @@ export function agentForm2AppWorkflow(
video: !!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 {
return {
......@@ -412,7 +399,7 @@ export function agentForm2AppWorkflow(
return {
nodes: [systemConfigTemplate(), workflowStartTemplate(), ...workflow.nodes],
edges: workflow.edges,
chatConfig
chatConfig: data.chatConfig
};
}
......
......@@ -112,26 +112,6 @@ const EditForm = ({
return selectedModel.quoteMaxToken || 3000;
}, [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(() => {
if (
appForm.dataset.datasetSearchUsingExtensionQuery &&
......@@ -402,7 +382,6 @@ const EditForm = ({
{/* File select */}
<Box {...BoxStyles}>
<FileSelectConfig
forbidVision={!selectedModel?.vision}
value={appForm.chatConfig.fileSelectConfig}
onChange={(e) => {
setAppForm((state) => ({
......
......@@ -226,19 +226,6 @@ export function form2AppWorkflow(
video: !!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 {
return {
......@@ -860,6 +847,6 @@ export function form2AppWorkflow(
return {
nodes: [systemConfigTemplate(), workflowStartTemplate(), ...workflow.nodes],
edges: workflow.edges,
chatConfig
chatConfig: data.chatConfig
};
}
......@@ -57,6 +57,45 @@ describe('form2AppWorkflow', () => {
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', () => {
const form: AppFormEditFormType = {
aiSettings: {
......@@ -395,6 +434,45 @@ describe('getAppQGuideCustomURL', () => {
describe('appWorkflow2AgentForm', () => {
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', () => {
const result = appWorkflow2AgentForm({
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