fix: respect direct composer execution mode
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled

This commit is contained in:
Your Name 2026-03-18 23:21:12 +08:00
parent c22d9019bf
commit 8a34cd4570
3 changed files with 74 additions and 26 deletions

View File

@ -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<string, unknown> }> = [];
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}`,
);

View File

@ -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, // 视频参数

View File

@ -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();