feat(chat-input): gate prompt optimize by image output capability (#13992)

This commit is contained in:
YuTengjing 2026-04-20 15:04:12 +08:00 committed by GitHub
parent 18042b7d31
commit eb99190f9f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 61 additions and 8 deletions

View file

@ -14,6 +14,7 @@ interface AgentBuilderConversationProps {
agentId: string;
}
const actions: ActionKeys[] = ['model'];
const rightActions: ActionKeys[] = [];
/**
* Agent Builder Conversation Component
@ -32,7 +33,7 @@ const AgentBuilderConversation = memo<AgentBuilderConversationProps>(({ agentId
<Flexbox flex={1} style={{ overflow: 'hidden' }}>
<ChatList welcome={<AgentBuilderWelcome />} />
</Flexbox>
<ChatInput leftActions={actions} showRuntimeConfig={false} />
<ChatInput leftActions={actions} rightActions={rightActions} showRuntimeConfig={false} />
</Flexbox>
</DragUploadZone>
);

View file

@ -12,13 +12,19 @@ const PromptTransform = memo(() => {
const onPromptChange = useCallback(
(prompt: string) => {
if (!editor) return;
editor.setDocument('markdown', prompt);
// `keepHistory` prevents setDocument from wiping the undo/redo stacks.
editor.setDocument('markdown', prompt, { keepHistory: true });
},
[editor],
);
// Image mode expands vague inputs; text mode forbids expansion.
return (
<PromptTransformAction mode={'text'} prompt={markdownContent} onPromptChange={onPromptChange} />
<PromptTransformAction
mode={'image'}
prompt={markdownContent}
onPromptChange={onPromptChange}
/>
);
});

View file

@ -124,7 +124,7 @@ const ChatInput = memo<ChatInputProps>(
allowExpand,
leftActions = [],
leftContent,
rightActions = ['promptTransform'],
rightActions = [],
children,
extraActionItems,
mentionItems,

View file

@ -23,8 +23,12 @@ export const usePromptTransform = ({ mode, prompt, onPromptChange }: UsePromptTr
const isRewriteActionEnabled = rewriteConfig?.enabled ?? false;
const getConfigByAction = useCallback(
(action: PromptTransformAction) =>
action === 'rewrite' ? (rewriteConfig ?? {}) : (translateConfig ?? {}),
(action: PromptTransformAction) => {
// Strip config-only fields (enabled, customPrompt); strict upstreams reject unknown OpenAI params.
const config = action === 'rewrite' ? rewriteConfig : translateConfig;
if (!config) return {};
return { model: config.model, provider: config.provider };
},
[rewriteConfig, translateConfig],
);

View file

@ -0,0 +1,10 @@
import { useAiInfraStore } from '@/store/aiInfra';
import { aiModelSelectors } from '@/store/aiInfra/selectors';
export const useModelSupportImageOutput = (id?: string, provider?: string) => {
return useAiInfraStore((s) => {
if (!id || !provider) return false;
return aiModelSelectors.isModelSupportImageOutput(id, provider)(s);
});
};

View file

@ -4,13 +4,17 @@ import { memo, useMemo } from 'react';
import { type ActionKeys } from '@/features/ChatInput';
import { ChatInput } from '@/features/Conversation';
import { useModelSupportImageOutput } from '@/hooks/useModelSupportImageOutput';
import { useAgentStore } from '@/store/agent';
import { agentSelectors } from '@/store/agent/selectors';
import { useChatStore } from '@/store/chat';
import { useUserStore } from '@/store/user';
import { userGeneralSettingsSelectors } from '@/store/user/selectors';
import { useSendMenuItems } from './useSendMenuItems';
const rightActions: ActionKeys[] = ['promptTransform'];
const emptyRightActions: ActionKeys[] = [];
const promptTransformRightActions: ActionKeys[] = ['promptTransform'];
/**
* MainChatInput
@ -24,6 +28,11 @@ const MainChatInput = memo(() => {
const isDevMode = useUserStore((s) => userGeneralSettingsSelectors.config(s).isDevMode);
const sendMenuItems = useSendMenuItems();
const model = useAgentStore(agentSelectors.currentAgentModel);
const provider = useAgentStore(agentSelectors.currentAgentModelProvider);
const supportsImageOutput = useModelSupportImageOutput(model, provider);
const rightActions = supportsImageOutput ? promptTransformRightActions : emptyRightActions;
const leftActions: ActionKeys[] = useMemo(
() => [
'model',

View file

@ -11,6 +11,7 @@ interface AgentBuilderConversationProps {
agentId: string;
}
const actions: ActionKeys[] = ['model'];
const rightActions: ActionKeys[] = [];
/**
* Agent Builder Conversation Component
@ -23,7 +24,7 @@ const AgentBuilderConversation = memo<AgentBuilderConversationProps>(({ agentId
<Flexbox flex={1} style={{ overflow: 'hidden' }}>
<ChatList welcome={<AgentBuilderWelcome mode="group" />} />
</Flexbox>
<ChatInput leftActions={actions} showRuntimeConfig={false} />
<ChatInput leftActions={actions} rightActions={rightActions} showRuntimeConfig={false} />
</Flexbox>
);
});

View file

@ -45,6 +45,7 @@ describe('aiModelSelectors', () => {
functionCall: true,
vision: true,
reasoning: true,
imageOutput: true,
},
contextWindowTokens: 4000,
settings: {
@ -210,6 +211,20 @@ describe('aiModelSelectors', () => {
false,
);
});
it('should check image output support', () => {
expect(aiModelSelectors.isModelSupportImageOutput('model1', 'provider1')(mockState)).toBe(
true,
);
// Missing ability defaults to false via `|| false` coercion.
expect(aiModelSelectors.isModelSupportImageOutput('model4', 'provider2')(mockState)).toBe(
false,
);
// Unknown model returns false instead of throwing.
expect(aiModelSelectors.isModelSupportImageOutput('missing', 'provider1')(mockState)).toBe(
false,
);
});
});
describe('context window checks', () => {

View file

@ -68,6 +68,12 @@ const isModelSupportVideo = (id: string, provider: string) => (s: AIProviderStor
return model?.abilities?.video;
};
const isModelSupportImageOutput = (id: string, provider: string) => (s: AIProviderStoreState) => {
const model = getEnabledModelById(id, provider)(s);
return model?.abilities?.imageOutput || false;
};
const isModelSupportReasoning = (id: string, provider: string) => (s: AIProviderStoreState) => {
const model = getEnabledModelById(id, provider)(s);
@ -155,6 +161,7 @@ export const aiModelSelectors = {
isModelHasExtendParams,
isModelLoading,
isModelSupportFiles,
isModelSupportImageOutput,
isModelSupportReasoning,
isModelSupportToolUse,
isModelSupportVideo,