diffio 0.1.41__tar.gz → 0.1.43__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.41
3
+ Version: 0.1.43
4
4
  Summary: Python SDK for the Diffio API.
5
5
  Author: Diffio
6
6
  License: MIT
@@ -89,11 +89,17 @@ generation = client.create_generation(
89
89
  apiProjectId=project.apiProjectId,
90
90
  model="diffio-3.5",
91
91
  sampling={"steps": 12, "guidance": 1.5},
92
+ idempotencyKey="restore-job-2026-001",
92
93
  )
93
94
 
94
95
  print(generation.generationId)
96
+ print(generation.idempotentReplay)
95
97
  ```
96
98
 
99
+ Use one stable `idempotencyKey` for every retry of the same logical generation request.
100
+ The response's optional `idempotentReplay` value is `True` when the API returns the
101
+ result of an earlier request with that key. Use a new key for a different generation.
102
+
97
103
  ## Audio isolation helper
98
104
 
99
105
  ```py
@@ -61,11 +61,17 @@ generation = client.create_generation(
61
61
  apiProjectId=project.apiProjectId,
62
62
  model="diffio-3.5",
63
63
  sampling={"steps": 12, "guidance": 1.5},
64
+ idempotencyKey="restore-job-2026-001",
64
65
  )
65
66
 
66
67
  print(generation.generationId)
68
+ print(generation.idempotentReplay)
67
69
  ```
68
70
 
71
+ Use one stable `idempotencyKey` for every retry of the same logical generation request.
72
+ The response's optional `idempotentReplay` value is `True` when the API returns the
73
+ result of an earlier request with that key. Use a new key for a different generation.
74
+
69
75
  ## Audio isolation helper
70
76
 
71
77
  ```py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "diffio"
7
- version = "0.1.41"
7
+ version = "0.1.43"
8
8
  description = "Python SDK for the Diffio API."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -70,4 +70,4 @@ __all__ = [
70
70
  "WebhooksClient",
71
71
  ]
72
72
 
73
- __version__ = "0.1.41"
73
+ __version__ = "0.1.43"
@@ -36,7 +36,6 @@ API_PREFIX = "v1"
36
36
  MODEL_ENDPOINTS = {
37
37
  "diffio-2": "diffio-2.0-generation",
38
38
  "diffio-2-flash": "diffio-2.0-flash-generation",
39
- "diffio-3": "diffio-3.0-generation",
40
39
  "diffio-3.4": "diffio-3.4-generation",
41
40
  "diffio-3.5": "diffio-3.5-generation",
42
41
  }
@@ -309,6 +308,7 @@ class DiffioClient:
309
308
  model="diffio-2",
310
309
  sampling=None,
311
310
  params=None,
311
+ idempotencyKey=None,
312
312
  requestOptions=None,
313
313
  ):
314
314
  endpoint = MODEL_ENDPOINTS.get(model)
@@ -320,6 +320,8 @@ class DiffioClient:
320
320
  payload["sampling"] = sampling
321
321
  if params:
322
322
  payload["params"] = params
323
+ if idempotencyKey is not None:
324
+ payload["idempotencyKey"] = idempotencyKey
323
325
 
324
326
  response = self._request("POST", endpoint, json_payload=payload, requestOptions=requestOptions)
325
327
  return CreateGenerationResponse.from_dict(response)
@@ -833,6 +835,7 @@ class GenerationsClient:
833
835
  model="diffio-2",
834
836
  sampling=None,
835
837
  params=None,
838
+ idempotencyKey=None,
836
839
  requestOptions=None,
837
840
  ):
838
841
  return self._parent.create_generation(
@@ -840,6 +843,7 @@ class GenerationsClient:
840
843
  model=model,
841
844
  sampling=sampling,
842
845
  params=params,
846
+ idempotencyKey=idempotencyKey,
843
847
  requestOptions=requestOptions,
844
848
  )
845
849
 
@@ -945,6 +949,7 @@ class GenerationsClient:
945
949
  model="diffio-2",
946
950
  sampling=None,
947
951
  params=None,
952
+ idempotencyKey=None,
948
953
  pollInterval=2.0,
949
954
  timeout=600.0,
950
955
  onProgress=None,
@@ -958,6 +963,7 @@ class GenerationsClient:
958
963
  model=model,
959
964
  sampling=sampling,
960
965
  params=params,
966
+ idempotencyKey=idempotencyKey,
961
967
  requestOptions=requestOptions,
962
968
  )
