diffio 0.1.80__tar.gz → 0.1.82__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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: diffio
3
- Version: 0.1.80
3
+ Version: 0.1.82
4
4
  Summary: Python SDK for the Diffio API.
5
5
  Author: Diffio
6
6
  License: MIT
@@ -155,6 +155,14 @@ print(info["apiProjectId"], info["generationId"])
155
155
  restoration or final settlement is still pending; stage progress alone does not
156
156
  indicate overall completion.
157
157
 
158
+ For Diffio 2.0, `complete` means restored media is ready. Transcription can still
159
+ be `pending`, become `available` later, or finish as `unavailable`. Read
160
+ `progress.transcription.status` independently; `progress.transcription` is `None`
161
+ for older responses that do not report availability. A completed generation
162
+ remains successful if transcription is unavailable. Completion webhooks expose
163
+ the same optional `event.transcription` object; a later transcript does not emit
164
+ another `generation.completed` event.
165
+
158
166
  ```py
159
167
  from diffio import DiffioClient
160
168
 
@@ -165,6 +173,8 @@ progress = client.generations.get_progress(
165
173
  )
166
174
 
167
175
  print(progress.status)
176
+ if progress.transcription is not None:
177
+ print(progress.transcription.status)
168
178
  ```
169
179
 
170
180
  ## Generation download
@@ -186,16 +196,41 @@ print(download.downloadUrl)
186
196
  If you only need the URL, use `client.generations.get_download`.
187
197
 
188
198
  Set `downloadType="transcript"` to download the transcript JSON artifact when the generation has one.
199
+ Pending transcripts return `DiffioApiError` with `statusCode == 409` and
200
+ `responseBody["code"] == "TRANSCRIPT_PENDING"`. Unavailable transcripts return
201
+ `statusCode == 404` and `responseBody["code"] == "TRANSCRIPT_UNAVAILABLE"`. These
202
+ responses include `responseBody["transcription"]["status"]`. Check the error code
203
+ to distinguish them from other 409 or 404 errors.
189
204
 
190
205
  ```py
191
- transcript = client.generations.download(
192
- generationId="gen_123",
193
- apiProjectId="proj_123",
194
- downloadType="transcript",
195
- downloadFilePath="word_timestamps.json",
196
- )
206
+ from diffio import DiffioApiError
207
+
208
+ try:
209
+ transcript = client.generations.download(
210
+ generationId="gen_123",
211
+ apiProjectId="proj_123",
212
+ downloadType="transcript",
213
+ downloadFilePath="word_timestamps.json",
214
+ )
215
+ except DiffioApiError as exc:
216
+ body = exc.responseBody if isinstance(exc.responseBody, dict) else {}
217
+ if exc.statusCode == 409 and body.get("code") == "TRANSCRIPT_PENDING":
218
+ print("Transcript is pending; check progress and retry later.")
219
+ elif exc.statusCode == 404 and body.get("code") == "TRANSCRIPT_UNAVAILABLE":
220
+ print("Transcript is unavailable; restored media remains available.")
221
+ else:
222
+ raise
197
223
  ```
198
224
 
225
+ `restore_audio(downloadType="transcript")` also makes one download request after
226
+ media completion. It does not wait for a pending transcript. With its default
227
+ `raiseOnError=False`, it returns `(None, info)` and preserves the API error in
228
+ `info["statusCode"]` and `info["responseBody"]`; `info["status"]` can still be
229
+ `complete` because media restoration succeeded. With `raiseOnError=True`, it raises
230
+ the same `DiffioApiError` and attaches the metadata as `exc.restoreInfo`. Callers
231
+ can poll progress and retry the transcript download explicitly. Audio and video
232
+ downloads proceed independently of transcription availability.
233
+
199
234
  ## Account, keys, usage, and webhook configuration
200
235
 
201
236
  Agent keys can manage account settings, scoped keys, usage, and webhook endpoints.
@@ -127,6 +127,14 @@ print(info["apiProjectId"], info["generationId"])
127
127
  restoration or final settlement is still pending; stage progress alone does not
128
128
  indicate overall completion.
129
129
 
