fix: route scale shrink through fal edit fallback
This commit is contained in:
parent
2e167d38f0
commit
2a8b303508
@ -326,6 +326,28 @@ function isScaleAdjustCoordinateGuideModel(
|
||||
return getScaleAdjustModelFamily(entry) === "nano-banana-2";
|
||||
}
|
||||
|
||||
function isScaleAdjustFalNanoBananaEditModel(
|
||||
entry: AiModelWithProviderForSelection | null | undefined
|
||||
) {
|
||||
if (
|
||||
!entry ||
|
||||
entry.provider.type !== "fal-image" ||
|
||||
getScaleAdjustModelFamily(entry) !== "nano-banana-2"
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
const label = `${entry.model.displayName || ""} ${entry.model.modelId || ""}`
|
||||
.toLowerCase()
|
||||
.replace(/\s+/g, " ");
|
||||
return label.includes("/edit") || /\bedit\b/.test(label);
|
||||
}
|
||||
|
||||
function isScaleAdjustHealthyModel(
|
||||
entry: AiModelWithProviderForSelection | null | undefined
|
||||
) {
|
||||
return !entry?.model?.healthStatus || entry.model.healthStatus === "healthy";
|
||||
}
|
||||
|
||||
function getScaleAdjustProviderPriority(providerType: string) {
|
||||
switch (providerType) {
|
||||
case "ruxa-image":
|
||||
@ -399,6 +421,7 @@ function compareScaleAdjustProviderCandidates(
|
||||
function resolveScaleAdjustPreferredModelId(params: {
|
||||
configuredModelId: string;
|
||||
honorRequestedModelBeforeGpt?: boolean;
|
||||
preferFalNanoBananaEdit?: boolean;
|
||||
preferMaskCapableGpt?: boolean;
|
||||
preferProviderTypes?: string[];
|
||||
requestedModelId: string;
|
||||
@ -433,19 +456,36 @@ function resolveScaleAdjustPreferredModelId(params: {
|
||||
if (params.preferMaskCapableGpt) {
|
||||
const maskCapableGptImage2 = gptImage2Candidates
|
||||
.filter(isScaleAdjustMaskCapableModel)
|
||||
.filter((entry) => entry.model.healthStatus === "healthy");
|
||||
.filter(isScaleAdjustHealthyModel);
|
||||
if (maskCapableGptImage2[0]) {
|
||||
return maskCapableGptImage2[0].model.id;
|
||||
}
|
||||
}
|
||||
for (const providerType of params.preferProviderTypes || []) {
|
||||
const providerCandidate = gptImage2Candidates.find(
|
||||
(entry) => entry.provider.type === providerType
|
||||
(entry) =>
|
||||
entry.provider.type === providerType && isScaleAdjustHealthyModel(entry)
|
||||
);
|
||||
if (providerCandidate) {
|
||||
return providerCandidate.model.id;
|
||||
}
|
||||
}
|
||||
if (params.preferFalNanoBananaEdit) {
|
||||
const falEditCandidate = params.models
|
||||
.filter((entry) => isScaleAdjustUsableImageEditModel(entry))
|
||||
.filter(isScaleAdjustFalNanoBananaEditModel)
|
||||
.filter(isScaleAdjustHealthyModel)
|
||||
.sort((left, right) =>
|
||||
compareScaleAdjustProviderCandidates(
|
||||
left,
|
||||
right,
|
||||
params.requestedModelId || params.configuredModelId
|
||||
)
|
||||
)[0];
|
||||
if (falEditCandidate) {
|
||||
return falEditCandidate.model.id;
|
||||
}
|
||||
}
|
||||
if (gptImage2Candidates[0]) {
|
||||
return gptImage2Candidates[0].model.id;
|
||||
}
|
||||
@ -1426,7 +1466,6 @@ function buildScaleAdjustStructureInitialMask(params: {
|
||||
for (let localY = 0; localY < box.height; localY += 1) {
|
||||
const y = box.top + localY;
|
||||
for (let localX = 0; localX < box.width; localX += 1) {
|
||||
if (localX >= box.width * 0.9) continue;
|
||||
const x = box.left + localX;
|
||||
const rawIndex = getScaleAdjustRawPixelIndex(width, x, y);
|
||||
const r = raw[rawIndex] || 0;
|
||||
@ -2311,8 +2350,10 @@ async function buildScaleAdjustStructureLockedComposite(params: {
|
||||
} = params;
|
||||
// The current RMBG provider can bake horizontal matte stripes into furniture cutouts.
|
||||
const allowRmbgStructureLock =
|
||||
process.env.CANVAS_SCALE_ADJUST_ENABLE_RMBG_STRUCTURE === "1";
|
||||
const rmbgComposite = includePersonPixels || !allowRmbgStructureLock
|
||||
process.env.CANVAS_SCALE_ADJUST_ENABLE_RMBG_STRUCTURE === "1" ||
|
||||
(includePersonPixels &&
|
||||
process.env.CANVAS_SCALE_ADJUST_DISABLE_PERSON_RMBG !== "1");
|
||||
const rmbgComposite = !allowRmbgStructureLock
|
||||
? null
|
||||
: await buildScaleAdjustRmbgStructureLockedComposite({
|
||||
placement,
|
||||
@ -2323,6 +2364,7 @@ async function buildScaleAdjustStructureLockedComposite(params: {
|
||||
userId,
|
||||
});
|
||||
if (rmbgComposite) return rmbgComposite;
|
||||
if (includePersonPixels) return null;
|
||||
|
||||
const initialMask = buildScaleAdjustStructureInitialMask({
|
||||
box: placement.sourceBox,
|
||||
@ -2535,6 +2577,7 @@ async function buildScaleAdjustVisualLayoutGuideBuffer(params: {
|
||||
sourceHeight: number;
|
||||
sourceImageUrl?: string;
|
||||
sourceWidth: number;
|
||||
structureLockAppliedRef?: { value: boolean };
|
||||
structureLockedDraft?: boolean;
|
||||
userId?: string;
|
||||
}) {
|
||||
@ -2545,6 +2588,7 @@ async function buildScaleAdjustVisualLayoutGuideBuffer(params: {
|
||||
sourceHeight,
|
||||
sourceImageUrl,
|
||||
sourceWidth,
|
||||
structureLockAppliedRef,
|
||||
structureLockedDraft = false,
|
||||
userId,
|
||||
} = params;
|
||||
@ -2610,6 +2654,9 @@ async function buildScaleAdjustVisualLayoutGuideBuffer(params: {
|
||||
: null;
|
||||
|
||||
if (structureLockedComposite) {
|
||||
if (structureLockAppliedRef) {
|
||||
structureLockAppliedRef.value = true;
|
||||
}
|
||||
subjectWashComposites.push(structureLockedComposite.wash);
|
||||
composites.push(structureLockedComposite.overlay);
|
||||
continue;
|
||||
@ -2736,9 +2783,11 @@ async function createScaleAdjustPatchReference(params: {
|
||||
} = params;
|
||||
const workspaceRect = getScaleAdjustPatchWorkspaceRect(regions, scalePercent);
|
||||
const workspacePixelRect = rectToPixels(workspaceRect, sourceWidth, sourceHeight);
|
||||
// Pixel-mask structure drafts are useful for enlargement, but in shrink
|
||||
// cases they can turn dense interior details into black mask blocks.
|
||||
const structureLockedDraft = hasScaleAdjustEnlargeTarget(regions, scalePercent);
|
||||
const shouldUseStructureLockedDraft = hasScaleAdjustStructureTarget(
|
||||
regions,
|
||||
scalePercent
|
||||
);
|
||||
const structureLockAppliedRef = { value: false };
|
||||
const targetDraftBuffer = await buildScaleAdjustVisualLayoutGuideBuffer({
|
||||
regions,
|
||||
scalePercent,
|
||||
@ -2746,7 +2795,8 @@ async function createScaleAdjustPatchReference(params: {
|
||||
sourceHeight,
|
||||
sourceImageUrl,
|
||||
sourceWidth,
|
||||
structureLockedDraft,
|
||||
structureLockAppliedRef,
|
||||
structureLockedDraft: shouldUseStructureLockedDraft,
|
||||
userId,
|
||||
});
|
||||
const [sourceCropBuffer, targetDraftCropBuffer] = await Promise.all([
|
||||
@ -2773,7 +2823,7 @@ async function createScaleAdjustPatchReference(params: {
|
||||
|
||||
return {
|
||||
sourceCropUrl: sourceCropUpload.url,
|
||||
structureLockedDraft,
|
||||
structureLockedDraft: structureLockAppliedRef.value,
|
||||
targetDraftUrl: targetDraftUpload.url,
|
||||
workspacePixelRect,
|
||||
workspaceRect,
|
||||
@ -3463,7 +3513,7 @@ function buildScaleAdjustCoordinateSystemPrompt() {
|
||||
function buildScaleAdjustTextOnlyPrompt(
|
||||
regions: NormalizedLocalEditRegion[],
|
||||
scalePercent: ScaleAdjustPercent | null | undefined,
|
||||
cropReferences: ScaleAdjustCropReference[] = [],
|
||||
cropReferences: ScaleAdjustCropReference[],
|
||||
sourceSize: { width: number; height: number }
|
||||
) {
|
||||
const cropLines = cropReferences
|
||||
@ -3780,6 +3830,8 @@ export async function POST(request: NextRequest) {
|
||||
: false;
|
||||
const preferScaleAdjustMaskCapableModel =
|
||||
hasScaleAdjustShrink && !hasScaleAdjustEnlarge;
|
||||
const preferScaleAdjustFalNanoBananaEdit =
|
||||
hasScaleAdjustShrink && !hasScaleAdjustEnlarge;
|
||||
const preferScaleAdjustProviderTypes =
|
||||
hasScaleAdjustShrink && !hasScaleAdjustEnlarge ? ["grsai-gpt-image"] : [];
|
||||
const displayPrompt = buildLocalEditDisplayPrompt({ operation, scalePercent });
|
||||
@ -3800,6 +3852,7 @@ export async function POST(request: NextRequest) {
|
||||
? resolveScaleAdjustPreferredModelId({
|
||||
configuredModelId: configuredDetailModel,
|
||||
honorRequestedModelBeforeGpt: hasScaleAdjustEnlarge,
|
||||
preferFalNanoBananaEdit: preferScaleAdjustFalNanoBananaEdit,
|
||||
preferMaskCapableGpt: preferScaleAdjustMaskCapableModel,
|
||||
preferProviderTypes: preferScaleAdjustProviderTypes,
|
||||
requestedModelId: requestedModel || "",
|
||||
@ -3840,8 +3893,12 @@ export async function POST(request: NextRequest) {
|
||||
!useProviderTextOnlyCoordinatePrompt;
|
||||
const useScaleAdjustMarkedPhotoGuide =
|
||||
useScaleAdjustCoordinatePrompt && !useScaleAdjustVisualLayoutGuide;
|
||||
const useScaleAdjustFullFrameFalEdit =
|
||||
isScaleAdjustOperation &&
|
||||
isScaleAdjustFalNanoBananaEditModel(resolvedScaleAdjustModelEntry);
|
||||
const useScaleAdjustStructureLockedPatchWorkflow =
|
||||
isScaleAdjustOperation &&
|
||||
!useScaleAdjustFullFrameFalEdit &&
|
||||
hasScaleAdjustStructureTarget(regions, scalePercent) &&
|
||||
(!preferScaleAdjustMaskCapableModel ||
|
||||
!isScaleAdjustMaskCapableModel(resolvedScaleAdjustModelEntry));
|
||||
@ -3851,6 +3908,7 @@ export async function POST(request: NextRequest) {
|
||||
isScaleAdjustMaskCapableModel(resolvedScaleAdjustModelEntry);
|
||||
const useScaleAdjustPatchWorkflow =
|
||||
isScaleAdjustOperation &&
|
||||
!useScaleAdjustFullFrameFalEdit &&
|
||||
(useScaleAdjustStructureLockedPatchWorkflow || !useScaleAdjustEditMask);
|
||||
const sendScaleAdjustAnnotatedReferenceToModel =
|
||||
isScaleAdjustOperation &&
|
||||
@ -4050,7 +4108,9 @@ export async function POST(request: NextRequest) {
|
||||
: [];
|
||||
const scaleAdjustProtectedRegions =
|
||||
isScaleAdjustOperation && scaleAdjustPatchReference
|
||||
? scaleAdjustProtectedChangeRegions
|
||||
? scaleAdjustPatchReference.structureLockedDraft
|
||||
? [scaleAdjustPatchReference.workspaceRect]
|
||||
: scaleAdjustProtectedChangeRegions
|
||||
: scaleAdjustCompositeRegions;
|
||||
const scaleAdjustVerificationConfig = isScaleAdjustOperation
|
||||
? {
|
||||
@ -4137,6 +4197,7 @@ export async function POST(request: NextRequest) {
|
||||
scaleAdjustPatchWorkflowApplied: Boolean(scaleAdjustPatchReference),
|
||||
scaleAdjustPatchStructureLockedDraftApplied:
|
||||
scaleAdjustPatchReference?.structureLockedDraft === true,
|
||||
scaleAdjustFullFrameFalEditApplied: useScaleAdjustFullFrameFalEdit,
|
||||
scaleAdjustPatchReferenceOrder:
|
||||
scaleAdjustPatchReference && useScaleAdjustPatchTargetDraftPrimary
|
||||
? includeScaleAdjustPatchSourceReference
|
||||
@ -4256,8 +4317,12 @@ export async function POST(request: NextRequest) {
|
||||
featherMaxPx: 42,
|
||||
featherMinPx: 10,
|
||||
featherRatio: 0.018,
|
||||
maskMode: "diff",
|
||||
regions: scaleAdjustPatchCompositeRegions,
|
||||
maskMode: scaleAdjustPatchReference.structureLockedDraft
|
||||
? "workspace"
|
||||
: "diff",
|
||||
regions: scaleAdjustPatchReference.structureLockedDraft
|
||||
? undefined
|
||||
: scaleAdjustPatchCompositeRegions,
|
||||
sourceImageUrl,
|
||||
workspaceRegion: scaleAdjustPatchReference.workspaceRect,
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user