963
969
  progress = self.wait_for_complete(
@@ -76,11 +76,19 @@ class ListProjectsResponse:
76
76
 
77
77
 
78
78
  class CreateGenerationResponse:
79
- def __init__(self, generationId, apiProjectId, modelKey, status):
79
+ def __init__(
80
+ self,
81
+ generationId,
82
+ apiProjectId,
83
+ modelKey,
84
+ status,
85
+ idempotentReplay=None,
86
+ ):
80
87
  self.generationId = generationId
81
88
  self.apiProjectId = apiProjectId
82
89
  self.modelKey = modelKey
83
90
  self.status = status
91
+ self.idempotentReplay = idempotentReplay
84
92
 
85
93
  @classmethod
86
94
  def from_dict(cls, data):
@@ -89,6 +97,7 @@ class CreateGenerationResponse:
89
97
  apiProjectId=data["apiProjectId"],
90
98
  modelKey=data["modelKey"],
91
99
  status=data["status"],
100
+ idempotentReplay=data.get("idempotentReplay"),
92
101
  )
93
102
 
94
103
 
@@ -419,7 +428,7 @@ class AudioIsolationResult:
419
428
  self.generation = generation
420
429
 
421
430
 
422
- ModelKey = ("diffio-2", "diffio-2-flash", "diffio-3", "diffio-3.4", "diffio-3.5")
431
+ ModelKey = ("diffio-2", "diffio-2-flash", "diffio-3.4", "diffio-3.5")
423
432
  DownloadType = ("audio", "mp3", "video", "transcript")
424
433
  WebhookMode = ("test", "live")
