runapi-kling 0.3.4__tar.gz → 0.4.0__tar.gz

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.
@@ -1,18 +1,18 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: runapi-kling
3
- Version: 0.3.4
3
+ Version: 0.4.0
4
4
  Summary: RunAPI Kling SDK for text-to-video, image-to-video, source-video editing, avatar, and motion-control workflows in JavaScript, Python, Ruby, Go, Java, and PHP
5
5
  Project-URL: Homepage, https://runapi.ai/models/kling
6
6
  Project-URL: Documentation, https://runapi.ai/docs/resources/sdks
7
7
  Project-URL: Source, https://github.com/runapi-ai/kling-sdk
8
8
  Project-URL: Issues, https://github.com/runapi-ai/kling-sdk/issues
9
9
  Project-URL: Changelog, https://github.com/runapi-ai/kling-sdk/blob/main/CHANGELOG.md
10
- Project-URL: Release Notes, https://github.com/runapi-ai/kling-sdk/releases/tag/python%2Fv0.3.4
10
+ Project-URL: Release Notes, https://github.com/runapi-ai/kling-sdk/releases/tag/python%2Fv0.4.0
11
11
  Author-email: RunAPI <contact@runapi.ai>
12
12
  License-Expression: Apache-2.0
13
13
  Keywords: api,golang,gradle,java,kling,kling-ai-api,maven,python,ruby,runapi,runapi-ai,sdk,text-to-video,typescript,video-api,video-generation
14
14
  Requires-Python: >=3.9
15
- Requires-Dist: runapi-core>=0.8.2
15
+ Requires-Dist: runapi-core>=0.9.1
16
16
  Description-Content-Type: text/markdown
17
17
 
18
18
  # Kling API Python SDK for RunAPI
@@ -4,14 +4,14 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "runapi-kling"
7
- version = "0.3.4"
7
+ version = "0.4.0"
8
8
  description = "RunAPI Kling SDK for text-to-video, image-to-video, source-video editing, avatar, and motion-control workflows in JavaScript, Python, Ruby, Go, Java, and PHP"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
11
11
  license = "Apache-2.0"
12
12
  authors = [{ name = "RunAPI", email = "contact@runapi.ai" }]
13
13
  keywords = ["runapi", "runapi-ai", "kling", "api", "sdk", "typescript", "python", "ruby", "golang", "java", "maven", "gradle", "video-generation", "text-to-video", "video-api", "kling-ai-api"]
14
- dependencies = ["runapi-core>=0.8.2"]
14
+ dependencies = ["runapi-core>=0.9.1"]
15
15
 
16
16
  [project.urls]
17
17
  Homepage = "https://runapi.ai/models/kling"
@@ -19,7 +19,7 @@ Documentation = "https://runapi.ai/docs/resources/sdks"
19
19
  Source = "https://github.com/runapi-ai/kling-sdk"
20
20
  Issues = "https://github.com/runapi-ai/kling-sdk/issues"
21
21
  Changelog = "https://github.com/runapi-ai/kling-sdk/blob/main/CHANGELOG.md"
22
- "Release Notes" = "https://github.com/runapi-ai/kling-sdk/releases/tag/python%2Fv0.3.4"
22
+ "Release Notes" = "https://github.com/runapi-ai/kling-sdk/releases/tag/python%2Fv0.4.0"
23
23
 
24
24
  [tool.hatch.build.targets.wheel]
25
25
  packages = ["src/runapi"]
@@ -500,11 +500,29 @@ CONTRACT = {
500
500
  "model": "kling-v2.5-turbo-image-to-video-pro"
501
501
  },
502
502
  "forbidden": ["output_resolution", "enable_sound", "reference_image_urls", "reference_video_url", "reference_video_type", "preserve_reference_video_audio"]
