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.
@@ -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,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -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__"]