runapi-kling 0.2.8 → 0.2.10

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '06496c392220522b5dedc8acd4f71436ba22fd51df6ac685d8600fc5a5f3d02f'
4
- data.tar.gz: ba507498208a38a168ec79b1e7a1041c95101c5aa510f1ba11cd55a3cd6e4a1a
3
+ metadata.gz: e320ba4bacde45051ec892890b4538a8fe2c415d86facc8c3c950bfeb43bd94e
4
+ data.tar.gz: 4ae9148e0a4b579a47f65c6b02ba40d3cc1c95bec9254fd423bf45b442ea99f3
5
5
  SHA512:
6
- metadata.gz: 361d632584c9a124c7e295bd3ae2f337f0434166ee76e13871afc5e17a96fc11e30f86802daa65724f1c8ee421cc4638ab97329cacce8dff1e14a701f12e0f89
7
- data.tar.gz: 2a4358323094f9d1d2269b857107927042707cb003df73abb7855d3305cf30d1bcf5e09bda2799dec94da92ca85fc72d5eb5ac334e448de688749ac81a16daba
6
+ metadata.gz: c1bc8ee1eb14d11292751fa2c24318335bb07b4a7bfa02b82454c30f9da7889281bf29067d5fc34d7d29c337d12fbadf934924c9a61c7df34b64bfe6ad937939
7
+ data.tar.gz: e64c06c27bebb03e97c45801a268ad752ab63a3b22cd24e85b7d3baf9d8fc4f7b16cb0547336c820c7a5c560a326fbf49614bf6806e6e9d3e9ec4561e486265d
data/README.md CHANGED
@@ -1,8 +1,8 @@
1
- # Kling AI API Ruby SDK for RunAPI
1
+ # Kling API Ruby SDK for RunAPI
2
2
 
3
- The kling ai api Ruby SDK is the language-specific package for Kling on RunAPI. Use this kling ai api package for text-to-video, image-to-video, video editing, and animation flows when your application needs JSON request bodies, task status lookup, and consistent RunAPI errors in Ruby.
3
+ The Kling Ruby SDK is the language-specific package for Kling on RunAPI. Use this package for video generation, animation, and video editing workflows when your application needs request bodies, task status lookup, and consistent RunAPI errors in Ruby.
4
4
 
5
- This kling ai api README is the Ruby package guide inside the public `kling-sdk` repository. For the repository overview, start at `../README.md`; for model details, use https://runapi.ai/models/kling; for API reference, use https://runapi.ai/docs#kling; for SDK docs, use https://runapi.ai/docs#sdk-kling.
5
+ This README is the Ruby package guide inside the public `kling-sdk` repository. For the repository overview, start at `../README.md`; for model details, use https://runapi.ai/models/kling; for API reference, use https://runapi.ai/docs#kling; for SDK docs, use https://runapi.ai/docs#sdk-kling.
6
6
 
7
7
  ## Install
8
8
 
@@ -13,13 +13,13 @@ gem install runapi-kling
13
13
  ## Quick start
14
14
 
15
15
  ```ruby
16
- require "runapi-kling"
16
+ require "runapi/kling"
17
17
 
18
18
  client = RunApi::Kling::Client.new
19
- task = client.generations.create(
19
+ task = client.text_to_video.create(
20
20
  # Pass the Kling JSON request body from https://runapi.ai/docs#kling.
21
21
  )
22
- status = client.generations.get(task.id)
22
+ status = client.text_to_video.get(task.id)
23
23
  ```
24
24
 
25
25
  Use `create` when you want to submit a task and return quickly, `get` when you need the latest task state, and `run` when a script should create and poll until completion. In web request handlers, prefer `create` plus webhook or later `get` polling so a worker is not held open.
@@ -28,7 +28,7 @@ RunAPI-generated file URLs are temporary. Download and store generated images, v
28
28
 
29
29
  ## Language notes
30
30
 
31
- Use Ruby keyword arguments and the `RunApi::Kling` error classes when building video jobs, Rails workers, or scripts. The available resources include text-to-video, image-to-video, AI avatars, V2.1/V2.5 video variants, and motion controls. Keep `RUNAPI_API_KEY` in the environment or your secret manager; never commit API keys or callback secrets.
31
+ Use Ruby keyword arguments and the `RunApi::Kling` error classes when building video jobs, Rails workers, or scripts. The available resources are `text_to_video`, `ai_avatar`, `image_to_video`, and `motion_control`. Keep `RUNAPI_API_KEY` in the environment or your secret manager; never commit API keys or callback secrets.
32
32
 
33
33
  ## Links
34
34
 