503
+ }, {
504
+ "enum": {
505
+ "enable_sound": [False]
506
+ },
507
+ "when": {
508
+ "mode": "std",
509
+ "model": "kling-v2.6"
510
+ }
503
511
  }, {
504
512
  "when": {
505
513
  "model": "kling-v2.6"
506
514
  },
507
515
  "forbidden": ["output_resolution", "negative_prompt", "cfg_scale", "reference_image_urls", "reference_video_url", "reference_video_type", "preserve_reference_video_audio"]
516
+ }, {
517
+ "enum": {
518
+ "enable_sound": [False]
519
+ },
520
+ "when": {
521
+ "mode": {
522
+ "present": False
523
+ },
524
+ "model": "kling-v2.6"
525
+ }
508
526
  }, {
509
527
  "when": {
510
528
  "model": "kling-v3-omni"
@@ -531,7 +549,8 @@ CONTRACT = {
531
549
  "required": True
532
550
  },
533
551
  "output_resolution": {
534
- "enum": ["720p", "1080p"]
552
+ "enum": ["720p", "1080p"],
553
+ "required": True
535
554
  },
536
555
  "reference_video_url": {
537
556
  "required": True
@@ -757,6 +776,24 @@ CONTRACT = {
757
776
  "model": "kling-v2.6"
758
777
  },
759
778
  "forbidden": ["output_resolution", "negative_prompt", "cfg_scale", "multi_shots", "multi_prompt", "first_frame_image_url", "last_frame_image_url", "kling_elements", "reference_image_urls", "reference_video_url", "reference_video_type", "preserve_reference_video_audio"]
779
+ }, {
780
+ "enum": {
781
+ "enable_sound": [False]
782
+ },
783
+ "when": {
784
+ "mode": "std",
785
+ "model": "kling-v2.6"
786
+ }
787
+ }, {
788
+ "enum": {
789
+ "enable_sound": [False]
790
+ },
791
+ "when": {
792
+ "mode": {
793
+ "present": False
794
+ },
795
+ "model": "kling-v2.6"
796
+ }
760
797
  }, {
761
798
  "when": {
762
799
  "model": "kling-v3-omni"
@@ -96,12 +96,9 @@ class ImageToVideo(Resource):
96
96
  def _validate_v26_params(
97
97
  self, params: Dict[str, Any], last_frame_image_url: Any
98
98
  ) -> None:
99
- mode = params.get("mode") or "std"
100
- if params.get("enable_sound") is True and mode != "pro":
101
- raise ValidationError(f"enable_sound requires mode pro for {V26_MODEL}")
102
99
  if not last_frame_image_url:
103
100
  return
104
- if mode != "pro":
101
+ if (params.get("mode") or "std") != "pro":
105
102
  raise ValidationError(f"last_frame_image_url requires mode pro for {V26_MODEL}")
106
103
  if params.get("duration_seconds", 5) != 5:
107
104
  raise ValidationError(
@@ -15,7 +15,6 @@ from ..types import (
15
15
  )
16
16
  from .o1_reference_validation import validate_kling_o1_references
17
17
 
18
- V26_MODEL = "kling-v2.6"
19
18
  V3_TURBO_MODEL = "kling-v3-turbo-text-to-video"
20
19
  V3_TURBO_UNSUPPORTED_FIELDS = (
21
20
  "enable_sound",
@@ -76,7 +75,6 @@ class TextToVideo(Resource):
76
75
  def _validate_params(self, params: Dict[str, Any]) -> None:
77
76
  self._reject_unsupported_v3_turbo_fields(params)
78
77
  self._validate_contract(CONTRACT["text-to-video"], params)
79
- self._validate_v26_params(params)
80
78
  validate_kling_o1_references(params)
81
79
 
82
80
  # Bespoke cross-field rules the contract cannot express.
@@ -93,14 +91,6 @@ class TextToVideo(Resource):
93
91
  if not params.get("prompt"):
94
92
  raise ValidationError("prompt is required")
95
93
 
96
- def _validate_v26_params(self, params: Dict[str, Any]) -> None:
97
- if (
98
- params.get("model") == V26_MODEL
99
- and params.get("enable_sound") is True
100
- and params.get("mode") != "pro"
101
- ):
102
- raise ValidationError(f"enable_sound requires mode pro for {V26_MODEL}")
103
-
104
94
  def _reject_unsupported_v3_turbo_fields(self, params: Dict[str, Any]) -> None:
105
95
  if params.get("model") != V3_TURBO_MODEL:
106
96
  return
@@ -97,8 +97,7 @@ def test_text_to_video_create_posts_compacted_body():
97
97
  "post",
98
98
  "/api/v1/kling/text_to_video",
99
99
  {"model": "kling-3.0", "prompt": "a cat in a garden", "aspect_ratio": "16:9"},
100
- ),
101
- ]
100
+ )]
102
101
  assert isinstance(result, TextToVideoResponse)
103
102
 
104
103
 
@@ -114,18 +113,14 @@ def test_text_to_video_create_posts_element_audio_and_time_fields():
114
113
  "description": "dog",
115
114
  "element_input_urls": [
116
115
  "https://upload.wikimedia.org/wikipedia/commons/6/6e/Golde33443.jpg",
117
- "https://upload.wikimedia.org/wikipedia/commons/9/9a/Pug_600.jpg",
118
- ],
119
- "element_input_audio_urls": ["https://cdn.runapi.ai/public/samples/music.mp3"],
120
- },
116
+ "https://upload.wikimedia.org/wikipedia/commons/9/9a/Pug_600.jpg"],
117
+ "element_input_audio_urls": ["https://cdn.runapi.ai/public/samples/music.mp3"]},
121
118
  {
122
119
  "name": "element_run",
123
120
  "description": "running dog",
124
121
  "element_input_urls": ["https://cdn.runapi.ai/public/samples/video.mp4"],
125
122
  "start_time": 1000,
126
- "end_time": 6000,
127
- },
128
- ],
123
+ "end_time": 6000}],
129
124
  )