425
434
  WebhookEventType = (
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: diffio
3
- Version: 0.1.41
3
+ Version: 0.1.43
4
4
  Summary: Python SDK for the Diffio API.
5
5
  Author: Diffio
6
6
  License: MIT
@@ -89,11 +89,17 @@ generation = client.create_generation(
89
89
  apiProjectId=project.apiProjectId,
90
90
  model="diffio-3.5",
91
91
  sampling={"steps": 12, "guidance": 1.5},
92
+ idempotencyKey="restore-job-2026-001",
92
93
  )
93
94
 
94
95
  print(generation.generationId)
96
+ print(generation.idempotentReplay)
95
97
  ```
96
98
 
99
+ Use one stable `idempotencyKey` for every retry of the same logical generation request.
100
+ The response's optional `idempotentReplay` value is `True` when the API returns the
101
+ result of an earlier request with that key. Use a new key for a different generation.
102
+
97
103
  ## Audio isolation helper
98
104
 
99
105
  ```py
@@ -5,7 +5,8 @@ from pathlib import Path
5
5
  import httpx
6
6
  import pytest
7
7
 
8
- from diffio import DiffioClient
8
+ from diffio import DiffioClient, ModelKey
9
+ from diffio.client import MODEL_ENDPOINTS
9
10
 
10
11
 
11
12
  def test_create_project_payload_and_headers(tmp_path: Path):
@@ -308,7 +309,143 @@ def test_create_generation_routes_to_diffio_3_5_endpoint():
308
309
 
309
310
  assert received["path"] == "/v1/diffio-3.5-generation"
310
311
  assert received["payload"]["apiProjectId"] == "proj_35"
312
+ assert "idempotencyKey" not in received["payload"]
311
313
  assert response.modelKey == "diffio-3.5"
314
+ assert response.idempotentReplay is None
315
+
316
+
317
+ def test_create_generation_sends_idempotency_key_and_parses_replay():
318
+ received = {}
319
+
320
+ def handler(request):
321
+ received["path"] = request.url.path
322
+ received["payload"] = json.loads(request.content.decode("utf-8"))
323
+ return httpx.Response(
324
+ 200,
325
+ json={
326
+ "generationId": "gen_original",
327
+ "apiProjectId": "proj_123",
328
+ "modelKey": "diffio-2",
329
+ "status": "queued",
330
+ "idempotentReplay": True,
331
+ },
332
+ )
333
+
334
+ transport = httpx.MockTransport(handler)
335
+ http_client = httpx.Client(base_url="https://api.test", transport=transport)
336
+ client = DiffioClient(apiKey="diffio_live_test", baseUrl="https://api.test", httpClient=http_client)
337
+
338
+ response = client.create_generation(
339
+ apiProjectId="proj_123",
340
+ idempotencyKey="restore-job-2026-001",
341
+ )
342
+
343
+ assert received["path"] == "/v1/diffio-2.0-generation"
344
+ assert received["payload"] == {
345
+ "apiProjectId": "proj_123",
346
+ "idempotencyKey": "restore-job-2026-001",
347
+ }
348
+ assert response.generationId == "gen_original"
349
+ assert response.idempotentReplay is True
350
+
351
+
352
+ def test_generations_create_forwards_idempotency_key():
353
+ received = {}
354
+
355
+ def handler(request):
356
+ received["payload"] = json.loads(request.content.decode("utf-8"))
357
+ return httpx.Response(
358
+ 200,
359
+ json={
360
+ "generationId": "gen_123",
361
+ "apiProjectId": "proj_123",
362
+ "modelKey": "diffio-2",
363
+ "status": "queued",
364
+ },
365
+ )
366
+
367
+ transport = httpx.MockTransport(handler)
368
+ http_client = httpx.Client(base_url="https://api.test", transport=transport)
369
+ client = DiffioClient(apiKey="diffio_live_test", baseUrl="https://api.test", httpClient=http_client)
370
+
371
+ response = client.generations.create(
372
+ apiProjectId="proj_123",
373
+ idempotencyKey="restore-job-2026-wrapper",
374
+ )
375
+
376
+ assert received["payload"]["idempotencyKey"] == "restore-job-2026-wrapper"
377
+ assert response.idempotentReplay is None
378
+
379
+
380
+ def test_generations_create_and_wait_forwards_idempotency_key():
381
+ received = {}
382
+
383
+ def handler(request):
384
+ if request.url.path == "/v1/diffio-2.0-generation":
385
+ received["createPayload"] = json.loads(request.content.decode("utf-8"))
386
+ return httpx.Response(
387
+ 200,
388
+ json={
389
+ "generationId": "gen_123",
390
+ "apiProjectId": "proj_123",
391
+ "modelKey": "diffio-2",
392
+ "status": "queued",
393
+ "idempotentReplay": False,
394
+ },
395
+ )
396
+ if request.url.path == "/v1/get_generation_progress":
397
+ return httpx.Response(
398
+ 200,
399
+ json={
400
+ "generationId": "gen_123",
401
+ "apiProjectId": "proj_123",
402
+ "status": "complete",
403
+ "hasVideo": False,
404
+ "preProcessing": {
405
+ "jobId": "job-pre",
406
+ "jobState": "SUCCEEDED",
407
+ "status": "complete",
408
+ "progress": 100,
409
+ "statusMessage": None,
410
+ "error": None,
411
+ "errorDetails": None,
412
+ },
413
+ "inference": {
414
+ "jobId": "job-run",
415
+ "jobState": "SUCCEEDED",
416
+ "status": "complete",
417
+ "progress": 100,
418
+ "statusMessage": None,
419
+ "error": None,
420
+ "errorDetails": None,
421
+ },
422
+ },
423
+ )
424
+ return httpx.Response(404, json={"error": "not found"})
425
+
426
+ transport = httpx.MockTransport(handler)
427
+ http_client = httpx.Client(base_url="https://api.test", transport=transport)
428
+ client = DiffioClient(apiKey="diffio_live_test", baseUrl="https://api.test", httpClient=http_client)
429
+
430
+ generation, progress = client.generations.create_and_wait(
431
+ apiProjectId="proj_123",
432
+ idempotencyKey="restore-job-2026-002",
433
+ pollInterval=0,
434
+ )
435
+
436
+ assert received["createPayload"] == {
437
+ "apiProjectId": "proj_123",
438
+ "idempotencyKey": "restore-job-2026-002",
439
+ }
440
+ assert generation.idempotentReplay is False
441
+ assert progress.status == "complete"
442
+
443
+
444
+ def test_advertised_models_match_runtime_model_support():
445
+ assert set(ModelKey) == set(MODEL_ENDPOINTS)
446
+ assert "diffio-3" not in ModelKey
447
+ assert "diffio-3.0-generation" not in MODEL_ENDPOINTS.values()
448
+ assert MODEL_ENDPOINTS["diffio-3.4"] == "diffio-3.4-generation"
312
449
 
313
450
 
314
451
  def test_restore_audio_runs_full_flow_and_downloads(tmp_path: Path, monkeypatch):
@@ -439,6 +576,18 @@ def test_create_generation_rejects_unknown_model():
439
576
  client.create_generation(apiProjectId="proj", model="diffio-unknown")
440
577
 
441
578
 
579
+ def test_create_generation_rejects_diffio_3():
580
+ def handler(request):
581
+ raise AssertionError("Unsupported models must be rejected before an HTTP request")
582
+
583
+ transport = httpx.MockTransport(handler)
584
+ http_client = httpx.Client(base_url="https://api.test", transport=transport)
585
+ client = DiffioClient(apiKey="diffio_live_test", baseUrl="https://api.test", httpClient=http_client)
586
+
587
+ with pytest.raises(ValueError, match="Unsupported model: diffio-3"):
588
+ client.create_generation(apiProjectId="proj", model="diffio-3")
589
+
590
+
442
591
  def test_get_generation_progress_payload_and_response():
443
592
  received = {}
444
593
 
File without changes
File without changes
File without changes
File without changes