runapi-core 0.1.2__tar.gz → 0.1.4__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.
Files changed (28) hide show
  1. {runapi_core-0.1.2 → runapi_core-0.1.4}/PKG-INFO +1 -1
  2. {runapi_core-0.1.2 → runapi_core-0.1.4}/src/runapi/core/contract_gen.py +93 -12
  3. {runapi_core-0.1.2 → runapi_core-0.1.4}/src/runapi/core/resource.py +10 -8
  4. runapi_core-0.1.4/src/runapi/core/version.py +1 -0
  5. {runapi_core-0.1.2 → runapi_core-0.1.4}/tests/test_resource.py +29 -0
  6. runapi_core-0.1.2/src/runapi/core/version.py +0 -1
  7. {runapi_core-0.1.2 → runapi_core-0.1.4}/README.md +0 -0
  8. {runapi_core-0.1.2 → runapi_core-0.1.4}/pyproject.toml +0 -0
  9. {runapi_core-0.1.2 → runapi_core-0.1.4}/src/runapi/core/__init__.py +0 -0
  10. {runapi_core-0.1.2 → runapi_core-0.1.4}/src/runapi/core/auth.py +0 -0
  11. {runapi_core-0.1.2 → runapi_core-0.1.4}/src/runapi/core/config.py +0 -0
  12. {runapi_core-0.1.2 → runapi_core-0.1.4}/src/runapi/core/constants.py +0 -0
  13. {runapi_core-0.1.2 → runapi_core-0.1.4}/src/runapi/core/errors.py +0 -0
  14. {runapi_core-0.1.2 → runapi_core-0.1.4}/src/runapi/core/files.py +0 -0
  15. {runapi_core-0.1.2 → runapi_core-0.1.4}/src/runapi/core/http_client.py +0 -0
  16. {runapi_core-0.1.2 → runapi_core-0.1.4}/src/runapi/core/models.py +0 -0
  17. {runapi_core-0.1.2 → runapi_core-0.1.4}/src/runapi/core/multipart.py +0 -0
  18. {runapi_core-0.1.2 → runapi_core-0.1.4}/src/runapi/core/options.py +0 -0
  19. {runapi_core-0.1.2 → runapi_core-0.1.4}/src/runapi/core/polling.py +0 -0
  20. {runapi_core-0.1.2 → runapi_core-0.1.4}/src/runapi/core/py.typed +0 -0
  21. {runapi_core-0.1.2 → runapi_core-0.1.4}/src/runapi/core/response.py +0 -0
  22. {runapi_core-0.1.2 → runapi_core-0.1.4}/tests/test_auth.py +0 -0
  23. {runapi_core-0.1.2 → runapi_core-0.1.4}/tests/test_config.py +0 -0
  24. {runapi_core-0.1.2 → runapi_core-0.1.4}/tests/test_errors.py +0 -0
  25. {runapi_core-0.1.2 → runapi_core-0.1.4}/tests/test_files.py +0 -0
  26. {runapi_core-0.1.2 → runapi_core-0.1.4}/tests/test_http_client.py +0 -0
  27. {runapi_core-0.1.2 → runapi_core-0.1.4}/tests/test_models.py +0 -0
  28. {runapi_core-0.1.2 → runapi_core-0.1.4}/tests/test_polling.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: runapi-core
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Summary: Core module for the RunAPI Python SDK
5
5
  Project-URL: Homepage, https://runapi.ai
6
6
  Project-URL: Documentation, https://runapi.ai/docs#sdk
@@ -211,7 +211,7 @@ CONTRACT = {
211
211
  }
212
212
  },