130
125
  assert fake.calls == [
131
126
  (
@@ -140,12 +135,10 @@ def test_text_to_video_create_posts_element_audio_and_time_fields():
140
135
  "description": "dog",
141
136
  "element_input_urls": [
142
137
  "https://upload.wikimedia.org/wikipedia/commons/6/6e/Golde33443.jpg",
143
- "https://upload.wikimedia.org/wikipedia/commons/9/9a/Pug_600.jpg",
144
- ],
138
+ "https://upload.wikimedia.org/wikipedia/commons/9/9a/Pug_600.jpg"],
145
139
  "element_input_audio_urls": [
146
140
  "https://cdn.runapi.ai/public/samples/music.mp3"
147
- ],
148
- },
141
+ ]},
149
142
  {
150
143
  "name": "element_run",
151
144
  "description": "running dog",
@@ -153,12 +146,8 @@ def test_text_to_video_create_posts_element_audio_and_time_fields():
153
146
  "https://cdn.runapi.ai/public/samples/video.mp4"
154
147
  ],
155
148
  "start_time": 1000,
156
- "end_time": 6000,
157
- },
158
- ],
159
- },
160
- ),
161
- ]
149
+ "end_time": 6000}]},
150
+ )]
162
151
  assert isinstance(result, TextToVideoResponse)
163
152
 
164
153
 
@@ -172,7 +161,7 @@ def test_text_to_video_get_fetches_by_id():
172
161
  def test_text_to_video_run_narrows_completed_type():
173
162
  fake = FakeHttp(
174
163
  {"id": "t1", "status": "pending"},
175
- {"id": "t1", "status": "completed", "videos": [{"url": "https://x/y.mp4"}]},
164
+ {"id": "t1", "status": "completed", "usage": {"cost": 0.05}, "videos": [{"url": "https://x/y.mp4"}]},
176
165
  )
177
166
  client = KlingClient(api_key="k", http_client=fake)
178
167
  result = client.text_to_video.run(model="kling-3.0", prompt="a serene forest")
@@ -322,10 +311,8 @@ def test_text_to_video_v3_turbo_posts_body():
322
311
  "prompt": "a silver train crossing a moonlit bridge",
