diffio 0.1.6__tar.gz → 0.1.7__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.6/src/diffio.egg-info → diffio-0.1.7}/PKG-INFO +34 -4
- {diffio-0.1.6 → diffio-0.1.7}/README.md +33 -3
- {diffio-0.1.6 → diffio-0.1.7}/pyproject.toml +1 -1
- {diffio-0.1.6 → diffio-0.1.7}/src/diffio/__init__.py +17 -1
- {diffio-0.1.6 → diffio-0.1.7}/src/diffio/client.py +181 -2
- {diffio-0.1.6 → diffio-0.1.7}/src/diffio/types.py +115 -2
- {diffio-0.1.6 → diffio-0.1.7/src/diffio.egg-info}/PKG-INFO +34 -4
- {diffio-0.1.6 → diffio-0.1.7}/tests/test_client.py +139 -0
- {diffio-0.1.6 → diffio-0.1.7}/tests/test_emulator_e2e.py +7 -2
- {diffio-0.1.6 → diffio-0.1.7}/LICENSE +0 -0
- {diffio-0.1.6 → diffio-0.1.7}/setup.cfg +0 -0
- {diffio-0.1.6 → diffio-0.1.7}/src/diffio/errors.py +0 -0
- {diffio-0.1.6 → diffio-0.1.7}/src/diffio/testing.py +0 -0
- {diffio-0.1.6 → diffio-0.1.7}/src/diffio.egg-info/SOURCES.txt +0 -0
- {diffio-0.1.6 → diffio-0.1.7}/src/diffio.egg-info/dependency_links.txt +0 -0
- {diffio-0.1.6 → diffio-0.1.7}/src/diffio.egg-info/requires.txt +0 -0
- {diffio-0.1.6 → diffio-0.1.7}/src/diffio.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: diffio
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.7
|
|
4
4
|
Summary: Python SDK for the Diffio API.
|
|
5
5
|
Author: Diffio
|
|
6
6
|
License: MIT
|
|
@@ -87,7 +87,7 @@ project = client.create_project(
|
|
|
87
87
|
|
|
88
88
|
generation = client.create_generation(
|
|
89
89
|
apiProjectId=project.apiProjectId,
|
|
90
|
-
model="diffio-
|
|
90
|
+
model="diffio-3.5",
|
|
91
91
|
sampling={"steps": 12, "guidance": 1.5},
|
|
92
92
|
)
|
|
93
93
|
|
|
@@ -102,7 +102,7 @@ from diffio import DiffioClient
|
|
|
102
102
|
client = DiffioClient(apiKey="diffio_live_...")
|
|
103
103
|
result = client.audio_isolation.isolate(
|
|
104
104
|
filePath="sample.wav",
|
|
105
|
-
model="diffio-
|
|
105
|
+
model="diffio-3.5",
|
|
106
106
|
sampling={"steps": 12, "guidance": 1.5},
|
|
107
107
|
)
|
|
108
108
|
|
|
@@ -119,7 +119,7 @@ from diffio import DiffioClient
|
|
|
119
119
|
client = DiffioClient(apiKey="diffio_live_...")
|
|
120
120
|
audio_bytes, info = client.restore_audio(
|
|
121
121
|
filePath="sample.wav",
|
|
122
|
-
model="diffio-
|
|
122
|
+
model="diffio-3.5",
|
|
123
123
|
sampling={"steps": 12, "guidance": 1.5},
|
|
124
124
|
onProgress=lambda progress: print(progress.status),
|
|
125
125
|
)
|
|
@@ -165,6 +165,36 @@ print(download.downloadUrl)
|
|
|
165
165
|
|
|
166
166
|
If you only need the URL, use `client.generations.get_download`.
|
|
167
167
|
|
|
168
|
+
Set `downloadType="transcript"` to download the transcript JSON artifact when the generation has one.
|
|
169
|
+
|
|
170
|
+
```py
|
|
171
|
+
transcript = client.generations.download(
|
|
172
|
+
generationId="gen_123",
|
|
173
|
+
apiProjectId="proj_123",
|
|
174
|
+
downloadType="transcript",
|
|
175
|
+
downloadFilePath="word_timestamps.json",
|
|
176
|
+
)
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Account, keys, usage, and webhook configuration
|
|
180
|
+
|
|
181
|
+
Agent keys can manage account settings, scoped keys, usage, and webhook endpoints.
|
|
182
|
+
|
|
183
|
+
```py
|
|
184
|
+
settings = client.account.get_settings()
|
|
185
|
+
key = client.api_keys.create(
|
|
186
|
+
label="Backend worker",
|
|
187
|
+
scopes=["projects:read", "projects:write", "generations:read", "generations:write", "artifacts:read"],
|
|
188
|
+
)
|
|
189
|
+
usage = client.usage.summary(apiKeyId=key.keyId)
|
|
190
|
+
webhook = client.webhooks.configure(
|
|
191
|
+
mode="live",
|
|
192
|
+
url="https://example.com/webhooks/diffio",
|
|
193
|
+
eventTypes=["generation.completed", "generation.failed"],
|
|
194
|
+
apiKeyId=key.keyId,
|
|
195
|
+
)
|
|
196
|
+
```
|
|
197
|
+
|
|
168
198
|
## List projects
|
|
169
199
|
|
|
170
200
|
```py
|
|
@@ -59,7 +59,7 @@ project = client.create_project(
|
|
|
59
59
|
|
|
60
60
|
generation = client.create_generation(
|
|
61
61
|
apiProjectId=project.apiProjectId,
|
|
62
|
-
model="diffio-
|
|
62
|
+
model="diffio-3.5",
|
|
63
63
|
sampling={"steps": 12, "guidance": 1.5},
|
|
64
64
|
)
|
|
65
65
|
|
|
@@ -74,7 +74,7 @@ from diffio import DiffioClient
|
|
|
74
74
|
client = DiffioClient(apiKey="diffio_live_...")
|
|
75
75
|
result = client.audio_isolation.isolate(
|
|
76
76
|
filePath="sample.wav",
|
|
77
|
-
model="diffio-
|
|
77
|
+
model="diffio-3.5",
|
|
78
78
|
sampling={"steps": 12, "guidance": 1.5},
|
|
79
79
|
)
|
|
80
80
|
|
|
@@ -91,7 +91,7 @@ from diffio import DiffioClient
|
|
|
91
91
|
client = DiffioClient(apiKey="diffio_live_...")
|
|
92
92
|
audio_bytes, info = client.restore_audio(
|
|
93
93
|
filePath="sample.wav",
|
|
94
|
-
model="diffio-
|
|
94
|
+
model="diffio-3.5",
|
|
95
95
|
sampling={"steps": 12, "guidance": 1.5},
|
|
96
96
|
onProgress=lambda progress: print(progress.status),
|
|
97
97
|
)
|
|
@@ -137,6 +137,36 @@ print(download.downloadUrl)
|
|
|
137
137
|
|
|
138
138
|
If you only need the URL, use `client.generations.get_download`.
|
|
139
139
|
|
|
140
|
+
Set `downloadType="transcript"` to download the transcript JSON artifact when the generation has one.
|
|
141
|
+
|
|
142
|
+
```py
|
|
143
|
+
transcript = client.generations.download(
|
|
144
|
+
generationId="gen_123",
|
|
145
|
+
apiProjectId="proj_123",
|
|
146
|
+
downloadType="transcript",
|
|
147
|
+
downloadFilePath="word_timestamps.json",
|
|
148
|
+
)
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Account, keys, usage, and webhook configuration
|
|
152
|
+
|
|
153
|
+
Agent keys can manage account settings, scoped keys, usage, and webhook endpoints.
|
|
154
|
+
|
|
155
|
+
```py
|
|
156
|
+
settings = client.account.get_settings()
|
|
157
|
+
key = client.api_keys.create(
|
|
158
|
+
label="Backend worker",
|
|
159
|
+
scopes=["projects:read", "projects:write", "generations:read", "generations:write", "artifacts:read"],
|
|
160
|
+
)
|
|
161
|
+
usage = client.usage.summary(apiKeyId=key.keyId)
|
|
162
|
+
webhook = client.webhooks.configure(
|
|
163
|
+
mode="live",
|
|
164
|
+
url="https://example.com/webhooks/diffio",
|
|
165
|
+
eventTypes=["generation.completed", "generation.failed"],
|
|
166
|
+
apiKeyId=key.keyId,
|
|
167
|
+
)
|
|
168
|
+
```
|
|
169
|
+
|
|
140
170
|
## List projects
|
|
141
171
|
|
|
142
172
|
```py
|
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
from .client import (
|
|
2
|
+
AccountClient,
|
|
3
|
+
ApiKeysClient,
|
|
2
4
|
AudioIsolationClient,
|
|
3
5
|
DiffioClient,
|
|
4
6
|
GenerationsClient,
|
|
5
7
|
ProjectsClient,
|
|
6
8
|
RequestOptions,
|
|
9
|
+
UsageClient,
|
|
7
10
|
WebhooksClient,
|
|
8
11
|
)
|
|
9
12
|
from .errors import DiffioApiError
|
|
10
13
|
from .types import (
|
|
14
|
+
AccountSettingsResponse,
|
|
15
|
+
ApiKeyResponse,
|
|
16
|
+
ApiKeysListResponse,
|
|
11
17
|
AudioIsolationResult,
|
|
12
18
|
CreateGenerationResponse,
|
|
13
19
|
CreateProjectResponse,
|
|
@@ -22,12 +28,19 @@ from .types import (
|
|
|
22
28
|
ModelKey,
|
|
23
29
|
ProjectGenerationSummary,
|
|
24
30
|
ProjectSummary,
|
|
31
|
+
UsageSummaryResponse,
|
|
32
|
+
WebhookConfigureResponse,
|
|
25
33
|
WebhookEventType,
|
|
26
34
|
WebhookMode,
|
|
27
35
|
WebhookTestEventResponse,
|
|
28
36
|
)
|
|
29
37
|
|
|
30
38
|
__all__ = [
|
|
39
|
+
"AccountClient",
|
|
40
|
+
"AccountSettingsResponse",
|
|
41
|
+
"ApiKeyResponse",
|
|
42
|
+
"ApiKeysClient",
|
|
43
|
+
"ApiKeysListResponse",
|
|
31
44
|
"AudioIsolationClient",
|
|
32
45
|
"AudioIsolationResult",
|
|
33
46
|
"CreateGenerationResponse",
|
|
@@ -48,10 +61,13 @@ __all__ = [
|
|
|
48
61
|
"ProjectSummary",
|
|
49
62
|
"ProjectsClient",
|
|
50
63
|
"RequestOptions",
|
|
64
|
+
"UsageClient",
|
|
65
|
+
"UsageSummaryResponse",
|
|
66
|
+
"WebhookConfigureResponse",
|
|
51
67
|
"WebhookEventType",
|
|
52
68
|
"WebhookMode",
|
|
53
69
|
"WebhookTestEventResponse",
|
|
54
70
|
"WebhooksClient",
|
|
55
71
|
]
|
|
56
72
|
|
|
57
|
-
__version__ = "0.1.
|
|
73
|
+
__version__ = "0.1.7"
|
|
@@ -11,6 +11,9 @@ from svix.webhooks import Webhook
|
|
|
11
11
|
|
|
12
12
|
from .errors import DiffioApiError
|
|
13
13
|
from .types import (
|
|
14
|
+
AccountSettingsResponse,
|
|
15
|
+
ApiKeyResponse,
|
|
16
|
+
ApiKeysListResponse,
|
|
14
17
|
AudioIsolationResult,
|
|
15
18
|
CreateGenerationResponse,
|
|
16
19
|
CreateProjectResponse,
|
|
@@ -21,6 +24,8 @@ from .types import (
|
|
|
21
24
|
ListProjectGenerationsResponse,
|
|
22
25
|
ListProjectsResponse,
|
|
23
26
|
ModelKey,
|
|
27
|
+
UsageSummaryResponse,
|
|
28
|
+
WebhookConfigureResponse,
|
|
24
29
|
WebhookEventType,
|
|
25
30
|
WebhookMode,
|
|
26
31
|
WebhookTestEventResponse,
|
|
@@ -32,6 +37,8 @@ MODEL_ENDPOINTS = {
|
|
|
32
37
|
"diffio-2": "diffio-2.0-generation",
|
|
33
38
|
"diffio-2-flash": "diffio-2.0-flash-generation",
|
|
34
39
|
"diffio-3": "diffio-3.0-generation",
|
|
40
|
+
"diffio-3.4": "diffio-3.4-generation",
|
|
41
|
+
"diffio-3.5": "diffio-3.5-generation",
|
|
35
42
|
}
|
|
36
43
|
DEFAULT_RETRY_STATUS_CODES = [408, 429, 500, 502, 503, 504]
|
|
37
44
|
DEFAULT_RETRY_BACKOFF = 0.5
|
|
@@ -175,6 +182,9 @@ class DiffioClient:
|
|
|
175
182
|
self.audio_isolation = AudioIsolationClient(self)
|
|
176
183
|
self.generations = GenerationsClient(self)
|
|
177
184
|
self.projects = ProjectsClient(self)
|
|
185
|
+
self.account = AccountClient(self)
|
|
186
|
+
self.api_keys = ApiKeysClient(self)
|
|
187
|
+
self.usage = UsageClient(self)
|
|
178
188
|
self.webhooks = WebhooksClient(self)
|
|
179
189
|
|
|
180
190
|
def __enter__(self):
|
|
@@ -575,6 +585,112 @@ class DiffioClient:
|
|
|
575
585
|
)
|
|
576
586
|
return WebhookTestEventResponse.from_dict(response)
|
|
577
587
|
|
|
588
|
+
def get_account_settings(self, *, requestOptions=None):
|
|
589
|
+
response = self._request(
|
|
590
|
+
"POST",
|
|
591
|
+
"account/settings/get",
|
|
592
|
+
json_payload={},
|
|
593
|
+
requestOptions=requestOptions,
|
|
594
|
+
)
|
|
595
|
+
return AccountSettingsResponse.from_dict(response)
|
|
596
|
+
|
|
597
|
+
def update_account_settings(self, *, billingPolicy, requestOptions=None):
|
|
598
|
+
if not isinstance(billingPolicy, dict):
|
|
599
|
+
raise ValueError("billingPolicy must be an object")
|
|
600
|
+
response = self._request(
|
|
601
|
+
"POST",
|
|
602
|
+
"account/settings/update",
|
|
603
|
+
json_payload={"billingPolicy": billingPolicy},
|
|
604
|
+
requestOptions=requestOptions,
|
|
605
|
+
)
|
|
606
|
+
return AccountSettingsResponse.from_dict(response)
|
|
607
|
+
|
|
608
|
+
def create_api_key(self, *, label, scopes, resourceBounds=None, requestOptions=None):
|
|
609
|
+
if not label:
|
|
610
|
+
raise ValueError("label is required")
|
|
611
|
+
if not isinstance(scopes, list):
|
|
612
|
+
raise ValueError("scopes must be a list")
|
|
613
|
+
response = self._request(
|
|
614
|
+
"POST",
|
|
615
|
+
"api_keys/create",
|
|
616
|
+
json_payload={
|
|
617
|
+
"label": label,
|
|
618
|
+
"scopes": scopes,
|
|
619
|
+
"resourceBounds": resourceBounds or {},
|
|
620
|
+
},
|
|
621
|
+
requestOptions=requestOptions,
|
|
622
|
+
)
|
|
623
|
+
return ApiKeyResponse.from_dict(response)
|
|
624
|
+
|
|
625
|
+
def list_api_keys(self, *, requestOptions=None):
|
|
626
|
+
response = self._request(
|
|
627
|
+
"POST",
|
|
628
|
+
"api_keys/list",
|
|
629
|
+
json_payload={},
|
|
630
|
+
requestOptions=requestOptions,
|
|
631
|
+
)
|
|
632
|
+
return ApiKeysListResponse.from_dict(response)
|
|
633
|
+
|
|
634
|
+
def rotate_api_key(self, *, keyId, requestOptions=None):
|
|
635
|
+
if not keyId:
|
|
636
|
+
raise ValueError("keyId is required")
|
|
637
|
+
response = self._request(
|
|
638
|
+
"POST",
|
|
639
|
+
"api_keys/rotate",
|
|
640
|
+
json_payload={"keyId": keyId},
|
|
641
|
+
requestOptions=requestOptions,
|
|
642
|
+
)
|
|
643
|
+
return ApiKeyResponse.from_dict(response)
|
|
644
|
+
|
|
645
|
+
def revoke_api_key(self, *, keyId, requestOptions=None):
|
|
646
|
+
if not keyId:
|
|
647
|
+
raise ValueError("keyId is required")
|
|
648
|
+
response = self._request(
|
|
649
|
+
"POST",
|
|
650
|
+
"api_keys/revoke",
|
|
651
|
+
json_payload={"keyId": keyId},
|
|
652
|
+
requestOptions=requestOptions,
|
|
653
|
+
)
|
|
654
|
+
return ApiKeyResponse.from_dict(response)
|
|
655
|
+
|
|
656
|
+
def get_usage_summary(self, *, apiKeyId=None, requestOptions=None):
|
|
657
|
+
payload = {}
|
|
658
|
+
if apiKeyId is not None:
|
|
659
|
+
payload["apiKeyId"] = apiKeyId
|
|
660
|
+
response = self._request(
|
|
661
|
+
"POST",
|
|
662
|
+
"usage/summary",
|
|
663
|
+
json_payload=payload,
|
|
664
|
+
requestOptions=requestOptions,
|
|
665
|
+
)
|
|
666
|
+
return UsageSummaryResponse.from_dict(response)
|
|
667
|
+
|
|
668
|
+
def configure_webhook(
|
|
669
|
+
self,
|
|
670
|
+
*,
|
|
671
|
+
mode,
|
|
672
|
+
url,
|
|
673
|
+
eventTypes,
|
|
674
|
+
apiKeyId=None,
|
|
675
|
+
requestOptions=None,
|
|
676
|
+
):
|
|
677
|
+
if mode not in WebhookMode:
|
|
678
|
+
raise ValueError("mode must be test or live")
|
|
679
|
+
if not url:
|
|
680
|
+
raise ValueError("url is required")
|
|
681
|
+
if not isinstance(eventTypes, list) or not eventTypes:
|
|
682
|
+
raise ValueError("eventTypes must be a non-empty list")
|
|
683
|
+
payload = {"mode": mode, "url": url, "eventTypes": eventTypes}
|
|
684
|
+
if apiKeyId is not None:
|
|
685
|
+
payload["apiKeyId"] = apiKeyId
|
|
686
|
+
response = self._request(
|
|
687
|
+
"POST",
|
|
688
|
+
"webhooks/configure",
|
|
689
|
+
json_payload=payload,
|
|
690
|
+
requestOptions=requestOptions,
|
|
691
|
+
)
|
|
692
|
+
return WebhookConfigureResponse.from_dict(response)
|
|
693
|
+
|
|
578
694
|
def restore_audio(
|
|
579
695
|
self,
|
|
580
696
|
*,
|
|
@@ -662,6 +778,50 @@ class DiffioClient:
|
|
|
662
778
|
return _raise_for_error(response)
|
|
663
779
|
|
|
664
780
|
|
|
781
|
+
class AccountClient:
|
|
782
|
+
def __init__(self, parent):
|
|
783
|
+
self._parent = parent
|
|
784
|
+
|
|
785
|
+
def get_settings(self, *, requestOptions=None):
|
|
786
|
+
return self._parent.get_account_settings(requestOptions=requestOptions)
|
|
787
|
+
|
|
788
|
+
def update_settings(self, *, billingPolicy, requestOptions=None):
|
|
789
|
+
return self._parent.update_account_settings(
|
|
790
|
+
billingPolicy=billingPolicy,
|
|
791
|
+
requestOptions=requestOptions,
|
|
792
|
+
)
|
|
793
|
+
|
|
794
|
+
|
|
795
|
+
class ApiKeysClient:
|
|
796
|
+
def __init__(self, parent):
|
|
797
|
+
self._parent = parent
|
|
798
|
+
|
|
799
|
+
def create(self, *, label, scopes, resourceBounds=None, requestOptions=None):
|
|
800
|
+
return self._parent.create_api_key(
|
|
801
|
+
label=label,
|
|
802
|
+
scopes=scopes,
|
|
803
|
+
resourceBounds=resourceBounds,
|
|
804
|
+
requestOptions=requestOptions,
|
|
805
|
+
)
|
|
806
|
+
|
|
807
|
+
def list(self, *, requestOptions=None):
|
|
808
|
+
return self._parent.list_api_keys(requestOptions=requestOptions)
|
|
809
|
+
|
|
810
|
+
def rotate(self, *, keyId, requestOptions=None):
|
|
811
|
+
return self._parent.rotate_api_key(keyId=keyId, requestOptions=requestOptions)
|
|
812
|
+
|
|
813
|
+
def revoke(self, *, keyId, requestOptions=None):
|
|
814
|
+
return self._parent.revoke_api_key(keyId=keyId, requestOptions=requestOptions)
|
|
815
|
+
|
|
816
|
+
|
|
817
|
+
class UsageClient:
|
|
818
|
+
def __init__(self, parent):
|
|
819
|
+
self._parent = parent
|
|
820
|
+
|
|
821
|
+
def summary(self, *, apiKeyId=None, requestOptions=None):
|
|
822
|
+
return self._parent.get_usage_summary(apiKeyId=apiKeyId, requestOptions=requestOptions)
|
|
823
|
+
|
|
824
|
+
|
|
665
825
|
class GenerationsClient:
|
|
666
826
|
def __init__(self, parent):
|
|
667
827
|
self._parent = parent
|
|
@@ -852,6 +1012,23 @@ class WebhooksClient:
|
|
|
852
1012
|
requestOptions=requestOptions,
|
|
853
1013
|
)
|
|
854
1014
|
|
|
1015
|
+
def configure(
|
|
1016
|
+
self,
|
|
1017
|
+
*,
|
|
1018
|
+
mode,
|
|
1019
|
+
url,
|
|
1020
|
+
eventTypes,
|
|
1021
|
+
apiKeyId=None,
|
|
1022
|
+
requestOptions=None,
|
|
1023
|
+
):
|
|
1024
|
+
return self._parent.configure_webhook(
|
|
1025
|
+
mode=mode,
|
|
1026
|
+
url=url,
|
|
1027
|
+
eventTypes=eventTypes,
|
|
1028
|
+
apiKeyId=apiKeyId,
|
|
1029
|
+
requestOptions=requestOptions,
|
|
1030
|
+
)
|
|
1031
|
+
|
|
855
1032
|
def verify_signature(
|
|
856
1033
|
self,
|
|
857
1034
|
*,
|
|
@@ -1151,9 +1328,9 @@ def _normalize_download_type(download_type):
|
|
|
1151
1328
|
return None, None
|
|
1152
1329
|
if download_type == "mp3":
|
|
1153
1330
|
return "audio", "mp3"
|
|
1154
|
-
if download_type in {"audio", "video"}:
|
|
1331
|
+
if download_type in {"audio", "video", "transcript"}:
|
|
1155
1332
|
return download_type, download_type
|
|
1156
|
-
raise ValueError("downloadType must be audio, mp3, or
|
|
1333
|
+
raise ValueError("downloadType must be audio, mp3, video, or transcript")
|
|
1157
1334
|
|
|
1158
1335
|
|
|
1159
1336
|
def _extension_from_file_name(file_name):
|
|
@@ -1190,6 +1367,8 @@ def _extension_from_url(download_url):
|
|
|
1190
1367
|
def _expected_download_extension(download):
|
|
1191
1368
|
if download.downloadType == "audio":
|
|
1192
1369
|
return ".mp3"
|
|
1370
|
+
if download.downloadType == "transcript":
|
|
1371
|
+
return ".json"
|
|
1193
1372
|
if download.downloadType == "video":
|
|
1194
1373
|
return (
|
|
1195
1374
|
_extension_from_file_name(download.fileName)
|
|
@@ -256,6 +256,119 @@ class WebhookTestEventResponse:
|
|
|
256
256
|
)
|
|
257
257
|
|
|
258
258
|
|
|
259
|
+
class AccountSettingsResponse:
|
|
260
|
+
def __init__(self, account, apiKeyId=None):
|
|
261
|
+
self.account = account
|
|
262
|
+
self.apiKeyId = apiKeyId
|
|
263
|
+
|
|
264
|
+
@classmethod
|
|
265
|
+
def from_dict(cls, data):
|
|
266
|
+
account = data.get("account")
|
|
267
|
+
if not isinstance(account, dict):
|
|
268
|
+
account = {}
|
|
269
|
+
return cls(account=account, apiKeyId=data.get("apiKeyId"))
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
class ApiKeyResponse:
|
|
273
|
+
def __init__(
|
|
274
|
+
self,
|
|
275
|
+
keyId,
|
|
276
|
+
label,
|
|
277
|
+
status,
|
|
278
|
+
keyPrefix,
|
|
279
|
+
role,
|
|
280
|
+
scopes,
|
|
281
|
+
resourceBounds,
|
|
282
|
+
key=None,
|
|
283
|
+
parentKeyId=None,
|
|
284
|
+
createdAt=None,
|
|
285
|
+
rotatedAt=None,
|
|
286
|
+
revokedAt=None,
|
|
287
|
+
permissions=None,
|
|
288
|
+
):
|
|
289
|
+
self.key = key
|
|
290
|
+
self.keyId = keyId
|
|
291
|
+
self.label = label
|
|
292
|
+
self.status = status
|
|
293
|
+
self.keyPrefix = keyPrefix
|
|
294
|
+
self.role = role
|
|
295
|
+
self.scopes = scopes
|
|
296
|
+
self.resourceBounds = resourceBounds
|
|
297
|
+
self.parentKeyId = parentKeyId
|
|
298
|
+
self.createdAt = createdAt
|
|
299
|
+
self.rotatedAt = rotatedAt
|
|
300
|
+
self.revokedAt = revokedAt
|
|
301
|
+
self.permissions = permissions
|
|
302
|
+
|
|
303
|
+
@classmethod
|
|
304
|
+
def from_dict(cls, data):
|
|
305
|
+
scopes = data.get("scopes")
|
|
306
|
+
if not isinstance(scopes, list):
|
|
307
|
+
scopes = []
|
|
308
|
+
resource_bounds = data.get("resourceBounds")
|
|
309
|
+
if not isinstance(resource_bounds, dict):
|
|
310
|
+
resource_bounds = {}
|
|
311
|
+
permissions = data.get("permissions")
|
|
312
|
+
if permissions is not None and not isinstance(permissions, dict):
|
|
313
|
+
permissions = None
|
|
314
|
+
return cls(
|
|
315
|
+
key=data.get("key"),
|
|
316
|
+
keyId=data["keyId"],
|
|
317
|
+
label=data.get("label") or "",
|
|
318
|
+
status=data.get("status") or "active",
|
|
319
|
+
keyPrefix=data.get("keyPrefix") or "",
|
|
320
|
+
role=data.get("role") or "scoped",
|
|
321
|
+
scopes=[str(scope) for scope in scopes],
|
|
322
|
+
resourceBounds=resource_bounds,
|
|
323
|
+
parentKeyId=data.get("parentKeyId"),
|
|
324
|
+
createdAt=data.get("createdAt"),
|
|
325
|
+
rotatedAt=data.get("rotatedAt"),
|
|
326
|
+
revokedAt=data.get("revokedAt"),
|
|
327
|
+
permissions=permissions,
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
class ApiKeysListResponse:
|
|
332
|
+
def __init__(self, keys):
|
|
333
|
+
self.keys = keys
|
|
334
|
+
|
|
335
|
+
@classmethod
|
|
336
|
+
def from_dict(cls, data):
|
|
337
|
+
items = data.get("keys")
|
|
338
|
+
if not isinstance(items, list):
|
|
339
|
+
items = []
|
|
340
|
+
keys = [ApiKeyResponse.from_dict(item) for item in items if isinstance(item, dict)]
|
|
341
|
+
return cls(keys=keys)
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
class UsageSummaryResponse:
|
|
345
|
+
def __init__(self, usage, billing):
|
|
346
|
+
self.usage = usage
|
|
347
|
+
self.billing = billing
|
|
348
|
+
|
|
349
|
+
@classmethod
|
|
350
|
+
def from_dict(cls, data):
|
|
351
|
+
usage = data.get("usage")
|
|
352
|
+
if not isinstance(usage, dict):
|
|
353
|
+
usage = {}
|
|
354
|
+
billing = data.get("billing")
|
|
355
|
+
if not isinstance(billing, dict):
|
|
356
|
+
billing = {}
|
|
357
|
+
return cls(usage=usage, billing=billing)
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
class WebhookConfigureResponse:
|
|
361
|
+
def __init__(self, webhook):
|
|
362
|
+
self.webhook = webhook
|
|
363
|
+
|
|
364
|
+
@classmethod
|
|
365
|
+
def from_dict(cls, data):
|
|
366
|
+
webhook = data.get("webhook")
|
|
367
|
+
if not isinstance(webhook, dict):
|
|
368
|
+
webhook = {}
|
|
369
|
+
return cls(webhook=webhook)
|
|
370
|
+
|
|
371
|
+
|
|
259
372
|
class GenerationWebhookEvent:
|
|
260
373
|
def __init__(
|
|
261
374
|
self,
|
|
@@ -306,8 +419,8 @@ class AudioIsolationResult:
|
|
|
306
419
|
self.generation = generation
|
|
307
420
|
|
|
308
421
|
|
|
309
|
-
ModelKey = ("diffio-2", "diffio-2-flash", "diffio-3")
|
|
310
|
-
DownloadType = ("audio", "mp3", "video")
|
|
422
|
+
ModelKey = ("diffio-2", "diffio-2-flash", "diffio-3", "diffio-3.4", "diffio-3.5")
|
|
423
|
+
DownloadType = ("audio", "mp3", "video", "transcript")
|
|
311
424
|
WebhookMode = ("test", "live")
|
|
312
425
|
WebhookEventType = (
|
|
313
426
|
"generation.queued",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: diffio
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.7
|
|
4
4
|
Summary: Python SDK for the Diffio API.
|
|
5
5
|
Author: Diffio
|
|
6
6
|
License: MIT
|
|
@@ -87,7 +87,7 @@ project = client.create_project(
|
|
|
87
87
|
|
|
88
88
|
generation = client.create_generation(
|
|
89
89
|
apiProjectId=project.apiProjectId,
|
|
90
|
-
model="diffio-
|
|
90
|
+
model="diffio-3.5",
|
|
91
91
|
sampling={"steps": 12, "guidance": 1.5},
|
|
92
92
|
)
|
|
93
93
|
|
|
@@ -102,7 +102,7 @@ from diffio import DiffioClient
|
|
|
102
102
|
client = DiffioClient(apiKey="diffio_live_...")
|
|
103
103
|
result = client.audio_isolation.isolate(
|
|
104
104
|
filePath="sample.wav",
|
|
105
|
-
model="diffio-
|
|
105
|
+
model="diffio-3.5",
|
|
106
106
|
sampling={"steps": 12, "guidance": 1.5},
|
|
107
107
|
)
|
|
108
108
|
|
|
@@ -119,7 +119,7 @@ from diffio import DiffioClient
|
|
|
119
119
|
client = DiffioClient(apiKey="diffio_live_...")
|
|
120
120
|
audio_bytes, info = client.restore_audio(
|
|
121
121
|
filePath="sample.wav",
|
|
122
|
-
model="diffio-
|
|
122
|
+
model="diffio-3.5",
|
|
123
123
|
sampling={"steps": 12, "guidance": 1.5},
|
|
124
124
|
onProgress=lambda progress: print(progress.status),
|
|
125
125
|
)
|
|
@@ -165,6 +165,36 @@ print(download.downloadUrl)
|
|
|
165
165
|
|
|
166
166
|
If you only need the URL, use `client.generations.get_download`.
|
|
167
167
|
|
|
168
|
+
Set `downloadType="transcript"` to download the transcript JSON artifact when the generation has one.
|
|
169
|
+
|
|
170
|
+
```py
|
|
171
|
+
transcript = client.generations.download(
|
|
172
|
+
generationId="gen_123",
|
|
173
|
+
apiProjectId="proj_123",
|
|
174
|
+
downloadType="transcript",
|
|
175
|
+
downloadFilePath="word_timestamps.json",
|
|
176
|
+
)
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Account, keys, usage, and webhook configuration
|
|
180
|
+
|
|
181
|
+
Agent keys can manage account settings, scoped keys, usage, and webhook endpoints.
|
|
182
|
+
|
|
183
|
+
```py
|
|
184
|
+
settings = client.account.get_settings()
|
|
185
|
+
key = client.api_keys.create(
|
|
186
|
+
label="Backend worker",
|
|
187
|
+
scopes=["projects:read", "projects:write", "generations:read", "generations:write", "artifacts:read"],
|
|
188
|
+
)
|
|
189
|
+
usage = client.usage.summary(apiKeyId=key.keyId)
|
|
190
|
+
webhook = client.webhooks.configure(
|
|
191
|
+
mode="live",
|
|
192
|
+
url="https://example.com/webhooks/diffio",
|
|
193
|
+
eventTypes=["generation.completed", "generation.failed"],
|
|
194
|
+
apiKeyId=key.keyId,
|
|
195
|
+
)
|
|
196
|
+
```
|
|
197
|
+
|
|
168
198
|
## List projects
|
|
169
199
|
|
|
170
200
|
```py
|
|
@@ -283,6 +283,34 @@ def test_audio_isolation_isolate_runs_full_flow(tmp_path: Path):
|
|
|
283
283
|
assert calls[2][0] == "POST"
|
|
284
284
|
|
|
285
285
|
|
|
286
|
+
def test_create_generation_routes_to_diffio_3_5_endpoint():
|
|
287
|
+
received = {}
|
|
288
|
+
|
|
289
|
+
def handler(request: httpx.Request) -> httpx.Response:
|
|
290
|
+
received["path"] = request.url.path
|
|
291
|
+
payload = json.loads(request.content.decode("utf-8"))
|
|
292
|
+
received["payload"] = payload
|
|
293
|
+
return httpx.Response(
|
|
294
|
+
200,
|
|
295
|
+
json={
|
|
296
|
+
"generationId": "gen_35",
|
|
297
|
+
"apiProjectId": "proj_35",
|
|
298
|
+
"modelKey": "diffio-3.5",
|
|
299
|
+
"status": "queued",
|
|
300
|
+
},
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
transport = httpx.MockTransport(handler)
|
|
304
|
+
http_client = httpx.Client(base_url="https://api.test", transport=transport)
|
|
305
|
+
client = DiffioClient(apiKey="diffio_live_test", baseUrl="https://api.test", httpClient=http_client)
|
|
306
|
+
|
|
307
|
+
response = client.create_generation(apiProjectId="proj_35", model="diffio-3.5")
|
|
308
|
+
|
|
309
|
+
assert received["path"] == "/v1/diffio-3.5-generation"
|
|
310
|
+
assert received["payload"]["apiProjectId"] == "proj_35"
|
|
311
|
+
assert response.modelKey == "diffio-3.5"
|
|
312
|
+
|
|
313
|
+
|
|
286
314
|
def test_restore_audio_runs_full_flow_and_downloads(tmp_path: Path, monkeypatch):
|
|
287
315
|
status_sequence = ["queued", "processing", "complete"]
|
|
288
316
|
|
|
@@ -501,6 +529,43 @@ def test_get_generation_download_payload_and_response():
|
|
|
501
529
|
assert download.mimeType == "audio/mpeg"
|
|
502
530
|
|
|
503
531
|
|
|
532
|
+
def test_get_generation_download_accepts_transcript():
|
|
533
|
+
received = {}
|
|
534
|
+
|
|
535
|
+
def handler(request: httpx.Request) -> httpx.Response:
|
|
536
|
+
received["path"] = request.url.path
|
|
537
|
+
payload = json.loads(request.content.decode("utf-8"))
|
|
538
|
+
received["payload"] = payload
|
|
539
|
+
return httpx.Response(
|
|
540
|
+
200,
|
|
541
|
+
json={
|
|
542
|
+
"generationId": "gen_456",
|
|
543
|
+
"apiProjectId": "proj_456",
|
|
544
|
+
"downloadType": "transcript",
|
|
545
|
+
"downloadUrl": "https://storage.test/word_timestamps.json",
|
|
546
|
+
"fileName": "word_timestamps.json",
|
|
547
|
+
"storagePath": "users/u/projects/proj_456/generations/gen_456/word_timestamps.json",
|
|
548
|
+
"bucket": "diffio_api",
|
|
549
|
+
"mimeType": "application/json",
|
|
550
|
+
},
|
|
551
|
+
)
|
|
552
|
+
|
|
553
|
+
transport = httpx.MockTransport(handler)
|
|
554
|
+
http_client = httpx.Client(base_url="https://api.test", transport=transport)
|
|
555
|
+
client = DiffioClient(apiKey="diffio_live_test", baseUrl="https://api.test", httpClient=http_client)
|
|
556
|
+
|
|
557
|
+
download = client.generations.get_download(
|
|
558
|
+
generationId="gen_456",
|
|
559
|
+
apiProjectId="proj_456",
|
|
560
|
+
downloadType="transcript",
|
|
561
|
+
)
|
|
562
|
+
|
|
563
|
+
assert received["path"] == "/v1/get_generation_download"
|
|
564
|
+
assert received["payload"]["downloadType"] == "transcript"
|
|
565
|
+
assert download.downloadType == "transcript"
|
|
566
|
+
assert download.mimeType == "application/json"
|
|
567
|
+
|
|
568
|
+
|
|
504
569
|
def test_generation_download_writes_file(tmp_path: Path):
|
|
505
570
|
received = {}
|
|
506
571
|
|
|
@@ -699,6 +764,80 @@ def test_webhooks_send_test_event_payload_and_response():
|
|
|
699
764
|
assert response.eventType == "generation.completed"
|
|
700
765
|
|
|
701
766
|
|
|
767
|
+
def test_account_keys_usage_and_webhook_configure_endpoints():
|
|
768
|
+
calls = []
|
|
769
|
+
|
|
770
|
+
def handler(request: httpx.Request) -> httpx.Response:
|
|
771
|
+
payload = json.loads(request.content.decode("utf-8"))
|
|
772
|
+
calls.append((request.url.path, payload))
|
|
773
|
+
if request.url.path == "/v1/account/settings/get":
|
|
774
|
+
return httpx.Response(
|
|
775
|
+
200,
|
|
776
|
+
json={
|
|
777
|
+
"apiKeyId": "agent_1",
|
|
778
|
+
"account": {"userId": "user_1", "billingPolicy": {"type": "internalDiffio"}},
|
|
779
|
+
},
|
|
780
|
+
)
|
|
781
|
+
if request.url.path == "/v1/api_keys/create":
|
|
782
|
+
return httpx.Response(
|
|
783
|
+
200,
|
|
784
|
+
json={
|
|
785
|
+
"key": "diffio_live_new",
|
|
786
|
+
"keyId": "key_1",
|
|
787
|
+
"label": "VFC worker",
|
|
788
|
+
"status": "active",
|
|
789
|
+
"keyPrefix": "diffio_live_",
|
|
790
|
+
"role": "scoped",
|
|
791
|
+
"scopes": ["projects:read", "projects:write"],
|
|
792
|
+
"resourceBounds": {},
|
|
793
|
+
"parentKeyId": "agent_1",
|
|
794
|
+
},
|
|
795
|
+
)
|
|
796
|
+
if request.url.path == "/v1/usage/summary":
|
|
797
|
+
return httpx.Response(
|
|
798
|
+
200,
|
|
799
|
+
json={
|
|
800
|
+
"usage": {"apiKeyId": "key_1", "periods": []},
|
|
801
|
+
"billing": {"billingPolicy": {"type": "internalDiffio"}},
|
|
802
|
+
},
|
|
803
|
+
)
|
|
804
|
+
if request.url.path == "/v1/webhooks/configure":
|
|
805
|
+
return httpx.Response(
|
|
806
|
+
200,
|
|
807
|
+
json={
|
|
808
|
+
"webhook": {
|
|
809
|
+
"apiKeyId": "key_1",
|
|
810
|
+
"mode": "live",
|
|
811
|
+
"endpointId": "ep_1",
|
|
812
|
+
}
|
|
813
|
+
},
|
|
814
|
+
)
|
|
815
|
+
return httpx.Response(404, json={"error": "not found"})
|
|
816
|
+
|
|
817
|
+
transport = httpx.MockTransport(handler)
|
|
818
|
+
http_client = httpx.Client(base_url="https://api.test", transport=transport)
|
|
819
|
+
client = DiffioClient(apiKey="diffio_live_test", baseUrl="https://api.test", httpClient=http_client)
|
|
820
|
+
|
|
821
|
+
settings = client.account.get_settings()
|
|
822
|
+
key = client.api_keys.create(label="VFC worker", scopes=["projects:read", "projects:write"])
|
|
823
|
+
usage = client.usage.summary(apiKeyId="key_1")
|
|
824
|
+
webhook = client.webhooks.configure(
|
|
825
|
+
mode="live",
|
|
826
|
+
url="https://example.com/webhook",
|
|
827
|
+
eventTypes=["generation.completed"],
|
|
828
|
+
apiKeyId="key_1",
|
|
829
|
+
)
|
|
830
|
+
|
|
831
|
+
assert settings.account["billingPolicy"]["type"] == "internalDiffio"
|
|
832
|
+
assert key.key == "diffio_live_new"
|
|
833
|
+
assert usage.billing["billingPolicy"]["type"] == "internalDiffio"
|
|
834
|
+
assert webhook.webhook["endpointId"] == "ep_1"
|
|
835
|
+
assert calls[1] == (
|
|
836
|
+
"/v1/api_keys/create",
|
|
837
|
+
{"label": "VFC worker", "scopes": ["projects:read", "projects:write"], "resourceBounds": {}},
|
|
838
|
+
)
|
|
839
|
+
|
|
840
|
+
|
|
702
841
|
def test_webhooks_send_test_event_rejects_invalid_type():
|
|
703
842
|
transport = httpx.MockTransport(lambda request: httpx.Response(200, json={}))
|
|
704
843
|
http_client = httpx.Client(base_url="https://api.test", transport=transport)
|
|
@@ -9,7 +9,7 @@ import httpx
|
|
|
9
9
|
import pytest
|
|
10
10
|
|
|
11
11
|
from diffio import DiffioApiError, DiffioClient
|
|
12
|
-
from emulator_api_key import create_emulator_api_key
|
|
12
|
+
from emulator_api_key import EmulatorApiKeyError, create_emulator_api_key
|
|
13
13
|
|
|
14
14
|
EMULATOR_BASE_URL = "http://127.0.0.1:5001/diffioai/us-central1"
|
|
15
15
|
FIXTURES_DIR = Path(__file__).resolve().parent / "fixtures"
|
|
@@ -33,7 +33,12 @@ def emulator_env() -> None:
|
|
|
33
33
|
|
|
34
34
|
@pytest.fixture(scope="session")
|
|
35
35
|
def emulator_api_key(emulator_env):
|
|
36
|
-
|
|
36
|
+
try:
|
|
37
|
+
result = create_emulator_api_key()
|
|
38
|
+
except EmulatorApiKeyError as exc:
|
|
39
|
+
if "Upgrade to the Developer plan" in str(exc):
|
|
40
|
+
pytest.skip("Local emulator account cannot create API keys without the Developer plan.")
|
|
41
|
+
raise
|
|
37
42
|
os.environ["DIFFIO_API_KEY"] = result.api_key
|
|
38
43
|
return result
|
|
39
44
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|