runapi-core 0.2.0__tar.gz → 0.2.1__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.2.0 → runapi_core-0.2.1}/PKG-INFO +2 -2
  2. {runapi_core-0.2.0 → runapi_core-0.2.1}/pyproject.toml +1 -1
  3. {runapi_core-0.2.0 → runapi_core-0.2.1}/src/runapi/core/contract_gen.py +10 -0
  4. {runapi_core-0.2.0 → runapi_core-0.2.1}/src/runapi/core/resource.py +22 -1
  5. runapi_core-0.2.1/src/runapi/core/version.py +1 -0
  6. {runapi_core-0.2.0 → runapi_core-0.2.1}/tests/test_resource.py +24 -0
  7. runapi_core-0.2.0/src/runapi/core/version.py +0 -1
  8. {runapi_core-0.2.0 → runapi_core-0.2.1}/README.md +0 -0
  9. {runapi_core-0.2.0 → runapi_core-0.2.1}/src/runapi/core/__init__.py +0 -0
  10. {runapi_core-0.2.0 → runapi_core-0.2.1}/src/runapi/core/auth.py +0 -0
  11. {runapi_core-0.2.0 → runapi_core-0.2.1}/src/runapi/core/config.py +0 -0
  12. {runapi_core-0.2.0 → runapi_core-0.2.1}/src/runapi/core/constants.py +0 -0
  13. {runapi_core-0.2.0 → runapi_core-0.2.1}/src/runapi/core/errors.py +0 -0
  14. {runapi_core-0.2.0 → runapi_core-0.2.1}/src/runapi/core/files.py +0 -0
  15. {runapi_core-0.2.0 → runapi_core-0.2.1}/src/runapi/core/http_client.py +0 -0
  16. {runapi_core-0.2.0 → runapi_core-0.2.1}/src/runapi/core/models.py +0 -0
  17. {runapi_core-0.2.0 → runapi_core-0.2.1}/src/runapi/core/multipart.py +0 -0
  18. {runapi_core-0.2.0 → runapi_core-0.2.1}/src/runapi/core/options.py +0 -0
  19. {runapi_core-0.2.0 → runapi_core-0.2.1}/src/runapi/core/polling.py +0 -0
  20. {runapi_core-0.2.0 → runapi_core-0.2.1}/src/runapi/core/py.typed +0 -0
  21. {runapi_core-0.2.0 → runapi_core-0.2.1}/src/runapi/core/response.py +0 -0
  22. {runapi_core-0.2.0 → runapi_core-0.2.1}/tests/test_auth.py +0 -0
  23. {runapi_core-0.2.0 → runapi_core-0.2.1}/tests/test_config.py +0 -0
  24. {runapi_core-0.2.0 → runapi_core-0.2.1}/tests/test_errors.py +0 -0
  25. {runapi_core-0.2.0 → runapi_core-0.2.1}/tests/test_files.py +0 -0
  26. {runapi_core-0.2.0 → runapi_core-0.2.1}/tests/test_http_client.py +0 -0
  27. {runapi_core-0.2.0 → runapi_core-0.2.1}/tests/test_models.py +0 -0
  28. {runapi_core-0.2.0 → runapi_core-0.2.1}/tests/test_polling.py +0 -0
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: runapi-core
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: RunAPI core SDK for JavaScript, Python, Ruby, Go, Java, and PHP
5
5
  Project-URL: Homepage, https://runapi.ai/models
6
6
  Project-URL: Documentation, https://runapi.ai/docs#runapi-sdks
7
7
  Project-URL: Source, https://github.com/runapi-ai/core-sdk
8
8
  Project-URL: Issues, https://github.com/runapi-ai/core-sdk/issues
9
9
  Project-URL: Changelog, https://github.com/runapi-ai/core-sdk/blob/main/CHANGELOG.md
10
- Project-URL: Release Notes, https://github.com/runapi-ai/core-sdk/releases/tag/python%2Fv0.2.0
10
+ Project-URL: Release Notes, https://github.com/runapi-ai/core-sdk/releases/tag/python%2Fv0.2.1
11
11
  Author-email: RunAPI <contact@runapi.ai>
12
12
  License-Expression: Apache-2.0
13
13
  Keywords: api,core,golang,gradle,java,maven,pypi,python,ruby,runapi,runapi-ai,sdk,typescript
@@ -19,7 +19,7 @@ Documentation = "https://runapi.ai/docs#runapi-sdks"
19
19
  Source = "https://github.com/runapi-ai/core-sdk"
20
20
  Issues = "https://github.com/runapi-ai/core-sdk/issues"
21
21
  Changelog = "https://github.com/runapi-ai/core-sdk/blob/main/CHANGELOG.md"
22
- "Release Notes" = "https://github.com/runapi-ai/core-sdk/releases/tag/python%2Fv0.2.0"
22
+ "Release Notes" = "https://github.com/runapi-ai/core-sdk/releases/tag/python%2Fv0.2.1"
23
23
 
