fix: harden scale adjust patch workflow
This commit is contained in:
parent
f483fab33a
commit
fec71038c5
@ -3710,6 +3710,9 @@ function buildScaleAdjustPatchPrompt(
|
||||
"Clean local image edit for a cropped workspace.",
|
||||
`Return one clean realistic crop with the same framing and aspect as input image 1. This crop maps to full-photo pixels left=${patchReference.workspacePixelRect.left}, top=${patchReference.workspacePixelRect.top}, width=${patchReference.workspacePixelRect.width}, height=${patchReference.workspacePixelRect.height}.`,
|
||||
"Input image 1 is the rough target draft. The selected subject is already placed at the requested final size and position; keep that final footprint.",
|
||||
structureLockedDraft
|
||||
? "Structure lock: treat the selected subject pixels in input image 1 as the locked product/person layer. AI work is limited to background completion, boundary blending, occlusion, lighting, contact shadows, and removing rough draft artifacts. Do not repaint the product/person interior."
|
||||
: "",
|
||||
includeSourceReference
|
||||
? "Input image 2 is the original clean crop for background texture, lighting, perspective, and non-target objects only. Do not restore the selected subject to its old size."
|
||||
: "",
|
||||
@ -3732,6 +3735,9 @@ function buildScaleAdjustPatchPrompt(
|
||||
firstInputRole,
|
||||
secondInputRole,
|
||||
cropLines,
|
||||
structureLockedDraft
|
||||
? "Structure lock: treat the selected subject pixels in input image 1 as the locked product/person layer. AI work is limited to background completion, boundary blending, occlusion, lighting, contact shadows, and removing rough draft artifacts. Do not repaint or redesign the target subject interior."
|
||||
: "",
|
||||
"Only edit the selected target subject inside the search area. The target subject can be a person/model/child/body part, furniture, product, or another movable object. Keep the crop borders, wall, window frame, floor, rug, sofa, lamps, decor, lighting, camera perspective, and all non-target subjects stable.",
|
||||
"If the selected target is a person, scale the whole visible person/body coherently to the final footprint while preserving identity, face, pose, anatomy, hand shape, hair, clothing, and natural human proportions. If a person appears outside the selected target, preserve that non-target person without resizing or redrawing them.",
|
||||
"When the final footprint is smaller, the selected subject must clearly shrink; erase the old larger subject footprint outside the final footprint and replace it with natural background/floor/rug/wall/shadow that matches the original crop.",
|
||||
@ -4170,9 +4176,7 @@ export async function POST(request: NextRequest) {
|
||||
: [];
|
||||
const scaleAdjustProtectedRegions =
|
||||
isScaleAdjustOperation && scaleAdjustPatchReference
|
||||
? scaleAdjustPatchReference.structureLockedDraft
|
||||
? [scaleAdjustPatchReference.workspaceRect]
|
||||
: scaleAdjustProtectedChangeRegions
|
||||
? scaleAdjustProtectedChangeRegions
|
||||
: scaleAdjustCompositeRegions;
|
||||
const scaleAdjustVerificationConfig = isScaleAdjustOperation
|
||||
? {
|
||||
@ -4386,12 +4390,8 @@ export async function POST(request: NextRequest) {
|
||||
featherMaxPx: 42,
|
||||
featherMinPx: 10,
|
||||
featherRatio: 0.018,
|
||||
maskMode: scaleAdjustPatchReference.structureLockedDraft
|
||||
? "workspace"
|
||||
: "diff",
|
||||
regions: scaleAdjustPatchReference.structureLockedDraft
|
||||
? undefined
|
||||
: scaleAdjustPatchCompositeRegions,
|
||||
maskMode: "diff",
|
||||
regions: scaleAdjustPatchCompositeRegions,
|
||||
sourceImageUrl,
|
||||
workspaceRegion: scaleAdjustPatchReference.workspaceRect,
|
||||
}
|
||||
|
||||
@ -764,7 +764,7 @@ type DirectDetailReviewResult = {
|
||||
};
|
||||
|
||||
const AGENT_IMAGE_TASK_POLL_INTERVAL_MS = 1600;
|
||||
const AGENT_IMAGE_TASK_TIMEOUT_MS = 10 * 60 * 1000;
|
||||
const AGENT_IMAGE_TASK_TIMEOUT_MS = 15 * 60 * 1000;
|
||||
const AGENT_DETAIL_IMAGE_PARALLEL_WORKER_COUNT = 4;
|
||||
|
||||
function sleep(ms: number) {
|
||||
|
||||
@ -796,7 +796,7 @@ const CANVAS_AGENT_SECTION_TITLES = new Set([
|
||||
"输出设置",
|
||||
]);
|
||||
const CANVAS_IMAGE_TASK_POLL_INTERVAL_MS = 1600;
|
||||
const CANVAS_IMAGE_TASK_TIMEOUT_MS = 10 * 60 * 1000;
|
||||
const CANVAS_IMAGE_TASK_TIMEOUT_MS = 15 * 60 * 1000;
|
||||
const CANVAS_GENERATION_NODE_GAP = 56;
|
||||
const CANVAS_GENERATION_STACK_GAP = 28;
|
||||
const CANVAS_GENERATION_COLLISION_GAP = 32;
|
||||
|
||||
@ -58,7 +58,9 @@ interface QueueState {
|
||||
draining: boolean;
|
||||
}
|
||||
|
||||
const STALE_RUNNING_MS = 5 * 60 * 1000;
|
||||
const DEFAULT_RUNNING_TIMEOUT_MS = 10 * 60 * 1000;
|
||||
const SCALE_ADJUST_RUNNING_TIMEOUT_MS = 15 * 60 * 1000;
|
||||
const LOCAL_ACTIVE_RUNNING_HARD_TIMEOUT_MS = 30 * 60 * 1000;
|
||||
const PENDING_LOOKAHEAD_LIMIT = 100;
|
||||
const QUEUE_STATUS_PENDING_LOOKAHEAD_LIMIT = 500;
|
||||
const QUEUE_STATUS_RECENT_SAMPLE_LIMIT = 120;
|
||||
@ -98,6 +100,29 @@ function normalizeTaskModelKey(request: unknown) {
|
||||
return normalizeCanvasImageModelKey(rawModel);
|
||||
}
|
||||
|
||||
function getRecord(value: unknown): Record<string, unknown> | null {
|
||||
return value && typeof value === "object" && !Array.isArray(value)
|
||||
? (value as Record<string, unknown>)
|
||||
: null;
|
||||
}
|
||||
|
||||
function getCanvasImageRunningTimeoutMs(request: unknown) {
|
||||
const record = getRecord(request);
|
||||
const debugMeta = getRecord(record?.debugMeta);
|
||||
const debugLabel = String(record?.debugLabel || "");
|
||||
const operation = String(debugMeta?.operation || "");
|
||||
const isScaleAdjust =
|
||||
debugLabel === "canvas-scale-adjust" ||
|
||||
operation === "scale-adjust" ||
|
||||
Boolean(debugMeta?.scaleAdjustPatchWorkflowApplied) ||
|
||||
Boolean(debugMeta?.scaleAdjustPatchForcedForShrink) ||
|
||||
Boolean(record?.scaleAdjustPatchComposite);
|
||||
|
||||
return isScaleAdjust
|
||||
? SCALE_ADJUST_RUNNING_TIMEOUT_MS
|
||||
: DEFAULT_RUNNING_TIMEOUT_MS;
|
||||
}
|
||||
|
||||
function isStyleProTaskRequest(request: unknown): request is StyleProTaskRequest {
|
||||
return Boolean(
|
||||
request &&
|
||||
@ -599,8 +624,16 @@ export async function getCanvasImageTaskForUser(params: {
|
||||
if (!task) return null;
|
||||
|
||||
if (task.status === "running" && task.startedAt) {
|
||||
const staleBefore = new Date(Date.now() - STALE_RUNNING_MS);
|
||||
if (task.startedAt < staleBefore) {
|
||||
const now = Date.now();
|
||||
const runningAgeMs = now - task.startedAt.getTime();
|
||||
const runningTimeoutMs = getCanvasImageRunningTimeoutMs(task.request);
|
||||
const locallyRunning = getQueueState().running.has(task.id);
|
||||
const shouldMarkStale =
|
||||
runningAgeMs >= runningTimeoutMs &&
|
||||
(!locallyRunning || runningAgeMs >= LOCAL_ACTIVE_RUNNING_HARD_TIMEOUT_MS);
|
||||
|
||||
if (shouldMarkStale) {
|
||||
const staleBefore = new Date(now - runningTimeoutMs);
|
||||
const timeoutMessage = "图片生成任务已超时,请重新发起生成";
|
||||
const completedAt = new Date();
|
||||
const [timedOutTask] = await db
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user