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;