adding kling 2.5 i2v

This commit is contained in:
Dreaming Computers
2026-01-10 16:38:28 +07:00
parent 37795f404e
commit 5feab93829
4 changed files with 268 additions and 0 deletions
+4
View File
@@ -42,6 +42,10 @@ This node pack currently includes the following nodes:
- Generates video from a single input image.
- Uses the Kling 2.6 image-to-video model.
- **Kling 2.5 I2V Pro**
- Generates video from a required first image and optional tail image.
- Uses the Kling 2.5 Turbo image-to-video Pro model.
- **Kling 2.6 Text-to-Video**
- Generates video directly from a text prompt.
- Supports aspect ratio, duration, and sound options as exposed by the API.
+153
View File
@@ -0,0 +1,153 @@
"""Kling 2.5 Turbo Image-to-Video Pro helper."""
import json
import time
from typing import Any, Callable
import torch
from .auth import _load_api_key
from .credits import _log_remaining_credits
from .http import TransientKieError, requests
from .jobs import _poll_task_until_complete
from .log import _log
from .results import _extract_result_urls
from .upload import _image_tensor_to_png_bytes, _truncate_url, _upload_image
from .validation import _validate_prompt
from .video import _download_video, _video_bytes_to_comfy_video
CREATE_TASK_URL = "https://api.kie.ai/api/v1/jobs/createTask"
MODEL_NAME = "kling/v2-5-turbo-image-to-video-pro"
PROMPT_MAX_LENGTH = 1000
DURATION_OPTIONS = ["5", "10"]
def _validate_options(duration: str, cfg_scale: float) -> None:
if duration not in DURATION_OPTIONS:
raise RuntimeError("Invalid duration. Use the pinned enum options.")
if not isinstance(cfg_scale, (int, float)):
raise RuntimeError("cfg_scale must be a float value.")
if cfg_scale < 0.0 or cfg_scale > 1.0:
raise RuntimeError("cfg_scale must be between 0.0 and 1.0.")
def _validate_image_input(images: torch.Tensor | None, *, label: str, required: bool) -> torch.Tensor | None:
if images is None:
if required:
raise RuntimeError(f"{label} input is required.")
return None
if not isinstance(images, torch.Tensor):
raise RuntimeError(f"{label} input must be a tensor batch.")
if images.dim() != 4 or images.shape[-1] != 3:
raise RuntimeError(f"{label} input must have shape [B, H, W, 3].")
if images.shape[0] < 1:
raise RuntimeError(f"{label} input batch is empty.")
return images
def _create_kling_task(api_key: str, payload: dict[str, Any]) -> tuple[str, str]:
try:
response = requests.post(
CREATE_TASK_URL,
headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"},
json=payload,
timeout=30,
)
except requests.RequestException as exc:
raise RuntimeError(f"Failed to call createTask endpoint: {exc}") from exc
if response.status_code == 429 or response.status_code >= 500:
raise TransientKieError(
f"createTask returned HTTP {response.status_code}: {response.text}", status_code=response.status_code
)
try:
payload_json: Any = response.json()
except json.JSONDecodeError as exc:
raise RuntimeError("createTask endpoint did not return valid JSON.") from exc
if payload_json.get("code") != 200:
message = payload_json.get("message") or payload_json.get("msg")
raise RuntimeError(f"createTask endpoint returned error code {payload_json.get('code')}: {message}")
task_id = (payload_json.get("data") or {}).get("taskId")
if not task_id:
raise RuntimeError("createTask endpoint did not return a taskId.")
return task_id, response.text
def run_kling25_i2v_job(
image: torch.Tensor,
tail_image: torch.Tensor | None,
prompt: str,
negative_prompt: str = "",
duration: str = "5",
cfg_scale: float = 0.5,
timeout_seconds: int | None = None,
log: Callable[[str], None] | None = None,
) -> dict:
_validate_prompt(prompt, max_length=PROMPT_MAX_LENGTH)
_validate_options(duration, cfg_scale)
images = _validate_image_input(image, label="image", required=True)
tail_images = _validate_image_input(tail_image, label="tail_image", required=False)
api_key = _load_api_key()
if images.shape[0] > 1:
_log(log, f"More than 1 image provided ({images.shape[0]}); only the first will be used.")
if tail_images is not None and tail_images.shape[0] > 1:
_log(log, f"More than 1 tail image provided ({tail_images.shape[0]}); only the first will be used.")
_log(log, "Uploading source image for Kling 2.5 I2V Pro...")
png_bytes = _image_tensor_to_png_bytes(images[0])
image_url = _upload_image(api_key, png_bytes)
_log(log, f"Image upload success: {_truncate_url(image_url)}")
tail_image_url = None
if tail_images is not None:
_log(log, "Uploading tail image for Kling 2.5 I2V Pro...")
tail_png_bytes = _image_tensor_to_png_bytes(tail_images[0])
tail_image_url = _upload_image(api_key, tail_png_bytes)
_log(log, f"Tail image upload success: {_truncate_url(tail_image_url)}")
payload_input: dict[str, Any] = {
"prompt": prompt,
"image_url": image_url,
"duration": duration,
"cfg_scale": cfg_scale,
}
if tail_image_url:
payload_input["tail_image_url"] = tail_image_url
if negative_prompt.strip():
payload_input["negative_prompt"] = negative_prompt
payload = {"model": MODEL_NAME, "input": payload_input}
_log(log, "Creating Kling 2.5 I2V Pro task...")
start_time = time.time()
task_id, create_response_text = _create_kling_task(api_key, payload)
_log(log, f"createTask response (elapsed={time.time() - start_time:.1f}s): {create_response_text}")
_log(log, f"Task created with ID {task_id}. Polling for completion...")
_log(log, "Check https://kie.ai/logs for request status if needed.")
effective_timeout = 1000 if timeout_seconds is None else timeout_seconds
record_data = _poll_task_until_complete(
api_key,
task_id,
10.0,
effective_timeout,
log,
start_time,
)
result_urls = _extract_result_urls(record_data)
video_url = result_urls[0]
_log(log, f"Final video URL: {video_url}")
video_bytes = _download_video(video_url)
video_output = _video_bytes_to_comfy_video(video_bytes)
_log_remaining_credits(log, record_data, api_key, _log)
return video_output
+86
View File
@@ -27,6 +27,10 @@ from .kie_api.kling26_i2v import (
DURATION_OPTIONS as KLING26_DURATION_OPTIONS,
run_kling26_i2v_video,
)
from .kie_api.kling25_i2v import (
DURATION_OPTIONS as KLING25_DURATION_OPTIONS,
run_kling25_i2v_job,
)
from .kie_api.kling26motion_i2v import (
CHARACTER_ORIENTATION_OPTIONS as KLING26MOTION_CHARACTER_ORIENTATION_OPTIONS,
MODE_OPTIONS as KLING26MOTION_MODE_OPTIONS,
@@ -250,6 +254,86 @@ Outputs:
return (image_tensor,)
class KIE_Kling25_I2V_Pro:
HELP = """
KIE Kling 2.5 I2V Pro
Generate a short video clip from a prompt and one or two input images using Kling 2.5 Turbo I2V Pro.
Inputs:
- prompt: Text prompt (required)
- first_frame: Source image batch (first image used)
- last_frame: Optional tail image batch (first image used)
- negative_prompt: Optional negative prompt
- duration: 5s or 10s
- cfg_scale: 0.0 to 1.0
- poll_interval_s / timeout_s / log
- retry_on_fail / max_retries / retry_backoff_s
Outputs:
- VIDEO: ComfyUI video output referencing a temporary .mp4 file
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"first_frame": ("IMAGE",),
"prompt": ("STRING", {"multiline": True, "default": ""}),
},
"optional": {
"last_frame": ("IMAGE",),
"negative_prompt": ("STRING", {"multiline": True, "default": ""}),
"duration": ("COMBO", {"options": KLING25_DURATION_OPTIONS, "default": "5"}),
"cfg_scale": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.1}),
"log": ("BOOLEAN", {"default": True}),
},
}
RETURN_TYPES = ("VIDEO",)
RETURN_NAMES = ("video",)
FUNCTION = "generate"
CATEGORY = "kie/api"
def generate(
self,
first_frame: torch.Tensor,
prompt: str,
last_frame: torch.Tensor | None = None,
negative_prompt: str = "",
duration: str = "5",
cfg_scale: float = 0.5,
log: bool = True,
poll_interval_s: float = 10.0,
timeout_s: int = 1000,
retry_on_fail: bool = True,
max_retries: int = 2,
retry_backoff_s: float = 3.0,
):
attempts = max_retries + 1 if retry_on_fail else 1
attempts = max(attempts, 1)
backoff = retry_backoff_s if retry_backoff_s >= 0 else 0.0
for attempt in range(1, attempts + 1):
try:
video_output = run_kling25_i2v_job(
image=first_frame,
tail_image=last_frame,
prompt=prompt,
negative_prompt=negative_prompt,
duration=duration,
cfg_scale=cfg_scale,
timeout_seconds=timeout_s,
log=log,
)
return (video_output,)
except TransientKieError:
if not retry_on_fail or attempt >= attempts:
raise
_log(log, f"Retrying (attempt {attempt + 1}/{attempts}) after {backoff}s")
time.sleep(backoff)
class KIE_Kling26_I2V:
HELP = """
KIE Kling 2.6 (Video)
@@ -641,6 +725,7 @@ NODE_CLASS_MAPPINGS = {
"KIE_Seedream45_Edit": KIE_Seedream45_Edit,
"KIE_SeedanceV1Pro_Fast_I2V": KIE_SeedanceV1Pro_Fast_I2V,
"KIE_Seedance15Pro_I2V": KIE_Seedance15Pro_I2V,
"KIE_Kling25_I2V_Pro": KIE_Kling25_I2V_Pro,
"KIE_Kling26_I2V": KIE_Kling26_I2V,
"KIE_Kling26_T2V": KIE_Kling26_T2V,
"KIE_Kling26Motion_I2V": KIE_Kling26Motion_I2V,
@@ -654,6 +739,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"KIE_Seedream45_Edit": "KIE Seedream 4.5 Edit",
"KIE_SeedanceV1Pro_Fast_I2V": "KIE Seedance V1 Pro Fast (I2V)",
"KIE_Seedance15Pro_I2V": "KIE Seedance 1.5 Pro (I2V/T2V)",
"KIE_Kling25_I2V_Pro": "KIE Kling 2.5 I2V Pro",
"KIE_Kling26_I2V": "KIE Kling 2.6 (I2V)",
"KIE_Kling26_T2V": "KIE Kling 2.6 (T2V)",
"KIE_Kling26Motion_I2V": "KIE Kling 2.6 Motion-Control (I2V)",
+25
View File
@@ -0,0 +1,25 @@
# KIE Kling 2.5 I2V Pro
## Overview
Generate a short video clip from a prompt and a required first image, with an optional tail image, using the Kling 2.5 Turbo I2V Pro model.
## Inputs
- first_frame (IMAGE, required): Source image batch; the first image is used.
- prompt (STRING, multiline, required): The text prompt for the video.
- last_frame (IMAGE, optional): Optional tail frame; the first image is used if provided.
- negative_prompt (STRING, multiline, optional): Optional negative prompt.
- duration (COMBO, seconds, default: 5): Video length. Options: 5, 10.
- cfg_scale (FLOAT, default: 0.5): Guidance scale from 0.0 to 1.0.
- poll_interval_s (FLOAT, seconds, default: 10.0): How often to poll job status.
- timeout_s (INT, seconds, default: 1000): Max wait time before timing out.
- log (BOOLEAN, default: true): Enable KIE console logs.
- retry_on_fail (BOOLEAN, default: true): Retry on transient API failures.
- max_retries (INT, default: 2): Number of retry attempts.
- retry_backoff_s (FLOAT, seconds, default: 3.0): Delay between retries.
## Outputs
- video (VIDEO): ComfyUI video output referencing a temporary .mp4 file.
## Troubleshooting
- If a job stalls or fails, check https://kie.ai/logs.
- Timeouts: Increase timeout_s or use duration 5 seconds.