130
+ For Diffio 2.0, `complete` means restored media is ready. Transcription can still
131
+ be `pending`, become `available` later, or finish as `unavailable`. Read
132
+ `progress.transcription.status` independently; `progress.transcription` is `None`
133
+ for older responses that do not report availability. A completed generation
134
+ remains successful if transcription is unavailable. Completion webhooks expose
135
+ the same optional `event.transcription` object; a later transcript does not emit
136
+ another `generation.completed` event.
137
+
130
138
  ```py
131
139
  from diffio import DiffioClient
132
140
 
@@ -137,6 +145,8 @@ progress = client.generations.get_progress(
137
145
  )
138
146
 
139
147
  print(progress.status)
148
+ if progress.transcription is not None:
149
+ print(progress.transcription.status)
140
150
  ```
141
151
 
142
152
  ## Generation download
@@ -158,16 +168,41 @@ print(download.downloadUrl)
158
168
  If you only need the URL, use `client.generations.get_download`.
159
169
 
160
170
  Set `downloadType="transcript"` to download the transcript JSON artifact when the generation has one.
171
+ Pending transcripts return `DiffioApiError` with `statusCode == 409` and
172
+ `responseBody["code"] == "TRANSCRIPT_PENDING"`. Unavailable transcripts return
173
+ `statusCode == 404` and `responseBody["code"] == "TRANSCRIPT_UNAVAILABLE"`. These
174
+ responses include `responseBody["transcription"]["status"]`. Check the error code
175
+ to distinguish them from other 409 or 404 errors.
161
176
 
162
177
  ```py
163
- transcript = client.generations.download(
164
- generationId="gen_123",
165
- apiProjectId="proj_123",
166
- downloadType="transcript",
167
- downloadFilePath="word_timestamps.json",
168
- )
178
+ from diffio import DiffioApiError
179
+
180
+ try:
181
+ transcript = client.generations.download(
182
+ generationId="gen_123",
183
+ apiProjectId="proj_123",
184
+ downloadType="transcript",
185
+ downloadFilePath="word_timestamps.json",
186
+ )
187
+ except DiffioApiError as exc:
188
+ body = exc.responseBody if isinstance(exc.responseBody, dict) else {}
189
+ if exc.statusCode == 409 and body.get("code") == "TRANSCRIPT_PENDING":
190
+ print("Transcript is pending; check progress and retry later.")
191
+ elif exc.statusCode == 404 and body.get("code") == "TRANSCRIPT_UNAVAILABLE":
192
+ print("Transcript is unavailable; restored media remains available.")
193
+ else:
194
+ raise
169
195
  ```
170
196
 
197
+ `restore_audio(downloadType="transcript")` also makes one download request after
198
+ media completion. It does not wait for a pending transcript. With its default
199
+ `raiseOnError=False`, it returns `(None, info)` and preserves the API error in
200
+ `info["statusCode"]` and `info["responseBody"]`; `info["status"]` can still be
201
+ `complete` because media restoration succeeded. With `raiseOnError=True`, it raises
202
+ the same `DiffioApiError` and attaches the metadata as `exc.restoreInfo`. Callers
203
+ can poll progress and retry the transcript download explicitly. Audio and video
204
+ downloads proceed independently of transcription availability.
205
+
171
206
  ## Account, keys, usage, and webhook configuration
172
207
 
173
208
  Agent keys can manage account settings, scoped keys, usage, and webhook endpoints.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "diffio"