323
312
  "duration_seconds": 7,
324
313
  "aspect_ratio": "16:9",
325
- "output_resolution": "1080p",
326
- },
327
- ),
328
- ]
314
+ "output_resolution": "1080p"},
315
+ )]
329
316
 
330
317
 
331
318
  def test_text_to_video_v3_turbo_rejects_unsupported_fields():
@@ -365,8 +352,7 @@ def test_text_to_video_v26_posts_mode_and_sound_fields():
365
352
  "mode": "pro",
366
353
  "duration_seconds": 10,
367
354
  "enable_sound": True,
368
- "aspect_ratio": "16:9",
369
- },
355
+ "aspect_ratio": "16:9"},
370
356
  )
371
357
  ]
372
358
 
@@ -394,8 +380,7 @@ def test_text_to_video_v3_omni_posts_resolution_and_sound_fields():
394
380
  "output_resolution": "1080p",
395
381
  "duration_seconds": 10,
396
382
  "enable_sound": True,
397
- "aspect_ratio": "16:9",
398
- },
383
+ "aspect_ratio": "16:9"},
399
384
  )
400
385
  ]
401
386
 
@@ -405,11 +390,18 @@ def test_text_to_video_v26_rejects_sound_outside_pro_mode():
405
390
  client = KlingClient(api_key="k", http_client=fake)
406
391
 
407
392
  with pytest.raises(
408
- ValidationError, match="enable_sound requires mode pro for kling-v2.6"
393
+ ValidationError, match="enable_sound must be one of: false when .*model is kling-v2.6"
409
394
  ):
410
395
  client.text_to_video.create(
411
396
  model="kling-v2.6", prompt="test", enable_sound=True
412
397
  )
398
+ with pytest.raises(
399
+ ValidationError,
400
+ match="enable_sound must be one of: false when mode is std and model is kling-v2.6",
401
+ ):
402
+ client.text_to_video.create(
403
+ model="kling-v2.6", prompt="test", mode="std", enable_sound=True
404
+ )
413
405
  assert fake.calls == []
414
406
 
415
407
 
@@ -440,8 +432,7 @@ def test_text_to_video_o1_posts_reference_fields():
440
432
  "reference_video_url": "https://cdn.runapi.ai/public/samples/video.mp4",
441
433
  "reference_video_type": "feature",
442
434
  "preserve_reference_video_audio": True,
443
- "duration_seconds": 5,
444
- },
435
+ "duration_seconds": 5},
445
436
  )
446
437
  ]
447
438
 
@@ -484,10 +475,8 @@ def test_ai_avatar_create_posts_body():
484
475
  "model": "kling-ai-avatar-pro",
485
476
  "prompt": "a host greeting",
486
477
  "source_image_url": "https://x/p.jpg",
487
- "source_audio_url": "https://x/a.mp3",
488
- },
489
- ),
490
- ]
478
+ "source_audio_url": "https://x/a.mp3"},
479
+ )]
491
480
  assert isinstance(result, AiAvatarResponse)
492
481
 
493
482
 
@@ -501,7 +490,7 @@ def test_ai_avatar_get_fetches_by_id():
501
490
  def test_ai_avatar_run_narrows_completed_type():
502
491
  fake = FakeHttp(
503
492
  {"id": "t1", "status": "pending"},
504
- {"id": "t1", "status": "completed", "videos": [{"url": "https://x/a.mp4"}]},
493
+ {"id": "t1", "status": "completed", "usage": {"cost": 0.05}, "videos": [{"url": "https://x/a.mp4"}]},
505
494
  )
506
495
  client = KlingClient(api_key="k", http_client=fake)
507
496
  result = client.ai_avatar.run(
@@ -568,10 +557,8 @@ def test_image_to_video_create_posts_body():
568
557
  {
569
558
  "model": "kling-v2.1-pro",
570
559
  "prompt": "zoom out slowly",
571
- "first_frame_image_url": "https://x/f.jpg",
572
- },
573
- ),
574
- ]
560
+ "first_frame_image_url": "https://x/f.jpg"},
561
+ )]
575
562
  assert isinstance(result, ImageToVideoResponse)
