fix: route canvas control prompts before image billing

This commit is contained in:
134 2026-06-23 20:01:32 +08:00
parent 76229dac7b
commit 8913daa2b0

View File

@ -94,6 +94,7 @@ import {
getCanvasAgentOperationErrorMessage,
normalizeCanvasAgentOperationErrorCode,
} from "@/lib/canvas/agent-operation-errors";
import { shouldBypassCanvasImagePointsPrecheckForAgentControl } from "@/lib/canvas/agent-client-intent";
import { applyCanvasOperationPlan } from "@/lib/canvas/agent-mutations";
import { isProductionEnvironment } from "@/lib/constants";
import type {
@ -892,6 +893,22 @@ export async function POST(request: Request) {
const canvasAgentFeatures = getCanvasAgentFeatureFlags();
const canApplyCanvasAgentMutation =
canApplyCanvasAgentMutations(canvasAgentFeatures);
const currentMessageAttachmentCount = message.parts.filter(
(part) => part.type === "file"
).length;
const hasCanvasControlIntentFastPath =
composerMode !== "agent" &&
(contextPayload?.source === "canvas" ||
contextPayload?.source === "realtime-canvas" ||
Boolean(requestCanvasAgentContext)) &&
shouldBypassCanvasImagePointsPrecheckForAgentControl({
text: requestTextContent,
activeComposerMode: composerMode,
attachmentCount: currentMessageAttachmentCount,
hasSkillTag: Boolean(agentPresetId || hiddenPrompt),
hasWorkflowContext: Boolean(agentModePayload),
agentEnabled: true,
});
if (requestCanvasAgentContext) {
chatApiLogger.info("[CanvasAgentContext] Normalized request context:", {
source: requestCanvasAgentContext.source,
@ -3195,7 +3212,7 @@ export async function POST(request: Request) {
const resolveCanvasViewOperationPlan = (): CanvasViewOperationPlan | null => {
if (!canvasAgentFeatures.viewOperationEnabled) return null;
if (composerMode !== "agent") return null;
if (composerMode !== "agent" && !hasCanvasControlIntentFastPath) return null;
if (contextPayload?.source !== "canvas" || !contextPayload.projectId) return null;
if (!requestCanvasAgentContext) return null;
@ -3331,7 +3348,7 @@ export async function POST(request: Request) {
const resolveCanvasLinkOperationPlan = (): CanvasOperationPlan | null => {
if (!canApplyCanvasAgentMutation) return null;
if (composerMode !== "agent") return null;
if (composerMode !== "agent" && !hasCanvasControlIntentFastPath) return null;
if (contextPayload?.source !== "canvas" || !contextPayload.projectId) return null;
if (!requestCanvasAgentContext) return null;
@ -3401,7 +3418,7 @@ export async function POST(request: Request) {
const resolveCanvasTextOperationPlan = (): CanvasOperationPlan | null => {
if (!canApplyCanvasAgentMutation) return null;
if (composerMode !== "agent") return null;
if (composerMode !== "agent" && !hasCanvasControlIntentFastPath) return null;
if (contextPayload?.source !== "canvas" || !contextPayload.projectId) return null;
if (!requestCanvasAgentContext) return null;
@ -3446,7 +3463,7 @@ export async function POST(request: Request) {
const resolveCanvasExplicitOperationPlan =
(): CanvasAgentExplicitOperationPlan | null => {
if (!canApplyCanvasAgentMutation) return null;
if (composerMode !== "agent") return null;
if (composerMode !== "agent" && !hasCanvasControlIntentFastPath) return null;
if (contextPayload?.source !== "canvas" || !contextPayload.projectId) return null;
if (!requestCanvasAgentContext) return null;
@ -3464,7 +3481,7 @@ export async function POST(request: Request) {
const resolveCanvasModelPlannerOutcome =
async (): Promise<CanvasAgentModelPlannerOutcome | null> => {
if (composerMode !== "agent") return null;
if (composerMode !== "agent" && !hasCanvasControlIntentFastPath) return null;
if (contextPayload?.source !== "canvas" || !contextPayload.projectId) return null;
if (!requestCanvasAgentContext) return null;
if (
@ -4688,6 +4705,53 @@ export async function POST(request: Request) {
return createSseResponse(canvasModelPlannerStream);
}
if (hasCanvasControlIntentFastPath) {
const canvasControlFallbackReply =
"我识别到这是画布控制指令,但当前没有找到可执行的画布目标。请先选中画布对象,或换成“放大画布 / 缩小看全局 / 聚焦刚生成结果”这类明确视角指令。";
const canvasControlFallbackStream = createUIMessageStream({
execute: async ({ writer }) => {
const messageId = generateUUID();
writeAgentProgressChunk(
writer,
{
stage: "clarify",
currentStep: "finalize",
title: "需要明确画布目标",
message: canvasControlFallbackReply,
toolName: "canvasOperation",
capability: "clarify_canvas_control",
},
"canvas-control-fast-path:clarify",
);
writeAssistantTextChunk(
writer,
canvasControlFallbackReply,
"canvas-control-fast-path:reply",
messageId,
);
await saveMessages({
messages: [
{
id: messageId,
chatId: id,
role: "assistant",
parts: [{ type: "text", text: canvasControlFallbackReply }],
attachments: [],
createdAt: new Date(),
videoStorageStatus: null,
temporaryVideoUrl: null,
permanentVideoUrl: null,
archivedAt: null,
videoMetadata: null,
},
],
});
},
});
return createSseResponse(canvasControlFallbackStream);
}
const promptOnlySourceImageUrls =
requestResolvedMediaInput.currentMessageImageUrls.length > 0
? requestResolvedMediaInput.currentMessageImageUrls.slice(0, 4)
@ -8377,7 +8441,7 @@ export async function POST(request: Request) {
resolution: String(effectiveVideoParams?.resolution || "").trim() || undefined,
});
if (isImageGenerationModel) {
if (isImageGenerationModel && !hasCanvasControlIntentFastPath) {
const quota = await checkUserImageQuota(session.user.id);
if (!quota.allowed) {
@ -8441,7 +8505,7 @@ export async function POST(request: Request) {
: undefined
);
if (isVideoGenerationModel) {
if (isVideoGenerationModel && !hasCanvasControlIntentFastPath) {
const pointsConfig = await getPointsSystemConfig();
const normalizedVideoUnitCost = Number.isFinite(modelCreditCostPerVideo)
? Math.max(0, Math.round(modelCreditCostPerVideo))