24
24
  [tool.hatch.version]
25
25
  path = "src/runapi/core/version.py"
@@ -786,6 +786,16 @@ CONTRACT = {
786
786
  "tts-1-hd": {}
787
787
  }
788
788
  },
789
+ "producer/text-to-music": {
790
+ "models": ["fuzz-2.0"],
791
+ "fields_by_model": {
792
+ "fuzz-2.0": {
793
+ "vocal_mode": {
794
+ "enum": ["exact_lyrics", "instrumental"]
795
+ }
796
+ }
797
+ }
798
+ },
789
799
  "qwen-2/edit-image": {
790
800
  "models": ["qwen-2-edit-image"],
791
801
  "fields_by_model": {
@@ -79,13 +79,16 @@ class Resource:
79
79
  self._validate_schema_field(params, field, rules)
80
80
 
81
81
  def _validate_schema_field(self, params: Dict[str, Any], field: str, rules: Dict[str, Any]) -> None:
82
+ value = params.get(field)
83
+ if value is not None and ("min_items" in rules or "max_items" in rules):
84
+ self._validate_item_count(field, value, rules)
85
+
82
86
  present = self._field_present(params, field)
83
87
  if rules.get("required") and not present:
84
88
  raise ValidationError(f"{field} is required")
85
89
  if not present:
86
90
  return
87
91
 
88
- value = params[field]
89
92
  enum = rules.get("enum")
90
93
  if enum is not None and not self._enum_allowed(enum, value):
91
94
  raise ValidationError(f"{field} must be one of: {', '.join(str(option) for option in enum)}")
@@ -96,6 +99,24 @@ class Resource:
96
99
  if "min" in rules or "max" in rules:
97
100
  self._validate_range(field, value, rules)
98
101
 
102
+ def _validate_item_count(self, field: str, value: Any, rules: Dict[str, Any]) -> None:
103
+ if not isinstance(value, (list, tuple)):
104
+ raise ValidationError(f"{field} must be an array")
105
+
106
+ minimum = rules.get("min_items")
107
+ maximum = rules.get("max_items")
108
+ if (minimum is None or len(value) >= minimum) and (maximum is None or len(value) <= maximum):
109
+ return
110
+ raise ValidationError(self._item_count_message(field, minimum, maximum))
111
+
112
+ @staticmethod
113
+ def _item_count_message(field: str, minimum: Any, maximum: Any) -> str:
114
+ if minimum is not None and maximum is not None:
115
+ return f"{field} must contain between {minimum} and {maximum} items"
116
+ if minimum is not None:
117
+ return f"{field} must contain at least {minimum} items"
118
+ return f"{field} must contain at most {maximum} items"
119
+
99
120
  @staticmethod
100
121
  def _validate_integer(field: str, value: Any, rules: Dict[str, Any]) -> None:
101
122
  # Mirrors GatewayEntry#validate_schema_integer!: a type: integer field
@@ -0,0 +1 @@
1
+ __version__ = "0.2.1"
@@ -135,6 +135,30 @@ def test_validate_integer_rejects_bool_and_whole_float():
135
135
  assert _run_validate({"model": "m", "tolerance": 5}) == ""
136
136
 
137
137
 
138
+ def test_validate_array_item_count_constraints():
139
+ schema = {
140
+ "models": ["m"],
141
+ "fields_by_model": {
142
+ "m": {
143
+ "reference_image_urls": {"min_items": 1, "max_items": 3},
144
+ }
145
+ },
146
+ }
147
+ resource = SampleResource(FakeHttp())
148
+
149
+ with pytest.raises(ValidationError, match="^reference_image_urls must be an array$"):
150
+ resource._validate_contract(schema, {"model": "m", "reference_image_urls": "image.png"})
151
+ with pytest.raises(ValidationError, match="^reference_image_urls must contain between 1 and 3 items$"):
152
+ resource._validate_contract(schema, {"model": "m", "reference_image_urls": []})
153
+ with pytest.raises(ValidationError, match="^reference_image_urls must contain between 1 and 3 items$"):
154
+ resource._validate_contract(schema, {"model": "m", "reference_image_urls": ["a", "b", "c", "d"]})
155
+ with pytest.raises(ValidationError, match="^reference_image_urls must contain between 1 and 3 items$"):
156
+ resource._validate_contract(schema, {"model": "m", "reference_image_urls": ("a", "b", "c", "d")})
157
+
158
+ resource._validate_contract(schema, {"model": "m", "reference_image_urls": ["a", "b", "c"]})
159
+ resource._validate_contract(schema, {"model": "m", "reference_image_urls": ("a", "b", "c")})
160
+
161
+
138
162
  def test_validate_contract_runs_rules_before_field_requirements():
139
163
  schema = {
140
164
  "models": ["m"],
@@ -1 +0,0 @@
1
- __version__ = "0.2.0"
File without changes