576
563
 
577
564
 
@@ -585,7 +572,7 @@ def test_image_to_video_get_fetches_by_id():
585
572
  def test_image_to_video_run_narrows_completed_type():
586
573
  fake = FakeHttp(
587
574
  {"id": "t1", "status": "pending"},
588
- {"id": "t1", "status": "completed", "videos": [{"url": "https://x/i.mp4"}]},
575
+ {"id": "t1", "status": "completed", "usage": {"cost": 0.05}, "videos": [{"url": "https://x/i.mp4"}]},
589
576
  )
590
577
  client = KlingClient(api_key="k", http_client=fake)
591
578
  result = client.image_to_video.run(
@@ -671,10 +658,8 @@ def test_image_to_video_v3_turbo_posts_body():
671
658
  "prompt": "camera glides toward the lighthouse",
672
659
  "first_frame_image_url": "https://x/lighthouse.jpg",
673
660
  "duration_seconds": 7,
674
- "output_resolution": "720p",
675
- },
676
- ),
677
- ]
661
+ "output_resolution": "720p"},
662
+ )]
678
663
 
679
664
 
680
665
  def test_image_to_video_v3_turbo_rejects_unsupported_fields():
@@ -719,8 +704,7 @@ def test_image_to_video_v26_posts_mode_sound_and_final_frame_fields():
719
704
  "mode": "pro",
720
705
  "duration_seconds": 5,
721
706
  "enable_sound": True,
722
- "aspect_ratio": "16:9",
723
- },
707
+ "aspect_ratio": "16:9"},
724
708
  )
725
709
  ]
726
710
 
@@ -752,8 +736,7 @@ def test_image_to_video_v3_omni_posts_resolution_sound_and_final_frame_fields():
752
736
  "output_resolution": "4k",
753
737
  "duration_seconds": 5,
754
738
  "enable_sound": False,
755
- "aspect_ratio": "9:16",
756
- },
739
+ "aspect_ratio": "9:16"},
757
740
  )
758
741
  ]
759
742
 
@@ -762,12 +745,23 @@ def test_image_to_video_v26_rejects_sound_outside_pro_mode():
762
745
  client = KlingClient(api_key="k", http_client=FakeHttp())
763
746
 
764
747
  with pytest.raises(
765
- ValidationError, match="enable_sound requires mode pro for kling-v2.6"
748
+ ValidationError, match="enable_sound must be one of: false when .*model is kling-v2.6"
749
+ ):
750
+ client.image_to_video.create(
751
+ model="kling-v2.6",
752
+ prompt="test",
753
+ first_frame_image_url="https://x/first.jpg",
754
+ enable_sound=True,
755
+ )
756
+ with pytest.raises(
757
+ ValidationError,
758
+ match="enable_sound must be one of: false when mode is std and model is kling-v2.6",
766
759
  ):
767
760
  client.image_to_video.create(
768
761
  model="kling-v2.6",
769
762
  prompt="test",
770
763
  first_frame_image_url="https://x/first.jpg",
764
+ mode="std",
771
765
  enable_sound=True,
772
766
  )
773
767
 
@@ -779,8 +773,7 @@ def test_image_to_video_v26_rejects_sound_outside_pro_mode():
779
773
  (
780
774
  {"mode": "pro", "duration_seconds": 10},
781
775
  "last_frame_image_url requires duration_seconds 5 for kling-v2.6",
782
- ),
783
- ],
776
+ )],
784
777
  )
785
778
  def test_image_to_video_v26_rejects_invalid_final_frame_combinations(extra, message):
786
779
  client = KlingClient(api_key="k", http_client=FakeHttp())
@@ -871,8 +864,7 @@ def test_text_to_video_o1_rejects_missing_video_reference():
871
864
  "http://2130706433/reference.jpg",
872
865
  "http://127.1/reference.jpg",
