From 701675720f2cbe745d9e188042ecefede893a14d Mon Sep 17 00:00:00 2001 From: shrimbly Date: Tue, 21 Apr 2026 08:06:49 +1200 Subject: [PATCH 1/3] feat(seedance2): expose all media inputs on I2V variants Add last_frame_url, reference_image_urls, reference_video_urls, and reference_audio_urls handles to Seedance 2.0 and Seedance 2.0 Fast I2V schemas so users can seed an end frame, supplementary reference images, reference videos, and reference audio in addition to the first frame. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/app/api/models/[modelId]/route.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/app/api/models/[modelId]/route.ts b/src/app/api/models/[modelId]/route.ts index c0fa0dd6..d8354bab 100644 --- a/src/app/api/models/[modelId]/route.ts +++ b/src/app/api/models/[modelId]/route.ts @@ -881,7 +881,11 @@ function getKieSchema(modelId: string): ExtractedSchema { ], inputs: [ { name: "prompt", type: "text", required: false, label: "Prompt" }, - { name: "first_frame_url", type: "image", required: true, label: "Image" }, + { name: "first_frame_url", type: "image", required: true, label: "First Frame", description: "Starting frame of the generated video." }, + { name: "last_frame_url", type: "image", required: false, label: "Last Frame", description: "Optional end frame — the video interpolates between first and last." }, + { name: "reference_image_urls", type: "image", required: false, isArray: true, label: "Reference Images", description: "Up to 9 supplementary reference images for style/subject guidance." }, + { name: "reference_video_urls", type: "image", required: false, isArray: true, label: "Reference Videos", description: "Up to 3 reference videos (uses image handle; accepts video data URLs)." }, + { name: "reference_audio_urls", type: "audio", required: false, isArray: true, label: "Reference Audio", description: "Up to 3 reference audio clips." }, ], }, "bytedance/seedance-2-fast/text-to-video": { @@ -906,7 +910,11 @@ function getKieSchema(modelId: string): ExtractedSchema { ], inputs: [ { name: "prompt", type: "text", required: false, label: "Prompt" }, - { name: "first_frame_url", type: "image", required: true, label: "Image" }, + { name: "first_frame_url", type: "image", required: true, label: "First Frame", description: "Starting frame of the generated video." }, + { name: "last_frame_url", type: "image", required: false, label: "Last Frame", description: "Optional end frame — the video interpolates between first and last." }, + { name: "reference_image_urls", type: "image", required: false, isArray: true, label: "Reference Images", description: "Up to 9 supplementary reference images for style/subject guidance." }, + { name: "reference_video_urls", type: "image", required: false, isArray: true, label: "Reference Videos", description: "Up to 3 reference videos (uses image handle; accepts video data URLs)." }, + { name: "reference_audio_urls", type: "audio", required: false, isArray: true, label: "Reference Audio", description: "Up to 3 reference audio clips." }, ], }, "grok-imagine/text-to-video": { From 2366f23cbf57c87b3634a5abf811b70fa40ee74b Mon Sep 17 00:00:00 2001 From: shrimbly Date: Tue, 21 Apr 2026 08:15:50 +1200 Subject: [PATCH 2/3] fix(seedance2): avoid sending first_frame with reference images MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Kie rejects the call when both reference_image_urls and first_frame_url (or last_frame_url) are populated — the API treats them as mutually exclusive scene modes. Two changes: 1. kie.ts submit: skip the legacy image fallback whenever any schema-driven image key was already handled via dynamicInputs. The executor passes both `images` and `dynamicInputs`, so the fallback was duplicating user-provided references into first_frame_url. 2. seedance2 I2V schema: mark first_frame_url optional and note the mutual exclusivity in the handle descriptions so users see it in the node UI. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/app/api/generate/providers/kie.ts | 7 +++++-- src/app/api/models/[modelId]/route.ts | 16 ++++++++-------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/app/api/generate/providers/kie.ts b/src/app/api/generate/providers/kie.ts index f85e13c0..87fe2191 100644 --- a/src/app/api/generate/providers/kie.ts +++ b/src/app/api/generate/providers/kie.ts @@ -624,9 +624,12 @@ export async function submitKieTask( } } - // Handle image inputs (fallback - only if dynamicInputs didn't already set the image key) + // Handle image inputs (fallback - only if dynamicInputs didn't already handle any image key). + // If the schema-driven dynamic inputs already placed images into specific fields + // (e.g. reference_image_urls), don't also populate the default key — some models + // treat these fields as mutually exclusive and reject the request. const imageKey = getKieImageInputKey(modelId); - if (input.images && input.images.length > 0 && !handledImageKeys.has(imageKey)) { + if (input.images && input.images.length > 0 && handledImageKeys.size === 0) { const imageUrls: string[] = []; for (const image of input.images) { if (image.startsWith("http")) { diff --git a/src/app/api/models/[modelId]/route.ts b/src/app/api/models/[modelId]/route.ts index d8354bab..d4d7d83c 100644 --- a/src/app/api/models/[modelId]/route.ts +++ b/src/app/api/models/[modelId]/route.ts @@ -881,10 +881,10 @@ function getKieSchema(modelId: string): ExtractedSchema { ], inputs: [ { name: "prompt", type: "text", required: false, label: "Prompt" }, - { name: "first_frame_url", type: "image", required: true, label: "First Frame", description: "Starting frame of the generated video." }, - { name: "last_frame_url", type: "image", required: false, label: "Last Frame", description: "Optional end frame — the video interpolates between first and last." }, - { name: "reference_image_urls", type: "image", required: false, isArray: true, label: "Reference Images", description: "Up to 9 supplementary reference images for style/subject guidance." }, - { name: "reference_video_urls", type: "image", required: false, isArray: true, label: "Reference Videos", description: "Up to 3 reference videos (uses image handle; accepts video data URLs)." }, + { name: "first_frame_url", type: "image", required: false, label: "First Frame", description: "Starting frame. Mutually exclusive with Reference Images." }, + { name: "last_frame_url", type: "image", required: false, label: "Last Frame", description: "Optional end frame — interpolates from First to Last. Mutually exclusive with Reference Images." }, + { name: "reference_image_urls", type: "image", required: false, isArray: true, label: "Reference Images", description: "Up to 9 reference images for style/subject guidance. Mutually exclusive with First/Last Frame." }, + { name: "reference_video_urls", type: "image", required: false, isArray: true, label: "Reference Videos", description: "Up to 3 reference videos (accepts video data URLs over the image handle)." }, { name: "reference_audio_urls", type: "audio", required: false, isArray: true, label: "Reference Audio", description: "Up to 3 reference audio clips." }, ], }, @@ -910,10 +910,10 @@ function getKieSchema(modelId: string): ExtractedSchema { ], inputs: [ { name: "prompt", type: "text", required: false, label: "Prompt" }, - { name: "first_frame_url", type: "image", required: true, label: "First Frame", description: "Starting frame of the generated video." }, - { name: "last_frame_url", type: "image", required: false, label: "Last Frame", description: "Optional end frame — the video interpolates between first and last." }, - { name: "reference_image_urls", type: "image", required: false, isArray: true, label: "Reference Images", description: "Up to 9 supplementary reference images for style/subject guidance." }, - { name: "reference_video_urls", type: "image", required: false, isArray: true, label: "Reference Videos", description: "Up to 3 reference videos (uses image handle; accepts video data URLs)." }, + { name: "first_frame_url", type: "image", required: false, label: "First Frame", description: "Starting frame. Mutually exclusive with Reference Images." }, + { name: "last_frame_url", type: "image", required: false, label: "Last Frame", description: "Optional end frame — interpolates from First to Last. Mutually exclusive with Reference Images." }, + { name: "reference_image_urls", type: "image", required: false, isArray: true, label: "Reference Images", description: "Up to 9 reference images for style/subject guidance. Mutually exclusive with First/Last Frame." }, + { name: "reference_video_urls", type: "image", required: false, isArray: true, label: "Reference Videos", description: "Up to 3 reference videos (accepts video data URLs over the image handle)." }, { name: "reference_audio_urls", type: "audio", required: false, isArray: true, label: "Reference Audio", description: "Up to 3 reference audio clips." }, ], }, From 0e405e58e8a8d8137aa3f5743af33647b0fd67ab Mon Sep 17 00:00:00 2001 From: shrimbly Date: Tue, 21 Apr 2026 09:44:24 +1200 Subject: [PATCH 3/3] release: v1.6.0 --- CHANGELOG.md | 10 ++++++++++ package.json | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e4ec563a..d94d89eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Unreleased] +## [1.6.0] - 2026-04-21 + +### Added + +- **Seedance 2 I2V: richer media inputs** — The ByteDance Seedance 2.0 and 2.0 Fast image-to-video nodes now expose Last Frame, Reference Images (up to 9), Reference Videos (up to 3), and Reference Audio (up to 3) handles alongside First Frame. Handle descriptions document the First/Last Frame vs Reference Images mutual-exclusivity rule. + +### Fixed + +- **Seedance 2 I2V: reference-only runs no longer rejected** — When connecting images only to Reference Images, the request no longer duplicates them into `first_frame_url`, which Kie was rejecting as a mutually-exclusive combination. + ## [1.5.0] - 2026-04-20 ### Added diff --git a/package.json b/package.json index 9b0d43b7..04d97e52 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-banana", - "version": "1.5.0", + "version": "1.6.0", "private": true, "scripts": { "dev": "node server.js",