213
213
  "grok-imagine/image-to-video": {
214
- "models": ["grok-imagine-image-to-video"],
214
+ "models": ["grok-imagine-image-to-video", "grok-imagine-video-1.5-preview"],
215
215
  "fields_by_model": {
216
216
  "grok-imagine-image-to-video": {
217
217
  "aspect_ratio": {
@@ -223,6 +223,14 @@ CONTRACT = {
223
223
  "output_resolution": {
224
224
  "enum": ["480p", "720p"]
225
225
  }
226
+ },
227
+ "grok-imagine-video-1.5-preview": {
228
+ "aspect_ratio": {
229
+ "enum": ["1:1", "16:9", "9:16", "3:2", "2:3", "auto"]
230
+ },
231
+ "output_resolution": {
232
+ "enum": ["480p", "720p"]
233
+ }
226
234
  }
227
235
  }
228
236
  },
@@ -237,7 +245,7 @@ CONTRACT = {
237
245
  }
238
246
  },
239
247
  "grok-imagine/text-to-video": {
240
- "models": ["grok-imagine-text-to-video"],
248
+ "models": ["grok-imagine-text-to-video", "grok-imagine-video-1.5-preview"],
241
249
  "fields_by_model": {
242
250
  "grok-imagine-text-to-video": {
243
251
  "aspect_ratio": {
@@ -249,6 +257,14 @@ CONTRACT = {
249
257
  "output_resolution": {
250
258
  "enum": ["480p", "720p"]
251
259
  }
260
+ },
261
+ "grok-imagine-video-1.5-preview": {
262
+ "aspect_ratio": {
263
+ "enum": ["1:1", "16:9", "9:16", "3:2", "2:3", "auto"]
264
+ },
265
+ "output_resolution": {
266
+ "enum": ["480p", "720p"]
267
+ }
252
268
  }
253
269
  }
254
270
  },
@@ -478,10 +494,7 @@ CONTRACT = {
478
494
  },
479
495
  "imagen-4-fast": {
480
496
  "aspect_ratio": {
481
- "enum": ["1:1", "16:9", "9:16", "3:4", "4:3"]
482
- },
483
- "output_count": {
484
- "enum": [1, 2, 3, 4]
497
+ "enum": ["1:1", "16:9", "9:16", "3:4", "4:3", "auto"]
485
498
  }
486
499
  },
487
500
  "imagen-4-ultra": {
@@ -511,7 +524,7 @@ CONTRACT = {
511
524
  }
512
525
  },
513
526
  "kling/image-to-video": {
514
- "models": ["kling-v2.1-master-image-to-video", "kling-v2.1-pro", "kling-v2.1-standard", "kling-v2.5-turbo-image-to-video-pro"],
527
+ "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-v3-turbo-image-to-video"],
515
528
  "fields_by_model": {
516
529
  "kling-v2.1-master-image-to-video": {
517
530
  "duration_seconds": {
@@ -532,6 +545,14 @@ CONTRACT = {
532
545
  "duration_seconds": {
533
546
  "enum": [5, 10]
534
547
  }
548
+ },
549
+ "kling-v3-turbo-image-to-video": {
550
+ "duration_seconds": {
551
+ "enum": [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
552
+ },
553
+ "output_resolution": {
554
+ "enum": ["720p", "1080p"]
555
+ }
535
556
  }
536
557
  }
537
558
  },
@@ -552,7 +573,7 @@ CONTRACT = {
552
573
  }
553
574
  },
554
575
  "kling/text-to-video": {
555
- "models": ["kling-3.0", "kling-v2.1-master-text-to-video", "kling-v2.5-turbo-text-to-video-pro"],
576
+ "models": ["kling-3.0", "kling-v2.1-master-text-to-video", "kling-v2.5-turbo-text-to-video-pro", "kling-v3-turbo-text-to-video"],
556
577
  "fields_by_model": {
557
578
  "kling-3.0": {
558
579
  "aspect_ratio": {
@@ -580,6 +601,17 @@ CONTRACT = {
580
601
  "duration_seconds": {
581
602
  "enum": [5, 10]
582
603
  }
604
+ },
605
+ "kling-v3-turbo-text-to-video": {
606
+ "aspect_ratio": {
607
+ "enum": ["16:9", "9:16", "1:1"]
608
+ },
609
+ "duration_seconds": {
610
+ "enum": [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
611
+ },
612
+ "output_resolution": {
613
+ "enum": ["720p", "1080p"]
614
+ }
583
615
  }
584
616
  }
585
617
  },
@@ -590,8 +622,13 @@ CONTRACT = {
590
622
  }
591
623
  },
592
624
  "nano-banana/edit-image": {
593
- "models": ["nano-banana-edit"],
625
+ "models": ["nano-banana-2-lite", "nano-banana-edit"],
594
626
  "fields_by_model": {
627
+ "nano-banana-2-lite": {
628
+ "aspect_ratio": {
629
+ "enum": ["1:1", "1:4", "1:8", "2:3", "3:2", "3:4", "4:1", "4:3", "4:5", "5:4", "8:1", "9:16", "16:9", "21:9", "auto"]
630
+ }
631
+ },
595
632
  "nano-banana-edit": {
596
633
  "aspect_ratio": {
597
634
  "enum": ["1:1", "9:16", "16:9", "3:4", "4:3", "3:2", "2:3", "5:4", "4:5", "21:9", "auto"]
@@ -642,6 +679,28 @@ CONTRACT = {
642
679
  }
643
680
  }
644
681
  },
682
+ "omnihuman/audio-to-video": {
683
+ "models": ["omnihuman-1.5"],
684
+ "fields_by_model": {
685
+ "omnihuman-1.5": {
686
+ "output_resolution": {
687
+ "enum": ["720p", "1080p"]
688
+ }
689
+ }
690
+ }
691
+ },
692
+ "omnihuman/human-identification": {
693
+ "models": ["omnihuman-1.5-human-identification"],
694
+ "fields_by_model": {
695
+ "omnihuman-1.5-human-identification": {}
696
+ }
697
+ },
698
+ "omnihuman/subject-detection": {
699
+ "models": ["omnihuman-1.5-subject-detection"],
700
+ "fields_by_model": {
701
+ "omnihuman-1.5-subject-detection": {}
702
+ }
703
+ },
645
704
  "qwen-2/edit-image": {
646
705
  "models": ["qwen-2-edit-image"],
647
706
  "fields_by_model": {
@@ -730,7 +789,7 @@ CONTRACT = {
730
789
  }
731
790
  },
732
791
  "seedance/text-to-video": {
733
- "models": ["seedance-1.5-pro", "seedance-2.0", "seedance-2.0-fast", "seedance-v1-lite", "seedance-v1-pro", "seedance-v1-pro-fast"],
792
+ "models": ["seedance-1.5-pro", "seedance-2-mini", "seedance-2.0", "seedance-2.0-fast", "seedance-v1-lite", "seedance-v1-pro", "seedance-v1-pro-fast"],
734
793
  "fields_by_model": {
735
794
  "seedance-1.5-pro": {
736
795
  "aspect_ratio": {
@@ -740,12 +799,20 @@ CONTRACT = {
740
799
  "enum": ["480p", "720p", "1080p"]
741
800
  }
742
801
  },
802
+ "seedance-2-mini": {
803
+ "aspect_ratio": {
804
+ "enum": ["1:1", "4:3", "3:4", "16:9", "9:16", "21:9", "auto"]
805
+ },
806
+ "output_resolution": {
807
+ "enum": ["480p", "720p"]
808
+ }
809
+ },
743
810
  "seedance-2.0": {
744
811
  "aspect_ratio": {
745
812
  "enum": ["1:1", "4:3", "3:4", "16:9", "9:16", "21:9", "auto"]
746
813
  },
747
814
  "output_resolution": {
748
- "enum": ["480p", "720p", "1080p"]
815
+ "enum": ["480p", "720p", "1080p", "4k"]
749
816
  }
750
817
  },
751
818
  "seedance-2.0-fast": {
@@ -1167,7 +1234,11 @@ CONTRACT = {
1167
1234
  "suno/replace-section": {
1168
1235
  "models": [],
1169
1236
  "fields_by_model": {
1170
- "_": {}
1237
+ "_": {
1238
+ "model": {
1239
+ "enum": ["suno-v4", "suno-v4.5", "suno-v4.5-all", "suno-v4.5-plus", "suno-v5", "suno-v5.5"]
1240
+ }
1241
+ }
1171
1242
  }
1172
1243
  },
1173
1244
  "suno/separate-audio-stems": {
@@ -1341,6 +1412,16 @@ CONTRACT = {
1341
1412
  }
1342
1413
  }
1343
1414
  },
1415
+ "volcengine-lip-sync/lip-sync-video": {
1416
+ "models": ["volcengine-lip-sync"],
1417
+ "fields_by_model": {
1418
+ "volcengine-lip-sync": {
1419
+ "mode": {
1420
+ "enum": ["lite", "basic"]
1421
+ }
1422
+ }
1423
+ }
1424
+ },
1344
1425
  "wan/animate": {
1345
1426
  "models": ["wan-2.2-animate-move", "wan-2.2-animate-replace"],
1346
1427
  "fields_by_model": {
@@ -59,22 +59,24 @@ class Resource:
59
59
 
60
60
  # ---- Contract validation -------------------------------------------
61
61
  # Validates request params against the generated contract: model
62
- # membership, then per-field required/enum/integer/min/max/length, then
63
- # declared cross-field rules. `schema` is one action entry from the generated
62
+ # membership, then declared cross-field rules, then per-field
63
+ # required/enum/integer/min/max/length. `schema` is one action entry from the generated
64
64
  # per-package CONTRACT (CONTRACT["<action>"]).
65
65
 
66
66
  def _validate_contract(self, schema: Dict[str, Any], params: Dict[str, Any]) -> None:
67
67
  model = params.get("model")
68
68
  models = schema.get("models", [])
69
- if model not in models:
70
- raise ValidationError(f"model must be one of: {', '.join(sorted(models))}")
71
-
72
- fields = schema.get("fields_by_model", {}).get(model, {})
73
- for field, rules in fields.items():
74
- self._validate_schema_field(params, field, rules)
69
+ if models:
70
+ if model not in models:
71
+ raise ValidationError(f"model must be one of: {', '.join(sorted(models))}")
72
+ fields = schema.get("fields_by_model", {}).get(model, {})
73
+ else:
74
+ fields = schema.get("fields_by_model", {}).get("_", {})
75
75
 
76
76
  for rule in schema.get("rules", []):
77
77
  self._enforce_rule(params, rule)
78
+ for field, rules in fields.items():
79
+ self._validate_schema_field(params, field, rules)
78
80
 
79
81
  def _validate_schema_field(self, params: Dict[str, Any], field: str, rules: Dict[str, Any]) -> None:
80
82
  present = self._field_present(params, field)
@@ -0,0 +1 @@
1
+ __version__ = "0.1.4"
@@ -109,6 +109,24 @@ def test_validate_integer_still_enforces_range_for_valid_int():
109
109
  assert _run_validate({"model": "m", "duration_int": 13}) == "duration_int must be between 4 and 12"
110
110
 
111
111
 
112
+ def test_validate_functional_action_uses_underscore_fields():
113
+ resource = SampleResource(FakeHttp())
114
+ schema = {
115
+ "models": [],
116
+ "fields_by_model": {
117
+ "_": {
118
+ "prompt": {"required": True},
119
+ "mode": {"enum": ["fast", "quality"]},
120
+ }
121
+ },
122
+ }
123
+ resource._validate_contract(schema, {"prompt": "hello", "mode": "fast"})
124
+ with pytest.raises(ValidationError, match="prompt is required"):
125
+ resource._validate_contract(schema, {"mode": "fast"})
126
+ with pytest.raises(ValidationError, match="mode must be one of: fast, quality"):
127
+ resource._validate_contract(schema, {"prompt": "hello", "mode": "slow"})
128
+
129
+
112
130
  def test_validate_integer_rejects_bool_and_whole_float():
113
131
  # bool is an int subclass but is not a valid integer value.
114
132
  assert _run_validate({"model": "m", "tolerance": True}) == "tolerance must be an integer"
@@ -117,6 +135,17 @@ def test_validate_integer_rejects_bool_and_whole_float():
117
135
  assert _run_validate({"model": "m", "tolerance": 5}) == ""
118
136
 
119
137
 
138
+ def test_validate_contract_runs_rules_before_field_requirements():
139
+ schema = {
140
+ "models": ["m"],
141
+ "rules": [{"when": {"model": "m"}, "forbidden": ["source_task_id"]}],
142
+ "fields_by_model": {"m": {"source_image_urls": {"required": True}}},
143
+ }
144
+ resource = SampleResource(FakeHttp())
145
+ with pytest.raises(ValidationError, match="source_task_id is not allowed when model is m"):
146
+ resource._validate_contract(schema, {"model": "m", "source_task_id": "src_1"})
147
+
148
+
120
149
  def test_poll_recoerces_to_completed_class():
121
150
  resource = SampleResource(FakeHttp())
122
151
  response = TaskResponse({"id": "1", "status": "completed", "images": [{"url": "u"}]})
@@ -1 +0,0 @@
1
- __version__ = "0.1.2"
File without changes
File without changes