873
866
  "http://0177.0.0.1/reference.jpg",
874
- "http://0x7f000001/reference.jpg",
875
- ],
867
+ "http://0x7f000001/reference.jpg"],
876
868
  )
877
869
  def test_text_to_video_o1_rejects_non_public_reference_media(reference_url):
878
870
  fake = FakeHttp()
@@ -900,6 +892,7 @@ def test_motion_control_create_posts_body():
900
892
  model="kling-3.0",
901
893
  source_image_url="https://x/s.jpg",
902
894
  reference_video_url="https://x/r.mp4",
895
+ output_resolution="720p",
903
896
  )
904
897
  assert fake.calls == [
905
898
  (
@@ -909,12 +902,23 @@ def test_motion_control_create_posts_body():
909
902
  "model": "kling-3.0",
910
903
  "source_image_url": "https://x/s.jpg",
911
904
  "reference_video_url": "https://x/r.mp4",
912
- },
913
- ),
914
- ]
905
+ "output_resolution": "720p"},
906
+ )]
915
907
  assert isinstance(result, MotionControlResponse)
916
908
 
917
909
 
910
+ def test_motion_control_kling_3_requires_output_resolution():
911
+ fake = FakeHttp()
912
+ client = KlingClient(api_key="k", http_client=fake)
913
+ with pytest.raises(ValidationError, match="output_resolution is required"):
914
+ client.motion_control.create(
915
+ model="kling-3.0",
916
+ source_image_url="https://x/s.jpg",
917
+ reference_video_url="https://x/r.mp4",
918
+ )
919
+ assert fake.calls == []
920
+
921
+
918
922
  def test_motion_control_create_posts_v26_body():
919
923
  fake = FakeHttp({"id": "t26", "status": "pending"})
920
924
  client = KlingClient(api_key="k", http_client=fake)
@@ -934,8 +938,7 @@ def test_motion_control_create_posts_v26_body():
934
938
  ("missing", "message"),
935
939
  [
936
940
  ("output_resolution", "output_resolution is required"),
937
- ("character_orientation", "character_orientation is required"),
938
- ],
941
+ ("character_orientation", "character_orientation is required")],
939
942
  )
940
943
  def test_motion_control_v26_requires_model_fields(missing, message):
941
944
  fake = FakeHttp()
@@ -945,8 +948,7 @@ def test_motion_control_v26_requires_model_fields(missing, message):
945
948
  "source_image_url": "https://x/s.jpg",
946
949
  "reference_video_url": "https://x/r.mp4",
947
950
  "output_resolution": "720p",
948
- "character_orientation": "video",
949
- }
951
+ "character_orientation": "video"}
950
952
  del params[missing]
951
953
 
952
954
  with pytest.raises(ValidationError, match=message):
@@ -982,13 +984,14 @@ def test_motion_control_get_fetches_by_id():
982
984
  def test_motion_control_run_narrows_completed_type():
983
985
  fake = FakeHttp(
984
986
  {"id": "t1", "status": "pending"},
985
- {"id": "t1", "status": "completed", "videos": [{"url": "https://x/m.mp4"}]},
987
+ {"id": "t1", "status": "completed", "usage": {"cost": 0.05}, "videos": [{"url": "https://x/m.mp4"}]},
986
988
  )
987
989
  client = KlingClient(api_key="k", http_client=fake)
988
990
  result = client.motion_control.run(
989
991
  model="kling-3.0",
990
992
  source_image_url="https://x/s.jpg",
991
993
  reference_video_url="https://x/r.mp4",
994
+ output_resolution="720p",
992
995
  )
993
996
  assert isinstance(result, CompletedMotionControlResponse)
994
997
  assert result.videos[0].url == "https://x/m.mp4"
@@ -1022,14 +1025,16 @@ def test_motion_control_requires_source_image_url():
1022
1025
  client = KlingClient(api_key="k", http_client=FakeHttp())
1023
1026
  with pytest.raises(ValidationError, match="source_image_url is required"):
