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.
- {diffio-0.1.80/src/diffio.egg-info → diffio-0.1.82}/PKG-INFO +42 -7
- {diffio-0.1.80 → diffio-0.1.82}/README.md +41 -6
- {diffio-0.1.80 → diffio-0.1.82}/pyproject.toml +1 -1
- {diffio-0.1.80 → diffio-0.1.82}/src/diffio/__init__.py +5 -1
- {diffio-0.1.80 → diffio-0.1.82}/src/diffio/client.py +17 -2
- {diffio-0.1.80 → diffio-0.1.82}/src/diffio/types.py +24 -0
- {diffio-0.1.80 → diffio-0.1.82/src/diffio.egg-info}/PKG-INFO +42 -7
- {diffio-0.1.80 → diffio-0.1.82}/src/diffio.egg-info/SOURCES.txt +2 -1
- {diffio-0.1.80 → diffio-0.1.82}/tests/test_client.py +9 -1
- diffio-0.1.82/tests/test_transcription.py +182 -0
- {diffio-0.1.80 → diffio-0.1.82}/LICENSE +0 -0
- {diffio-0.1.80 → diffio-0.1.82}/setup.cfg +0 -0
- {diffio-0.1.80 → diffio-0.1.82}/src/diffio/errors.py +0 -0
- {diffio-0.1.80 → diffio-0.1.82}/src/diffio/testing.py +0 -0
- {diffio-0.1.80 → diffio-0.1.82}/src/diffio.egg-info/dependency_links.txt +0 -0
- {diffio-0.1.80 → diffio-0.1.82}/src/diffio.egg-info/requires.txt +0 -0
- {diffio-0.1.80 → diffio-0.1.82}/src/diffio.egg-info/top_level.txt +0 -0
- {diffio-0.1.80 → diffio-0.1.82}/tests/test_emulator_e2e.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: diffio
|
|
3
|
-
Version: 0.1.
|
|
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
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
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.
|
|
@@ -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.
|
|
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
|
|
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
|
|
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.
|
|
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
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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.
|
|
@@ -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
|
-
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|