diff --git a/app/(chat)/api/chat/route.ts b/app/(chat)/api/chat/route.ts index 34a6b2909..cddfa35d3 100644 --- a/app/(chat)/api/chat/route.ts +++ b/app/(chat)/api/chat/route.ts @@ -3042,6 +3042,7 @@ export async function POST(request: Request) { agentPresetId, selectedChatModel, selectedVisibilityType, + composerMode, hiddenPrompt, autoContext, contextPayload, @@ -3054,6 +3055,7 @@ export async function POST(request: Request) { agentPresetId?: string; selectedChatModel: ChatModel["id"]; selectedVisibilityType: VisibilityType; + composerMode?: "agent" | "image" | "video" | "detail"; hiddenPrompt?: string; autoContext?: string; contextPayload?: PostRequestBody["contextPayload"]; @@ -3137,8 +3139,16 @@ export async function POST(request: Request) { | null = null; let shouldAskGridConfirmation = false; const deepThinkingOn = deepThinkingEnabled !== false; + const shouldRespectDirectComposerMode = + composerMode === "image" || composerMode === "video" || composerMode === "detail"; let shouldRunAgentConversation = finalModelId === "auto"; let shouldKeepSelectedModel = false; + if (shouldRespectDirectComposerMode && shouldRunAgentConversation) { + shouldRunAgentConversation = false; + console.log( + `[Smart Auto Mode] ⛳ Direct composer mode (${composerMode}) disables initial auto-agent routing`, + ); + } const pendingProtocolDebugEvents: Array<{ phase: string; payload: Record }> = []; const requestedModelId = selectedChatModel; let requestedModelProviderType: string | undefined; @@ -3203,20 +3213,32 @@ export async function POST(request: Request) { if (deepThinkingOn) { if (isAiModelAgentAuto(selectedModelConfig)) { - shouldRunAgentConversation = true; - shouldKeepSelectedModel = false; - preferredAgentModelId = selectedModelConfig.id; - configuredAgentConversationModelId = selectedModelConfig.id; - console.log( - `[Smart Auto Mode] 🧠 Agent-only model detected, force auto routing: ${selectedModelConfig.displayName}` - ); + if (shouldRespectDirectComposerMode) { + console.log( + `[Smart Auto Mode] ⛳ Respect direct composer mode (${composerMode}), skip forcing agent-only routing for: ${selectedModelConfig.displayName}`, + ); + } else { + shouldRunAgentConversation = true; + shouldKeepSelectedModel = false; + preferredAgentModelId = selectedModelConfig.id; + configuredAgentConversationModelId = selectedModelConfig.id; + console.log( + `[Smart Auto Mode] 🧠 Agent-only model detected, force auto routing: ${selectedModelConfig.displayName}` + ); + } } else if (isFixedVisualModel || isConversationMultimodalModel) { - shouldRunAgentConversation = true; - shouldKeepSelectedModel = true; - preferredAgentModelId = selectedModelConfig.id; - console.log( - `[Smart Auto Mode] 🎯 Agent conversation enabled for selected model: ${selectedModelConfig.displayName} (${selectedModelProviderType || "unknown"}), preferredAgentModelId=${preferredAgentModelId}` - ); + if (shouldRespectDirectComposerMode) { + console.log( + `[Smart Auto Mode] ⛳ Respect direct composer mode (${composerMode}), keep direct execution model: ${selectedModelConfig.displayName}`, + ); + } else { + shouldRunAgentConversation = true; + shouldKeepSelectedModel = true; + preferredAgentModelId = selectedModelConfig.id; + console.log( + `[Smart Auto Mode] 🎯 Agent conversation enabled for selected model: ${selectedModelConfig.displayName} (${selectedModelProviderType || "unknown"}), preferredAgentModelId=${preferredAgentModelId}` + ); + } } } else if ( isFixedVisualModel || @@ -3397,11 +3419,17 @@ export async function POST(request: Request) { const effectiveWorkingState = prunedTaskBoundaryContext.workingState; if (taskBoundaryRequiresAgentConversation && !shouldRunAgentConversation) { - shouldRunAgentConversation = true; - shouldKeepSelectedModel = false; - console.log( - `[Smart Auto Mode] 🧭 Force agent conversation due to task boundary: ${taskBoundaryDecision.taskType}/${taskBoundaryDecision.taskMode}`, - ); + if (shouldRespectDirectComposerMode) { + console.log( + `[Smart Auto Mode] ⛳ Respect direct composer mode (${composerMode}), skip task-boundary agent force: ${taskBoundaryDecision.taskType}/${taskBoundaryDecision.taskMode}`, + ); + } else { + shouldRunAgentConversation = true; + shouldKeepSelectedModel = false; + console.log( + `[Smart Auto Mode] 🧭 Force agent conversation due to task boundary: ${taskBoundaryDecision.taskType}/${taskBoundaryDecision.taskMode}`, + ); + } } if (taskBoundaryOverridesLegacyWorkflow && shouldKeepSelectedModel) { @@ -3412,7 +3440,11 @@ export async function POST(request: Request) { } if (resolvedConversationAction.forceAgentConversation && !shouldRunAgentConversation) { - if (taskBoundaryOverridesLegacyWorkflow) { + if (shouldRespectDirectComposerMode) { + console.log( + `[Smart Auto Mode] ⛳ Respect direct composer mode (${composerMode}), skip legacy workflow force: ${resolvedConversationAction.kind}`, + ); + } else if (taskBoundaryOverridesLegacyWorkflow) { console.log( `[Smart Auto Mode] 🪫 Ignore legacy workflow follow-up due to task boundary override: ${resolvedConversationAction.kind} -> ${taskBoundaryDecision.taskType}`, ); diff --git a/app/(chat)/api/chat/schema.ts b/app/(chat)/api/chat/schema.ts index 0be3a5e6a..25b5dca2e 100644 --- a/app/(chat)/api/chat/schema.ts +++ b/app/(chat)/api/chat/schema.ts @@ -103,6 +103,7 @@ export const postRequestBodySchema = z.object({ // 支持预定义模型名称或数据库中的模型 UUID selectedChatModel: z.string().min(1).max(128), selectedVisibilityType: z.enum(["public", "private"]), + composerMode: z.enum(["agent", "image", "video", "detail"]).optional(), deepThinkingEnabled: z.boolean().optional(), debugProtocol: z.boolean().optional(), videoParams: videoParamsSchema, // 视频参数 diff --git a/components/canvas/CanvasChatPanel.tsx b/components/canvas/CanvasChatPanel.tsx index 200455f2f..a29cafdda 100644 --- a/components/canvas/CanvasChatPanel.tsx +++ b/components/canvas/CanvasChatPanel.tsx @@ -2950,6 +2950,15 @@ export function CanvasChatPanel({ const isAgentModel = !isDetailGenerationMode && (!selectedModelId || selectedModelId === "auto" || isConversationModel); + const computedComposerMode = useMemo<"agent" | "image" | "video" | "detail">(() => { + if (isDetailGenerationMode) return "detail"; + if (isVideoModel) return "video"; + if (isAgentModel) return "agent"; + return "image"; + }, [isAgentModel, isDetailGenerationMode, isVideoModel]); + const [composerModeOverride, setComposerModeOverride] = useState< + "agent" | "image" | "video" | "detail" | null + >(null); const agentSelectableModels = useMemo( () => [SMART_AUTO_MODEL, ...conversationOnlyModels], [SMART_AUTO_MODEL, conversationOnlyModels] @@ -2989,12 +2998,7 @@ export function CanvasChatPanel({ if (videoParams.mode === "first-frame") return ["首帧"] as string[]; return [] as string[]; }, [isVideoModel, videoParams]); - const activeComposerMode = useMemo<"agent" | "image" | "video" | "detail">(() => { - if (isDetailGenerationMode) return "detail"; - if (isVideoModel) return "video"; - if (isAgentModel) return "agent"; - return "image"; - }, [isAgentModel, isDetailGenerationMode, isVideoModel]); + const activeComposerMode = composerModeOverride || computedComposerMode; const agentUploadSlots = useMemo(() => { if ( activeComposerMode !== "agent" || @@ -3256,6 +3260,7 @@ export function CanvasChatPanel({ }, [modelSelectValue, selectedModelId]); useEffect(() => { + if (activeComposerMode !== "agent") return; if (!selectedAgentPresetId) return; if (!selectedAgentPreset?.defaultModelId) return; if (currentModelIdRef.current === selectedAgentPreset.defaultModelId) return; @@ -3266,6 +3271,7 @@ export function CanvasChatPanel({ setIsDetailGenerationMode(false); } }, [ + activeComposerMode, isDetailGenerationMode, selectedAgentPreset, selectedAgentPresetId, @@ -3448,6 +3454,10 @@ export function CanvasChatPanel({ const handleSelectGenerationMode = useCallback( (mode: "agent" | "image" | "video" | "detail") => { + hiddenPromptRef.current = null; + autoContextRef.current = null; + setComposerModeOverride(mode); + if (mode === "agent") { const preferredAgentModelId = allModels.find( @@ -3909,6 +3919,7 @@ export function CanvasChatPanel({ ? selectedAgentPreset.id : undefined, selectedChatModel: currentModelIdRef.current, + composerMode: activeComposerMode, selectedVisibilityType: "private", deepThinkingEnabled: requestIsConversationModel ? false @@ -7085,7 +7096,11 @@ export function CanvasChatPanel({ `【工作流设定】\n${workflowContext.promptText}\n\n请严格遵循以上工作流设定输出内容,并保持整体结果一致。` ); } - if (selectedAgentPreset?.type === "text" && selectedAgentPreset.slots?.length) { + if ( + activeComposerMode === "agent" && + selectedAgentPreset?.type === "text" && + selectedAgentPreset.slots?.length + ) { const presetPrompt = selectedAgentPreset.slots .map((slot, index) => { const title = String(slot.title || slot.uploadLabel || `模板${index + 1}`).trim();