7
- version = "0.1.80"
7
+ version = "0.1.82"
8
8
  description = "Python SDK for the Diffio API."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -21,6 +21,7 @@ from .types import (
21
21
  GenerationDownloadResponse,
22
22
  GenerationProgressResponse,
23
23
  GenerationProgressStage,
24
+ GenerationTranscription,
24
25
  GenerationWebhookEvent,
25
26
  GenerationWebhookStatus,
26
27
  ListProjectGenerationsResponse,
@@ -28,6 +29,7 @@ from .types import (
28
29
  ModelKey,
29
30
  ProjectGenerationSummary,
30
31
  ProjectSummary,
32
+ TranscriptionStatus,
31
33
  UsageSummaryResponse,
32
34
  WebhookConfigureResponse,
33
35
  WebhookEventType,
@@ -51,6 +53,7 @@ __all__ = [
51
53
  "GenerationDownloadResponse",
52
54
  "GenerationProgressResponse",
53
55
  "GenerationProgressStage",
56
+ "GenerationTranscription",
54
57
  "GenerationWebhookEvent",
55
58
  "GenerationWebhookStatus",
56
59
  "GenerationsClient",
@@ -61,6 +64,7 @@ __all__ = [
61
64
  "ProjectSummary",
62
65
  "ProjectsClient",
63
66
  "RequestOptions",
67
+ "TranscriptionStatus",
64
68
  "UsageClient",
65
69
  "UsageSummaryResponse",
66
70
  "WebhookConfigureResponse",
@@ -70,4 +74,4 @@ __all__ = [
70
74
  "WebhooksClient",
71
75
  ]
72
76
 
73
- __version__ = "0.1.80"
77
+ __version__ = "0.1.82"
@@ -449,6 +449,9 @@ class DiffioClient:
449
449
  """
450
450
  Polls generation progress until completion or failure.
451
451
 
452
+ For Diffio 2.0, completion means restored media is ready. The returned
453
+ transcription state can still be pending or unavailable.
454
+
452
455
  Parameters
453
456
  ----------
454
457
  generationId : str
@@ -513,13 +516,20 @@ class DiffioClient:
513
516
  apiProjectId : str
514
517
  The project id that owns the generation.
515
518
  downloadType : str, optional
516
- Optional download type, audio, mp3, or video.
519
+ Optional download type, audio, mp3, video, or transcript.
517
520
 
518
521
  Returns
519
522
  -------
520
523
  GenerationDownloadResponse
521
524
  Signed download URL and file metadata.
522
525
 
526
+ Raises
527
+ ------
528
+ DiffioApiError
529
+ Transcript downloads return statusCode 409 with responseBody code
530
+ TRANSCRIPT_PENDING, or 404 with code TRANSCRIPT_UNAVAILABLE. Overall
531
+ generation completion does not guarantee transcript availability.
532
+
523
533
  Examples
524
534
  --------
525
535
  from diffio import DiffioClient
@@ -906,7 +916,7 @@ class GenerationsClient:
906
916
  downloadFilePath : str
907
917
  Local file path to write the downloaded media to.
908
918
  downloadType : str, optional
909
- Optional download type, audio, mp3, or video.
919
+ Optional download type, audio, mp3, video, or transcript.
910
920
 
911
921
  Returns
912
922
  -------
@@ -1495,6 +1505,8 @@ def _init_restore_metadata():
1495
1505
  "errorDetails": None,
1496
1506
  "exceptionType": None,
1497
1507
  "exceptionMessage": None,
1508
+ "statusCode": None,
1509
+ "responseBody": None,
1498
1510
  }
1499
1511
 
1500
1512
 
@@ -1502,6 +1514,9 @@ def _set_restore_error(metadata, exc):
1502
1514
  metadata["error"] = str(exc)
1503
1515
  metadata["exceptionType"] = exc.__class__.__name__
1504
1516
  metadata["exceptionMessage"] = str(exc)
1517
+ if isinstance(exc, DiffioApiError):
1518
+ metadata["statusCode"] = exc.statusCode
1519
+ metadata["responseBody"] = exc.responseBody
1505
1520
 
1506
1521
 
1507
1522
  def _attach_restore_metadata(exc, metadata):
@@ -172,6 +172,15 @@ class GenerationProgressStage:
172
172
  )
173
173
 
174
174
 
175
+ class GenerationTranscription:
176
+ def __init__(self, status):
177
+ self.status = status
178
+
179
+ @classmethod
180
+ def from_dict(cls, data):
181
+ return cls(status=data["status"])
182
+
183
+
175
184
  class GenerationProgressResponse:
176
185
  def __init__(
177
186
  self,
@@ -184,6 +193,7 @@ class GenerationProgressResponse:
184
193
  restoredVideo,
185
194
  error,
186
195
  errorDetails,
196
+ transcription=None,
187
197
  ):
188
198
  self.generationId = generationId
189
199
  self.apiProjectId = apiProjectId
@@ -194,10 +204,12 @@ class GenerationProgressResponse:
194
204
  self.restoredVideo = restoredVideo
195
205
  self.error = error
196
206
  self.errorDetails = errorDetails
207
+ self.transcription = transcription
197
208
 
198
209
  @classmethod
199
210
  def from_dict(cls, data):
200
211
  restored_video = data.get("restoredVideo")
212
+ transcription = data.get("transcription")
201
213
  return cls(
202
214
  generationId=data["generationId"],
203
215
  apiProjectId=data["apiProjectId"],
@@ -208,6 +220,10 @@ class GenerationProgressResponse:
208
220
  restoredVideo=(GenerationProgressStage.from_dict(restored_video) if restored_video else None),
209
221
  error=data.get("error"),
210
222
  errorDetails=data.get("errorDetails"),
223
+ transcription=(
224
+ GenerationTranscription.from_dict(transcription)
225
+ if transcription is not None else None
226
+ ),
211
227
  )
212
228
 
213
229
 
@@ -392,6 +408,7 @@ class GenerationWebhookEvent:
392
408
  modelKey,
393
409
  error,
394
410
  errorDetails,
411
+ transcription=None,
395
412
  ):
396
413
  self.eventType = eventType
397
414
  self.eventId = eventId
@@ -404,9 +421,11 @@ class GenerationWebhookEvent:
404
421
  self.modelKey = modelKey
405
422
  self.error = error
406
423
  self.errorDetails = errorDetails
424
+ self.transcription = transcription
407
425
 
408
426
  @classmethod
409
427
  def from_dict(cls, data):
428
+ transcription = data.get("transcription")
410
429
  return cls(
411
430
  eventType=data["eventType"],
412
431
  eventId=data["eventId"],
@@ -419,6 +438,10 @@ class GenerationWebhookEvent:
419
438
  modelKey=data.get("modelKey"),
420
439
  error=data.get("error"),
421
440
  errorDetails=data.get("errorDetails"),
441
+ transcription=(
442
+ GenerationTranscription.from_dict(transcription)
443
+ if transcription is not None else None
444
+ ),
422
445
  )
423
446
 
424
447
 
@@ -438,3 +461,4 @@ WebhookEventType = (
438
461
  "generation.completed",
439
462
  )
440
463
  GenerationWebhookStatus = ("queued", "processing", "error", "complete")
464
+ TranscriptionStatus = ("pending", "available", "unavailable")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: diffio
3
- Version: 0.1.80
3
+ Version: 0.1.82
4
4
  Summary: Python SDK for the Diffio API.
5
5
  Author: Diffio
6
6
  License: MIT
@@ -155,6 +155,14 @@ print(info["apiProjectId"], info["generationId"])
155
155
  restoration or final settlement is still pending; stage progress alone does not
156
156
  indicate overall completion.
157
157
 
158
+ For Diffio 2.0, `complete` means restored media is ready. Transcription can still
159
+ be `pending`, become `available` later, or finish as `unavailable`. Read
160
+ `progress.transcription.status` independently; `progress.transcription` is `None`
161
+ for older responses that do not report availability. A completed generation
162
+ remains successful if transcription is unavailable. Completion webhooks expose
163
+ the same optional `event.transcription` object; a later transcript does not emit
164
+ another `generation.completed` event.
165
+
158
166
  ```py
159
167
  from diffio import DiffioClient
160
168
 
@@ -165,6 +173,8 @@ progress = client.generations.get_progress(
165
173
  )
166
174
 
167
175
  print(progress.status)
176
+ if progress.transcription is not None:
177
+ print(progress.transcription.status)
168
178
  ```
169
179
 
170
180
  ## Generation download
@@ -186,16 +196,41 @@ print(download.downloadUrl)
186
196
  If you only need the URL, use `client.generations.get_download`.
187
197
 
188
198
  Set `downloadType="transcript"` to download the transcript JSON artifact when the generation has one.
199
+ Pending transcripts return `DiffioApiError` with `statusCode == 409` and
200
+ `responseBody["code"] == "TRANSCRIPT_PENDING"`. Unavailable transcripts return
201
+ `statusCode == 404` and `responseBody["code"] == "TRANSCRIPT_UNAVAILABLE"`. These
202
+ responses include `responseBody["transcription"]["status"]`. Check the error code
203
+ to distinguish them from other 409 or 404 errors.
189
204
 
190
205
  ```py
191
- transcript = client.generations.download(
192
- generationId="gen_123",
193
- apiProjectId="proj_123",
194
- downloadType="transcript",
195
- downloadFilePath="word_timestamps.json",
196
- )
206
+ from diffio import DiffioApiError
207
+
208
+ try:
209
+ transcript = client.generations.download(
210
+ generationId="gen_123",
211
+ apiProjectId="proj_123",
212
+ downloadType="transcript",
213
+ downloadFilePath="word_timestamps.json",
214
+ )
215
+ except DiffioApiError as exc:
216
+ body = exc.responseBody if isinstance(exc.responseBody, dict) else {}
217
+ if exc.statusCode == 409 and body.get("code") == "TRANSCRIPT_PENDING":
218
+ print("Transcript is pending; check progress and retry later.")
219
+ elif exc.statusCode == 404 and body.get("code") == "TRANSCRIPT_UNAVAILABLE":
220
+ print("Transcript is unavailable; restored media remains available.")
221
+ else:
222
+ raise
197
223
  ```
198
224
 
225
+ `restore_audio(downloadType="transcript")` also makes one download request after
226
+ media completion. It does not wait for a pending transcript. With its default
227
+ `raiseOnError=False`, it returns `(None, info)` and preserves the API error in
228
+ `info["statusCode"]` and `info["responseBody"]`; `info["status"]` can still be
229
+ `complete` because media restoration succeeded. With `raiseOnError=True`, it raises
230
+ the same `DiffioApiError` and attaches the metadata as `exc.restoreInfo`. Callers
231
+ can poll progress and retry the transcript download explicitly. Audio and video
232
+ downloads proceed independently of transcription availability.
233
+
199
234
  ## Account, keys, usage, and webhook configuration
200
235
 
201
236
  Agent keys can manage account settings, scoped keys, usage, and webhook endpoints.
@@ -12,4 +12,5 @@ src/diffio.egg-info/dependency_links.txt
12
12
  src/diffio.egg-info/requires.txt
13
13
  src/diffio.egg-info/top_level.txt
14
14
  tests/test_client.py
15
- tests/test_emulator_e2e.py
15
+ tests/test_emulator_e2e.py
16
+ tests/test_transcription.py
@@ -449,7 +449,8 @@ def test_advertised_models_match_runtime_model_support():
449
449
  assert MODEL_ENDPOINTS["diffio-3.4"] == "diffio-3.4-generation"
450
450
 
451
451
 
452
- def test_restore_audio_runs_full_flow_and_downloads(tmp_path: Path, monkeypatch):
452
+ @pytest.mark.parametrize("transcription_status", [None, "pending", "available", "unavailable"])
453
+ def test_restore_audio_runs_full_flow_and_downloads(tmp_path, monkeypatch, transcription_status):
453
454
  status_sequence = ["queued", "processing", "complete"]
454
455
 
455
456
  def handler(request: httpx.Request) -> httpx.Response:
@@ -492,6 +493,7 @@ def test_restore_audio_runs_full_flow_and_downloads(tmp_path: Path, monkeypatch)
492
493
  return httpx.Response(
493
494
  200,
494
495
  json={
496
+ **({"transcription": {"status": transcription_status}} if transcription_status else {}),
495
497
  "generationId": "gen_123",
496
498
  "apiProjectId": "proj_abc",
497
499
  "status": status,
@@ -566,6 +568,12 @@ def test_restore_audio_runs_full_flow_and_downloads(tmp_path: Path, monkeypatch)
566
568
  assert info["downloadUrl"] == "https://download.test/output.mp3"
567
569
  assert info["error"] is None
568
570
  assert info["ok"] is True
571
+ assert info["statusCode"] is None
572
+ assert info["responseBody"] is None
573
+ if transcription_status is None:
574
+ assert info["progress"].transcription is None
575
+ else:
576
+ assert info["progress"].transcription.status == transcription_status
569
577
 
570
578
 
571
579
  def test_create_generation_rejects_unknown_model():
@@ -0,0 +1,182 @@
1
+ import base64
2
+ import json
3
+ from datetime import datetime, timezone
4
+ from types import SimpleNamespace
5
+
6
+ import httpx
7
+ import pytest
8
+ from svix.webhooks import Webhook
9
+
10
+ from diffio import DiffioApiError, DiffioClient, GenerationTranscription, TranscriptionStatus
11
+
12
+
13
+ def completed_progress(transcription_status):
14
+ payload = {
15
+ "generationId": "gen_123",
16
+ "apiProjectId": "proj_123",
17
+ "status": "complete",
18
+ "hasVideo": False,
19
+ "preProcessing": {"status": "complete", "progress": 100},
20
+ "inference": {"status": "complete", "progress": 100},
21
+ }
22
+ if transcription_status is not None:
23
+ payload["transcription"] = {"status": transcription_status}
24
+ return payload
25
+
26
+
27
+ @pytest.mark.parametrize("transcription_status", [None, *TranscriptionStatus])
28
+ @pytest.mark.parametrize("method", ["wait_for_generation", "wait_for_complete"])
29
+ def test_media_completion_does_not_wait_for_transcription(transcription_status, method):
30
+ requests = []
31
+ progress_updates = []
32
+
33
+ def handler(request):
34
+ requests.append(request)
35
+ assert request.url.path == "/v1/get_generation_progress"
36
+ return httpx.Response(200, json=completed_progress(transcription_status))
37
+
38
+ with httpx.Client(base_url="https://api.test", transport=httpx.MockTransport(handler)) as http_client:
39
+ client = DiffioClient(apiKey="diffio_test", httpClient=http_client)
40
+ wait = client.wait_for_generation if method == "wait_for_generation" else client.generations.wait_for_complete
41
+ progress = wait(
42
+ generationId="gen_123",
43
+ apiProjectId="proj_123",
44
+ pollInterval=0,
45
+ timeout=5,
46
+ onProgress=progress_updates.append,
47
+ )
48
+
49
+ assert len(requests) == 1
50
+ assert progress_updates == [progress]
51
+ assert progress.status == "complete"
52
+ if transcription_status is None:
53
+ assert progress.transcription is None
54
+ else:
55
+ assert isinstance(progress.transcription, GenerationTranscription)
56
+ assert progress.transcription.status == transcription_status
57
+
58
+
59
+ @pytest.mark.parametrize("transcription_status", [None, *TranscriptionStatus])
60
+ def test_verified_webhook_preserves_transcription_state(transcription_status):
61
+ payload = {
62
+ "eventType": "generation.completed",
63
+ "eventId": "evt_123",
64
+ "createdAt": "2026-09-14T00:00:00Z",
65
+ "apiKeyId": "key_123",
66
+ "apiProjectId": "proj_123",
67
+ "generationId": "gen_123",
68
+ "status": "complete",
69
+ "modelKey": "diffio-2",
70
+ "hasVideo": False,
71
+ }
72
+ if transcription_status is not None:
73
+ payload["transcription"] = {"status": transcription_status}
74
+ body = json.dumps(payload)
75
+ secret = "whsec_" + base64.b64encode(b"test-webhook-secret").decode("ascii")
76
+ timestamp = datetime.now(timezone.utc)
77
+ headers = {
78
+ "svix-id": "msg_123",
79
+ "svix-timestamp": str(int(timestamp.timestamp())),
80
+ "svix-signature": Webhook(secret).sign("msg_123", timestamp, body),
81
+ }
82
+
83
+ with DiffioClient(apiKey="diffio_test") as client:
84
+ event = client.webhooks.verify_signature(payload=body.encode("utf-8"), headers=headers, secret=secret)
85
+
86
+ assert event.status == "complete"
87
+ assert event.eventType == "generation.completed"
88
+ if transcription_status is None:
89
+ assert event.transcription is None
90
+ else:
91
+ assert isinstance(event.transcription, GenerationTranscription)
92
+ assert event.transcription.status == transcription_status
93
+
94
+
95
+ @pytest.fixture(params=[
96
+ (409, "TRANSCRIPT_PENDING", "pending", "Transcript is not ready yet."),
97
+ (404, "TRANSCRIPT_UNAVAILABLE", "unavailable", "Transcript is unavailable."),
98
+ ])
99
+ def transcript_error(request):
100
+ status_code, code, status, message = request.param
101
+ return status_code, {
102
+ "error": message,
103
+ "code": code,
104
+ "transcription": {"status": status},
105
+ }
106
+
107
+
108
+ @pytest.mark.parametrize("method", ["get_download", "download"])
109
+ def test_transcript_download_preserves_availability_errors(tmp_path, transcript_error, method):
110
+ status_code, body = transcript_error
111
+ requests = []
112
+ output_path = tmp_path / "word_timestamps.json"
113
+ output_path.write_text("existing transcript", encoding="utf-8")
114
+
115
+ def handler(request):
116
+ requests.append(request)
117
+ assert request.url.path == "/v1/get_generation_download"
118
+ assert json.loads(request.content)["downloadType"] == "transcript"
119
+ return httpx.Response(status_code, json=body)
120
+
121
+ with httpx.Client(base_url="https://api.test", transport=httpx.MockTransport(handler)) as http_client:
122
+ client = DiffioClient(apiKey="diffio_test", httpClient=http_client)
123
+ arguments = {
124
+ "generationId": "gen_123",
125
+ "apiProjectId": "proj_123",
126
+ "downloadType": "transcript",
127
+ }
128
+ if method == "download":
129
+ arguments["downloadFilePath"] = output_path
130
+ with pytest.raises(DiffioApiError) as raised:
131
+ getattr(client.generations, method)(**arguments)
132
+
133
+ assert len(requests) == 1
134
+ assert raised.value.message == body["error"]
135
+ assert raised.value.statusCode == status_code
136
+ assert raised.value.responseBody == body
137
+ assert output_path.read_text(encoding="utf-8") == "existing transcript"
138
+
139
+
140
+ @pytest.mark.parametrize("raise_on_error", [False, True])
141
+ def test_restore_transcript_preserves_errors_after_media_completion(monkeypatch, transcript_error, raise_on_error):
142
+ status_code, body = transcript_error
143
+ requests = []
144
+
145
+ def handler(request):
146
+ requests.append(request.url.path)
147
+ if request.url.path == "/v1/get_generation_progress":
148
+ return httpx.Response(200, json=completed_progress("pending"))
149
+ assert request.url.path == "/v1/get_generation_download"
150
+ assert json.loads(request.content)["downloadType"] == "transcript"
151
+ return httpx.Response(status_code, json=body)
152
+
153
+ with httpx.Client(base_url="https://api.test", transport=httpx.MockTransport(handler)) as http_client:
154
+ client = DiffioClient(apiKey="diffio_test", httpClient=http_client)
155
+ monkeypatch.setattr(client.audio_isolation, "isolate", lambda **kwargs: SimpleNamespace(
156
+ project=SimpleNamespace(apiProjectId="proj_123"),
157
+ generation=SimpleNamespace(generationId="gen_123"),
158
+ ))
159
+ arguments = {
160
+ "filePath": "sample.wav",
161
+ "model": "diffio-2",
162
+ "downloadType": "transcript",
163
+ "raiseOnError": raise_on_error,
164
+ }
165
+ if raise_on_error:
166
+ with pytest.raises(DiffioApiError) as raised:
167
+ client.restore_audio(**arguments)
168
+ assert raised.value.statusCode == status_code
169
+ assert raised.value.responseBody == body
170
+ info = raised.value.restoreInfo
171
+ else:
172
+ content, info = client.restore_audio(**arguments)
173
+ assert content is None
174
+
175
+ assert requests == ["/v1/get_generation_progress", "/v1/get_generation_download"]
176
+ assert info["status"] == "complete"
177
+ assert info["progress"].transcription.status == "pending"
178
+ assert info["stage"] == "download_info"
179
+ assert info["ok"] is False
180
+ assert info["exceptionType"] == "DiffioApiError"
181
+ assert info["statusCode"] == status_code
182
+ assert info["responseBody"] == body
File without changes
File without changes
File without changes
File without changes