runapi-kling 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- runapi/kling/__init__.py +24 -0
- runapi/kling/client.py +33 -0
- runapi/kling/py.typed +0 -0
- runapi/kling/resources/__init__.py +6 -0
- runapi/kling/resources/ai_avatar.py +70 -0
- runapi/kling/resources/image_to_video.py +90 -0
- runapi/kling/resources/motion_control.py +80 -0
- runapi/kling/resources/text_to_video.py +136 -0
- runapi/kling/types.py +110 -0
- runapi_kling-0.1.0.dist-info/METADATA +87 -0
- runapi_kling-0.1.0.dist-info/RECORD +12 -0
- runapi_kling-0.1.0.dist-info/WHEEL +4 -0
runapi/kling/__init__.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Kling client for RunAPI."""
|
|
2
|
+
|
|
3
|
+
from runapi.core import (
|
|
4
|
+
AuthenticationError,
|
|
5
|
+
InsufficientCreditsError,
|
|
6
|
+
NotFoundError,
|
|
7
|
+
RateLimitError,
|
|
8
|
+
TaskFailedError,
|
|
9
|
+
TaskTimeoutError,
|
|
10
|
+
ValidationError,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
from .client import KlingClient
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"KlingClient",
|
|
17
|
+
"AuthenticationError",
|
|
18
|
+
"RateLimitError",
|
|
19
|
+
"InsufficientCreditsError",
|
|
20
|
+
"NotFoundError",
|
|
21
|
+
"ValidationError",
|
|
22
|
+
"TaskFailedError",
|
|
23
|
+
"TaskTimeoutError",
|
|
24
|
+
]
|
runapi/kling/client.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""Kling client."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Optional
|
|
6
|
+
|
|
7
|
+
from runapi.core import ClientOptions, HttpClient, resolve_api_key
|
|
8
|
+
|
|
9
|
+
from .resources.ai_avatar import AiAvatar
|
|
10
|
+
from .resources.image_to_video import ImageToVideo
|
|
11
|
+
from .resources.motion_control import MotionControl
|
|
12
|
+
from .resources.text_to_video import TextToVideo
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class KlingClient:
|
|
16
|
+
"""Kling video generation client.
|
|
17
|
+
|
|
18
|
+
Example::
|
|
19
|
+
|
|
20
|
+
client = KlingClient(api_key="sk-...")
|
|
21
|
+
result = client.text_to_video.run(
|
|
22
|
+
model="kling-3.0", prompt="A cat walking through a garden"
|
|
23
|
+
)
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __init__(self, api_key: Optional[str] = None, **options: Any) -> None:
|
|
27
|
+
resolved_api_key = resolve_api_key(api_key)
|
|
28
|
+
client_options = ClientOptions(api_key=resolved_api_key, **options)
|
|
29
|
+
http = client_options.http_client or HttpClient(client_options)
|
|
30
|
+
self.text_to_video = TextToVideo(http)
|
|
31
|
+
self.ai_avatar = AiAvatar(http)
|
|
32
|
+
self.image_to_video = ImageToVideo(http)
|
|
33
|
+
self.motion_control = MotionControl(http)
|
runapi/kling/py.typed
ADDED
|
File without changes
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""Kling AI avatar resource."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Dict
|
|
6
|
+
|
|
7
|
+
from runapi.core import Resource, ValidationError
|
|
8
|
+
|
|
9
|
+
from ..types import AI_AVATAR_MODELS, AiAvatarResponse, CompletedAiAvatarResponse
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class AiAvatar(Resource):
|
|
13
|
+
"""Generate talking avatar videos from images and audio."""
|
|
14
|
+
|
|
15
|
+
ENDPOINT = "/api/v1/kling/ai_avatar"
|
|
16
|
+
|
|
17
|
+
RESPONSE_CLASS = AiAvatarResponse
|
|
18
|
+
COMPLETED_RESPONSE_CLASS = CompletedAiAvatarResponse
|
|
19
|
+
|
|
20
|
+
def run(self, **params: Any) -> Any:
|
|
21
|
+
"""Generate an AI avatar video and poll until it completes.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
**params: AI avatar parameters (model, source_image_url, source_audio_url, prompt, ...).
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
The completed task with videos.
|
|
28
|
+
"""
|
|
29
|
+
task = self.create(**params)
|
|
30
|
+
return self._poll_until_complete(lambda: self.get(task.id))
|
|
31
|
+
|
|
32
|
+
def create(self, **params: Any) -> Any:
|
|
33
|
+
"""Create an AI avatar task and return immediately with an ``id``.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
**params: AI avatar parameters (model, source_image_url, source_audio_url, prompt, ...).
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
The task creation result with an id.
|
|
40
|
+
"""
|
|
41
|
+
compacted = self._compact_params(params)
|
|
42
|
+
self._validate_params(compacted)
|
|
43
|
+
return self._request("post", self.ENDPOINT, body=compacted)
|
|
44
|
+
|
|
45
|
+
def get(self, id: str) -> Any:
|
|
46
|
+
"""Fetch the current status of an AI avatar task.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
id: The task id.
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
The current task status.
|
|
53
|
+
"""
|
|
54
|
+
return self._request("get", f"{self.ENDPOINT}/{id}")
|
|
55
|
+
|
|
56
|
+
def _validate_params(self, params: Dict[str, Any]) -> None:
|
|
57
|
+
model = params.get("model")
|
|
58
|
+
if not model:
|
|
59
|
+
raise ValidationError("model is required")
|
|
60
|
+
if model not in AI_AVATAR_MODELS:
|
|
61
|
+
raise ValidationError(
|
|
62
|
+
f"Invalid model: {model}. Must be one of: {', '.join(AI_AVATAR_MODELS)}"
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
if not params.get("source_image_url"):
|
|
66
|
+
raise ValidationError("source_image_url is required")
|
|
67
|
+
if not params.get("source_audio_url"):
|
|
68
|
+
raise ValidationError("source_audio_url is required")
|
|
69
|
+
if not params.get("prompt"):
|
|
70
|
+
raise ValidationError("prompt is required")
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""Kling image-to-video resource."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Dict
|
|
6
|
+
|
|
7
|
+
from runapi.core import Resource, ValidationError
|
|
8
|
+
|
|
9
|
+
from ..types import (
|
|
10
|
+
FIXED_DURATIONS,
|
|
11
|
+
IMAGE_TO_VIDEO_MODELS,
|
|
12
|
+
CompletedImageToVideoResponse,
|
|
13
|
+
ImageToVideoResponse,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ImageToVideo(Resource):
|
|
18
|
+
"""Generate videos from an input image with Kling models."""
|
|
19
|
+
|
|
20
|
+
ENDPOINT = "/api/v1/kling/image_to_video"
|
|
21
|
+
|
|
22
|
+
RESPONSE_CLASS = ImageToVideoResponse
|
|
23
|
+
COMPLETED_RESPONSE_CLASS = CompletedImageToVideoResponse
|
|
24
|
+
|
|
25
|
+
def run(self, **params: Any) -> Any:
|
|
26
|
+
"""Generate a video from an image and poll until it completes.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
**params: Image-to-video parameters (model, prompt, first_frame_image_url, ...).
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
The completed task with videos.
|
|
33
|
+
"""
|
|
34
|
+
task = self.create(**params)
|
|
35
|
+
return self._poll_until_complete(lambda: self.get(task.id))
|
|
36
|
+
|
|
37
|
+
def create(self, **params: Any) -> Any:
|
|
38
|
+
"""Create an image-to-video task and return immediately with an ``id``.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
**params: Image-to-video parameters (model, prompt, first_frame_image_url, ...).
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
The task creation result with an id.
|
|
45
|
+
"""
|
|
46
|
+
compacted = self._compact_params(params)
|
|
47
|
+
self._validate_params(compacted)
|
|
48
|
+
return self._request("post", self.ENDPOINT, body=compacted)
|
|
49
|
+
|
|
50
|
+
def get(self, id: str) -> Any:
|
|
51
|
+
"""Fetch the current status of an image-to-video task.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
id: The task id.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
The current task status.
|
|
58
|
+
"""
|
|
59
|
+
return self._request("get", f"{self.ENDPOINT}/{id}")
|
|
60
|
+
|
|
61
|
+
def _validate_params(self, params: Dict[str, Any]) -> None:
|
|
62
|
+
model = params.get("model")
|
|
63
|
+
if not model:
|
|
64
|
+
raise ValidationError("model is required")
|
|
65
|
+
if model not in IMAGE_TO_VIDEO_MODELS:
|
|
66
|
+
raise ValidationError(
|
|
67
|
+
f"Invalid model: {model}. Must be one of: {', '.join(IMAGE_TO_VIDEO_MODELS)}"
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
if not params.get("prompt"):
|
|
71
|
+
raise ValidationError("prompt is required")
|
|
72
|
+
if not params.get("first_frame_image_url"):
|
|
73
|
+
raise ValidationError("first_frame_image_url is required")
|
|
74
|
+
|
|
75
|
+
duration_seconds = params.get("duration_seconds")
|
|
76
|
+
if duration_seconds is not None and duration_seconds not in FIXED_DURATIONS:
|
|
77
|
+
raise ValidationError(
|
|
78
|
+
f"Invalid duration_seconds: {duration_seconds}. "
|
|
79
|
+
f"Must be one of: {', '.join(str(d) for d in FIXED_DURATIONS)}"
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
last_frame_image_url = params.get("last_frame_image_url")
|
|
83
|
+
if last_frame_image_url and model not in (
|
|
84
|
+
"kling-v2.5-turbo-image-to-video-pro",
|
|
85
|
+
"kling-v2.1-pro",
|
|
86
|
+
):
|
|
87
|
+
raise ValidationError(
|
|
88
|
+
"last_frame_image_url is only supported by "
|
|
89
|
+
"kling-v2.5-turbo-image-to-video-pro and kling-v2.1-pro"
|
|
90
|
+
)
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""Kling motion control resource."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Dict
|
|
6
|
+
|
|
7
|
+
from runapi.core import Resource, ValidationError
|
|
8
|
+
|
|
9
|
+
from ..types import (
|
|
10
|
+
MOTION_CONTROL_BACKGROUND_SOURCES,
|
|
11
|
+
MOTION_CONTROL_CHARACTER_ORIENTATIONS,
|
|
12
|
+
MOTION_CONTROL_MODELS,
|
|
13
|
+
MOTION_CONTROL_OUTPUT_RESOLUTIONS,
|
|
14
|
+
CompletedMotionControlResponse,
|
|
15
|
+
MotionControlResponse,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class MotionControl(Resource):
|
|
20
|
+
"""Generate videos with motion transfer from reference videos."""
|
|
21
|
+
|
|
22
|
+
ENDPOINT = "/api/v1/kling/motion_control"
|
|
23
|
+
|
|
24
|
+
RESPONSE_CLASS = MotionControlResponse
|
|
25
|
+
COMPLETED_RESPONSE_CLASS = CompletedMotionControlResponse
|
|
26
|
+
|
|
27
|
+
def run(self, **params: Any) -> Any:
|
|
28
|
+
"""Generate a motion control video and poll until it completes.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
**params: Motion control parameters (model, source_image_url, reference_video_url, ...).
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
The completed task with videos.
|
|
35
|
+
"""
|
|
36
|
+
task = self.create(**params)
|
|
37
|
+
return self._poll_until_complete(lambda: self.get(task.id))
|
|
38
|
+
|
|
39
|
+
def create(self, **params: Any) -> Any:
|
|
40
|
+
"""Create a motion control task and return immediately with an ``id``.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
**params: Motion control parameters (model, source_image_url, reference_video_url, ...).
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
The task creation result with an id.
|
|
47
|
+
"""
|
|
48
|
+
compacted = self._compact_params(params)
|
|
49
|
+
self._validate_params(compacted)
|
|
50
|
+
return self._request("post", self.ENDPOINT, body=compacted)
|
|
51
|
+
|
|
52
|
+
def get(self, id: str) -> Any:
|
|
53
|
+
"""Fetch the current status of a motion control task.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
id: The task id.
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
The current task status.
|
|
60
|
+
"""
|
|
61
|
+
return self._request("get", f"{self.ENDPOINT}/{id}")
|
|
62
|
+
|
|
63
|
+
def _validate_params(self, params: Dict[str, Any]) -> None:
|
|
64
|
+
model = params.get("model")
|
|
65
|
+
if not model:
|
|
66
|
+
raise ValidationError("model is required")
|
|
67
|
+
if model not in MOTION_CONTROL_MODELS:
|
|
68
|
+
raise ValidationError(
|
|
69
|
+
f"Invalid model: {model}. Must be one of: {', '.join(MOTION_CONTROL_MODELS)}"
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
self._validate_optional(params, "output_resolution", MOTION_CONTROL_OUTPUT_RESOLUTIONS)
|
|
73
|
+
self._validate_optional(params, "character_orientation", MOTION_CONTROL_CHARACTER_ORIENTATIONS)
|
|
74
|
+
self._validate_optional(params, "background_source", MOTION_CONTROL_BACKGROUND_SOURCES)
|
|
75
|
+
|
|
76
|
+
if not params.get("source_image_url"):
|
|
77
|
+
raise ValidationError("source_image_url is required")
|
|
78
|
+
|
|
79
|
+
if not params.get("reference_video_url"):
|
|
80
|
+
raise ValidationError("reference_video_url is required")
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"""Kling text-to-video resource."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Dict
|
|
6
|
+
|
|
7
|
+
from runapi.core import Resource, ValidationError
|
|
8
|
+
|
|
9
|
+
from ..types import (
|
|
10
|
+
ASPECT_RATIOS,
|
|
11
|
+
DURATION_RANGE,
|
|
12
|
+
FIXED_DURATIONS,
|
|
13
|
+
MULTI_PROMPT_DURATION_RANGE,
|
|
14
|
+
MULTI_PROMPT_MAX_LENGTH,
|
|
15
|
+
TEXT_TO_VIDEO_MODELS,
|
|
16
|
+
TEXT_TO_VIDEO_OUTPUT_RESOLUTIONS,
|
|
17
|
+
CompletedTextToVideoResponse,
|
|
18
|
+
TextToVideoResponse,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class TextToVideo(Resource):
|
|
23
|
+
"""Generate videos from text prompts with Kling models."""
|
|
24
|
+
|
|
25
|
+
ENDPOINT = "/api/v1/kling/text_to_video"
|
|
26
|
+
|
|
27
|
+
RESPONSE_CLASS = TextToVideoResponse
|
|
28
|
+
COMPLETED_RESPONSE_CLASS = CompletedTextToVideoResponse
|
|
29
|
+
|
|
30
|
+
def run(self, **params: Any) -> Any:
|
|
31
|
+
"""Generate a video from text and poll until it completes.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
**params: Text-to-video parameters (model, prompt, ...).
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
The completed task with videos.
|
|
38
|
+
"""
|
|
39
|
+
task = self.create(**params)
|
|
40
|
+
return self._poll_until_complete(lambda: self.get(task.id))
|
|
41
|
+
|
|
42
|
+
def create(self, **params: Any) -> Any:
|
|
43
|
+
"""Create a text-to-video task and return immediately with an ``id``.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
**params: Text-to-video parameters (model, prompt, ...).
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
The task creation result with an id.
|
|
50
|
+
"""
|
|
51
|
+
compacted = self._compact_params(params)
|
|
52
|
+
self._validate_params(compacted)
|
|
53
|
+
return self._request("post", self.ENDPOINT, body=compacted)
|
|
54
|
+
|
|
55
|
+
def get(self, id: str) -> Any:
|
|
56
|
+
"""Fetch the current status of a text-to-video task.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
id: The task id.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
The current task status.
|
|
63
|
+
"""
|
|
64
|
+
return self._request("get", f"{self.ENDPOINT}/{id}")
|
|
65
|
+
|
|
66
|
+
def _validate_params(self, params: Dict[str, Any]) -> None:
|
|
67
|
+
model = params.get("model")
|
|
68
|
+
if not model:
|
|
69
|
+
raise ValidationError("model is required")
|
|
70
|
+
if model not in TEXT_TO_VIDEO_MODELS:
|
|
71
|
+
raise ValidationError(
|
|
72
|
+
f"Invalid model: {model}. Must be one of: {', '.join(TEXT_TO_VIDEO_MODELS)}"
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
multi_shots = params.get("multi_shots") is True
|
|
76
|
+
|
|
77
|
+
if multi_shots:
|
|
78
|
+
if params.get("enable_sound") is not True:
|
|
79
|
+
raise ValidationError("enable_sound must be true when multi_shots is true")
|
|
80
|
+
if params.get("last_frame_image_url"):
|
|
81
|
+
raise ValidationError("last_frame_image_url is not supported when multi_shots is true")
|
|
82
|
+
|
|
83
|
+
self._validate_multi_prompt(params.get("multi_prompt"))
|
|
84
|
+
else:
|
|
85
|
+
if not params.get("prompt"):
|
|
86
|
+
raise ValidationError("prompt is required")
|
|
87
|
+
|
|
88
|
+
self._validate_optional(params, "output_resolution", TEXT_TO_VIDEO_OUTPUT_RESOLUTIONS)
|
|
89
|
+
self._validate_optional(params, "aspect_ratio", ASPECT_RATIOS)
|
|
90
|
+
|
|
91
|
+
duration_seconds = params.get("duration_seconds")
|
|
92
|
+
if duration_seconds is not None:
|
|
93
|
+
try:
|
|
94
|
+
dur_int = int(duration_seconds)
|
|
95
|
+
except (TypeError, ValueError):
|
|
96
|
+
dur_int = None
|
|
97
|
+
if model in ("kling-v2.1-master-text-to-video", "kling-v2.5-turbo-text-to-video-pro"):
|
|
98
|
+
if duration_seconds not in FIXED_DURATIONS:
|
|
99
|
+
raise ValidationError(
|
|
100
|
+
f"Invalid duration_seconds: {duration_seconds}. "
|
|
101
|
+
f"Must be one of: {', '.join(str(d) for d in FIXED_DURATIONS)}"
|
|
102
|
+
)
|
|
103
|
+
elif dur_int is None or dur_int not in DURATION_RANGE:
|
|
104
|
+
raise ValidationError(
|
|
105
|
+
f"Invalid duration_seconds: {duration_seconds}. "
|
|
106
|
+
f"Must be an integer between {DURATION_RANGE.start} and {DURATION_RANGE.stop - 1}"
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
def _validate_multi_prompt(self, multi_prompt: Any) -> None:
|
|
110
|
+
if not (isinstance(multi_prompt, list) and len(multi_prompt) > 0):
|
|
111
|
+
raise ValidationError("multi_prompt must be a non-empty array when multi_shots is true")
|
|
112
|
+
|
|
113
|
+
for index, shot in enumerate(multi_prompt):
|
|
114
|
+
prompt = shot.get("prompt") if isinstance(shot, dict) else None
|
|
115
|
+
duration_seconds = shot.get("duration_seconds") if isinstance(shot, dict) else None
|
|
116
|
+
|
|
117
|
+
if prompt is None or len(prompt) == 0:
|
|
118
|
+
raise ValidationError(f"multi_prompt[{index}].prompt is required")
|
|
119
|
+
|
|
120
|
+
if len(prompt) > MULTI_PROMPT_MAX_LENGTH:
|
|
121
|
+
raise ValidationError(
|
|
122
|
+
f"multi_prompt[{index}].prompt exceeds {MULTI_PROMPT_MAX_LENGTH} characters"
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
if duration_seconds is None:
|
|
126
|
+
raise ValidationError(f"multi_prompt[{index}].duration_seconds is required")
|
|
127
|
+
|
|
128
|
+
try:
|
|
129
|
+
dur_int = int(duration_seconds)
|
|
130
|
+
except (TypeError, ValueError):
|
|
131
|
+
dur_int = None
|
|
132
|
+
if dur_int is None or dur_int not in MULTI_PROMPT_DURATION_RANGE:
|
|
133
|
+
raise ValidationError(
|
|
134
|
+
f"multi_prompt[{index}].duration_seconds must be between "
|
|
135
|
+
f"{MULTI_PROMPT_DURATION_RANGE.start} and {MULTI_PROMPT_DURATION_RANGE.stop - 1}"
|
|
136
|
+
)
|
runapi/kling/types.py
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"""Kling model lists, enums, and response models."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from runapi.core import BaseModel, TaskResponse, optional, required
|
|
6
|
+
|
|
7
|
+
TEXT_TO_VIDEO_MODELS = [
|
|
8
|
+
"kling-3.0",
|
|
9
|
+
"kling-v2.5-turbo-text-to-video-pro",
|
|
10
|
+
"kling-v2.1-master-text-to-video",
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
AI_AVATAR_MODELS = [
|
|
14
|
+
"kling-ai-avatar-pro",
|
|
15
|
+
"kling-ai-avatar-standard",
|
|
16
|
+
"kling-ai-avatar-v1-pro",
|
|
17
|
+
"kling-v1-avatar-standard",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
IMAGE_TO_VIDEO_MODELS = [
|
|
21
|
+
"kling-v2.5-turbo-image-to-video-pro",
|
|
22
|
+
"kling-v2.1-pro",
|
|
23
|
+
"kling-v2.1-standard",
|
|
24
|
+
"kling-v2.1-master-image-to-video",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
TEXT_TO_VIDEO_OUTPUT_RESOLUTIONS = ["720p", "1080p", "4k"]
|
|
28
|
+
|
|
29
|
+
MOTION_CONTROL_MODELS = ["kling-3.0"]
|
|
30
|
+
|
|
31
|
+
MOTION_CONTROL_OUTPUT_RESOLUTIONS = ["720p", "1080p"]
|
|
32
|
+
|
|
33
|
+
MOTION_CONTROL_CHARACTER_ORIENTATIONS = ["video", "image"]
|
|
34
|
+
|
|
35
|
+
MOTION_CONTROL_BACKGROUND_SOURCES = ["video", "image"]
|
|
36
|
+
|
|
37
|
+
ASPECT_RATIOS = ["16:9", "9:16", "1:1"]
|
|
38
|
+
|
|
39
|
+
DURATION_RANGE = range(3, 16)
|
|
40
|
+
|
|
41
|
+
MULTI_PROMPT_DURATION_RANGE = range(1, 13)
|
|
42
|
+
|
|
43
|
+
FIXED_DURATIONS = [5, 10]
|
|
44
|
+
|
|
45
|
+
MULTI_PROMPT_MAX_LENGTH = 500
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class Video(BaseModel):
|
|
49
|
+
url = optional(str)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class AsyncTaskResponse(TaskResponse):
|
|
53
|
+
"""Base response for an asynchronous Kling task."""
|
|
54
|
+
|
|
55
|
+
id = required(str)
|
|
56
|
+
status = optional(str, enum=lambda: TaskResponse.Status.ALL)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class TextToVideoResponse(AsyncTaskResponse):
|
|
60
|
+
"""Response for a text-to-video task."""
|
|
61
|
+
|
|
62
|
+
videos = optional([lambda: Video])
|
|
63
|
+
error = optional(str)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class AiAvatarResponse(AsyncTaskResponse):
|
|
67
|
+
"""Response for an AI avatar task."""
|
|
68
|
+
|
|
69
|
+
videos = optional([lambda: Video])
|
|
70
|
+
error = optional(str)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class ImageToVideoResponse(AsyncTaskResponse):
|
|
74
|
+
"""Response for an image-to-video task."""
|
|
75
|
+
|
|
76
|
+
videos = optional([lambda: Video])
|
|
77
|
+
error = optional(str)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class MotionControlResponse(AsyncTaskResponse):
|
|
81
|
+
"""Response for a motion control task."""
|
|
82
|
+
|
|
83
|
+
videos = optional([lambda: Video])
|
|
84
|
+
error = optional(str)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
# Narrowed responses returned by ``run()`` once polling observes completion.
|
|
88
|
+
# ``videos`` is required so consumers never have to null-check it on success.
|
|
89
|
+
class CompletedTextToVideoResponse(TextToVideoResponse):
|
|
90
|
+
"""Narrowed text-to-video response once polling observes completion."""
|
|
91
|
+
|
|
92
|
+
videos = required([lambda: Video])
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class CompletedAiAvatarResponse(AiAvatarResponse):
|
|
96
|
+
"""Narrowed AI avatar response once polling observes completion."""
|
|
97
|
+
|
|
98
|
+
videos = required([lambda: Video])
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class CompletedImageToVideoResponse(ImageToVideoResponse):
|
|
102
|
+
"""Narrowed image-to-video response once polling observes completion."""
|
|
103
|
+
|
|
104
|
+
videos = required([lambda: Video])
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class CompletedMotionControlResponse(MotionControlResponse):
|
|
108
|
+
"""Narrowed motion control response once polling observes completion."""
|
|
109
|
+
|
|
110
|
+
videos = required([lambda: Video])
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: runapi-kling
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Kling video generation client for RunAPI
|
|
5
|
+
Project-URL: Homepage, https://runapi.ai/models/kling
|
|
6
|
+
Project-URL: Documentation, https://runapi.ai/docs#sdk-kling
|
|
7
|
+
Author-email: RunAPI <contact@runapi.ai>
|
|
8
|
+
License-Expression: Apache-2.0
|
|
9
|
+
Keywords: ai,image-to-video,kling,runapi,sdk,text-to-video
|
|
10
|
+
Requires-Python: >=3.9
|
|
11
|
+
Requires-Dist: runapi-core
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
# Kling Python SDK for RunAPI
|
|
15
|
+
|
|
16
|
+
The Kling Python SDK is the language-specific package for Kling on RunAPI.
|
|
17
|
+
Use it for text-to-video, image-to-video, AI avatar, and motion control flows
|
|
18
|
+
when your application needs JSON request bodies, task status lookup, and
|
|
19
|
+
consistent RunAPI errors in Python.
|
|
20
|
+
|
|
21
|
+
For model details, use https://runapi.ai/models/kling; for API reference, use
|
|
22
|
+
https://runapi.ai/docs#kling; for SDK docs, use https://runapi.ai/docs#sdk-kling.
|
|
23
|
+
|
|
24
|
+
## Install
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pip install runapi-kling
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Quick start
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
from runapi.kling import KlingClient
|
|
34
|
+
|
|
35
|
+
client = KlingClient() # reads RUNAPI_API_KEY, or pass api_key="sk-..."
|
|
36
|
+
|
|
37
|
+
task = client.text_to_video.create(
|
|
38
|
+
model="kling-3.0",
|
|
39
|
+
prompt="A cat walking through a garden",
|
|
40
|
+
)
|
|
41
|
+
status = client.text_to_video.get(task.id)
|
|
42
|
+
|
|
43
|
+
avatar = client.ai_avatar.create(
|
|
44
|
+
model="kling-ai-avatar-pro",
|
|
45
|
+
prompt="A friendly host greeting the audience",
|
|
46
|
+
source_image_url="https://example.com/portrait.jpg",
|
|
47
|
+
source_audio_url="https://example.com/voice.mp3",
|
|
48
|
+
)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Use `create` to submit a task and return quickly, `get` to fetch the latest task
|
|
52
|
+
state, and `run` to create and poll until completion:
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
result = client.text_to_video.run(
|
|
56
|
+
model="kling-3.0",
|
|
57
|
+
prompt="A serene drone shot over a misty forest",
|
|
58
|
+
)
|
|
59
|
+
print(result.videos[0].url)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
In web request handlers, prefer `create` plus webhook or later `get` polling so a
|
|
63
|
+
worker is not held open.
|
|
64
|
+
|
|
65
|
+
RunAPI-generated file URLs are temporary. Download and store generated videos in
|
|
66
|
+
your own durable storage within 7 days; do not treat returned URLs as long-term
|
|
67
|
+
assets.
|
|
68
|
+
|
|
69
|
+
## Language notes
|
|
70
|
+
|
|
71
|
+
Pass parameters as keyword arguments and catch the `runapi.kling` error classes
|
|
72
|
+
when building video jobs or scripts. The available resources are
|
|
73
|
+
`text_to_video`, `ai_avatar`, `image_to_video`, and `motion_control`. Keep
|
|
74
|
+
`RUNAPI_API_KEY` in the environment or your secret manager; never commit API keys
|
|
75
|
+
or callback secrets.
|
|
76
|
+
|
|
77
|
+
## Links
|
|
78
|
+
|
|
79
|
+
- Model page: https://runapi.ai/models/kling
|
|
80
|
+
- SDK docs: https://runapi.ai/docs#sdk-kling
|
|
81
|
+
- Product docs: https://runapi.ai/docs#kling
|
|
82
|
+
- Pricing and rate limits: https://runapi.ai/models/kling
|
|
83
|
+
- Full catalog: https://runapi.ai/models
|
|
84
|
+
|
|
85
|
+
## License
|
|
86
|
+
|
|
87
|
+
Licensed under the Apache License, Version 2.0.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
runapi/kling/__init__.py,sha256=UgYgGJF6XmaT-Uh0stCmoeEFAIK-LRrna78E0Thnl2U,457
|
|
2
|
+
runapi/kling/client.py,sha256=YNVEc2iOzeynzLtS9wl1zqqQLX1hf353Lm2QUF9zJIo,1073
|
|
3
|
+
runapi/kling/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
runapi/kling/types.py,sha256=5PMnLV5UW1VLBkhFbUbDZoyv7ZLyrDkvD8xsK56qAsk,2759
|
|
5
|
+
runapi/kling/resources/__init__.py,sha256=eC8NJ7kgXrhTJq_uds_YiIa5ZtFfHsvVqsoZDjuY7mM,226
|
|
6
|
+
runapi/kling/resources/ai_avatar.py,sha256=5AHkJLKiYiOxS_hYJCptbY682dK7ZlzsBxuM-EC0ro4,2284
|
|
7
|
+
runapi/kling/resources/image_to_video.py,sha256=eI46MRIb6_b0wQ6kDbGaV47jsGwYRgeKjSip60X7JfQ,2994
|
|
8
|
+
runapi/kling/resources/motion_control.py,sha256=D4J7dlGI2_EtdcTB3BZ_uHSCHBKX4yIEhsrNXhtSGog,2701
|
|
9
|
+
runapi/kling/resources/text_to_video.py,sha256=C2mFonAtCobhGb3DUOoytyJlU8fDys2MDAOhYFZ2M_4,5177
|
|
10
|
+
runapi_kling-0.1.0.dist-info/METADATA,sha256=QF1O_MLSvqO5zYCgOGgseGRESSDBMhdqeo3jOWyfN_g,2692
|
|
11
|
+
runapi_kling-0.1.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
|
|
12
|
+
runapi_kling-0.1.0.dist-info/RECORD,,
|