From b6296d40225b48640918f6db8bbf2916b893457c Mon Sep 17 00:00:00 2001 From: 134 Date: Fri, 19 Jun 2026 22:38:31 +0800 Subject: [PATCH] fix: use detail model for scale adjust --- app/api/canvas/local-edit/route.ts | 41 ++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/app/api/canvas/local-edit/route.ts b/app/api/canvas/local-edit/route.ts index 7bbef034c..faed4c1c0 100644 --- a/app/api/canvas/local-edit/route.ts +++ b/app/api/canvas/local-edit/route.ts @@ -9,7 +9,7 @@ import { getCanvasImageTaskForUser, } from "@/lib/canvas/image-task-queue"; import type { CanvasGenerateImageRequest } from "@/lib/canvas/generate-image"; -import { getPointsSystemConfig } from "@/lib/db/queries"; +import { 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"; @@ -607,11 +607,12 @@ function buildScaleAdjustPrompt( .join("\n"); return [ - "输入图片1是定位参考图:它只用于告诉你用户框选的目标主体位置。输入图片1里的彩色框、编号、标签、半透明遮罩、文字和 UI 只是定位标记,绝不能出现在最终画面中。", - "输入图片1中的红色实线框表示目标主体当前大致位置;青绿色或绿色虚线框只表示比例调整后的安全尺寸范围。最终画面不得出现这些框线,主体也不需要填满虚线矩形;必须保持主体自身原始宽高比和透视形体。", - "输入图片2是原始干净图,也是最终输出必须继承的唯一画面基底。如果系统或模型只能读取一张参考图,则必须使用输入图片1中的框选位置定位目标,同时主动去除所有定位标记并恢复干净画面。", + "输入图片1是原始干净图,也是最终输出必须继承的唯一画面基底。除目标主体尺寸变化与必要背景补全外,输入图片1中的文字、Logo、装饰、排版、背景、光线、构图边界和所有未选区域必须保持不变。", + "输入图片2是定位参考图:它只用于告诉你用户框选的目标主体位置和目标尺寸范围,不是输出底图。输入图片2里的彩色框、编号、标签、半透明遮罩、文字和 UI 只是定位标记,绝不能出现在最终画面中,也不得影响输入图片1的文字和排版。", + "输入图片2中的红色实线框表示目标主体当前大致位置;青绿色或绿色虚线框只表示比例调整后的安全尺寸范围。最终画面不得出现这些框线,主体也不需要填满虚线矩形;必须保持主体自身原始宽高比和透视形体。", + "如果系统或模型只能读取一张参考图,必须优先以输入图片1为输出基底,不得清理、改写或删除输入图片1中框选区域外的任何文字和图形。", "系统已根据用户框选区域计算出下方每个区域的原图坐标;框选区域只是定位目标主体的搜索范围,不是最终画面的裁切块、贴片或蒙版边界。", - "最终结果必须保持原始干净图的画幅比例、构图边界、镜头透视、光线、色温、材质质感和清晰度。", + "最终结果必须保持原始干净图的画幅比例、构图边界、镜头透视、光线、色温、材质质感和清晰度;严禁整张图整体缩放、裁切、平移、重新排版或重新生成。", "请不要生成任何彩色框、矩形边框、编号圆点、提示词气泡、标注文字、UI 控件或半透明色块。", "", `任务:对用户框选的主体物体做“比例调整”,统一执行${actionLabel}。目标主体调整后的视觉尺寸必须约为原主体的 ${targetLabel},普通用户肉眼应能清楚看出主体尺寸已经变化;如果输出看起来和原图一样,就是失败。`, @@ -624,7 +625,7 @@ function buildScaleAdjustPrompt( `4. 放大时,目标主体应围绕原位置视觉中心自然变大到约 ${targetLabel};宽度和高度都要同步变大,不能出现“高度变高但宽度变窄”的非等比变形;主体边缘与遮挡关系要合理,不能产生突兀裁切。`, "5. 如果主体靠近画布边界导致目标尺寸空间不足,请用能完整放入画面的最大统一倍率整体缩放;绝不能为了塞进画面而只压缩宽度或只拉伸高度。", "6. 框选区域内非目标主体的背景、墙面、桌面、植物、文字和其它物体应保持原场景逻辑,不要跟着主体一起缩放。", - "7. 未受比例调整影响的其它画面区域必须尽量保持原图不变,不允许整体重绘、改色、补光、移动物体或改变背景细节。", + "7. 未受比例调整影响的其它画面区域必须保持原图不变,尤其是框选区域外的文字、Logo、标题、尺寸标注、装饰条和页面排版不得被清理、改写、模糊或重绘。", "8. 最终输出必须是一张完整、干净、无缝的照片,不得出现任何选区框、说明文字、对比线、比例数字或 UI 元素。", "9. 输出前自检:如果目标主体大小与原图相比没有明显变化,或者主体宽高比发生非等比变形,必须重新生成;禁止返回看起来和原图相同或被拉伸的结果。", ].join("\n"); @@ -697,7 +698,10 @@ export async function POST(request: NextRequest) { ); } - const pointsConfig = await getPointsSystemConfig(); + const [pointsConfig, siteSettingsForModel] = await Promise.all([ + getPointsSystemConfig(), + getSiteSettings().catch(() => null), + ]); const pointsCharge = await prepareFeaturePointsCharge({ userId: session.user.id, unitCost: getImageToolCreditCost(pointsConfig, "repaint"), @@ -745,13 +749,22 @@ export async function POST(request: NextRequest) { region.referenceImages.map((reference) => reference.url) ); const referenceImages = isScaleAdjustOperation - ? [annotatedUpload.url, sourceImageUrl, ...regionReferenceImageUrls] + ? [sourceImageUrl, annotatedUpload.url, ...regionReferenceImageUrls] : [sourceImageUrl, ...regionReferenceImageUrls]; const resolvedAspectRatio = resolveLocalEditAspectRatio( body.aspectRatio, sourceWidth, sourceHeight ); + const requestedModel = + typeof body.model === "string" && body.model.trim() + ? body.model.trim() + : undefined; + const detailFixedModel = + isScaleAdjustOperation && typeof siteSettingsForModel?.canvasDetailDefaultModelId === "string" + ? siteSettingsForModel.canvasDetailDefaultModelId.trim() + : ""; + const resolvedModel = detailFixedModel || requestedModel; const imageRequest: CanvasGenerateImageRequest = { aspectRatio: resolvedAspectRatio, @@ -761,9 +774,14 @@ export async function POST(request: NextRequest) { debugMeta: { annotatedReferenceUrl: annotatedUpload.url, annotatedReferenceSentToModel: isScaleAdjustOperation, + cleanSourceReferenceIndex: 1, + detailFixedModelApplied: Boolean(detailFixedModel), + detailFixedModelId: detailFixedModel || null, displayPrompt, operation, + requestedModel: requestedModel || null, resolvedAspectRatio, + resolvedModel: resolvedModel || null, regionReferenceImageCount: regionReferenceImageUrls.length, regionCount: regions.length, scalePercent, @@ -778,13 +796,10 @@ export async function POST(request: NextRequest) { typeof body.imageSize === "string" && body.imageSize.trim() ? body.imageSize.trim() : "2K", - model: - typeof body.model === "string" && body.model.trim() - ? body.model.trim() - : undefined, + model: resolvedModel || undefined, negativePrompt: isScaleAdjustOperation - ? "彩色框、蓝色边框、绿色边框、红色边框、橙色边框、矩形框、编号圆点、提示词气泡、标注文字、UI 控件、半透明色块、选区边框、白边、画幅压扁、画面拉伸、主体拉伸、主体压扁、主体变窄、主体变高但变窄、横向挤压、纵向拉伸、透视变形、矩形贴片、硬边、拼接线、补丁感、局部裁切、色差边缘、背景断裂、主体未缩放、整块框选区域缩放" + ? "彩色框、蓝色边框、绿色边框、红色边框、橙色边框、矩形框、编号圆点、提示词气泡、标注文字、UI 控件、半透明色块、选区边框、白边、画幅压扁、画面拉伸、整张图缩放、全图放大、全图裁切、全图平移、整体重绘、删除文字、清理文字、改写文字、文字模糊、Logo消失、主体拉伸、主体压扁、主体变窄、主体变高但变窄、横向挤压、纵向拉伸、透视变形、矩形贴片、硬边、拼接线、补丁感、局部裁切、色差边缘、背景断裂、主体未缩放、整块框选区域缩放" : "彩色框、蓝色边框、绿色边框、红色边框、橙色边框、矩形框、编号圆点、提示词气泡、标注文字、UI 控件、半透明色块、选区边框、白边、画幅压扁、画面拉伸、主体截断、动物截断、缺头、缺尾巴、缺四肢、贴边裁切、矩形贴片、硬边、拼接线、补丁感、色差边缘、背景错位、地面纹理断裂、椅脚错位", prompt, promptExtend: false,