audacity-sdk 0.1.0__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.
- audacity_sdk-0.1.0/LICENSE +1 -0
- audacity_sdk-0.1.0/PKG-INFO +270 -0
- audacity_sdk-0.1.0/README.md +246 -0
- audacity_sdk-0.1.0/pyproject.toml +38 -0
- audacity_sdk-0.1.0/setup.cfg +4 -0
- audacity_sdk-0.1.0/src/audacity/__init__.py +20 -0
- audacity_sdk-0.1.0/src/audacity/_client.py +256 -0
- audacity_sdk-0.1.0/src/audacity/_mapping.py +346 -0
- audacity_sdk-0.1.0/src/audacity/_sse.py +280 -0
- audacity_sdk-0.1.0/src/audacity/_version.py +2 -0
- audacity_sdk-0.1.0/src/audacity/exceptions.py +349 -0
- audacity_sdk-0.1.0/src/audacity/py.typed +0 -0
- audacity_sdk-0.1.0/src/audacity_sdk.egg-info/PKG-INFO +270 -0
- audacity_sdk-0.1.0/src/audacity_sdk.egg-info/SOURCES.txt +15 -0
- audacity_sdk-0.1.0/src/audacity_sdk.egg-info/dependency_links.txt +1 -0
- audacity_sdk-0.1.0/src/audacity_sdk.egg-info/top_level.txt +1 -0
- audacity_sdk-0.1.0/tests/test_sdk.py +1795 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Copyright Audacity Investments. All rights reserved.
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: audacity-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Audacity Investments AI SDK — Bedrock-parity client for the Audacity LLM gateway
|
|
5
|
+
Author: Audacity Investments
|
|
6
|
+
License: Copyright Audacity Investments. All rights reserved.
|
|
7
|
+
|
|
8
|
+
Project-URL: Homepage, https://portal.audacityinvestments.com
|
|
9
|
+
Project-URL: Repository, https://github.com/Audacity-Investments/audacity
|
|
10
|
+
Keywords: audacity,llm,ai,bedrock
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Classifier: Typing :: Typed
|
|
20
|
+
Requires-Python: >=3.9
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Dynamic: license-file
|
|
24
|
+
|
|
25
|
+
# audacity-sdk
|
|
26
|
+
|
|
27
|
+
Python client for the Audacity Investments LLM gateway. Exposes the same
|
|
28
|
+
**Amazon Bedrock Converse** surface so teams migrating off Bedrock can swap
|
|
29
|
+
the client constructor and keep the rest of their call-sites unchanged.
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install audacity-sdk
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Zero runtime dependencies — stdlib only. Requires Python 3.9+.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Quick start
|
|
44
|
+
|
|
45
|
+
### Non-streaming (Converse)
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
from audacity import Audacity
|
|
49
|
+
|
|
50
|
+
client = Audacity(api_key="audacity_api_…") # or set AUDACITY_API_KEY
|
|
51
|
+
|
|
52
|
+
response = client.converse(
|
|
53
|
+
modelId="gpt-5.4-mini",
|
|
54
|
+
messages=[{"role": "user", "content": [{"text": "What is 2+2?"}]}],
|
|
55
|
+
inferenceConfig={"maxTokens": 256, "temperature": 0.0},
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
print(response["output"]["message"]["content"][0]["text"])
|
|
59
|
+
print(response["stopReason"]) # "end_turn"
|
|
60
|
+
print(response["usage"]) # {"inputTokens": …, "outputTokens": …, "totalTokens": …}
|
|
61
|
+
print(response["metrics"]) # {"latencyMs": …}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Streaming (ConverseStream)
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
from audacity import Audacity
|
|
68
|
+
|
|
69
|
+
client = Audacity()
|
|
70
|
+
|
|
71
|
+
stream_response = client.converse_stream(
|
|
72
|
+
modelId="gpt-5.4-mini",
|
|
73
|
+
messages=[{"role": "user", "content": [{"text": "Write me a haiku."}]}],
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
for event in stream_response["stream"]: # boto3 parity: response["stream"]
|
|
77
|
+
if "contentBlockDelta" in event:
|
|
78
|
+
delta = event["contentBlockDelta"]["delta"]
|
|
79
|
+
print(delta.get("text", ""), end="", flush=True)
|
|
80
|
+
|
|
81
|
+
print() # newline at end
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Migrating from boto3 bedrock-runtime
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
# BEFORE — boto3
|
|
90
|
+
import boto3
|
|
91
|
+
client = boto3.client("bedrock-runtime", region_name="us-east-1")
|
|
92
|
+
|
|
93
|
+
response = client.converse(
|
|
94
|
+
modelId="anthropic.claude-3-sonnet-20240229-v1:0",
|
|
95
|
+
messages=[{"role": "user", "content": [{"text": "Hi"}]}],
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
# AFTER — Audacity SDK
|
|
99
|
+
from audacity import Audacity
|
|
100
|
+
client = Audacity(api_key="audacity_api_…") # only line that changes
|
|
101
|
+
|
|
102
|
+
response = client.converse(
|
|
103
|
+
modelId="gpt-5.4-mini", # use Audacity model ID
|
|
104
|
+
messages=[{"role": "user", "content": [{"text": "Hi"}]}],
|
|
105
|
+
)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Streaming diff:
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
# BEFORE — boto3
|
|
112
|
+
stream_resp = client.converse_stream(modelId=…, messages=…)
|
|
113
|
+
for event in stream_resp["stream"]:
|
|
114
|
+
…
|
|
115
|
+
|
|
116
|
+
# AFTER — Audacity SDK (identical call-site)
|
|
117
|
+
stream_resp = client.converse_stream(modelId=…, messages=…)
|
|
118
|
+
for event in stream_resp["stream"]:
|
|
119
|
+
…
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## Tool use
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
response = client.converse(
|
|
128
|
+
modelId="gpt-5.4-mini",
|
|
129
|
+
messages=[{"role": "user", "content": [{"text": "What's the weather in NYC?"}]}],
|
|
130
|
+
toolConfig={
|
|
131
|
+
"tools": [{
|
|
132
|
+
"toolSpec": {
|
|
133
|
+
"name": "get_weather",
|
|
134
|
+
"description": "Get current weather for a city",
|
|
135
|
+
"inputSchema": {
|
|
136
|
+
"json": {
|
|
137
|
+
"type": "object",
|
|
138
|
+
"properties": {"city": {"type": "string"}},
|
|
139
|
+
"required": ["city"],
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
}
|
|
143
|
+
}],
|
|
144
|
+
"toolChoice": {"auto": {}},
|
|
145
|
+
},
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
# Assistant responds with a tool call
|
|
149
|
+
tool_use = response["output"]["message"]["content"][0]["toolUse"]
|
|
150
|
+
print(tool_use["name"]) # "get_weather"
|
|
151
|
+
print(tool_use["input"]) # {"city": "NYC"}
|
|
152
|
+
|
|
153
|
+
# Send tool result back
|
|
154
|
+
response2 = client.converse(
|
|
155
|
+
modelId="gpt-5.4-mini",
|
|
156
|
+
messages=[
|
|
157
|
+
{"role": "user", "content": [{"text": "What's the weather in NYC?"}]},
|
|
158
|
+
{"role": "assistant", "content": response["output"]["message"]["content"]},
|
|
159
|
+
{"role": "user", "content": [
|
|
160
|
+
{"toolResult": {
|
|
161
|
+
"toolUseId": tool_use["toolUseId"],
|
|
162
|
+
"content": [{"text": "Sunny, 72°F"}],
|
|
163
|
+
}}
|
|
164
|
+
]},
|
|
165
|
+
],
|
|
166
|
+
)
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## Images (vision models)
|
|
172
|
+
|
|
173
|
+
Bedrock-style `image` content blocks are supported in user messages. Pass raw
|
|
174
|
+
bytes (encoded for you) or a URL (Audacity extension):
|
|
175
|
+
|
|
176
|
+
```python
|
|
177
|
+
with open("chart.png", "rb") as f:
|
|
178
|
+
image_bytes = f.read()
|
|
179
|
+
|
|
180
|
+
response = client.converse(
|
|
181
|
+
modelId="gpt-5.5",
|
|
182
|
+
messages=[{
|
|
183
|
+
"role": "user",
|
|
184
|
+
"content": [
|
|
185
|
+
{"text": "What does this chart show?"},
|
|
186
|
+
{"image": {"format": "png", "source": {"bytes": image_bytes}}},
|
|
187
|
+
],
|
|
188
|
+
}],
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
# Or reference a hosted image directly (not available in Bedrock):
|
|
192
|
+
# {"image": {"format": "jpeg", "source": {"url": "https://example.com/photo.jpg"}}}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
`format` is one of `png`, `jpeg`, `gif`, `webp`. Use a vision-capable model.
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## Error handling
|
|
200
|
+
|
|
201
|
+
```python
|
|
202
|
+
from audacity import Audacity
|
|
203
|
+
from audacity.exceptions import (
|
|
204
|
+
MissingApiKeyError,
|
|
205
|
+
AccessDeniedException,
|
|
206
|
+
ThrottlingException,
|
|
207
|
+
ServiceQuotaExceededException,
|
|
208
|
+
ModelStreamErrorException,
|
|
209
|
+
SdkError,
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
client = Audacity()
|
|
213
|
+
|
|
214
|
+
try:
|
|
215
|
+
response = client.converse(modelId="gpt-5.4-mini", messages=[…])
|
|
216
|
+
except client.exceptions.ThrottlingException as e:
|
|
217
|
+
print(f"Rate limited: {e.message}, retry after {e.retry_after_seconds}s")
|
|
218
|
+
except client.exceptions.AccessDeniedException as e:
|
|
219
|
+
print(f"Auth error [{e.status_code}]: {e.message}")
|
|
220
|
+
except client.exceptions.ServiceQuotaExceededException as e:
|
|
221
|
+
print(f"Budget exhausted: {e.message}")
|
|
222
|
+
except SdkError as e:
|
|
223
|
+
print(f"Network/decode error: {e.message}")
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Streaming errors surface when you iterate the stream:
|
|
227
|
+
|
|
228
|
+
```python
|
|
229
|
+
try:
|
|
230
|
+
for event in stream_response["stream"]:
|
|
231
|
+
…
|
|
232
|
+
except client.exceptions.ModelStreamErrorException as e:
|
|
233
|
+
print(f"Stream broken: {e.message}")
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
## Configuration & environment variables
|
|
239
|
+
|
|
240
|
+
| Constructor parameter | Environment variable | Default |
|
|
241
|
+
|---|---|---|
|
|
242
|
+
| `api_key` | `AUDACITY_API_KEY` | — (required) |
|
|
243
|
+
| `base_url` | `AUDACITY_BASE_URL` | `https://portal.audacityinvestments.com` |
|
|
244
|
+
| `timeout` | — | `120.0` seconds |
|
|
245
|
+
| `max_retries` | — | `2` (3 total attempts) |
|
|
246
|
+
|
|
247
|
+
```python
|
|
248
|
+
client = Audacity(
|
|
249
|
+
api_key="audacity_api_…",
|
|
250
|
+
base_url="https://portal.audacityinvestments.com",
|
|
251
|
+
timeout=60.0,
|
|
252
|
+
max_retries=3,
|
|
253
|
+
)
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## Retry behaviour
|
|
259
|
+
|
|
260
|
+
The SDK automatically retries transient failures with jittered exponential
|
|
261
|
+
backoff (capped at 20 s):
|
|
262
|
+
|
|
263
|
+
- Retried: `ThrottlingException` (429), `ModelTimeoutException` (408),
|
|
264
|
+
`ServiceUnavailableException` (502/503/504), `InternalServerException` (500),
|
|
265
|
+
network errors.
|
|
266
|
+
- Never retried: `AccessDeniedException`, `ValidationException`,
|
|
267
|
+
`ResourceNotFoundException`, `ServiceQuotaExceededException`
|
|
268
|
+
(including `BUDGET_EXCEEDED` at 429/402), or any 4xx except 408/429.
|
|
269
|
+
- Streaming: retries apply only before the first SSE byte; after that,
|
|
270
|
+
connection drops raise `ModelStreamErrorException`.
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
# audacity-sdk
|
|
2
|
+
|
|
3
|
+
Python client for the Audacity Investments LLM gateway. Exposes the same
|
|
4
|
+
**Amazon Bedrock Converse** surface so teams migrating off Bedrock can swap
|
|
5
|
+
the client constructor and keep the rest of their call-sites unchanged.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install audacity-sdk
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Zero runtime dependencies — stdlib only. Requires Python 3.9+.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Quick start
|
|
20
|
+
|
|
21
|
+
### Non-streaming (Converse)
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
from audacity import Audacity
|
|
25
|
+
|
|
26
|
+
client = Audacity(api_key="audacity_api_…") # or set AUDACITY_API_KEY
|
|
27
|
+
|
|
28
|
+
response = client.converse(
|
|
29
|
+
modelId="gpt-5.4-mini",
|
|
30
|
+
messages=[{"role": "user", "content": [{"text": "What is 2+2?"}]}],
|
|
31
|
+
inferenceConfig={"maxTokens": 256, "temperature": 0.0},
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
print(response["output"]["message"]["content"][0]["text"])
|
|
35
|
+
print(response["stopReason"]) # "end_turn"
|
|
36
|
+
print(response["usage"]) # {"inputTokens": …, "outputTokens": …, "totalTokens": …}
|
|
37
|
+
print(response["metrics"]) # {"latencyMs": …}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Streaming (ConverseStream)
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
from audacity import Audacity
|
|
44
|
+
|
|
45
|
+
client = Audacity()
|
|
46
|
+
|
|
47
|
+
stream_response = client.converse_stream(
|
|
48
|
+
modelId="gpt-5.4-mini",
|
|
49
|
+
messages=[{"role": "user", "content": [{"text": "Write me a haiku."}]}],
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
for event in stream_response["stream"]: # boto3 parity: response["stream"]
|
|
53
|
+
if "contentBlockDelta" in event:
|
|
54
|
+
delta = event["contentBlockDelta"]["delta"]
|
|
55
|
+
print(delta.get("text", ""), end="", flush=True)
|
|
56
|
+
|
|
57
|
+
print() # newline at end
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Migrating from boto3 bedrock-runtime
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
# BEFORE — boto3
|
|
66
|
+
import boto3
|
|
67
|
+
client = boto3.client("bedrock-runtime", region_name="us-east-1")
|
|
68
|
+
|
|
69
|
+
response = client.converse(
|
|
70
|
+
modelId="anthropic.claude-3-sonnet-20240229-v1:0",
|
|
71
|
+
messages=[{"role": "user", "content": [{"text": "Hi"}]}],
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# AFTER — Audacity SDK
|
|
75
|
+
from audacity import Audacity
|
|
76
|
+
client = Audacity(api_key="audacity_api_…") # only line that changes
|
|
77
|
+
|
|
78
|
+
response = client.converse(
|
|
79
|
+
modelId="gpt-5.4-mini", # use Audacity model ID
|
|
80
|
+
messages=[{"role": "user", "content": [{"text": "Hi"}]}],
|
|
81
|
+
)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Streaming diff:
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
# BEFORE — boto3
|
|
88
|
+
stream_resp = client.converse_stream(modelId=…, messages=…)
|
|
89
|
+
for event in stream_resp["stream"]:
|
|
90
|
+
…
|
|
91
|
+
|
|
92
|
+
# AFTER — Audacity SDK (identical call-site)
|
|
93
|
+
stream_resp = client.converse_stream(modelId=…, messages=…)
|
|
94
|
+
for event in stream_resp["stream"]:
|
|
95
|
+
…
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Tool use
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
response = client.converse(
|
|
104
|
+
modelId="gpt-5.4-mini",
|
|
105
|
+
messages=[{"role": "user", "content": [{"text": "What's the weather in NYC?"}]}],
|
|
106
|
+
toolConfig={
|
|
107
|
+
"tools": [{
|
|
108
|
+
"toolSpec": {
|
|
109
|
+
"name": "get_weather",
|
|
110
|
+
"description": "Get current weather for a city",
|
|
111
|
+
"inputSchema": {
|
|
112
|
+
"json": {
|
|
113
|
+
"type": "object",
|
|
114
|
+
"properties": {"city": {"type": "string"}},
|
|
115
|
+
"required": ["city"],
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
}
|
|
119
|
+
}],
|
|
120
|
+
"toolChoice": {"auto": {}},
|
|
121
|
+
},
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
# Assistant responds with a tool call
|
|
125
|
+
tool_use = response["output"]["message"]["content"][0]["toolUse"]
|
|
126
|
+
print(tool_use["name"]) # "get_weather"
|
|
127
|
+
print(tool_use["input"]) # {"city": "NYC"}
|
|
128
|
+
|
|
129
|
+
# Send tool result back
|
|
130
|
+
response2 = client.converse(
|
|
131
|
+
modelId="gpt-5.4-mini",
|
|
132
|
+
messages=[
|
|
133
|
+
{"role": "user", "content": [{"text": "What's the weather in NYC?"}]},
|
|
134
|
+
{"role": "assistant", "content": response["output"]["message"]["content"]},
|
|
135
|
+
{"role": "user", "content": [
|
|
136
|
+
{"toolResult": {
|
|
137
|
+
"toolUseId": tool_use["toolUseId"],
|
|
138
|
+
"content": [{"text": "Sunny, 72°F"}],
|
|
139
|
+
}}
|
|
140
|
+
]},
|
|
141
|
+
],
|
|
142
|
+
)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Images (vision models)
|
|
148
|
+
|
|
149
|
+
Bedrock-style `image` content blocks are supported in user messages. Pass raw
|
|
150
|
+
bytes (encoded for you) or a URL (Audacity extension):
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
with open("chart.png", "rb") as f:
|
|
154
|
+
image_bytes = f.read()
|
|
155
|
+
|
|
156
|
+
response = client.converse(
|
|
157
|
+
modelId="gpt-5.5",
|
|
158
|
+
messages=[{
|
|
159
|
+
"role": "user",
|
|
160
|
+
"content": [
|
|
161
|
+
{"text": "What does this chart show?"},
|
|
162
|
+
{"image": {"format": "png", "source": {"bytes": image_bytes}}},
|
|
163
|
+
],
|
|
164
|
+
}],
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
# Or reference a hosted image directly (not available in Bedrock):
|
|
168
|
+
# {"image": {"format": "jpeg", "source": {"url": "https://example.com/photo.jpg"}}}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
`format` is one of `png`, `jpeg`, `gif`, `webp`. Use a vision-capable model.
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## Error handling
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
from audacity import Audacity
|
|
179
|
+
from audacity.exceptions import (
|
|
180
|
+
MissingApiKeyError,
|
|
181
|
+
AccessDeniedException,
|
|
182
|
+
ThrottlingException,
|
|
183
|
+
ServiceQuotaExceededException,
|
|
184
|
+
ModelStreamErrorException,
|
|
185
|
+
SdkError,
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
client = Audacity()
|
|
189
|
+
|
|
190
|
+
try:
|
|
191
|
+
response = client.converse(modelId="gpt-5.4-mini", messages=[…])
|
|
192
|
+
except client.exceptions.ThrottlingException as e:
|
|
193
|
+
print(f"Rate limited: {e.message}, retry after {e.retry_after_seconds}s")
|
|
194
|
+
except client.exceptions.AccessDeniedException as e:
|
|
195
|
+
print(f"Auth error [{e.status_code}]: {e.message}")
|
|
196
|
+
except client.exceptions.ServiceQuotaExceededException as e:
|
|
197
|
+
print(f"Budget exhausted: {e.message}")
|
|
198
|
+
except SdkError as e:
|
|
199
|
+
print(f"Network/decode error: {e.message}")
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Streaming errors surface when you iterate the stream:
|
|
203
|
+
|
|
204
|
+
```python
|
|
205
|
+
try:
|
|
206
|
+
for event in stream_response["stream"]:
|
|
207
|
+
…
|
|
208
|
+
except client.exceptions.ModelStreamErrorException as e:
|
|
209
|
+
print(f"Stream broken: {e.message}")
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## Configuration & environment variables
|
|
215
|
+
|
|
216
|
+
| Constructor parameter | Environment variable | Default |
|
|
217
|
+
|---|---|---|
|
|
218
|
+
| `api_key` | `AUDACITY_API_KEY` | — (required) |
|
|
219
|
+
| `base_url` | `AUDACITY_BASE_URL` | `https://portal.audacityinvestments.com` |
|
|
220
|
+
| `timeout` | — | `120.0` seconds |
|
|
221
|
+
| `max_retries` | — | `2` (3 total attempts) |
|
|
222
|
+
|
|
223
|
+
```python
|
|
224
|
+
client = Audacity(
|
|
225
|
+
api_key="audacity_api_…",
|
|
226
|
+
base_url="https://portal.audacityinvestments.com",
|
|
227
|
+
timeout=60.0,
|
|
228
|
+
max_retries=3,
|
|
229
|
+
)
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## Retry behaviour
|
|
235
|
+
|
|
236
|
+
The SDK automatically retries transient failures with jittered exponential
|
|
237
|
+
backoff (capped at 20 s):
|
|
238
|
+
|
|
239
|
+
- Retried: `ThrottlingException` (429), `ModelTimeoutException` (408),
|
|
240
|
+
`ServiceUnavailableException` (502/503/504), `InternalServerException` (500),
|
|
241
|
+
network errors.
|
|
242
|
+
- Never retried: `AccessDeniedException`, `ValidationException`,
|
|
243
|
+
`ResourceNotFoundException`, `ServiceQuotaExceededException`
|
|
244
|
+
(including `BUDGET_EXCEEDED` at 429/402), or any 4xx except 408/429.
|
|
245
|
+
- Streaming: retries apply only before the first SSE byte; after that,
|
|
246
|
+
connection drops raise `ModelStreamErrorException`.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "audacity-sdk"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Audacity Investments AI SDK — Bedrock-parity client for the Audacity LLM gateway"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
dependencies = []
|
|
12
|
+
license = { file = "LICENSE" }
|
|
13
|
+
authors = [{ name = "Audacity Investments" }]
|
|
14
|
+
keywords = ["audacity", "llm", "ai", "bedrock"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"Programming Language :: Python :: 3.9",
|
|
18
|
+
"Programming Language :: Python :: 3.10",
|
|
19
|
+
"Programming Language :: Python :: 3.11",
|
|
20
|
+
"Programming Language :: Python :: 3.12",
|
|
21
|
+
"Programming Language :: Python :: 3.13",
|
|
22
|
+
"Programming Language :: Python :: 3.14",
|
|
23
|
+
"Operating System :: OS Independent",
|
|
24
|
+
"Typing :: Typed",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[project.urls]
|
|
28
|
+
Homepage = "https://portal.audacityinvestments.com"
|
|
29
|
+
Repository = "https://github.com/Audacity-Investments/audacity"
|
|
30
|
+
|
|
31
|
+
[tool.setuptools.dynamic]
|
|
32
|
+
version = { attr = "audacity._version.__version__" }
|
|
33
|
+
|
|
34
|
+
[tool.setuptools.packages.find]
|
|
35
|
+
where = ["src"]
|
|
36
|
+
|
|
37
|
+
[tool.setuptools.package-data]
|
|
38
|
+
audacity = ["py.typed"]
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""
|
|
2
|
+
audacity — Audacity Investments AI SDK (Python).
|
|
3
|
+
|
|
4
|
+
Usage::
|
|
5
|
+
|
|
6
|
+
from audacity import Audacity
|
|
7
|
+
|
|
8
|
+
client = Audacity(api_key="audacity_api_…")
|
|
9
|
+
response = client.converse(
|
|
10
|
+
modelId="gpt-5.4-mini",
|
|
11
|
+
messages=[{"role": "user", "content": [{"text": "Hi"}]}],
|
|
12
|
+
)
|
|
13
|
+
print(response["output"]["message"]["content"][0]["text"])
|
|
14
|
+
"""
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from ._client import Audacity
|
|
18
|
+
from ._version import __version__
|
|
19
|
+
|
|
20
|
+
__all__ = ["Audacity", "__version__"]
|