V 2.0.0 - Universal api #68 - Kling
This commit is contained in:
+22
-15
@@ -68,6 +68,8 @@ class PrimereApiProcessor:
|
||||
"reference_images": ("IMAGE", {"default": None, "forceInput": True}),
|
||||
"first_image": ("IMAGE", {"default": None, "forceInput": True}),
|
||||
"last_image": ("IMAGE", {"default": None, "forceInput": True}),
|
||||
"frontal_image": ("IMAGE", {"default": None, "forceInput": True}),
|
||||
"reference_video": ("VIDEO", {"default": None, "forceInput": True}),
|
||||
"width": ("INT", {"default": 1024, "max": 8192, "min": 64, "step": 64, "forceInput": True}),
|
||||
"height": ("INT", {"default": 1024, "max": 8192, "min": 64, "step": 64, "forceInput": True}),
|
||||
"aspect_ratio": ("STRING", {"forceInput": True, "default": "1:1"}),
|
||||
@@ -82,7 +84,7 @@ class PrimereApiProcessor:
|
||||
|
||||
return {"required": cls.required_inputs, "optional": cls.optional_inputs, "hidden": hidden_inputs}
|
||||
|
||||
def process_uniapi(self, processor, api_provider, api_service, prompt, negative_prompt = None, batch = 1, reference_images = None, first_image = None, last_image = None, width = 1024, height = 1024, aspect_ratio = '1:1', seed = None, debug_mode = False, unique_id = None, **kwargs):
|
||||
def process_uniapi(self, processor, api_provider, api_service, prompt, negative_prompt = None, batch = 1, reference_images = None, first_image = None, last_image = None, frontal_image = None, reference_video = None, width = 1024, height = 1024, aspect_ratio = '1:1', seed = None, debug_mode = False, unique_id = None, **kwargs):
|
||||
API_SCHEMAS_PATH = os.path.join(PRIMERE_ROOT, 'front_end', 'api_schemas.json')
|
||||
API_CONFIG_PATH = os.path.join(PRIMERE_ROOT, 'json', 'apiconfig.json')
|
||||
API_SCHEMA_REGISTRY = api_schema_registry.load_and_validate_api_schema_registry(API_SCHEMAS_PATH, API_CONFIG_PATH)
|
||||
@@ -187,22 +189,27 @@ class PrimereApiProcessor:
|
||||
|
||||
if isinstance(img_binary_api, dict) and len(img_binary_api) > 0:
|
||||
selected_parameters.update(img_binary_api)
|
||||
elif img_binary_api not in (None, "") and (not isinstance(img_binary_api, list) or len(img_binary_api) > 0):
|
||||
elif isinstance(img_binary_api, list) and len(img_binary_api) > 0:
|
||||
selected_parameters["reference_images"] = img_binary_api
|
||||
|
||||
if first_image is not None:
|
||||
first_image_source = first_image
|
||||
elif reference_images is not None:
|
||||
first_image_source = reference_images[0] if isinstance(reference_images, list) else reference_images
|
||||
else:
|
||||
first_image_source = None
|
||||
if first_image_source is not None:
|
||||
first_image_result = external_api_backend.apply_reference_images_handler(
|
||||
schema, api_provider,
|
||||
{"img_binary_api": first_image_source, "loaded_client_for_upload": loaded_client_for_upload}
|
||||
)
|
||||
if isinstance(first_image_result, dict) and first_image_result:
|
||||
selected_parameters.update(first_image_result)
|
||||
first_image_source = first_image if first_image is not None else (reference_images[0] if isinstance(reference_images, list) else reference_images) if reference_images is not None else None
|
||||
|
||||
single_ref_inputs = [
|
||||
("first_image", first_image_source),
|
||||
("last_image", last_image),
|
||||
("frontal_image", frontal_image),
|
||||
("reference_video", reference_video),
|
||||
]
|
||||
if not debug_mode:
|
||||
for ref_key, ref_source in single_ref_inputs:
|
||||
if ref_source is None:
|
||||
continue
|
||||
ref_result = external_api_backend.apply_reference_images_handler(
|
||||
schema, api_provider,
|
||||
{"img_binary_api": ref_source, "loaded_client_for_upload": loaded_client_for_upload, "target_key": ref_key}
|
||||
)
|
||||
if isinstance(ref_result, dict) and ref_result:
|
||||
selected_parameters.update(ref_result)
|
||||
|
||||
possible_parameters = schema.get("possible_parameters", {}) if isinstance(schema, dict) else {}
|
||||
if isinstance(possible_parameters, dict):
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -57,8 +57,8 @@ def _marker_default(marker: Any) -> Any:
|
||||
|
||||
def _default_value(name: str) -> Any:
|
||||
k = name.lower()
|
||||
if "prompt" in k:
|
||||
return "cute cat walking in the futuristic metropolis"
|
||||
if k in {"prompt", "negative_prompt", "multi_prompt"}:
|
||||
return None
|
||||
if "response_modalities" in k:
|
||||
return "IMAGE"
|
||||
if "model" in k:
|
||||
@@ -69,7 +69,7 @@ def _default_value(name: str) -> Any:
|
||||
return "1K"
|
||||
if "number" in k or "count" in k or "width" in k or "height" in k:
|
||||
return 1
|
||||
return f"default_{name}"
|
||||
return None
|
||||
|
||||
def _is_optional_image_input(name: str) -> bool:
|
||||
low = str(name or "").lower()
|
||||
@@ -121,6 +121,21 @@ def _build_values(spec: dict[str, Any], values: dict[str, Any] | None = None) ->
|
||||
marker_selected = _marker_default(possible_parameters.get(canonical))
|
||||
selected = marker_selected if marker_selected is not None else _default_value(canonical)
|
||||
|
||||
if selected is not None:
|
||||
raw_marker = possible_parameters.get(key) if not isinstance(possible_parameters.get(key), list) else possible_parameters.get(canonical)
|
||||
if isinstance(raw_marker, str):
|
||||
t = raw_marker.strip().upper()
|
||||
if t == "INT":
|
||||
try:
|
||||
selected = int(selected)
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
elif t == "FLOAT":
|
||||
try:
|
||||
selected = round(float(selected), 1)
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
|
||||
resolved[key] = selected
|
||||
|
||||
return resolved
|
||||
@@ -165,21 +180,30 @@ def _remove_none_values(value: Any) -> Any:
|
||||
for key, child in value.items():
|
||||
if child is None:
|
||||
continue
|
||||
cleaned[key] = _remove_none_values(child)
|
||||
result = _remove_none_values(child)
|
||||
if result == {} or result == []:
|
||||
continue
|
||||
cleaned[key] = result
|
||||
return cleaned
|
||||
if isinstance(value, list):
|
||||
cleaned_list = []
|
||||
for child in value:
|
||||
if child is None:
|
||||
continue
|
||||
cleaned_list.append(_remove_none_values(child))
|
||||
result = _remove_none_values(child)
|
||||
if result == {} or result == []:
|
||||
continue
|
||||
cleaned_list.append(result)
|
||||
return cleaned_list
|
||||
if isinstance(value, tuple):
|
||||
cleaned_tuple = []
|
||||
for child in value:
|
||||
if child is None:
|
||||
continue
|
||||
cleaned_tuple.append(_remove_none_values(child))
|
||||
result = _remove_none_values(child)
|
||||
if result == {} or result == []:
|
||||
continue
|
||||
cleaned_tuple.append(result)
|
||||
return tuple(cleaned_tuple)
|
||||
return value
|
||||
|
||||
|
||||
@@ -757,6 +757,8 @@ def canonical_param_name(name: str, *, number_of_images_as_seed: bool = False) -
|
||||
return "model"
|
||||
if number_of_images_as_seed and low == "number_of_images":
|
||||
return "seed"
|
||||
if low in {"negative_prompt", "multi_prompt", "system_prompt"}:
|
||||
return low
|
||||
if low in {"prompt", "contents"} or low.endswith("_prompt"):
|
||||
return "prompt"
|
||||
if "response_modalities" in low:
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
from typing import Any
|
||||
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
|
||||
|
||||
def handle_reference_images(img_binary_api: Any = None, temp_file_ref: str = "", loaded_client_for_upload: Any = None, target_key: str = "first_image", **_: Any):
|
||||
if temp_file_ref:
|
||||
output = img_binary_api if isinstance(img_binary_api, list) else []
|
||||
if hasattr(loaded_client_for_upload, "upload_file"):
|
||||
output.append(loaded_client_for_upload.upload_file(temp_file_ref))
|
||||
else:
|
||||
output.append(temp_file_ref)
|
||||
return output
|
||||
|
||||
if img_binary_api is not None and type(img_binary_api).__name__ == "Tensor":
|
||||
img_array = (img_binary_api[0].cpu().numpy() * 255).astype(np.uint8)
|
||||
pil_image = Image.fromarray(img_array)
|
||||
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp:
|
||||
tmp_path = tmp.name
|
||||
pil_image.save(tmp_path, format="PNG")
|
||||
try:
|
||||
uploaded = loaded_client_for_upload.upload_file(tmp_path) if hasattr(loaded_client_for_upload, "upload_file") else tmp_path
|
||||
finally:
|
||||
try:
|
||||
os.unlink(tmp_path)
|
||||
except OSError:
|
||||
pass
|
||||
return {target_key: uploaded}
|
||||
|
||||
if img_binary_api is not None and isinstance(img_binary_api, str) and img_binary_api:
|
||||
if os.path.isfile(img_binary_api) and hasattr(loaded_client_for_upload, "upload_file"):
|
||||
uploaded = loaded_client_for_upload.upload_file(img_binary_api)
|
||||
else:
|
||||
uploaded = img_binary_api
|
||||
return {target_key: uploaded}
|
||||
|
||||
return img_binary_api if isinstance(img_binary_api, list) else []
|
||||
@@ -0,0 +1,23 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
|
||||
from . import response_helper
|
||||
|
||||
|
||||
def handle_response(api_result, schema=None):
|
||||
try:
|
||||
json_object = json.loads(json.dumps(api_result))
|
||||
except (ValueError, TypeError) as exc:
|
||||
raise RuntimeError(f"Invalid JSON response received: {api_result}") from exc
|
||||
|
||||
video = json_object.get("video") if isinstance(json_object, dict) else None
|
||||
if not isinstance(video, dict):
|
||||
raise RuntimeError(f"No 'video' key in Kling response: {json_object}")
|
||||
|
||||
video_url = video.get("url")
|
||||
if not video_url:
|
||||
raise RuntimeError(f"No URL in Kling video response: {video}")
|
||||
|
||||
video_bytes = response_helper.fetch_url_bytes(video_url)
|
||||
return ["video_result", video_bytes]
|
||||
+120
-37
@@ -89,6 +89,9 @@
|
||||
"model": "{{model}}",
|
||||
"prompt": "{{prompt}}",
|
||||
"image": "{{first_image}}",
|
||||
"lastFrame": "{{last_image}}",
|
||||
"referenceImages": "{{reference_images}}",
|
||||
"video": "{{reference_video}}",
|
||||
"config": {
|
||||
"$call": "types.GenerateVideosConfig",
|
||||
"$args": [],
|
||||
@@ -99,7 +102,6 @@
|
||||
"durationSeconds": "{{duration_seconds}}",
|
||||
"personGeneration": "{{person_generation}}",
|
||||
"reference_images": "{{reference_images}}",
|
||||
"last_frame": "{{last_image}}",
|
||||
"negative_prompt": "{{negative_prompt}}"
|
||||
}
|
||||
}
|
||||
@@ -198,60 +200,141 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Kling_V3PRO_T2V": {
|
||||
"Kling": {
|
||||
"provider": "FAL",
|
||||
"service": "Kling_V3PRO_T2V",
|
||||
"service": "Kling",
|
||||
"response_handler": "FAL_Kling.py",
|
||||
"reference_images_handler": "FAL_Kling.py",
|
||||
"import_modules": [
|
||||
"import fal_client"
|
||||
],
|
||||
"possible_parameters": {
|
||||
"aspect_ratio": ["16:9", "9:16", "1:1"],
|
||||
"duration": [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
|
||||
"generate_audio": [false, true],
|
||||
"with_logs": [false, true]
|
||||
"aspect_ratio": [
|
||||
"16:9",
|
||||
"9:16",
|
||||
"1:1"
|
||||
],
|
||||
"duration": [
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9,
|
||||
10,
|
||||
11,
|
||||
12,
|
||||
13,
|
||||
14,
|
||||
15
|
||||
],
|
||||
"generate_audio": [
|
||||
false,
|
||||
true
|
||||
],
|
||||
"with_logs": [
|
||||
false,
|
||||
true
|
||||
],
|
||||
"model_type": [
|
||||
"text-to-video",
|
||||
"image-to-video"
|
||||
],
|
||||
"version": [
|
||||
"v3/pro",
|
||||
"v3/standard",
|
||||
"o3/pro",
|
||||
"o3/standard"
|
||||
],
|
||||
"cfg_scale": "FLOAT"
|
||||
},
|
||||
"request": {
|
||||
"method": "SDK",
|
||||
"endpoint": "fal_client.submit",
|
||||
"sdk_call": {
|
||||
"args": [
|
||||
"{{arg0}}"
|
||||
],
|
||||
"kwargs": {
|
||||
"arguments": {
|
||||
"prompt": "{{prompt}}",
|
||||
"duration": "{{duration}}",
|
||||
"aspect_ratio": "{{aspect_ratio}}",
|
||||
"generate_audio": "{{generate_audio}}",
|
||||
"with_logs": "{{with_logs}}"
|
||||
}
|
||||
}
|
||||
"request_exclusions": [
|
||||
{
|
||||
"when": {
|
||||
"path": "version",
|
||||
"equals": "v3/pro"
|
||||
},
|
||||
"remove": [
|
||||
"image_url"
|
||||
]
|
||||
},
|
||||
{
|
||||
"when": {
|
||||
"path": "version",
|
||||
"equals": "v3/standard"
|
||||
},
|
||||
"remove": [
|
||||
"image_url"
|
||||
]
|
||||
},
|
||||
{
|
||||
"when": {
|
||||
"path": "version",
|
||||
"equals": "o3/standard"
|
||||
},
|
||||
"remove": [
|
||||
"negative_prompt",
|
||||
"cfg_scale",
|
||||
"start_image_url",
|
||||
"elements",
|
||||
"aspect_ratio"
|
||||
]
|
||||
},
|
||||
{
|
||||
"when": {
|
||||
"path": "version",
|
||||
"equals": "o3/pro"
|
||||
},
|
||||
"remove": [
|
||||
"negative_prompt",
|
||||
"cfg_scale",
|
||||
"start_image_url",
|
||||
"elements",
|
||||
"aspect_ratio"
|
||||
]
|
||||
},
|
||||
{
|
||||
"when": {
|
||||
"path": "model_type",
|
||||
"equals": "text-to-video"
|
||||
},
|
||||
"remove": [
|
||||
"multi_prompt",
|
||||
"start_image_url",
|
||||
"image_url",
|
||||
"elements"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Kling_V3PRO_I2V": {
|
||||
"provider": "FAL",
|
||||
"service": "Kling_V3PRO_I2V",
|
||||
"import_modules": [
|
||||
"import fal_client"
|
||||
],
|
||||
"possible_parameters": {
|
||||
"aspect_ratio": ["16:9", "9:16", "1:1"],
|
||||
"duration": [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
|
||||
"generate_audio": [false, true],
|
||||
"with_logs": [false, true]
|
||||
"parameter_constraints": {
|
||||
"cfg_scale": {"max": 1.0}
|
||||
},
|
||||
"request": {
|
||||
"method": "SDK",
|
||||
"endpoint": "fal_client.subscribe",
|
||||
"sdk_call": {
|
||||
"args": [
|
||||
"{{arg0}}"
|
||||
"fal-ai/kling-video/{{version}}/{{model_type}}"
|
||||
],
|
||||
"kwargs": {
|
||||
"arguments": {
|
||||
"prompt": "{{prompt}}",
|
||||
"start_image_url": "{{reference_images}}",
|
||||
"negative_prompt": "{{negative_prompt}}",
|
||||
"multi_prompt": "{{multi_prompt}}",
|
||||
"cfg_scale": "{{cfg_scale}}",
|
||||
"start_image_url": "{{first_image}}",
|
||||
"image_url": "{{first_image}}",
|
||||
"end_image_url": "{{last_image}}",
|
||||
"elements": [
|
||||
{
|
||||
"reference_image_urls": "{{reference_images}}",
|
||||
"frontal_image_url": "{{frontal_image}}"
|
||||
},
|
||||
{
|
||||
"video_url": "{{reference_video}}"
|
||||
}
|
||||
],
|
||||
"duration": "{{duration}}",
|
||||
"aspect_ratio": "{{aspect_ratio}}",
|
||||
"generate_audio": "{{generate_audio}}",
|
||||
|
||||
@@ -181,6 +181,8 @@ def canonical_param_name(name: str) -> str:
|
||||
return "resolution"
|
||||
if low == "model" or low.endswith("_model"):
|
||||
return "model"
|
||||
if low in {"negative_prompt", "multi_prompt", "system_prompt"}:
|
||||
return low
|
||||
if low in {"prompt", "contents"} or low.endswith("_prompt"):
|
||||
return "prompt"
|
||||
if "response_modalities" in low:
|
||||
|
||||
Reference in New Issue
Block a user