1024
1027
  client.motion_control.create(
1025
- model="kling-3.0", reference_video_url="https://x/r.mp4"
1028
+ model="kling-3.0", reference_video_url="https://x/r.mp4", output_resolution="720p"
1026
1029
  )
1027
1030
 
1028
1031
 
1029
1032
  def test_motion_control_requires_reference_video_url():
1030
1033
  client = KlingClient(api_key="k", http_client=FakeHttp())
1031
1034
  with pytest.raises(ValidationError, match="reference_video_url is required"):
1032
- client.motion_control.create(model="kling-3.0", source_image_url="https://x/s.jpg")
1035
+ client.motion_control.create(
1036
+ model="kling-3.0", source_image_url="https://x/s.jpg", output_resolution="720p"
1037
+ )
1033
1038
 
1034
1039
 
1035
1040
  def test_text_to_video_non_numeric_duration_raises_validation_error():
@@ -1058,8 +1063,7 @@ def test_text_to_video_accepts_reference_image_model():
1058
1063
  "model": "kling-v3-omni-reference",
1059
1064
  "prompt": "Keep the subject from the reference image",
1060
1065
  "reference_image_urls": ["https://cdn.runapi.ai/public/samples/image.jpg"],
1061
- "aspect_ratio": "16:9",
1062
- }
1066
+ "aspect_ratio": "16:9"}
1063
1067
 
1064
1068
  client.text_to_video.create(**params)
1065
1069
  assert fake.calls == [("post", "/api/v1/kling/text_to_video", params)]
@@ -1070,15 +1074,14 @@ def test_edit_video_create_get_and_run_for_edit_model():
1070
1074
  {"id": "edit-create", "status": "processing"},
1071
1075
  {"id": "edit-get", "status": "processing"},
1072
1076
  {"id": "edit-run", "status": "processing"},
1073
- {"id": "edit-run", "status": "completed", "videos": [{"url": "https://file.runapi.ai/edit.mp4"}]},
1077
+ {"id": "edit-run", "status": "completed", "usage": {"cost": 0.05}, "videos": [{"url": "https://file.runapi.ai/edit.mp4"}]},
1074
1078
  )
1075
1079
  client = KlingClient(api_key="k", http_client=fake)
1076
1080
  params = {
1077
1081
  "model": "kling-v3-omni-edit",
1078
1082
  "prompt": "Turn the source video into a watercolor scene",
1079
1083
  "source_video_url": "https://cdn.runapi.ai/public/samples/video.mp4",
1080
- "aspect_ratio": "auto",
1081
- }
1084
+ "aspect_ratio": "auto"}
1082
1085
 
1083
1086
  client.edit_video.create(**params)
1084
1087
  client.edit_video.get("edit-get")
@@ -1088,8 +1091,7 @@ def test_edit_video_create_get_and_run_for_edit_model():
1088
1091
  ("post", "/api/v1/kling/edit_video", params),
1089
1092
  ("get", "/api/v1/kling/edit_video/edit-get", None),
1090
1093
  ("post", "/api/v1/kling/edit_video", params),
1091
- ("get", "/api/v1/kling/edit_video/edit-run", None),
1092
- ]
1094
+ ("get", "/api/v1/kling/edit_video/edit-run", None)]
1093
1095
  assert result.status == "completed"
1094
1096
 
1095
1097
 
@@ -1102,8 +1104,7 @@ def test_edit_video_accepts_reference_model_and_requires_model():
1102
1104
  "source_video_url": "https://cdn.runapi.ai/public/samples/video.mp4",
1103
1105
  "reference_image_urls": ["https://cdn.runapi.ai/public/samples/image.jpg"],
1104
1106
  "aspect_ratio": "16:9",
1105
- "enable_sound": False,
1106
- }
1107
+ "enable_sound": False}
1107
1108
 
1108
1109
  client.edit_video.create(**params)
1109
1110
  assert fake.calls == [("post", "/api/v1/kling/edit_video", params)]
File without changes