@@ -0,0 +1,347 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RunApi
4
+ module Kling
5
+ CONTRACT = {
6
+ "avatar" => {
7
+ "models" => ["kling-ai-avatar-pro", "kling-ai-avatar-standard", "kling-ai-avatar-v1-pro", "kling-v1-avatar-standard"],
8
+ "fields_by_model" => {
9
+ "kling-ai-avatar-pro" => {
10
+ "model" => {
11
+ "required" => true
12
+ },
13
+ "prompt" => {
14
+ "required" => true
15
+ },
16
+ "source_audio_url" => {
17
+ "required" => true
18
+ },
19
+ "source_image_url" => {
20
+ "required" => true
21
+ }
22
+ },
23
+ "kling-ai-avatar-standard" => {
24
+ "model" => {
25
+ "required" => true
26
+ },
27
+ "prompt" => {
28
+ "required" => true
29
+ },
30
+ "source_audio_url" => {
31
+ "required" => true
32
+ },
33
+ "source_image_url" => {
34
+ "required" => true
35
+ }
36
+ },
37
+ "kling-ai-avatar-v1-pro" => {
38
+ "model" => {
39
+ "required" => true
40
+ },
41
+ "prompt" => {
42
+ "required" => true
43
+ },
44
+ "source_audio_url" => {
45
+ "required" => true
46
+ },
47
+ "source_image_url" => {
48
+ "required" => true
49
+ }
50
+ },
51
+ "kling-v1-avatar-standard" => {
52
+ "model" => {
53
+ "required" => true
54
+ },
55
+ "prompt" => {
56
+ "required" => true
57
+ },
58
+ "source_audio_url" => {
59
+ "required" => true
60
+ },
61
+ "source_image_url" => {
62
+ "required" => true
63
+ }
64
+ }
65
+ }
66
+ },
67
+ "image-to-video" => {
68
+ "models" => ["kling-v2.1-master-image-to-video", "kling-v2.1-pro", "kling-v2.1-standard", "kling-v2.5-turbo-image-to-video-pro", "kling-v2.6", "kling-v3-turbo-image-to-video"],
69
+ "fields_by_model" => {
70
+ "kling-v2.1-master-image-to-video" => {
71
+ "duration_seconds" => {
72
+ "enum" => [5, 10],
73
+ "type" => "integer"
74
+ },
75
+ "first_frame_image_url" => {
76
+ "required" => true
77
+ },
78
+ "model" => {
79
+ "required" => true
80
+ },
81
+ "prompt" => {
82
+ "required" => true
83
+ }
84
+ },
85
+ "kling-v2.1-pro" => {
86
+ "duration_seconds" => {
87
+ "enum" => [5, 10],
88
+ "type" => "integer"
89
+ },
90
+ "first_frame_image_url" => {
91
+ "required" => true
92
+ },
93
+ "model" => {
94
+ "required" => true
95
+ },
96
+ "prompt" => {
97
+ "required" => true
98
+ }
99
+ },
100
+ "kling-v2.1-standard" => {
101
+ "duration_seconds" => {
102
+ "enum" => [5, 10],
103
+ "type" => "integer"
104
+ },
105
+ "first_frame_image_url" => {
106
+ "required" => true
107
+ },
108
+ "model" => {
109
+ "required" => true
110
+ },
111
+ "prompt" => {
112
+ "required" => true
113
+ }
114
+ },
115
+ "kling-v2.5-turbo-image-to-video-pro" => {
116
+ "duration_seconds" => {
117
+ "enum" => [5, 10],
118
+ "type" => "integer"
119
+ },
120
+ "first_frame_image_url" => {
121
+ "required" => true
122
+ },
123
+ "model" => {
124
+ "required" => true
125
+ },
126
+ "prompt" => {
127
+ "required" => true
128
+ }
129
+ },
130
+ "kling-v2.6" => {
131
+ "aspect_ratio" => {
132
+ "enum" => ["16:9", "9:16", "1:1"]
133
+ },
134
+ "duration_seconds" => {
135
+ "enum" => [5, 10],
136
+ "type" => "integer"
137
+ },
138
+ "first_frame_image_url" => {
139
+ "required" => true
140
+ },
141
+ "mode" => {
142
+ "enum" => ["std", "pro"]
143
+ },
144
+ "model" => {
145
+ "required" => true
146
+ },
147
+ "prompt" => {
148
+ "required" => true,
149
+ "min" => 1,
150
+ "max" => 2500,
151
+ "length" => true
152
+ }
153
+ },
154
+ "kling-v3-turbo-image-to-video" => {
155
+ "duration_seconds" => {
156
+ "enum" => [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
157
+ "type" => "integer"
158
+ },
159
+ "first_frame_image_url" => {
160
+ "required" => true
161
+ },
162
+ "model" => {
163
+ "required" => true
164
+ },
165
+ "output_resolution" => {
166
+ "enum" => ["720p", "1080p"]
167
+ },
168
+ "prompt" => {
169
+ "required" => true,
170
+ "min" => 1,
171
+ "max" => 2500,
172
+ "length" => true
173
+ }
174
+ }
175
+ },
176
+ "rules" => [{
177
+ "when" => {
178
+ "model" => "kling-v2.1-master-image-to-video"
179
+ },
180
+ "forbidden" => ["output_resolution", "mode", "enable_sound"]
181
+ }, {
182
+ "when" => {
183
+ "model" => "kling-v2.1-pro"
184
+ },
185
+ "forbidden" => ["output_resolution", "mode", "enable_sound"]
186
+ }, {
187
+ "when" => {
188
+ "model" => "kling-v2.1-standard"
189
+ },
190
+ "forbidden" => ["output_resolution", "mode", "enable_sound"]
191
+ }, {
192
+ "when" => {
193
+ "model" => "kling-v2.5-turbo-image-to-video-pro"
194
+ },
195
+ "forbidden" => ["output_resolution", "mode", "enable_sound"]
196
+ }, {
197
+ "when" => {
198
+ "model" => "kling-v2.6"
199
+ },
200
+ "forbidden" => ["output_resolution", "negative_prompt", "cfg_scale"]
201
+ }, {
202
+ "when" => {
203
+ "model" => "kling-v3-turbo-image-to-video"
204
+ },
205
+ "forbidden" => ["mode", "enable_sound", "aspect_ratio", "negative_prompt", "cfg_scale", "last_frame_image_url"]
206
+ }]
207
+ },
208
+ "motion-control" => {
209
+ "models" => ["kling-3.0"],
210
+ "fields_by_model" => {
211
+ "kling-3.0" => {
212
+ "background_source" => {
213
+ "enum" => ["video", "image"]
214
+ },
215
+ "character_orientation" => {
216
+ "enum" => ["video", "image"]
217
+ },
218
+ "model" => {
219
+ "required" => true
220
+ },
221
+ "output_resolution" => {
222
+ "enum" => ["720p", "1080p"]
223
+ },
224
+ "reference_video_url" => {
225
+ "required" => true
226
+ },
227
+ "source_image_url" => {
228
+ "required" => true
229
+ }
230
+ }
231
+ }
232
+ },
233
+ "text-to-video" => {
234
+ "models" => ["kling-3.0", "kling-v2.1-master-text-to-video", "kling-v2.5-turbo-text-to-video-pro", "kling-v2.6", "kling-v3-turbo-text-to-video"],
235
+ "fields_by_model" => {
236
+ "kling-3.0" => {
237
+ "aspect_ratio" => {
238
+ "enum" => ["16:9", "9:16", "1:1"]
239
+ },
240
+ "duration_seconds" => {
241
+ "enum" => [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
242
+ "type" => "integer"
243
+ },
244
+ "model" => {
245
+ "required" => true
246
+ },
247
+ "output_resolution" => {
248
+ "enum" => ["720p", "1080p", "4k"]
249
+ }
250
+ },
251
+ "kling-v2.1-master-text-to-video" => {
252
+ "aspect_ratio" => {
253
+ "enum" => ["16:9", "9:16", "1:1"]
254
+ },
255
+ "duration_seconds" => {
256
+ "enum" => [5, 10],
257
+ "type" => "integer"
258
+ },
259
+ "model" => {
260
+ "required" => true
261
+ }
262
+ },
263
+ "kling-v2.5-turbo-text-to-video-pro" => {
264
+ "aspect_ratio" => {
265
+ "enum" => ["16:9", "9:16", "1:1"]
266
+ },
267
+ "duration_seconds" => {
268
+ "enum" => [5, 10],
269
+ "type" => "integer"
270
+ },
271
+ "model" => {
272
+ "required" => true
273
+ }
274
+ },
275
+ "kling-v2.6" => {
276
+ "aspect_ratio" => {
277
+ "enum" => ["16:9", "9:16", "1:1"]
278
+ },
279
+ "duration_seconds" => {
280
+ "enum" => [5, 10],
281
+ "type" => "integer"
282
+ },
283
+ "mode" => {
284
+ "enum" => ["std", "pro"]
285
+ },
286
+ "model" => {
287
+ "required" => true
288
+ },
289
+ "prompt" => {
290
+ "required" => true,
291
+ "min" => 1,
292
+ "max" => 2500,
293
+ "length" => true
294
+ }
295
+ },
296
+ "kling-v3-turbo-text-to-video" => {
297
+ "aspect_ratio" => {
298
+ "enum" => ["16:9", "9:16", "1:1"]
299
+ },
300
+ "duration_seconds" => {
301
+ "enum" => [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
302
+ "type" => "integer"
303
+ },
304
+ "model" => {
305
+ "required" => true
306
+ },
307
+ "output_resolution" => {
308
+ "enum" => ["720p", "1080p"]
309
+ },
310
+ "prompt" => {
311
+ "required" => true,
312
+ "min" => 1,
313
+ "max" => 2500,
314
+ "length" => true
315
+ }
316
+ }
317
+ },
318
+ "rules" => [{
319
+ "when" => {
320
+ "model" => "kling-3.0"
321
+ },
322
+ "forbidden" => ["mode"]
323
+ }, {
324
+ "when" => {
325
+ "model" => "kling-v2.1-master-text-to-video"
326
+ },
327
+ "forbidden" => ["mode"]
328
+ }, {
329
+ "when" => {
330
+ "model" => "kling-v2.5-turbo-text-to-video-pro"
331
+ },
332
+ "forbidden" => ["mode"]
333
+ }, {
334
+ "when" => {
335
+ "model" => "kling-v2.6"
336
+ },
337
+ "forbidden" => ["output_resolution", "negative_prompt", "cfg_scale", "multi_shots", "multi_prompt", "first_frame_image_url", "last_frame_image_url", "kling_elements"]
338
+ }, {
339
+ "when" => {
340
+ "model" => "kling-v3-turbo-text-to-video"
341
+ },
342
+ "forbidden" => ["mode", "enable_sound", "negative_prompt", "cfg_scale", "multi_shots", "multi_prompt", "first_frame_image_url", "last_frame_image_url", "kling_elements"]
343
+ }]
344
+ }
345
+ }.freeze
346
+ end
347
+ end
@@ -21,41 +21,33 @@ module RunApi
21
21
  #
22
22
  # @param params [Hash] AI avatar parameters
23
23
  # @return [RunApi::Kling::Types::CompletedAiAvatarResponse] completed task with videos
24
- def run(**params)
25
- task = create(**params)
26
- poll_until_complete { get(task.id) }
24
+ def run(options: nil, **params)
25
+ task = create(options: options, **params)
26
+ poll_until_complete { get(task.id, options: options) }
27
27
  end
28
28
 
29
29
  # Create an AI avatar generation task.
30
30
  #
31
31
  # @param params [Hash] AI avatar parameters
32
32
  # @return [RunApi::Kling::Types::AiAvatarResponse] task creation result with id
33
- def create(**params)
33
+ def create(options: nil, **params)
34
34
  params = compact_params(params)
35
35
  validate_params!(params)
36
- request(:post, ENDPOINT, body: params)
36
+ request(:post, ENDPOINT, body: params, options: options)
37
37
  end
38
38
 
39
39
  # Get AI avatar task status by task ID.
40
40
  #
41
41
  # @param id [String] task ID
42
42
  # @return [RunApi::Kling::Types::AiAvatarResponse] current task status
43
- def get(id)
44
- request(:get, "#{ENDPOINT}/#{id}")
43
+ def get(id, options: nil)
44
+ request(:get, "#{ENDPOINT}/#{id}", options: options)
45
45
  end
46
46
 
47
47
  private
48
48
 
49
49
  def validate_params!(params)
50
- model = param(params, :model)
51
- raise Core::ValidationError, "model is required" unless model
52
- unless Types::AI_AVATAR_MODELS.include?(model)
53
- raise Core::ValidationError, "Invalid model: #{model}. Must be one of: #{Types::AI_AVATAR_MODELS.join(", ")}"
54
- end
55
-
56
- raise Core::ValidationError, "source_image_url is required" unless param(params, :source_image_url)
57
- raise Core::ValidationError, "source_audio_url is required" unless param(params, :source_audio_url)
58
- raise Core::ValidationError, "prompt is required" unless param(params, :prompt)
50
+ validate_contract!(CONTRACT["avatar"], params)
59
51
  end
60
52
  end
61
53
  end
@@ -12,6 +12,14 @@ module RunApi
12
12
 
13
13
  RESPONSE_CLASS = Types::ImageToVideoResponse
14
14
  COMPLETED_RESPONSE_CLASS = Types::CompletedImageToVideoResponse
15
+ V26_MODEL = "kling-v2.6"
16
+ V3_TURBO_MODEL = "kling-v3-turbo-image-to-video"
17
+ V3_TURBO_UNSUPPORTED_FIELDS = %i[
18
+ aspect_ratio
19
+ negative_prompt
20
+ cfg_scale
21
+ last_frame_image_url
22
+ ].freeze
15
23
 
16
24
  def initialize(http)
17
25
  @http = http
@@ -21,50 +29,66 @@ module RunApi
21
29
  #
22
30
  # @param params [Hash] image-to-video parameters
23
31
  # @return [RunApi::Kling::Types::CompletedImageToVideoResponse] completed task with videos
24
- def run(**params)
25
- task = create(**params)
26
- poll_until_complete { get(task.id) }
32
+ def run(options: nil, **params)
33
+ task = create(options: options, **params)
34
+ poll_until_complete { get(task.id, options: options) }
27
35
  end
28
36
 
29
37
  # Create an image-to-video task.
30
38
  #
31
39
  # @param params [Hash] image-to-video parameters
32
40
  # @return [RunApi::Kling::Types::ImageToVideoResponse] task creation result with id
33
- def create(**params)
41
+ def create(options: nil, **params)
34
42
  params = compact_params(params)
35
43
  validate_params!(params)
36
- request(:post, ENDPOINT, body: params)
44
+ request(:post, ENDPOINT, body: params, options: options)
37
45
  end
38
46
 
39
47
  # Get image-to-video task status by task ID.
40
48
  #
41
49
  # @param id [String] task ID
42
50
  # @return [RunApi::Kling::Types::ImageToVideoResponse] current task status
43
- def get(id)
44
- request(:get, "#{ENDPOINT}/#{id}")
51
+ def get(id, options: nil)
52
+ request(:get, "#{ENDPOINT}/#{id}", options: options)
45
53
  end
46
54
 
47
55
  private
48
56
 
49
57
  def validate_params!(params)
58
+ reject_unsupported_v3_turbo_fields!(params)
59
+ validate_contract!(CONTRACT["image-to-video"], params)
60
+
61
+ # Bespoke: last_frame_image_url is only allowed for select models
62
+ # (model-gating, not expressible as a contract enum/required rule).
50
63
  model = param(params, :model)
51
- raise Core::ValidationError, "model is required" unless model
52
- unless Types::IMAGE_TO_VIDEO_MODELS.include?(model)
53
- raise Core::ValidationError, "Invalid model: #{model}. Must be one of: #{Types::IMAGE_TO_VIDEO_MODELS.join(", ")}"
64
+ last_frame_image_url = param(params, :last_frame_image_url)
65
+ if model == V26_MODEL
66
+ validate_v26_params!(params, last_frame_image_url)
67
+ elsif last_frame_image_url && !%w[kling-v2.5-turbo-image-to-video-pro kling-v2.1-pro].include?(model)
68
+ raise Core::ValidationError, "last_frame_image_url is only supported by kling-v2.5-turbo-image-to-video-pro and kling-v2.1-pro"
54
69
  end
70
+ end
55
71
 
56
- raise Core::ValidationError, "prompt is required" unless param(params, :prompt)
57
- raise Core::ValidationError, "first_frame_image_url is required" unless param(params, :first_frame_image_url)
58
-
59
- duration_seconds = param(params, :duration_seconds)
60
- if duration_seconds && !Types::FIXED_DURATIONS.include?(duration_seconds)
61
- raise Core::ValidationError, "Invalid duration_seconds: #{duration_seconds}. Must be one of: #{Types::FIXED_DURATIONS.join(", ")}"
72
+ def validate_v26_params!(params, last_frame_image_url)
73
+ mode = param(params, :mode) || "std"
74
+ if param(params, :enable_sound) == true && mode != "pro"
75
+ raise Core::ValidationError, "enable_sound requires mode pro for #{V26_MODEL}"
62
76
  end
77
+ return unless last_frame_image_url
63
78
 
64
- last_frame_image_url = param(params, :last_frame_image_url)
65
- if last_frame_image_url && !%w[kling-v2.5-turbo-image-to-video-pro kling-v2.1-pro].include?(model)
66
- raise Core::ValidationError, "last_frame_image_url is only supported by kling-v2.5-turbo-image-to-video-pro and kling-v2.1-pro"
67
- end
79
+ raise Core::ValidationError, "last_frame_image_url requires mode pro for #{V26_MODEL}" unless mode == "pro"
80
+
81
+ duration_seconds = param(params, :duration_seconds) || 5
82
+ return if duration_seconds.to_i == 5
83
+
84
+ raise Core::ValidationError, "last_frame_image_url requires duration_seconds 5 for #{V26_MODEL}"
85
+ end
86
+
87
+ def reject_unsupported_v3_turbo_fields!(params)
88
+ return unless param(params, :model) == V3_TURBO_MODEL
89
+
90
+ field = V3_TURBO_UNSUPPORTED_FIELDS.find { |candidate| field_present?(params, candidate) }
91
+ raise Core::ValidationError, "#{field} is not supported by #{V3_TURBO_MODEL}" if field
68
92
  end
69
93
  end
70
94
  end
@@ -21,46 +21,33 @@ module RunApi
21
21
  #
22
22
  # @param params [Hash] motion control parameters
23
23
  # @return [RunApi::Kling::Types::CompletedMotionControlResponse] completed task with videos
24
- def run(**params)
25
- task = create(**params)
26
- poll_until_complete { get(task.id) }
24
+ def run(options: nil, **params)
25
+ task = create(options: options, **params)
26
+ poll_until_complete { get(task.id, options: options) }
27
27
  end
28
28
 
29
29
  # Create a motion control generation task.
30
30
  #
31
31
  # @param params [Hash] motion control parameters
32
32
  # @return [RunApi::Kling::Types::MotionControlResponse] task creation result with id
33
- def create(**params)
33
+ def create(options: nil, **params)
34
34
  params = compact_params(params)
35
35
  validate_params!(params)
36
- request(:post, ENDPOINT, body: params)
36
+ request(:post, ENDPOINT, body: params, options: options)
37
37
  end
38
38
 
39
39
  # Get motion control task status by task ID.
40
40
  #
41
41
  # @param id [String] task ID
42
42
  # @return [RunApi::Kling::Types::MotionControlResponse] current task status
43
- def get(id)
44
- request(:get, "#{ENDPOINT}/#{id}")
43
+ def get(id, options: nil)
44
+ request(:get, "#{ENDPOINT}/#{id}", options: options)
45
45
  end
46
46
 
47
47
  private
48
48
 
49
49
  def validate_params!(params)
50
- model = param(params, :model)
51
- raise Core::ValidationError, "model is required" unless model
52
- unless Types::MOTION_CONTROL_MODELS.include?(model)
53
- raise Core::ValidationError, "Invalid model: #{model}. Must be one of: #{Types::MOTION_CONTROL_MODELS.join(", ")}"
54
- end
55
- validate_optional!(params, :output_resolution, Types::MOTION_CONTROL_OUTPUT_RESOLUTIONS)
56
- validate_optional!(params, :character_orientation, Types::MOTION_CONTROL_CHARACTER_ORIENTATIONS)
57
- validate_optional!(params, :background_source, Types::MOTION_CONTROL_BACKGROUND_SOURCES)
58
-
59
- source_image_url = param(params, :source_image_url)
60
- raise Core::ValidationError, "source_image_url is required" unless source_image_url
61
-
62
- reference_video_url = param(params, :reference_video_url)
63
- raise Core::ValidationError, "reference_video_url is required" unless reference_video_url
50
+ validate_contract!(CONTRACT["motion-control"], params)
64
51
  end
65
52
  end
66
53
  end
@@ -12,6 +12,18 @@ module RunApi
12
12
 
13
13
  RESPONSE_CLASS = Types::TextToVideoResponse
14
14
  COMPLETED_RESPONSE_CLASS = Types::CompletedTextToVideoResponse
15
+ V26_MODEL = "kling-v2.6"
16
+ V3_TURBO_MODEL = "kling-v3-turbo-text-to-video"
17
+ V3_TURBO_UNSUPPORTED_FIELDS = %i[
18
+ enable_sound
19
+ negative_prompt
20
+ cfg_scale
21
+ multi_shots
22
+ multi_prompt
23
+ first_frame_image_url
24
+ last_frame_image_url
25
+ kling_elements
26
+ ].freeze
15
27
 
16
28
  def initialize(http)
17
29
  @http = http
@@ -21,38 +33,37 @@ module RunApi
21
33
  #
22
34
  # @param params [Hash] text-to-video parameters
23
35
  # @return [RunApi::Kling::Types::CompletedTextToVideoResponse] completed task with videos
24
- def run(**params)
25
- task = create(**params)
26
- poll_until_complete { get(task.id) }
36
+ def run(options: nil, **params)
37
+ task = create(options: options, **params)
38
+ poll_until_complete { get(task.id, options: options) }
27
39
  end
28
40
 
29
41
  # Create a text-to-video task.
30
42
  #
31
43
  # @param params [Hash] text-to-video parameters
32
44
  # @return [RunApi::Kling::Types::TextToVideoResponse] task creation result with id
33
- def create(**params)
45
+ def create(options: nil, **params)
34
46
  params = compact_params(params)
35
47
  validate_params!(params)
36
- request(:post, ENDPOINT, body: params)
48
+ request(:post, ENDPOINT, body: params, options: options)
37
49
  end
38
50
 
39
51
  # Get text-to-video task status by task ID.
40
52
  #
41
53
  # @param id [String] task ID
42
54
  # @return [RunApi::Kling::Types::TextToVideoResponse] current task status
43
- def get(id)
44
- request(:get, "#{ENDPOINT}/#{id}")
55
+ def get(id, options: nil)
56
+ request(:get, "#{ENDPOINT}/#{id}", options: options)
45
57
  end
46
58
 
47
59
  private
48
60
 
49
61
  def validate_params!(params)
50
- model = param(params, :model)
51
- raise Core::ValidationError, "model is required" unless model
52
- unless Types::TEXT_TO_VIDEO_MODELS.include?(model)
53
- raise Core::ValidationError, "Invalid model: #{model}. Must be one of: #{Types::TEXT_TO_VIDEO_MODELS.join(", ")}"
54
- end
62
+ reject_unsupported_v3_turbo_fields!(params)
63
+ validate_contract!(CONTRACT["text-to-video"], params)
64
+ validate_v26_params!(params)
55
65
 
66
+ # Bespoke cross-field rules the contract cannot express.
56
67
  multi_shots = param(params, :multi_shots) == true
57
68
 
58
69
  if multi_shots
@@ -64,21 +75,20 @@ module RunApi
64
75
  else
65
76
  raise Core::ValidationError, "prompt is required" unless param(params, :prompt)
66
77
  end
78
+ end
67
79
 
68
- validate_optional!(params, :output_resolution, Types::TEXT_TO_VIDEO_OUTPUT_RESOLUTIONS)
69
- validate_optional!(params, :aspect_ratio, Types::ASPECT_RATIOS)
80
+ def validate_v26_params!(params)
81
+ return unless param(params, :model) == V26_MODEL
82
+ return unless param(params, :enable_sound) == true && param(params, :mode) != "pro"
70
83
 
71
- duration_seconds = param(params, :duration_seconds)
72
- if duration_seconds
73
- dur_int = duration_seconds.to_i
74
- if model == "kling-v2.1-master-text-to-video" || model == "kling-v2.5-turbo-text-to-video-pro"
75
- unless Types::FIXED_DURATIONS.include?(duration_seconds)
76
- raise Core::ValidationError, "Invalid duration_seconds: #{duration_seconds}. Must be one of: #{Types::FIXED_DURATIONS.join(", ")}"
77
- end
78
- elsif !Types::DURATION_RANGE.cover?(dur_int)
79
- raise Core::ValidationError, "Invalid duration_seconds: #{duration_seconds}. Must be an integer between #{Types::DURATION_RANGE.min} and #{Types::DURATION_RANGE.max}"
80
- end
81
- end
84
+ raise Core::ValidationError, "enable_sound requires mode pro for #{V26_MODEL}"
85
+ end
86
+
87
+ def reject_unsupported_v3_turbo_fields!(params)
88
+ return unless param(params, :model) == V3_TURBO_MODEL
89
+
90
+ field = V3_TURBO_UNSUPPORTED_FIELDS.find { |candidate| field_present?(params, candidate) }
91
+ raise Core::ValidationError, "#{field} is not supported by #{V3_TURBO_MODEL}" if field
82
92
  end
83
93
 
84
94
  def validate_multi_prompt!(multi_prompt)
@@ -4,54 +4,10 @@ module RunApi
4
4
  module Kling
5
5
  # Type definitions and constants for Kling video generation.
6
6
  module Types
7
- # Text-to-video model variants. kling-3.0 supports multi-shot, sound, and elements;
8
- # V2.x models support negative prompts and cfg_scale.
9
- TEXT_TO_VIDEO_MODELS = %w[
10
- kling-3.0
11
- kling-v2.5-turbo-text-to-video-pro
12
- kling-v2.1-master-text-to-video
13
- ].freeze
14
-
15
- # AI avatar lip-sync quality tiers, from highest to fastest.
16
- AI_AVATAR_MODELS = %w[
17
- kling-ai-avatar-pro
18
- kling-ai-avatar-standard
19
- kling-ai-avatar-v1-pro
20
- kling-v1-avatar-standard
21
- ].freeze
22
-
23
- # Image-to-video model variants. V2.5 turbo and V2.1 pro support last-frame image control.
24
- IMAGE_TO_VIDEO_MODELS = %w[
25
- kling-v2.5-turbo-image-to-video-pro
26
- kling-v2.1-pro
27
- kling-v2.1-standard
28
- kling-v2.1-master-image-to-video
29
- ].freeze
30
-
31
- # Output resolution options. 4k is highest quality but slowest.
32
- TEXT_TO_VIDEO_OUTPUT_RESOLUTIONS = %w[720p 1080p 4k].freeze
33
-
34
- MOTION_CONTROL_MODELS = %w[kling-3.0].freeze
35
-
36
- MOTION_CONTROL_OUTPUT_RESOLUTIONS = %w[720p 1080p].freeze
37
-
38
- # Whether the character faces the direction from the video or the image.
39
- MOTION_CONTROL_CHARACTER_ORIENTATIONS = %w[video image].freeze
40
-
41
- # Whether the background comes from the reference video or the subject image.
42
- MOTION_CONTROL_BACKGROUND_SOURCES = %w[video image].freeze
43
-
44
- ASPECT_RATIOS = %w[16:9 9:16 1:1].freeze
45
-
46
- # Duration range for kling-3.0 (seconds).
47
- DURATION_RANGE = (3..15)
48
-
49
- # Per-shot duration range in multi-shot mode (seconds).
7
+ # Per-shot duration range in multi-shot mode (seconds). Bespoke constant for
8
+ # the multi_prompt[] nested-array validation, which the contract cannot express.
50
9
  MULTI_PROMPT_DURATION_RANGE = (1..12)
51
10
 
52
- # Fixed duration options for V2.x models (seconds).
53
- FIXED_DURATIONS = [5, 10].freeze
54
-
55
11
  # Maximum character length for each multi-shot prompt segment.
56
12
  MULTI_PROMPT_MAX_LENGTH = 500
57
13
 
data/lib/runapi/kling.rb CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "runapi/core"
4
4
  require_relative "kling/types"
5
+ require_relative "kling/contract_gen"
5
6
  require_relative "kling/resources/text_to_video"
6
7
  require_relative "kling/resources/ai_avatar"
7
8
  require_relative "kling/resources/image_to_video"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: runapi-kling
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.8
4
+ version: 0.2.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - RunAPI
@@ -15,18 +15,18 @@ dependencies:
15
15
  requirements:
16
16
  - - "~>"
17
17
  - !ruby/object:Gem::Version
18
- version: 0.2.6
18
+ version: 0.2.14
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
- version: 0.2.6
26
- description: The kling ai api Ruby SDK is the language-specific package for Kling
27
- on RunAPI. Use this kling ai api package for text-to-video, image-to-video, video
28
- editing, and animation flows when your application needs JSON request bodies, task
29
- status lookup, and consistent RunAPI errors in Ruby.
25
+ version: 0.2.14
26
+ description: The Kling Ruby SDK is the language-specific package for Kling on RunAPI.
27
+ Use this package for video generation, animation, and video editing workflows when
28
+ your application needs request bodies, task status lookup, and consistent RunAPI
29
+ errors in Ruby.
30
30
  email:
31
31
  - contact@runapi.ai
32
32
  executables: []
@@ -39,6 +39,7 @@ files:
39
39
  - lib/runapi-kling.rb
40
40
  - lib/runapi/kling.rb
41
41
  - lib/runapi/kling/client.rb
42
+ - lib/runapi/kling/contract_gen.rb
42
43
  - lib/runapi/kling/resources/ai_avatar.rb
43
44
  - lib/runapi/kling/resources/image_to_video.rb
44
45
  - lib/runapi/kling/resources/motion_control.rb
@@ -48,9 +49,11 @@ homepage: https://runapi.ai/models/kling
48
49
  licenses:
49
50
  - Apache-2.0
50
51
  metadata:
52
+ runapi_slug: kling
51
53
  homepage_uri: https://runapi.ai/models/kling
52
54
  documentation_uri: https://github.com/runapi-ai/kling-sdk/blob/main/ruby/README.md
53
55
  source_code_uri: https://github.com/runapi-ai/kling-sdk
56
+ bug_tracker_uri: https://github.com/runapi-ai/kling-sdk/issues
54
57
  changelog_uri: https://github.com/runapi-ai/kling-sdk/blob/main/CHANGELOG.md
55
58
  rdoc_options: []
56
59
  require_paths:
@@ -68,5 +71,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
68
71
  requirements: []
69
72
  rubygems_version: 4.0.10
70
73
  specification_version: 4
71
- summary: Kling AI API Ruby SDK for RunAPI
74
+ summary: Kling API Ruby SDK for RunAPI
72
75
  test_files: []