genblaze-replicate 0.2.0__tar.gz → 0.2.2__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,10 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: genblaze-replicate
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: Replicate provider adapter for genblaze
5
+ Project-URL: Homepage, https://github.com/backblaze-labs/genblaze
5
6
  Project-URL: Documentation, https://github.com/backblaze-labs/genblaze
6
7
  Project-URL: Repository, https://github.com/backblaze-labs/genblaze
7
8
  Project-URL: Issues, https://github.com/backblaze-labs/genblaze/issues
9
+ Author-email: Jeronimo De Leon <jdeleon@backblaze.com>
8
10
  License-Expression: MIT
9
11
  Classifier: Development Status :: 3 - Alpha
10
12
  Classifier: License :: OSI Approved :: MIT License
@@ -29,6 +29,7 @@ from genblaze_core.providers import (
29
29
  route_by_media_type,
30
30
  validate_asset_url,
31
31
  )
32
+ from genblaze_core.providers.retry import retry_after_from_response
32
33
  from genblaze_core.runnable.config import RunnableConfig
33
34
 
34
35
  from ._errors import map_replicate_error
@@ -123,6 +124,7 @@ class ReplicateProvider(BaseProvider):
123
124
  raise ProviderError(
124
125
  f"Replicate submit failed: {exc}",
125
126
  error_code=map_replicate_error(exc),
127
+ retry_after=retry_after_from_response(exc),
126
128
  ) from exc
127
129
 
128
130
  def poll(self, prediction_id: Any, config: RunnableConfig | None = None) -> bool:
@@ -137,6 +139,7 @@ class ReplicateProvider(BaseProvider):
137
139
  raise ProviderError(
138
140
  f"Replicate poll failed: {exc}",
139
141
  error_code=map_replicate_error(exc),
142
+ retry_after=retry_after_from_response(exc),
140
143
  ) from exc
141
144
 
142
145
  def fetch_output(self, prediction_id: Any, step: Step) -> Step:
@@ -176,14 +179,35 @@ class ReplicateProvider(BaseProvider):
176
179
  error_code=ProviderErrorCode.UNKNOWN,
177
180
  )
178
181
 
179
- output = prediction.output
180
- if isinstance(output, str):
181
- output = [output]
182
- elif output is None:
183
- output = []
182
+ # Replicate output shapes vary by model: str (single URL), list[str]
183
+ # (multi-asset), dict[str, str | list] (e.g. {"video": url,
184
+ # "subtitles": url} from text-to-video models with side-channels),
185
+ # or None (no output). Normalize to list[str].
186
+ raw_output = prediction.output
187
+ urls: list[str]
188
+ if raw_output is None:
189
+ urls = []
190
+ elif isinstance(raw_output, str):
191
+ urls = [raw_output]
192
+ elif isinstance(raw_output, list):
193
+ # Nested lists happen on batch-output models; flatten one level.
194
+ urls = []
195
+ for item in raw_output:
196
+ if isinstance(item, str):
197
+ urls.append(item)
198
+ elif isinstance(item, list):
199
+ urls.extend(str(u) for u in item if isinstance(u, (str, bytes)))
200
+ elif isinstance(raw_output, dict):
201
+ # Multi-channel outputs: keep only URL-shaped string values.
202
+ urls = [str(v) for v in raw_output.values() if isinstance(v, str)]
203
+ else:
204
+ raise ProviderError(
205
+ f"Unexpected Replicate output shape "
206
+ f"({type(raw_output).__name__}): {raw_output!r}",
207
+ error_code=ProviderErrorCode.SERVER_ERROR,
208
+ )
184
209
 
185
- for url in output:
186
- url_str = str(url)
210
+ for url_str in urls:
187
211
  validate_asset_url(url_str)
188
212
  path = urlparse(url_str).path
189
213
  mime, _ = mimetypes.guess_type(path)
@@ -199,4 +223,5 @@ class ReplicateProvider(BaseProvider):
199
223
  raise ProviderError(
200
224
  f"Replicate fetch_output failed: {exc}",
201
225
  error_code=map_replicate_error(exc),
226
+ retry_after=retry_after_from_response(exc),
202
227
  ) from exc
@@ -4,8 +4,9 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "genblaze-replicate"
7
- version = "0.2.0"
7
+ version = "0.2.2"
8
8
  description = "Replicate provider adapter for genblaze"
9
+ authors = [{name = "Jeronimo De Leon", email = "jdeleon@backblaze.com"}]
9
10
  readme = "README.md"
10
11
  requires-python = ">=3.11"
11
12
  license = "MIT"
@@ -20,6 +21,7 @@ dependencies = [
20
21
  ]
21
22
 
22
23
  [project.urls]
24
+ Homepage = "https://github.com/backblaze-labs/genblaze"
23
25
  Documentation = "https://github.com/backblaze-labs/genblaze"
24
26
  Repository = "https://github.com/backblaze-labs/genblaze"
25
27
  Issues = "https://github.com/backblaze-labs/genblaze/issues"
@@ -185,6 +185,54 @@ def test_fetch_output_success_with_none():
185
185
  assert len(result.assets) == 0
186
186
 
187
187
 
188
+ def test_fetch_output_success_with_dict():
189
+ """Some Replicate models return dict outputs like {'video': url, 'audio': url}."""
190
+ provider = ReplicateProvider(api_token="test-token")
191
+ mock_client = MagicMock()
192
+ mock_client.predictions.get.return_value = FakePrediction(
193
+ output={
194
+ "video": "https://example.com/video.mp4",
195
+ "subtitles": "https://example.com/subs.vtt",
196
+ }
197
+ )
198
+ provider._client = mock_client
199
+
200
+ step = _make_step()
201
+ result = provider.fetch_output("pred-abc123", step)
202
+
203
+ urls = {a.url for a in result.assets}
204
+ assert urls == {"https://example.com/video.mp4", "https://example.com/subs.vtt"}
205
+
206
+
207
+ def test_fetch_output_success_with_nested_list():
208
+ """Batch-output models return list[list[str]]; flatten one level."""
209
+ provider = ReplicateProvider(api_token="test-token")
210
+ mock_client = MagicMock()
211
+ mock_client.predictions.get.return_value = FakePrediction(
212
+ output=[["https://example.com/a.png", "https://example.com/b.png"]]
213
+ )
214
+ provider._client = mock_client
215
+
216
+ step = _make_step()
217
+ result = provider.fetch_output("pred-abc123", step)
218
+
219
+ assert len(result.assets) == 2
220
+
221
+
222
+ def test_fetch_output_unknown_shape_raises_clear_error():
223
+ """Unknown output shapes raise ProviderError with a specific message."""
224
+ from genblaze_core.exceptions import ProviderError
225
+
226
+ provider = ReplicateProvider(api_token="test-token")
227
+ mock_client = MagicMock()
228
+ mock_client.predictions.get.return_value = FakePrediction(output=42)
229
+ provider._client = mock_client
230
+
231
+ step = _make_step()
232
+ with pytest.raises(ProviderError, match="Unexpected Replicate output shape"):
233
+ provider.fetch_output("pred-abc123", step)
234
+
235
+
188
236
  def test_fetch_output_failed():
189
237
  from genblaze_core.exceptions import ProviderError
190
238