From 56e72e8c3479126f1793322cc52081566393c391 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jim=20Liu=20=E5=AE=9D=E7=8E=89?= Date: Sun, 12 Apr 2026 01:04:57 -0500 Subject: [PATCH] Preserve Replicate compatibility when shared defaults leak across providers Addressed the new PR review findings by teaching baoyu-imagine to track where aspect-ratio defaults came from, mirroring the earlier imageSize fix, so unsupported Replicate models can still run prompt-only requests when the value was inherited from shared config. Also corrected Seedream 4.5 custom size encoding to use the API's custom width/height schema instead of sending literal WxH strings. Constraint: Shared EXTEND defaults still need to apply globally for providers that support them Constraint: Seedream 4.5 custom sizes must follow Replicate's documented custom size schema Rejected: Ignore all aspect ratios for unknown Replicate models | would hide explicit unsupported CLI/task input Rejected: Keep Seedream custom sizes as literal strings | validated locally but fails against the provider API Confidence: high Scope-risk: narrow Reversibility: clean Directive: Any future inherited-default validation for provider-specific flags should record the source explicitly before rejecting it Tested: node --import tsx --test skills/baoyu-imagine/scripts/main.test.ts skills/baoyu-imagine/scripts/providers/replicate.test.ts Tested: npm test Not-tested: Live Replicate API calls for Seedream 4.5 custom-size requests --- skills/baoyu-imagine/scripts/main.test.ts | 3 ++ skills/baoyu-imagine/scripts/main.ts | 9 +++++- .../scripts/providers/replicate.test.ts | 32 +++++++++++++++++++ .../scripts/providers/replicate.ts | 20 +++++++++--- skills/baoyu-imagine/scripts/types.ts | 1 + 5 files changed, 60 insertions(+), 5 deletions(-) diff --git a/skills/baoyu-imagine/scripts/main.test.ts b/skills/baoyu-imagine/scripts/main.test.ts index f7123ee..4928367 100644 --- a/skills/baoyu-imagine/scripts/main.test.ts +++ b/skills/baoyu-imagine/scripts/main.test.ts @@ -28,6 +28,7 @@ function makeArgs(overrides: Partial = {}): CliArgs { provider: null, model: null, aspectRatio: null, + aspectRatioSource: null, size: null, quality: null, imageSize: null, @@ -98,6 +99,7 @@ test("parseArgs parses the main baoyu-imagine CLI flags", () => { assert.equal(args.imagePath, "out/hero"); assert.equal(args.provider, "zai"); assert.equal(args.quality, "2k"); + assert.equal(args.aspectRatioSource, null); assert.equal(args.imageSize, "4K"); assert.equal(args.imageSizeSource, "cli"); assert.deepEqual(args.referenceImages, ["ref/one.png", "ref/two.jpg"]); @@ -256,6 +258,7 @@ test("mergeConfig only fills values missing from CLI args", () => { assert.equal(merged.provider, "openai"); assert.equal(merged.quality, "2k"); assert.equal(merged.aspectRatio, "3:2"); + assert.equal(merged.aspectRatioSource, "config"); assert.equal(merged.imageSize, "4K"); assert.equal(merged.imageSizeSource, "cli"); }); diff --git a/skills/baoyu-imagine/scripts/main.ts b/skills/baoyu-imagine/scripts/main.ts index 59bce7d..6be1b08 100644 --- a/skills/baoyu-imagine/scripts/main.ts +++ b/skills/baoyu-imagine/scripts/main.ts @@ -165,6 +165,7 @@ export function parseArgs(argv: string[]): CliArgs { provider: null, model: null, aspectRatio: null, + aspectRatioSource: null, size: null, quality: null, imageSize: null, @@ -272,6 +273,7 @@ export function parseArgs(argv: string[]): CliArgs { const v = argv[++i]; if (!v) throw new Error("Missing value for --ar"); out.aspectRatio = v; + out.aspectRatioSource = "cli"; continue; } @@ -544,12 +546,16 @@ export async function loadExtendConfig( } export function mergeConfig(args: CliArgs, extend: Partial): CliArgs { + const aspectRatio = args.aspectRatio ?? extend.default_aspect_ratio ?? null; const imageSize = args.imageSize ?? extend.default_image_size ?? null; return { ...args, provider: args.provider ?? extend.default_provider ?? null, quality: args.quality ?? extend.default_quality ?? null, - aspectRatio: args.aspectRatio ?? extend.default_aspect_ratio ?? null, + aspectRatio, + aspectRatioSource: + args.aspectRatioSource ?? + (args.aspectRatio !== null ? "cli" : (aspectRatio !== null ? "config" : null)), imageSize, imageSizeSource: args.imageSizeSource ?? @@ -880,6 +886,7 @@ export function createTaskArgs(baseArgs: CliArgs, task: BatchTaskInput, batchDir provider: task.provider ?? baseArgs.provider ?? null, model: task.model ?? baseArgs.model ?? null, aspectRatio: task.ar ?? baseArgs.aspectRatio ?? null, + aspectRatioSource: task.ar != null ? "task" : (baseArgs.aspectRatioSource ?? null), size: task.size ?? baseArgs.size ?? null, quality: task.quality ?? baseArgs.quality ?? null, imageSize: task.imageSize ?? baseArgs.imageSize ?? null, diff --git a/skills/baoyu-imagine/scripts/providers/replicate.test.ts b/skills/baoyu-imagine/scripts/providers/replicate.test.ts index f6a5327..cd90def 100644 --- a/skills/baoyu-imagine/scripts/providers/replicate.test.ts +++ b/skills/baoyu-imagine/scripts/providers/replicate.test.ts @@ -19,6 +19,7 @@ function makeArgs(overrides: Partial = {}): CliArgs { provider: null, model: null, aspectRatio: null, + aspectRatioSource: null, size: null, quality: null, imageSize: null, @@ -124,6 +125,21 @@ test("Replicate Seedream and Wan inputs use family-specific request fields", () }, ); + assert.deepEqual( + buildInput( + "bytedance/seedream-4.5", + "A cinematic portrait", + makeArgs({ size: "1536x1024" }), + [], + ), + { + prompt: "A cinematic portrait", + size: "custom", + width: 1536, + height: 1024, + }, + ); + assert.deepEqual( buildInput( "bytedance/seedream-5-lite", @@ -237,6 +253,22 @@ test("Replicate validateArgs blocks misleading multi-output and unsupported fami /do not use --imageSize/, ); + assert.doesNotThrow(() => + validateArgs( + "stability-ai/sdxl", + makeArgs({ aspectRatio: "16:9", aspectRatioSource: "config" }), + ), + ); + + assert.throws( + () => + validateArgs( + "stability-ai/sdxl", + makeArgs({ aspectRatio: "16:9", aspectRatioSource: "cli" }), + ), + /compatibility list/, + ); + assert.doesNotThrow(() => validateArgs( "stability-ai/sdxl", diff --git a/skills/baoyu-imagine/scripts/providers/replicate.ts b/skills/baoyu-imagine/scripts/providers/replicate.ts index aea8b52..d10e5f1 100644 --- a/skills/baoyu-imagine/scripts/providers/replicate.ts +++ b/skills/baoyu-imagine/scripts/providers/replicate.ts @@ -32,6 +32,8 @@ type PixelSize = { height: number; }; +type Seedream45Size = "2K" | "4K" | { width: number; height: number }; + export function getDefaultModel(): string { return process.env.REPLICATE_IMAGE_MODEL || DEFAULT_MODEL; } @@ -194,7 +196,7 @@ function getNanoBananaResolution(args: CliArgs): "1K" | "2K" { return getQualityPreset(args) === "normal" ? "1K" : "2K"; } -function resolveSeedream45Size(args: CliArgs): "2K" | "4K" | string { +function resolveSeedream45Size(args: CliArgs): Seedream45Size { if (args.size) { const upper = args.size.trim().toUpperCase(); if (upper === "2K" || upper === "4K") { @@ -208,7 +210,7 @@ function resolveSeedream45Size(args: CliArgs): "2K" | "4K" | string { if (parsed.width < 1024 || parsed.width > 4096 || parsed.height < 1024 || parsed.height > 4096) { throw new Error("Replicate Seedream 4.5 custom --size must keep width and height between 1024 and 4096."); } - return `${parsed.width}x${parsed.height}`; + return parsed; } return getQualityPreset(args) === "normal" ? "2K" : "4K"; @@ -317,11 +319,19 @@ function buildSeedreamInput( args: CliArgs, referenceImages: string[], ): Record { + const size = family === "seedream45" ? resolveSeedream45Size(args) : resolveSeedream5LiteSize(args); const input: Record = { prompt, - size: family === "seedream45" ? resolveSeedream45Size(args) : resolveSeedream5LiteSize(args), }; + if (family === "seedream45" && typeof size === "object") { + input.size = "custom"; + input.width = size.width; + input.height = size.height; + } else { + input.size = size; + } + if (referenceImages.length > 0) { input.image_input = referenceImages; } @@ -417,7 +427,9 @@ export function validateArgs(model: string, args: CliArgs): void { return; } - if (args.referenceImages.length > 0 || args.aspectRatio || args.size) { + const hasExplicitAspectRatio = !!args.aspectRatio && args.aspectRatioSource !== "config"; + + if (args.referenceImages.length > 0 || hasExplicitAspectRatio || args.size) { throw new Error( `Replicate model ${model} is not in the baoyu-imagine compatibility list. Supported families: google/nano-banana*, bytedance/seedream-4.5, bytedance/seedream-5-lite, wan-video/wan-2.7-image, wan-video/wan-2.7-image-pro.` ); diff --git a/skills/baoyu-imagine/scripts/types.ts b/skills/baoyu-imagine/scripts/types.ts index 994db36..b7c7640 100644 --- a/skills/baoyu-imagine/scripts/types.ts +++ b/skills/baoyu-imagine/scripts/types.ts @@ -18,6 +18,7 @@ export type CliArgs = { provider: Provider | null; model: string | null; aspectRatio: string | null; + aspectRatioSource?: "cli" | "task" | "config" | null; size: string | null; quality: Quality | null; imageSize: string | null;