From eb2153818df371b33eca65948b67742a407d8c3f Mon Sep 17 00:00:00 2001 From: 134 Date: Mon, 22 Jun 2026 01:22:25 +0800 Subject: [PATCH] fix: prefer detail model for scale adjustments --- app/api/canvas/local-edit/route.ts | 101 +++++++++++++++++- .../agents/AgentGenerationWorkspaceDarkV2.tsx | 5 +- 2 files changed, 100 insertions(+), 6 deletions(-) diff --git a/app/api/canvas/local-edit/route.ts b/app/api/canvas/local-edit/route.ts index 5135ce328..3ef7d7b09 100644 --- a/app/api/canvas/local-edit/route.ts +++ b/app/api/canvas/local-edit/route.ts @@ -9,7 +9,11 @@ import { getCanvasImageTaskForUser, } from "@/lib/canvas/image-task-queue"; import type { CanvasGenerateImageRequest } from "@/lib/canvas/generate-image"; -import { getPointsSystemConfig, getSiteSettings } from "@/lib/db/queries"; +import { + getEnabledAiModelsWithProvider, + getPointsSystemConfig, + getSiteSettings, +} from "@/lib/db/queries"; import { getImageToolCreditCost } from "@/lib/points/config"; import { prepareFeaturePointsCharge } from "@/lib/points/feature-charge"; import { uploadFile } from "@/lib/storage/service"; @@ -89,6 +93,9 @@ type ScaleAdjustCropReference = { regionIndex: number; url: string; }; +type AiModelWithProviderForSelection = Awaited< + ReturnType +>[number]; const DEFAULT_REGION_COLORS = [ "#ff3b30", "#1677ff", @@ -179,6 +186,61 @@ function normalizeHexColor(input: unknown, fallback: string) { return /^#[0-9a-f]{6}$/i.test(expanded) ? expanded.toLowerCase() : fallback; } +function findModelSelectionEntry( + models: AiModelWithProviderForSelection[], + modelRef: string +) { + const normalizedRef = String(modelRef || "").trim(); + if (!normalizedRef) return null; + return ( + models.find( + (entry) => + entry.model.id === normalizedRef || entry.model.modelId === normalizedRef + ) || null + ); +} + +function isScaleAdjustDedicatedDetailModel( + entry: AiModelWithProviderForSelection | null | undefined +) { + if (!entry?.model?.supportsImageGen || entry.model.supportsVision === false) { + return false; + } + const label = `${entry.model.displayName || ""} ${entry.model.modelId || ""}` + .toLowerCase() + .replace(/\s+/g, ""); + return label.includes("详情专线"); +} + +function resolveScaleAdjustPreferredModelId(params: { + configuredModelId: string; + requestedModelId: string; + models: AiModelWithProviderForSelection[]; +}) { + const requestedModel = findModelSelectionEntry( + params.models, + params.requestedModelId + ); + if (isScaleAdjustDedicatedDetailModel(requestedModel)) { + return requestedModel?.model.id || params.requestedModelId; + } + + const configuredModel = findModelSelectionEntry( + params.models, + params.configuredModelId + ); + if (isScaleAdjustDedicatedDetailModel(configuredModel)) { + return configuredModel?.model.id || params.configuredModelId; + } + + const autoDetailModel = params.models.find(isScaleAdjustDedicatedDetailModel); + if (autoDetailModel) { + return autoDetailModel.model.id; + } + + return params.configuredModelId || params.requestedModelId; +} + function normalizeHttpImageUrl(input: unknown) { const raw = String(input || "").trim(); if (!/^https?:\/\//i.test(raw)) return ""; @@ -805,9 +867,21 @@ export async function POST(request: NextRequest) { ); } - const [pointsConfig, siteSettingsForModel] = await Promise.all([ + const shouldUseScaleAdjustDetailModel = + operation === SCALE_ADJUST_OPERATION && scalePercent !== null; + const [pointsConfig, siteSettingsForModel, scaleAdjustModelCandidates] = + await Promise.all([ getPointsSystemConfig(), getSiteSettings().catch(() => null), + shouldUseScaleAdjustDetailModel + ? getEnabledAiModelsWithProvider(false, true).catch((error) => { + console.warn( + "[Canvas Local Edit] Failed to load scale adjust model candidates:", + error + ); + return [] as AiModelWithProviderForSelection[]; + }) + : Promise.resolve([] as AiModelWithProviderForSelection[]), ]); const pointsCharge = await prepareFeaturePointsCharge({ userId: session.user.id, @@ -887,10 +961,28 @@ export async function POST(request: NextRequest) { typeof body.model === "string" && body.model.trim() ? body.model.trim() : undefined; - const detailFixedModel = - isScaleAdjustOperation && typeof siteSettingsForModel?.canvasDetailDefaultModelId === "string" + const configuredDetailModel = + typeof siteSettingsForModel?.canvasDetailDefaultModelId === "string" ? siteSettingsForModel.canvasDetailDefaultModelId.trim() : ""; + const detailFixedModel = isScaleAdjustOperation + ? resolveScaleAdjustPreferredModelId({ + configuredModelId: configuredDetailModel, + requestedModelId: requestedModel || "", + models: scaleAdjustModelCandidates, + }) + : ""; + if ( + isScaleAdjustOperation && + detailFixedModel && + detailFixedModel !== configuredDetailModel + ) { + console.info("[Canvas Local Edit] Override scale adjust model to detail line:", { + configuredDetailModel: configuredDetailModel || null, + requestedModel: requestedModel || null, + resolvedModel: detailFixedModel, + }); + } const resolvedModel = detailFixedModel || requestedModel; const scaleAdjustCompositeRegions = isScaleAdjustOperation ? buildScaleAdjustCompositeRegions( @@ -908,6 +1000,7 @@ export async function POST(request: NextRequest) { annotatedReferenceUrl: annotatedUpload.url, annotatedReferenceSentToModel: isScaleAdjustOperation, cleanSourceReferenceIndex: 1, + configuredDetailModelId: configuredDetailModel || null, detailFixedModelApplied: Boolean(detailFixedModel), detailFixedModelId: detailFixedModel || null, displayPrompt, diff --git a/components/agents/AgentGenerationWorkspaceDarkV2.tsx b/components/agents/AgentGenerationWorkspaceDarkV2.tsx index 30381c7ec..0ead2d8b5 100644 --- a/components/agents/AgentGenerationWorkspaceDarkV2.tsx +++ b/components/agents/AgentGenerationWorkspaceDarkV2.tsx @@ -6142,8 +6142,9 @@ export function AgentGenerationWorkspaceDarkV2({ return; } if (detailLocalEditSubmittingId) return; - const resolvedLocalEditModelId = - selectedModelId !== "auto" + const resolvedLocalEditModelId = isScaleAdjust + ? undefined + : selectedModelId !== "auto" ? selectedModelId : effectiveDefaultModelId || undefined;