airia 0.1.9__tar.gz → 0.1.10__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.
- {airia-0.1.9 → airia-0.1.10}/PKG-INFO +99 -7
- {airia-0.1.9 → airia-0.1.10}/README.md +98 -6
- {airia-0.1.9 → airia-0.1.10}/airia/client/async_client.py +51 -20
- {airia-0.1.9 → airia-0.1.10}/airia/client/base_client.py +42 -16
- {airia-0.1.9 → airia-0.1.10}/airia/client/sync_client.py +51 -20
- airia-0.1.10/airia/constants.py +9 -0
- {airia-0.1.9 → airia-0.1.10}/airia.egg-info/PKG-INFO +99 -7
- {airia-0.1.9 → airia-0.1.10}/airia.egg-info/SOURCES.txt +2 -0
- {airia-0.1.9 → airia-0.1.10}/pyproject.toml +1 -1
- airia-0.1.10/tests/test_bearer_token_auth.py +90 -0
- {airia-0.1.9 → airia-0.1.10}/LICENSE +0 -0
- {airia-0.1.9 → airia-0.1.10}/airia/__init__.py +0 -0
- {airia-0.1.9 → airia-0.1.10}/airia/client/__init__.py +0 -0
- {airia-0.1.9 → airia-0.1.10}/airia/exceptions.py +0 -0
- {airia-0.1.9 → airia-0.1.10}/airia/logs.py +0 -0
- {airia-0.1.9 → airia-0.1.10}/airia/types/__init__.py +0 -0
- {airia-0.1.9 → airia-0.1.10}/airia/types/api/get_pipeline_config.py +0 -0
- {airia-0.1.9 → airia-0.1.10}/airia/types/api/get_projects.py +0 -0
- {airia-0.1.9 → airia-0.1.10}/airia/types/api/pipeline_execution.py +0 -0
- {airia-0.1.9 → airia-0.1.10}/airia/types/api_version.py +0 -0
- {airia-0.1.9 → airia-0.1.10}/airia/types/request_data.py +0 -0
- {airia-0.1.9 → airia-0.1.10}/airia/types/sse_messages.py +0 -0
- {airia-0.1.9 → airia-0.1.10}/airia/utils/sse_parser.py +0 -0
- {airia-0.1.9 → airia-0.1.10}/airia.egg-info/dependency_links.txt +0 -0
- {airia-0.1.9 → airia-0.1.10}/airia.egg-info/requires.txt +0 -0
- {airia-0.1.9 → airia-0.1.10}/airia.egg-info/top_level.txt +0 -0
- {airia-0.1.9 → airia-0.1.10}/setup.cfg +0 -0
- {airia-0.1.9 → airia-0.1.10}/tests/test_anthropic_gateway.py +0 -0
- {airia-0.1.9 → airia-0.1.10}/tests/test_execute_pipeline.py +0 -0
- {airia-0.1.9 → airia-0.1.10}/tests/test_get_active_pipelines_ids.py +0 -0
- {airia-0.1.9 → airia-0.1.10}/tests/test_get_pipeline_config.py +0 -0
- {airia-0.1.9 → airia-0.1.10}/tests/test_get_projects.py +0 -0
- {airia-0.1.9 → airia-0.1.10}/tests/test_openai_gateway.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: airia
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.10
|
|
4
4
|
Summary: Python SDK for Airia API
|
|
5
5
|
Author-email: Airia LLC <support@airia.com>
|
|
6
6
|
License: MIT
|
|
@@ -43,7 +43,9 @@ Airia Python API Library that provides a clean and intuitive interface to intera
|
|
|
43
43
|
- **Gateway Support**: Seamlessly integrate with OpenAI and Anthropic services through Airia gateways
|
|
44
44
|
- **Error Handling**: Comprehensive error handling with custom exceptions
|
|
45
45
|
- **Logging**: Built-in configurable logging with correlation ID support for request tracing
|
|
46
|
-
- **
|
|
46
|
+
- **Flexible Authentication**: Support for both API keys and bearer tokens with flexible configuration
|
|
47
|
+
- **API Key Management**: API key configuration via parameters or environment variables
|
|
48
|
+
- **Bearer Token Support**: Bearer token authentication for ephemeral, short-lived credentials
|
|
47
49
|
|
|
48
50
|
## Installation
|
|
49
51
|
|
|
@@ -181,17 +183,38 @@ This will create both wheel and source distribution in the `dist/` directory.
|
|
|
181
183
|
```python
|
|
182
184
|
from airia import AiriaClient
|
|
183
185
|
|
|
186
|
+
# API Key Authentication
|
|
184
187
|
client = AiriaClient(
|
|
185
188
|
base_url="https://api.airia.ai", # Default: "https://api.airia.ai"
|
|
186
|
-
api_key=None,
|
|
187
|
-
timeout=30.0,
|
|
188
|
-
log_requests=False,
|
|
189
|
-
custom_logger=None
|
|
189
|
+
api_key=None, # Or set AIRIA_API_KEY environment variable
|
|
190
|
+
timeout=30.0, # Request timeout in seconds (default: 30.0)
|
|
191
|
+
log_requests=False, # Enable request/response logging (default: False)
|
|
192
|
+
custom_logger=None # Use custom logger (default: None - uses built-in)
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
# Bearer Token Authentication
|
|
196
|
+
client = AiriaClient(
|
|
197
|
+
base_url="https://api.airia.ai", # Default: "https://api.airia.ai"
|
|
198
|
+
bearer_token="your_bearer_token", # Must be provided explicitly (no env var fallback)
|
|
199
|
+
timeout=30.0, # Request timeout in seconds (default: 30.0)
|
|
200
|
+
log_requests=False, # Enable request/response logging (default: False)
|
|
201
|
+
custom_logger=None # Use custom logger (default: None - uses built-in)
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
# Convenience method for bearer token
|
|
205
|
+
client = AiriaClient.with_bearer_token(
|
|
206
|
+
bearer_token="your_bearer_token",
|
|
207
|
+
base_url="https://api.airia.ai", # Optional, uses default if not provided
|
|
208
|
+
timeout=30.0, # Optional, uses default if not provided
|
|
209
|
+
log_requests=False, # Optional, uses default if not provided
|
|
210
|
+
custom_logger=None # Optional, uses default if not provided
|
|
190
211
|
)
|
|
191
212
|
```
|
|
192
213
|
|
|
193
214
|
### Synchronous Usage
|
|
194
215
|
|
|
216
|
+
#### With API Key
|
|
217
|
+
|
|
195
218
|
```python
|
|
196
219
|
from airia import AiriaClient
|
|
197
220
|
|
|
@@ -207,6 +230,23 @@ response = client.execute_pipeline(
|
|
|
207
230
|
print(response.result)
|
|
208
231
|
```
|
|
209
232
|
|
|
233
|
+
#### With Bearer Token
|
|
234
|
+
|
|
235
|
+
```python
|
|
236
|
+
from airia import AiriaClient
|
|
237
|
+
|
|
238
|
+
# Initialize client with bearer token
|
|
239
|
+
client = AiriaClient.with_bearer_token(bearer_token="your_bearer_token")
|
|
240
|
+
|
|
241
|
+
# Execute a pipeline
|
|
242
|
+
response = client.execute_pipeline(
|
|
243
|
+
pipeline_id="your_pipeline_id",
|
|
244
|
+
user_input="Tell me about quantum computing"
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
print(response.result)
|
|
248
|
+
```
|
|
249
|
+
|
|
210
250
|
#### Synchronous Streaming
|
|
211
251
|
|
|
212
252
|
```python
|
|
@@ -214,6 +254,7 @@ from airia import AiriaClient
|
|
|
214
254
|
|
|
215
255
|
# Initialize client (API key can be passed directly or via AIRIA_API_KEY environment variable)
|
|
216
256
|
client = AiriaClient(api_key="your_api_key")
|
|
257
|
+
# Or with bearer token: client = AiriaClient.with_bearer_token(bearer_token="your_bearer_token")
|
|
217
258
|
|
|
218
259
|
# Execute a pipeline
|
|
219
260
|
response = client.execute_pipeline(
|
|
@@ -228,6 +269,8 @@ for c in response.stream:
|
|
|
228
269
|
|
|
229
270
|
### Asynchronous Usage
|
|
230
271
|
|
|
272
|
+
#### With API Key
|
|
273
|
+
|
|
231
274
|
```python
|
|
232
275
|
import asyncio
|
|
233
276
|
from airia import AiriaAsyncClient
|
|
@@ -243,6 +286,23 @@ async def main():
|
|
|
243
286
|
asyncio.run(main())
|
|
244
287
|
```
|
|
245
288
|
|
|
289
|
+
#### With Bearer Token
|
|
290
|
+
|
|
291
|
+
```python
|
|
292
|
+
import asyncio
|
|
293
|
+
from airia import AiriaAsyncClient
|
|
294
|
+
|
|
295
|
+
async def main():
|
|
296
|
+
client = AiriaAsyncClient.with_bearer_token(bearer_token="your_bearer_token")
|
|
297
|
+
response = await client.execute_pipeline(
|
|
298
|
+
pipeline_id="your_pipeline_id",
|
|
299
|
+
user_input="Tell me about quantum computing"
|
|
300
|
+
)
|
|
301
|
+
print(response.result)
|
|
302
|
+
|
|
303
|
+
asyncio.run(main())
|
|
304
|
+
```
|
|
305
|
+
|
|
246
306
|
#### Asynchronous Streaming
|
|
247
307
|
|
|
248
308
|
```python
|
|
@@ -251,6 +311,7 @@ from airia import AiriaAsyncClient
|
|
|
251
311
|
|
|
252
312
|
async def main():
|
|
253
313
|
client = AiriaAsyncClient(api_key="your_api_key")
|
|
314
|
+
# Or with bearer token: client = AiriaAsyncClient.with_bearer_token(bearer_token="your_bearer_token")
|
|
254
315
|
response = await client.execute_pipeline(
|
|
255
316
|
pipeline_id="your_pipeline_id",
|
|
256
317
|
user_input="Tell me about quantum computing",
|
|
@@ -320,6 +381,7 @@ from airia import AiriaClient
|
|
|
320
381
|
from airia.types import AgentModelStreamFragmentMessage
|
|
321
382
|
|
|
322
383
|
client = AiriaClient(api_key="your_api_key")
|
|
384
|
+
# Or with bearer token: client = AiriaClient.with_bearer_token(bearer_token="your_bearer_token")
|
|
323
385
|
|
|
324
386
|
response = client.execute_pipeline(
|
|
325
387
|
pipeline_id="your_pipeline_id",
|
|
@@ -343,6 +405,7 @@ You can retrieve detailed configuration information about a pipeline using the `
|
|
|
343
405
|
from airia import AiriaClient
|
|
344
406
|
|
|
345
407
|
client = AiriaClient(api_key="your_api_key")
|
|
408
|
+
# Or with bearer token: client = AiriaClient.with_bearer_token(bearer_token="your_bearer_token")
|
|
346
409
|
|
|
347
410
|
# Get pipeline configuration
|
|
348
411
|
config = client.get_pipeline_config(pipeline_id="your_pipeline_id")
|
|
@@ -351,10 +414,35 @@ config = client.get_pipeline_config(pipeline_id="your_pipeline_id")
|
|
|
351
414
|
print(f"Pipeline Name: {config.agent.name}")
|
|
352
415
|
```
|
|
353
416
|
|
|
417
|
+
## Authentication Methods
|
|
418
|
+
|
|
419
|
+
Airia supports two authentication methods:
|
|
420
|
+
|
|
421
|
+
### API Keys
|
|
422
|
+
- Can be passed as a parameter or via `AIRIA_API_KEY` environment variable
|
|
423
|
+
- Support gateway functionality (OpenAI and Anthropic gateways)
|
|
424
|
+
- Suitable for long-term, persistent authentication
|
|
425
|
+
|
|
426
|
+
### Bearer Tokens
|
|
427
|
+
- Must be provided explicitly (no environment variable fallback)
|
|
428
|
+
- **Important**: Bearer tokens cannot be used with gateway functionality
|
|
429
|
+
- Suitable for ephemeral, short-lived authentication scenarios
|
|
430
|
+
- Ideal for temporary access or programmatic token generation
|
|
431
|
+
|
|
432
|
+
```python
|
|
433
|
+
# ✅ API key with gateway support
|
|
434
|
+
client = AiriaClient.with_openai_gateway(api_key="your_api_key")
|
|
435
|
+
|
|
436
|
+
# ❌ Bearer token with gateway - NOT SUPPORTED
|
|
437
|
+
# client = AiriaClient.with_openai_gateway(bearer_token="token") # This won't work
|
|
438
|
+
```
|
|
439
|
+
|
|
354
440
|
## Gateway Usage
|
|
355
441
|
|
|
356
442
|
Airia provides gateway capabilities for popular AI services like OpenAI and Anthropic, allowing you to use your Airia API key with these services.
|
|
357
443
|
|
|
444
|
+
> **Note**: Gateway functionality requires API key authentication. Bearer tokens are not supported for gateway usage.
|
|
445
|
+
|
|
358
446
|
### OpenAI Gateway
|
|
359
447
|
|
|
360
448
|
```python
|
|
@@ -482,7 +570,11 @@ console_logger = configure_logging(
|
|
|
482
570
|
The SDK uses custom exceptions to provide clear error messages:
|
|
483
571
|
|
|
484
572
|
```python
|
|
485
|
-
from airia import AiriaAPIError
|
|
573
|
+
from airia import AiriaAPIError, AiriaClient
|
|
574
|
+
|
|
575
|
+
# Works with both API keys and bearer tokens
|
|
576
|
+
client = AiriaClient(api_key="your_api_key")
|
|
577
|
+
# Or: client = AiriaClient.with_bearer_token(bearer_token="your_bearer_token")
|
|
486
578
|
|
|
487
579
|
try:
|
|
488
580
|
response = client.execute_pipeline(
|
|
@@ -13,7 +13,9 @@ Airia Python API Library that provides a clean and intuitive interface to intera
|
|
|
13
13
|
- **Gateway Support**: Seamlessly integrate with OpenAI and Anthropic services through Airia gateways
|
|
14
14
|
- **Error Handling**: Comprehensive error handling with custom exceptions
|
|
15
15
|
- **Logging**: Built-in configurable logging with correlation ID support for request tracing
|
|
16
|
-
- **
|
|
16
|
+
- **Flexible Authentication**: Support for both API keys and bearer tokens with flexible configuration
|
|
17
|
+
- **API Key Management**: API key configuration via parameters or environment variables
|
|
18
|
+
- **Bearer Token Support**: Bearer token authentication for ephemeral, short-lived credentials
|
|
17
19
|
|
|
18
20
|
## Installation
|
|
19
21
|
|
|
@@ -151,17 +153,38 @@ This will create both wheel and source distribution in the `dist/` directory.
|
|
|
151
153
|
```python
|
|
152
154
|
from airia import AiriaClient
|
|
153
155
|
|
|
156
|
+
# API Key Authentication
|
|
154
157
|
client = AiriaClient(
|
|
155
158
|
base_url="https://api.airia.ai", # Default: "https://api.airia.ai"
|
|
156
|
-
api_key=None,
|
|
157
|
-
timeout=30.0,
|
|
158
|
-
log_requests=False,
|
|
159
|
-
custom_logger=None
|
|
159
|
+
api_key=None, # Or set AIRIA_API_KEY environment variable
|
|
160
|
+
timeout=30.0, # Request timeout in seconds (default: 30.0)
|
|
161
|
+
log_requests=False, # Enable request/response logging (default: False)
|
|
162
|
+
custom_logger=None # Use custom logger (default: None - uses built-in)
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
# Bearer Token Authentication
|
|
166
|
+
client = AiriaClient(
|
|
167
|
+
base_url="https://api.airia.ai", # Default: "https://api.airia.ai"
|
|
168
|
+
bearer_token="your_bearer_token", # Must be provided explicitly (no env var fallback)
|
|
169
|
+
timeout=30.0, # Request timeout in seconds (default: 30.0)
|
|
170
|
+
log_requests=False, # Enable request/response logging (default: False)
|
|
171
|
+
custom_logger=None # Use custom logger (default: None - uses built-in)
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
# Convenience method for bearer token
|
|
175
|
+
client = AiriaClient.with_bearer_token(
|
|
176
|
+
bearer_token="your_bearer_token",
|
|
177
|
+
base_url="https://api.airia.ai", # Optional, uses default if not provided
|
|
178
|
+
timeout=30.0, # Optional, uses default if not provided
|
|
179
|
+
log_requests=False, # Optional, uses default if not provided
|
|
180
|
+
custom_logger=None # Optional, uses default if not provided
|
|
160
181
|
)
|
|
161
182
|
```
|
|
162
183
|
|
|
163
184
|
### Synchronous Usage
|
|
164
185
|
|
|
186
|
+
#### With API Key
|
|
187
|
+
|
|
165
188
|
```python
|
|
166
189
|
from airia import AiriaClient
|
|
167
190
|
|
|
@@ -177,6 +200,23 @@ response = client.execute_pipeline(
|
|
|
177
200
|
print(response.result)
|
|
178
201
|
```
|
|
179
202
|
|
|
203
|
+
#### With Bearer Token
|
|
204
|
+
|
|
205
|
+
```python
|
|
206
|
+
from airia import AiriaClient
|
|
207
|
+
|
|
208
|
+
# Initialize client with bearer token
|
|
209
|
+
client = AiriaClient.with_bearer_token(bearer_token="your_bearer_token")
|
|
210
|
+
|
|
211
|
+
# Execute a pipeline
|
|
212
|
+
response = client.execute_pipeline(
|
|
213
|
+
pipeline_id="your_pipeline_id",
|
|
214
|
+
user_input="Tell me about quantum computing"
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
print(response.result)
|
|
218
|
+
```
|
|
219
|
+
|
|
180
220
|
#### Synchronous Streaming
|
|
181
221
|
|
|
182
222
|
```python
|
|
@@ -184,6 +224,7 @@ from airia import AiriaClient
|
|
|
184
224
|
|
|
185
225
|
# Initialize client (API key can be passed directly or via AIRIA_API_KEY environment variable)
|
|
186
226
|
client = AiriaClient(api_key="your_api_key")
|
|
227
|
+
# Or with bearer token: client = AiriaClient.with_bearer_token(bearer_token="your_bearer_token")
|
|
187
228
|
|
|
188
229
|
# Execute a pipeline
|
|
189
230
|
response = client.execute_pipeline(
|
|
@@ -198,6 +239,8 @@ for c in response.stream:
|
|
|
198
239
|
|
|
199
240
|
### Asynchronous Usage
|
|
200
241
|
|
|
242
|
+
#### With API Key
|
|
243
|
+
|
|
201
244
|
```python
|
|
202
245
|
import asyncio
|
|
203
246
|
from airia import AiriaAsyncClient
|
|
@@ -213,6 +256,23 @@ async def main():
|
|
|
213
256
|
asyncio.run(main())
|
|
214
257
|
```
|
|
215
258
|
|
|
259
|
+
#### With Bearer Token
|
|
260
|
+
|
|
261
|
+
```python
|
|
262
|
+
import asyncio
|
|
263
|
+
from airia import AiriaAsyncClient
|
|
264
|
+
|
|
265
|
+
async def main():
|
|
266
|
+
client = AiriaAsyncClient.with_bearer_token(bearer_token="your_bearer_token")
|
|
267
|
+
response = await client.execute_pipeline(
|
|
268
|
+
pipeline_id="your_pipeline_id",
|
|
269
|
+
user_input="Tell me about quantum computing"
|
|
270
|
+
)
|
|
271
|
+
print(response.result)
|
|
272
|
+
|
|
273
|
+
asyncio.run(main())
|
|
274
|
+
```
|
|
275
|
+
|
|
216
276
|
#### Asynchronous Streaming
|
|
217
277
|
|
|
218
278
|
```python
|
|
@@ -221,6 +281,7 @@ from airia import AiriaAsyncClient
|
|
|
221
281
|
|
|
222
282
|
async def main():
|
|
223
283
|
client = AiriaAsyncClient(api_key="your_api_key")
|
|
284
|
+
# Or with bearer token: client = AiriaAsyncClient.with_bearer_token(bearer_token="your_bearer_token")
|
|
224
285
|
response = await client.execute_pipeline(
|
|
225
286
|
pipeline_id="your_pipeline_id",
|
|
226
287
|
user_input="Tell me about quantum computing",
|
|
@@ -290,6 +351,7 @@ from airia import AiriaClient
|
|
|
290
351
|
from airia.types import AgentModelStreamFragmentMessage
|
|
291
352
|
|
|
292
353
|
client = AiriaClient(api_key="your_api_key")
|
|
354
|
+
# Or with bearer token: client = AiriaClient.with_bearer_token(bearer_token="your_bearer_token")
|
|
293
355
|
|
|
294
356
|
response = client.execute_pipeline(
|
|
295
357
|
pipeline_id="your_pipeline_id",
|
|
@@ -313,6 +375,7 @@ You can retrieve detailed configuration information about a pipeline using the `
|
|
|
313
375
|
from airia import AiriaClient
|
|
314
376
|
|
|
315
377
|
client = AiriaClient(api_key="your_api_key")
|
|
378
|
+
# Or with bearer token: client = AiriaClient.with_bearer_token(bearer_token="your_bearer_token")
|
|
316
379
|
|
|
317
380
|
# Get pipeline configuration
|
|
318
381
|
config = client.get_pipeline_config(pipeline_id="your_pipeline_id")
|
|
@@ -321,10 +384,35 @@ config = client.get_pipeline_config(pipeline_id="your_pipeline_id")
|
|
|
321
384
|
print(f"Pipeline Name: {config.agent.name}")
|
|
322
385
|
```
|
|
323
386
|
|
|
387
|
+
## Authentication Methods
|
|
388
|
+
|
|
389
|
+
Airia supports two authentication methods:
|
|
390
|
+
|
|
391
|
+
### API Keys
|
|
392
|
+
- Can be passed as a parameter or via `AIRIA_API_KEY` environment variable
|
|
393
|
+
- Support gateway functionality (OpenAI and Anthropic gateways)
|
|
394
|
+
- Suitable for long-term, persistent authentication
|
|
395
|
+
|
|
396
|
+
### Bearer Tokens
|
|
397
|
+
- Must be provided explicitly (no environment variable fallback)
|
|
398
|
+
- **Important**: Bearer tokens cannot be used with gateway functionality
|
|
399
|
+
- Suitable for ephemeral, short-lived authentication scenarios
|
|
400
|
+
- Ideal for temporary access or programmatic token generation
|
|
401
|
+
|
|
402
|
+
```python
|
|
403
|
+
# ✅ API key with gateway support
|
|
404
|
+
client = AiriaClient.with_openai_gateway(api_key="your_api_key")
|
|
405
|
+
|
|
406
|
+
# ❌ Bearer token with gateway - NOT SUPPORTED
|
|
407
|
+
# client = AiriaClient.with_openai_gateway(bearer_token="token") # This won't work
|
|
408
|
+
```
|
|
409
|
+
|
|
324
410
|
## Gateway Usage
|
|
325
411
|
|
|
326
412
|
Airia provides gateway capabilities for popular AI services like OpenAI and Anthropic, allowing you to use your Airia API key with these services.
|
|
327
413
|
|
|
414
|
+
> **Note**: Gateway functionality requires API key authentication. Bearer tokens are not supported for gateway usage.
|
|
415
|
+
|
|
328
416
|
### OpenAI Gateway
|
|
329
417
|
|
|
330
418
|
```python
|
|
@@ -452,7 +540,11 @@ console_logger = configure_logging(
|
|
|
452
540
|
The SDK uses custom exceptions to provide clear error messages:
|
|
453
541
|
|
|
454
542
|
```python
|
|
455
|
-
from airia import AiriaAPIError
|
|
543
|
+
from airia import AiriaAPIError, AiriaClient
|
|
544
|
+
|
|
545
|
+
# Works with both API keys and bearer tokens
|
|
546
|
+
client = AiriaClient(api_key="your_api_key")
|
|
547
|
+
# Or: client = AiriaClient.with_bearer_token(bearer_token="your_bearer_token")
|
|
456
548
|
|
|
457
549
|
try:
|
|
458
550
|
response = client.execute_pipeline(
|
|
@@ -6,6 +6,7 @@ from urllib.parse import urljoin
|
|
|
6
6
|
import aiohttp
|
|
7
7
|
import loguru
|
|
8
8
|
|
|
9
|
+
from ..constants import DEFAULT_ANTHROPIC_GATEWAY_URL, DEFAULT_BASE_URL, DEFAULT_OPENAI_GATEWAY_URL, DEFAULT_TIMEOUT
|
|
9
10
|
from ..exceptions import AiriaAPIError
|
|
10
11
|
from ..types import (
|
|
11
12
|
ApiVersion,
|
|
@@ -26,9 +27,10 @@ class AiriaAsyncClient(AiriaBaseClient):
|
|
|
26
27
|
|
|
27
28
|
def __init__(
|
|
28
29
|
self,
|
|
29
|
-
base_url: str =
|
|
30
|
+
base_url: str = DEFAULT_BASE_URL,
|
|
30
31
|
api_key: Optional[str] = None,
|
|
31
|
-
|
|
32
|
+
bearer_token: Optional[str] = None,
|
|
33
|
+
timeout: float = DEFAULT_TIMEOUT,
|
|
32
34
|
log_requests: bool = False,
|
|
33
35
|
custom_logger: Optional["loguru.Logger"] = None,
|
|
34
36
|
):
|
|
@@ -38,6 +40,7 @@ class AiriaAsyncClient(AiriaBaseClient):
|
|
|
38
40
|
Args:
|
|
39
41
|
base_url: Base URL of the Airia API.
|
|
40
42
|
api_key: API key for authentication. If not provided, will attempt to use AIRIA_API_KEY environment variable.
|
|
43
|
+
bearer_token: Bearer token for authentication. Must be provided explicitly (no environment variable fallback).
|
|
41
44
|
timeout: Request timeout in seconds.
|
|
42
45
|
log_requests: Whether to log API requests and responses. Default is False.
|
|
43
46
|
custom_logger: Optional custom logger object to use for logging. If not provided, will use a default logger when `log_requests` is True.
|
|
@@ -45,6 +48,7 @@ class AiriaAsyncClient(AiriaBaseClient):
|
|
|
45
48
|
super().__init__(
|
|
46
49
|
base_url=base_url,
|
|
47
50
|
api_key=api_key,
|
|
51
|
+
bearer_token=bearer_token,
|
|
48
52
|
timeout=timeout,
|
|
49
53
|
log_requests=log_requests,
|
|
50
54
|
custom_logger=custom_logger,
|
|
@@ -80,10 +84,10 @@ class AiriaAsyncClient(AiriaBaseClient):
|
|
|
80
84
|
@classmethod
|
|
81
85
|
def with_openai_gateway(
|
|
82
86
|
cls,
|
|
83
|
-
base_url: str =
|
|
84
|
-
gateway_url: str =
|
|
87
|
+
base_url: str = DEFAULT_BASE_URL,
|
|
88
|
+
gateway_url: str = DEFAULT_OPENAI_GATEWAY_URL,
|
|
85
89
|
api_key: Optional[str] = None,
|
|
86
|
-
timeout: float =
|
|
90
|
+
timeout: float = DEFAULT_TIMEOUT,
|
|
87
91
|
log_requests: bool = False,
|
|
88
92
|
custom_logger: Optional["loguru.Logger"] = None,
|
|
89
93
|
**kwargs,
|
|
@@ -102,22 +106,22 @@ class AiriaAsyncClient(AiriaBaseClient):
|
|
|
102
106
|
"""
|
|
103
107
|
from openai import AsyncOpenAI
|
|
104
108
|
|
|
105
|
-
|
|
109
|
+
client = cls(base_url=base_url, api_key=api_key, timeout=timeout, log_requests=log_requests, custom_logger=custom_logger)
|
|
106
110
|
cls.openai = AsyncOpenAI(
|
|
107
|
-
api_key=api_key,
|
|
111
|
+
api_key=client.api_key,
|
|
108
112
|
base_url=gateway_url,
|
|
109
113
|
**kwargs,
|
|
110
114
|
)
|
|
111
115
|
|
|
112
|
-
return
|
|
116
|
+
return client
|
|
113
117
|
|
|
114
118
|
@classmethod
|
|
115
119
|
def with_anthropic_gateway(
|
|
116
120
|
cls,
|
|
117
|
-
base_url: str =
|
|
118
|
-
gateway_url: str =
|
|
121
|
+
base_url: str = DEFAULT_BASE_URL,
|
|
122
|
+
gateway_url: str = DEFAULT_ANTHROPIC_GATEWAY_URL,
|
|
119
123
|
api_key: Optional[str] = None,
|
|
120
|
-
timeout: float =
|
|
124
|
+
timeout: float = DEFAULT_TIMEOUT,
|
|
121
125
|
log_requests: bool = False,
|
|
122
126
|
custom_logger: Optional["loguru.Logger"] = None,
|
|
123
127
|
**kwargs,
|
|
@@ -136,14 +140,41 @@ class AiriaAsyncClient(AiriaBaseClient):
|
|
|
136
140
|
"""
|
|
137
141
|
from anthropic import AsyncAnthropic
|
|
138
142
|
|
|
139
|
-
|
|
143
|
+
client = cls(base_url=base_url, api_key=api_key, timeout=timeout, log_requests=log_requests, custom_logger=custom_logger)
|
|
140
144
|
cls.anthropic = AsyncAnthropic(
|
|
141
|
-
api_key=api_key,
|
|
145
|
+
api_key=client.api_key,
|
|
142
146
|
base_url=gateway_url,
|
|
143
147
|
**kwargs,
|
|
144
148
|
)
|
|
145
149
|
|
|
146
|
-
return
|
|
150
|
+
return client
|
|
151
|
+
|
|
152
|
+
@classmethod
|
|
153
|
+
def with_bearer_token(
|
|
154
|
+
cls,
|
|
155
|
+
bearer_token: str,
|
|
156
|
+
base_url: str = DEFAULT_BASE_URL,
|
|
157
|
+
timeout: float = DEFAULT_TIMEOUT,
|
|
158
|
+
log_requests: bool = False,
|
|
159
|
+
custom_logger: Optional["loguru.Logger"] = None,
|
|
160
|
+
):
|
|
161
|
+
"""
|
|
162
|
+
Initialize the asynchronous Airia API client with bearer token authentication.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
bearer_token: Bearer token for authentication.
|
|
166
|
+
base_url: Base URL of the Airia API.
|
|
167
|
+
timeout: Request timeout in seconds.
|
|
168
|
+
log_requests: Whether to log API requests and responses. Default is False.
|
|
169
|
+
custom_logger: Optional custom logger object to use for logging. If not provided, will use a default logger when `log_requests` is True.
|
|
170
|
+
"""
|
|
171
|
+
return cls(
|
|
172
|
+
base_url=base_url,
|
|
173
|
+
bearer_token=bearer_token,
|
|
174
|
+
timeout=timeout,
|
|
175
|
+
log_requests=log_requests,
|
|
176
|
+
custom_logger=custom_logger,
|
|
177
|
+
)
|
|
147
178
|
|
|
148
179
|
def _handle_exception(
|
|
149
180
|
self, e: aiohttp.ClientResponseError, url: str, correlation_id: str
|
|
@@ -159,12 +190,12 @@ class AiriaAsyncClient(AiriaBaseClient):
|
|
|
159
190
|
# Extract error details from response
|
|
160
191
|
error_message = e.message
|
|
161
192
|
|
|
162
|
-
# Make sure
|
|
163
|
-
sanitized_message =
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
193
|
+
# Make sure sensitive auth information is not included in error messages
|
|
194
|
+
sanitized_message = error_message
|
|
195
|
+
if self.api_key and self.api_key in sanitized_message:
|
|
196
|
+
sanitized_message = sanitized_message.replace(self.api_key, "[REDACTED]")
|
|
197
|
+
if self.bearer_token and self.bearer_token in sanitized_message:
|
|
198
|
+
sanitized_message = sanitized_message.replace(self.bearer_token, "[REDACTED]")
|
|
168
199
|
|
|
169
200
|
# Raise custom exception with status code and sanitized message
|
|
170
201
|
raise AiriaAPIError(status_code=e.status, message=sanitized_message) from e
|
|
@@ -5,6 +5,7 @@ from urllib.parse import urljoin
|
|
|
5
5
|
|
|
6
6
|
import loguru
|
|
7
7
|
|
|
8
|
+
from ..constants import DEFAULT_BASE_URL, DEFAULT_TIMEOUT
|
|
8
9
|
from ..logs import configure_logging, set_correlation_id
|
|
9
10
|
from ..types import ApiVersion, RequestData
|
|
10
11
|
|
|
@@ -17,9 +18,10 @@ class AiriaBaseClient:
|
|
|
17
18
|
|
|
18
19
|
def __init__(
|
|
19
20
|
self,
|
|
20
|
-
base_url: str =
|
|
21
|
+
base_url: str = DEFAULT_BASE_URL,
|
|
21
22
|
api_key: Optional[str] = None,
|
|
22
|
-
|
|
23
|
+
bearer_token: Optional[str] = None,
|
|
24
|
+
timeout: float = DEFAULT_TIMEOUT,
|
|
23
25
|
log_requests: bool = False,
|
|
24
26
|
custom_logger: Optional["loguru.Logger"] = None,
|
|
25
27
|
):
|
|
@@ -28,12 +30,13 @@ class AiriaBaseClient:
|
|
|
28
30
|
|
|
29
31
|
Args:
|
|
30
32
|
api_key: API key for authentication. If not provided, will attempt to use AIRIA_API_KEY environment variable.
|
|
33
|
+
bearer_token: Bearer token for authentication. Must be provided explicitly (no environment variable fallback).
|
|
31
34
|
timeout: Request timeout in seconds.
|
|
32
35
|
log_requests: Whether to log API requests and responses. Default is False.
|
|
33
36
|
custom_logger: Optional custom logger object to use for logging. If not provided, will use a default logger when `log_requests` is True.
|
|
34
37
|
"""
|
|
35
|
-
# Resolve
|
|
36
|
-
self.api_key = self.__class__.
|
|
38
|
+
# Resolve authentication credentials
|
|
39
|
+
self.api_key, self.bearer_token = self.__class__._resolve_auth_credentials(api_key, bearer_token)
|
|
37
40
|
|
|
38
41
|
# Store configuration
|
|
39
42
|
self.base_url = base_url
|
|
@@ -44,27 +47,43 @@ class AiriaBaseClient:
|
|
|
44
47
|
self.logger = configure_logging() if custom_logger is None else custom_logger
|
|
45
48
|
|
|
46
49
|
@staticmethod
|
|
47
|
-
def
|
|
50
|
+
def _resolve_auth_credentials(api_key: Optional[str] = None, bearer_token: Optional[str] = None):
|
|
48
51
|
"""
|
|
49
|
-
|
|
52
|
+
Resolve authentication credentials from parameters and environment variables.
|
|
50
53
|
|
|
51
54
|
Args:
|
|
52
55
|
api_key (Optional[str]): The API key provided as a parameter. Defaults to None.
|
|
56
|
+
bearer_token (Optional[str]): The bearer token provided as a parameter. Defaults to None.
|
|
53
57
|
|
|
54
58
|
Returns:
|
|
55
|
-
|
|
59
|
+
tuple: (api_key, bearer_token) - exactly one will be non-None
|
|
56
60
|
|
|
57
61
|
Raises:
|
|
58
|
-
ValueError: If no
|
|
62
|
+
ValueError: If no authentication method is provided or if both are provided.
|
|
59
63
|
"""
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
if not api_key:
|
|
64
|
+
# Check for explicit conflict first
|
|
65
|
+
if api_key and bearer_token:
|
|
63
66
|
raise ValueError(
|
|
64
|
-
"
|
|
67
|
+
"Cannot provide both api_key and bearer_token. Please use only one authentication method."
|
|
65
68
|
)
|
|
66
|
-
|
|
67
|
-
|
|
69
|
+
|
|
70
|
+
# If bearer token is explicitly provided, use it exclusively
|
|
71
|
+
if bearer_token:
|
|
72
|
+
return None, bearer_token
|
|
73
|
+
|
|
74
|
+
# If API key is explicitly provided, use it exclusively
|
|
75
|
+
if api_key:
|
|
76
|
+
return api_key, None
|
|
77
|
+
|
|
78
|
+
# If neither is provided explicitly, fall back to environment variable
|
|
79
|
+
resolved_api_key = os.environ.get("AIRIA_API_KEY")
|
|
80
|
+
if resolved_api_key:
|
|
81
|
+
return resolved_api_key, None
|
|
82
|
+
|
|
83
|
+
# No authentication method found
|
|
84
|
+
raise ValueError(
|
|
85
|
+
"Authentication required. Provide either api_key (or set AIRIA_API_KEY environment variable) or bearer_token."
|
|
86
|
+
)
|
|
68
87
|
|
|
69
88
|
def _prepare_request(
|
|
70
89
|
self,
|
|
@@ -76,13 +95,18 @@ class AiriaBaseClient:
|
|
|
76
95
|
# Set correlation ID if provided or generate a new one
|
|
77
96
|
correlation_id = set_correlation_id(correlation_id)
|
|
78
97
|
|
|
79
|
-
#
|
|
98
|
+
# Set up base headers
|
|
80
99
|
headers = {
|
|
81
|
-
"X-API-KEY": self.api_key,
|
|
82
100
|
"X-Correlation-ID": correlation_id,
|
|
83
101
|
"Content-Type": "application/json",
|
|
84
102
|
}
|
|
85
103
|
|
|
104
|
+
# Add authentication header based on the method used
|
|
105
|
+
if self.api_key:
|
|
106
|
+
headers["X-API-KEY"] = self.api_key
|
|
107
|
+
elif self.bearer_token:
|
|
108
|
+
headers["Authorization"] = f"Bearer {self.bearer_token}"
|
|
109
|
+
|
|
86
110
|
# Log the request if enabled
|
|
87
111
|
if self.log_requests:
|
|
88
112
|
# Create a sanitized copy of headers and params for logging
|
|
@@ -92,6 +116,8 @@ class AiriaBaseClient:
|
|
|
92
116
|
# Filter out sensitive headers
|
|
93
117
|
if "X-API-KEY" in log_headers:
|
|
94
118
|
log_headers["X-API-KEY"] = "[REDACTED]"
|
|
119
|
+
if "Authorization" in log_headers:
|
|
120
|
+
log_headers["Authorization"] = "[REDACTED]"
|
|
95
121
|
|
|
96
122
|
# Process payload for logging
|
|
97
123
|
log_payload = payload.copy() if payload is not None else {}
|
|
@@ -4,6 +4,7 @@ from urllib.parse import urljoin
|
|
|
4
4
|
import loguru
|
|
5
5
|
import requests
|
|
6
6
|
|
|
7
|
+
from ..constants import DEFAULT_ANTHROPIC_GATEWAY_URL, DEFAULT_BASE_URL, DEFAULT_OPENAI_GATEWAY_URL, DEFAULT_TIMEOUT
|
|
7
8
|
from ..exceptions import AiriaAPIError
|
|
8
9
|
from ..types import (
|
|
9
10
|
ApiVersion,
|
|
@@ -24,9 +25,10 @@ class AiriaClient(AiriaBaseClient):
|
|
|
24
25
|
|
|
25
26
|
def __init__(
|
|
26
27
|
self,
|
|
27
|
-
base_url: str =
|
|
28
|
+
base_url: str = DEFAULT_BASE_URL,
|
|
28
29
|
api_key: Optional[str] = None,
|
|
29
|
-
|
|
30
|
+
bearer_token: Optional[str] = None,
|
|
31
|
+
timeout: float = DEFAULT_TIMEOUT,
|
|
30
32
|
log_requests: bool = False,
|
|
31
33
|
custom_logger: Optional["loguru.Logger"] = None,
|
|
32
34
|
):
|
|
@@ -36,6 +38,7 @@ class AiriaClient(AiriaBaseClient):
|
|
|
36
38
|
Args:
|
|
37
39
|
base_url: Base URL of the Airia API.
|
|
38
40
|
api_key: API key for authentication. If not provided, will attempt to use AIRIA_API_KEY environment variable.
|
|
41
|
+
bearer_token: Bearer token for authentication. Must be provided explicitly (no environment variable fallback).
|
|
39
42
|
timeout: Request timeout in seconds.
|
|
40
43
|
log_requests: Whether to log API requests and responses. Default is False.
|
|
41
44
|
custom_logger: Optional custom logger object to use for logging. If not provided, will use a default logger when `log_requests` is True.
|
|
@@ -43,6 +46,7 @@ class AiriaClient(AiriaBaseClient):
|
|
|
43
46
|
super().__init__(
|
|
44
47
|
base_url=base_url,
|
|
45
48
|
api_key=api_key,
|
|
49
|
+
bearer_token=bearer_token,
|
|
46
50
|
timeout=timeout,
|
|
47
51
|
log_requests=log_requests,
|
|
48
52
|
custom_logger=custom_logger,
|
|
@@ -55,10 +59,10 @@ class AiriaClient(AiriaBaseClient):
|
|
|
55
59
|
@classmethod
|
|
56
60
|
def with_openai_gateway(
|
|
57
61
|
cls,
|
|
58
|
-
base_url: str =
|
|
59
|
-
gateway_url: str =
|
|
62
|
+
base_url: str = DEFAULT_BASE_URL,
|
|
63
|
+
gateway_url: str = DEFAULT_OPENAI_GATEWAY_URL,
|
|
60
64
|
api_key: Optional[str] = None,
|
|
61
|
-
timeout: float =
|
|
65
|
+
timeout: float = DEFAULT_TIMEOUT,
|
|
62
66
|
log_requests: bool = False,
|
|
63
67
|
custom_logger: Optional["loguru.Logger"] = None,
|
|
64
68
|
**kwargs,
|
|
@@ -77,22 +81,22 @@ class AiriaClient(AiriaBaseClient):
|
|
|
77
81
|
"""
|
|
78
82
|
from openai import OpenAI
|
|
79
83
|
|
|
80
|
-
|
|
84
|
+
client = cls(base_url=base_url, api_key=api_key, timeout=timeout, log_requests=log_requests, custom_logger=custom_logger)
|
|
81
85
|
cls.openai = OpenAI(
|
|
82
|
-
api_key=api_key,
|
|
86
|
+
api_key=client.api_key,
|
|
83
87
|
base_url=gateway_url,
|
|
84
88
|
**kwargs,
|
|
85
89
|
)
|
|
86
90
|
|
|
87
|
-
return
|
|
91
|
+
return client
|
|
88
92
|
|
|
89
93
|
@classmethod
|
|
90
94
|
def with_anthropic_gateway(
|
|
91
95
|
cls,
|
|
92
|
-
base_url: str =
|
|
93
|
-
gateway_url: str =
|
|
96
|
+
base_url: str = DEFAULT_BASE_URL,
|
|
97
|
+
gateway_url: str = DEFAULT_ANTHROPIC_GATEWAY_URL,
|
|
94
98
|
api_key: Optional[str] = None,
|
|
95
|
-
timeout: float =
|
|
99
|
+
timeout: float = DEFAULT_TIMEOUT,
|
|
96
100
|
log_requests: bool = False,
|
|
97
101
|
custom_logger: Optional["loguru.Logger"] = None,
|
|
98
102
|
**kwargs,
|
|
@@ -111,14 +115,41 @@ class AiriaClient(AiriaBaseClient):
|
|
|
111
115
|
"""
|
|
112
116
|
from anthropic import Anthropic
|
|
113
117
|
|
|
114
|
-
|
|
118
|
+
client = cls(base_url=base_url, api_key=api_key, timeout=timeout, log_requests=log_requests, custom_logger=custom_logger)
|
|
115
119
|
cls.anthropic = Anthropic(
|
|
116
|
-
api_key=api_key,
|
|
120
|
+
api_key=client.api_key,
|
|
117
121
|
base_url=gateway_url,
|
|
118
122
|
**kwargs,
|
|
119
123
|
)
|
|
120
124
|
|
|
121
|
-
return
|
|
125
|
+
return client
|
|
126
|
+
|
|
127
|
+
@classmethod
|
|
128
|
+
def with_bearer_token(
|
|
129
|
+
cls,
|
|
130
|
+
bearer_token: str,
|
|
131
|
+
base_url: str = DEFAULT_BASE_URL,
|
|
132
|
+
timeout: float = DEFAULT_TIMEOUT,
|
|
133
|
+
log_requests: bool = False,
|
|
134
|
+
custom_logger: Optional["loguru.Logger"] = None,
|
|
135
|
+
):
|
|
136
|
+
"""
|
|
137
|
+
Initialize the synchronous Airia API client with bearer token authentication.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
bearer_token: Bearer token for authentication.
|
|
141
|
+
base_url: Base URL of the Airia API.
|
|
142
|
+
timeout: Request timeout in seconds.
|
|
143
|
+
log_requests: Whether to log API requests and responses. Default is False.
|
|
144
|
+
custom_logger: Optional custom logger object to use for logging. If not provided, will use a default logger when `log_requests` is True.
|
|
145
|
+
"""
|
|
146
|
+
return cls(
|
|
147
|
+
base_url=base_url,
|
|
148
|
+
bearer_token=bearer_token,
|
|
149
|
+
timeout=timeout,
|
|
150
|
+
log_requests=log_requests,
|
|
151
|
+
custom_logger=custom_logger,
|
|
152
|
+
)
|
|
122
153
|
|
|
123
154
|
def _handle_exception(self, e: requests.HTTPError, url: str, correlation_id: str):
|
|
124
155
|
# Log the error response if enabled
|
|
@@ -141,12 +172,12 @@ class AiriaClient(AiriaBaseClient):
|
|
|
141
172
|
# If JSON parsing fails or expected keys are missing
|
|
142
173
|
error_message = f"API request failed: {str(e)}"
|
|
143
174
|
|
|
144
|
-
# Make sure
|
|
145
|
-
sanitized_message =
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
175
|
+
# Make sure sensitive auth information is not included in error messages
|
|
176
|
+
sanitized_message = error_message
|
|
177
|
+
if self.api_key and self.api_key in sanitized_message:
|
|
178
|
+
sanitized_message = sanitized_message.replace(self.api_key, "[REDACTED]")
|
|
179
|
+
if self.bearer_token and self.bearer_token in sanitized_message:
|
|
180
|
+
sanitized_message = sanitized_message.replace(self.bearer_token, "[REDACTED]")
|
|
150
181
|
|
|
151
182
|
# Raise custom exception with status code and sanitized message
|
|
152
183
|
raise AiriaAPIError(
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""Constants used throughout the Airia SDK."""
|
|
2
|
+
|
|
3
|
+
# Default API endpoints
|
|
4
|
+
DEFAULT_BASE_URL = "https://api.airia.ai/"
|
|
5
|
+
DEFAULT_OPENAI_GATEWAY_URL = "https://gateway.airia.ai/openai/v1"
|
|
6
|
+
DEFAULT_ANTHROPIC_GATEWAY_URL = "https://gateway.airia.ai/anthropic"
|
|
7
|
+
|
|
8
|
+
# Default timeouts
|
|
9
|
+
DEFAULT_TIMEOUT = 30.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: airia
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.10
|
|
4
4
|
Summary: Python SDK for Airia API
|
|
5
5
|
Author-email: Airia LLC <support@airia.com>
|
|
6
6
|
License: MIT
|
|
@@ -43,7 +43,9 @@ Airia Python API Library that provides a clean and intuitive interface to intera
|
|
|
43
43
|
- **Gateway Support**: Seamlessly integrate with OpenAI and Anthropic services through Airia gateways
|
|
44
44
|
- **Error Handling**: Comprehensive error handling with custom exceptions
|
|
45
45
|
- **Logging**: Built-in configurable logging with correlation ID support for request tracing
|
|
46
|
-
- **
|
|
46
|
+
- **Flexible Authentication**: Support for both API keys and bearer tokens with flexible configuration
|
|
47
|
+
- **API Key Management**: API key configuration via parameters or environment variables
|
|
48
|
+
- **Bearer Token Support**: Bearer token authentication for ephemeral, short-lived credentials
|
|
47
49
|
|
|
48
50
|
## Installation
|
|
49
51
|
|
|
@@ -181,17 +183,38 @@ This will create both wheel and source distribution in the `dist/` directory.
|
|
|
181
183
|
```python
|
|
182
184
|
from airia import AiriaClient
|
|
183
185
|
|
|
186
|
+
# API Key Authentication
|
|
184
187
|
client = AiriaClient(
|
|
185
188
|
base_url="https://api.airia.ai", # Default: "https://api.airia.ai"
|
|
186
|
-
api_key=None,
|
|
187
|
-
timeout=30.0,
|
|
188
|
-
log_requests=False,
|
|
189
|
-
custom_logger=None
|
|
189
|
+
api_key=None, # Or set AIRIA_API_KEY environment variable
|
|
190
|
+
timeout=30.0, # Request timeout in seconds (default: 30.0)
|
|
191
|
+
log_requests=False, # Enable request/response logging (default: False)
|
|
192
|
+
custom_logger=None # Use custom logger (default: None - uses built-in)
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
# Bearer Token Authentication
|
|
196
|
+
client = AiriaClient(
|
|
197
|
+
base_url="https://api.airia.ai", # Default: "https://api.airia.ai"
|
|
198
|
+
bearer_token="your_bearer_token", # Must be provided explicitly (no env var fallback)
|
|
199
|
+
timeout=30.0, # Request timeout in seconds (default: 30.0)
|
|
200
|
+
log_requests=False, # Enable request/response logging (default: False)
|
|
201
|
+
custom_logger=None # Use custom logger (default: None - uses built-in)
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
# Convenience method for bearer token
|
|
205
|
+
client = AiriaClient.with_bearer_token(
|
|
206
|
+
bearer_token="your_bearer_token",
|
|
207
|
+
base_url="https://api.airia.ai", # Optional, uses default if not provided
|
|
208
|
+
timeout=30.0, # Optional, uses default if not provided
|
|
209
|
+
log_requests=False, # Optional, uses default if not provided
|
|
210
|
+
custom_logger=None # Optional, uses default if not provided
|
|
190
211
|
)
|
|
191
212
|
```
|
|
192
213
|
|
|
193
214
|
### Synchronous Usage
|
|
194
215
|
|
|
216
|
+
#### With API Key
|
|
217
|
+
|
|
195
218
|
```python
|
|
196
219
|
from airia import AiriaClient
|
|
197
220
|
|
|
@@ -207,6 +230,23 @@ response = client.execute_pipeline(
|
|
|
207
230
|
print(response.result)
|
|
208
231
|
```
|
|
209
232
|
|
|
233
|
+
#### With Bearer Token
|
|
234
|
+
|
|
235
|
+
```python
|
|
236
|
+
from airia import AiriaClient
|
|
237
|
+
|
|
238
|
+
# Initialize client with bearer token
|
|
239
|
+
client = AiriaClient.with_bearer_token(bearer_token="your_bearer_token")
|
|
240
|
+
|
|
241
|
+
# Execute a pipeline
|
|
242
|
+
response = client.execute_pipeline(
|
|
243
|
+
pipeline_id="your_pipeline_id",
|
|
244
|
+
user_input="Tell me about quantum computing"
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
print(response.result)
|
|
248
|
+
```
|
|
249
|
+
|
|
210
250
|
#### Synchronous Streaming
|
|
211
251
|
|
|
212
252
|
```python
|
|
@@ -214,6 +254,7 @@ from airia import AiriaClient
|
|
|
214
254
|
|
|
215
255
|
# Initialize client (API key can be passed directly or via AIRIA_API_KEY environment variable)
|
|
216
256
|
client = AiriaClient(api_key="your_api_key")
|
|
257
|
+
# Or with bearer token: client = AiriaClient.with_bearer_token(bearer_token="your_bearer_token")
|
|
217
258
|
|
|
218
259
|
# Execute a pipeline
|
|
219
260
|
response = client.execute_pipeline(
|
|
@@ -228,6 +269,8 @@ for c in response.stream:
|
|
|
228
269
|
|
|
229
270
|
### Asynchronous Usage
|
|
230
271
|
|
|
272
|
+
#### With API Key
|
|
273
|
+
|
|
231
274
|
```python
|
|
232
275
|
import asyncio
|
|
233
276
|
from airia import AiriaAsyncClient
|
|
@@ -243,6 +286,23 @@ async def main():
|
|
|
243
286
|
asyncio.run(main())
|
|
244
287
|
```
|
|
245
288
|
|
|
289
|
+
#### With Bearer Token
|
|
290
|
+
|
|
291
|
+
```python
|
|
292
|
+
import asyncio
|
|
293
|
+
from airia import AiriaAsyncClient
|
|
294
|
+
|
|
295
|
+
async def main():
|
|
296
|
+
client = AiriaAsyncClient.with_bearer_token(bearer_token="your_bearer_token")
|
|
297
|
+
response = await client.execute_pipeline(
|
|
298
|
+
pipeline_id="your_pipeline_id",
|
|
299
|
+
user_input="Tell me about quantum computing"
|
|
300
|
+
)
|
|
301
|
+
print(response.result)
|
|
302
|
+
|
|
303
|
+
asyncio.run(main())
|
|
304
|
+
```
|
|
305
|
+
|
|
246
306
|
#### Asynchronous Streaming
|
|
247
307
|
|
|
248
308
|
```python
|
|
@@ -251,6 +311,7 @@ from airia import AiriaAsyncClient
|
|
|
251
311
|
|
|
252
312
|
async def main():
|
|
253
313
|
client = AiriaAsyncClient(api_key="your_api_key")
|
|
314
|
+
# Or with bearer token: client = AiriaAsyncClient.with_bearer_token(bearer_token="your_bearer_token")
|
|
254
315
|
response = await client.execute_pipeline(
|
|
255
316
|
pipeline_id="your_pipeline_id",
|
|
256
317
|
user_input="Tell me about quantum computing",
|
|
@@ -320,6 +381,7 @@ from airia import AiriaClient
|
|
|
320
381
|
from airia.types import AgentModelStreamFragmentMessage
|
|
321
382
|
|
|
322
383
|
client = AiriaClient(api_key="your_api_key")
|
|
384
|
+
# Or with bearer token: client = AiriaClient.with_bearer_token(bearer_token="your_bearer_token")
|
|
323
385
|
|
|
324
386
|
response = client.execute_pipeline(
|
|
325
387
|
pipeline_id="your_pipeline_id",
|
|
@@ -343,6 +405,7 @@ You can retrieve detailed configuration information about a pipeline using the `
|
|
|
343
405
|
from airia import AiriaClient
|
|
344
406
|
|
|
345
407
|
client = AiriaClient(api_key="your_api_key")
|
|
408
|
+
# Or with bearer token: client = AiriaClient.with_bearer_token(bearer_token="your_bearer_token")
|
|
346
409
|
|
|
347
410
|
# Get pipeline configuration
|
|
348
411
|
config = client.get_pipeline_config(pipeline_id="your_pipeline_id")
|
|
@@ -351,10 +414,35 @@ config = client.get_pipeline_config(pipeline_id="your_pipeline_id")
|
|
|
351
414
|
print(f"Pipeline Name: {config.agent.name}")
|
|
352
415
|
```
|
|
353
416
|
|
|
417
|
+
## Authentication Methods
|
|
418
|
+
|
|
419
|
+
Airia supports two authentication methods:
|
|
420
|
+
|
|
421
|
+
### API Keys
|
|
422
|
+
- Can be passed as a parameter or via `AIRIA_API_KEY` environment variable
|
|
423
|
+
- Support gateway functionality (OpenAI and Anthropic gateways)
|
|
424
|
+
- Suitable for long-term, persistent authentication
|
|
425
|
+
|
|
426
|
+
### Bearer Tokens
|
|
427
|
+
- Must be provided explicitly (no environment variable fallback)
|
|
428
|
+
- **Important**: Bearer tokens cannot be used with gateway functionality
|
|
429
|
+
- Suitable for ephemeral, short-lived authentication scenarios
|
|
430
|
+
- Ideal for temporary access or programmatic token generation
|
|
431
|
+
|
|
432
|
+
```python
|
|
433
|
+
# ✅ API key with gateway support
|
|
434
|
+
client = AiriaClient.with_openai_gateway(api_key="your_api_key")
|
|
435
|
+
|
|
436
|
+
# ❌ Bearer token with gateway - NOT SUPPORTED
|
|
437
|
+
# client = AiriaClient.with_openai_gateway(bearer_token="token") # This won't work
|
|
438
|
+
```
|
|
439
|
+
|
|
354
440
|
## Gateway Usage
|
|
355
441
|
|
|
356
442
|
Airia provides gateway capabilities for popular AI services like OpenAI and Anthropic, allowing you to use your Airia API key with these services.
|
|
357
443
|
|
|
444
|
+
> **Note**: Gateway functionality requires API key authentication. Bearer tokens are not supported for gateway usage.
|
|
445
|
+
|
|
358
446
|
### OpenAI Gateway
|
|
359
447
|
|
|
360
448
|
```python
|
|
@@ -482,7 +570,11 @@ console_logger = configure_logging(
|
|
|
482
570
|
The SDK uses custom exceptions to provide clear error messages:
|
|
483
571
|
|
|
484
572
|
```python
|
|
485
|
-
from airia import AiriaAPIError
|
|
573
|
+
from airia import AiriaAPIError, AiriaClient
|
|
574
|
+
|
|
575
|
+
# Works with both API keys and bearer tokens
|
|
576
|
+
client = AiriaClient(api_key="your_api_key")
|
|
577
|
+
# Or: client = AiriaClient.with_bearer_token(bearer_token="your_bearer_token")
|
|
486
578
|
|
|
487
579
|
try:
|
|
488
580
|
response = client.execute_pipeline(
|
|
@@ -2,6 +2,7 @@ LICENSE
|
|
|
2
2
|
README.md
|
|
3
3
|
pyproject.toml
|
|
4
4
|
airia/__init__.py
|
|
5
|
+
airia/constants.py
|
|
5
6
|
airia/exceptions.py
|
|
6
7
|
airia/logs.py
|
|
7
8
|
airia.egg-info/PKG-INFO
|
|
@@ -22,6 +23,7 @@ airia/types/api/get_projects.py
|
|
|
22
23
|
airia/types/api/pipeline_execution.py
|
|
23
24
|
airia/utils/sse_parser.py
|
|
24
25
|
tests/test_anthropic_gateway.py
|
|
26
|
+
tests/test_bearer_token_auth.py
|
|
25
27
|
tests/test_execute_pipeline.py
|
|
26
28
|
tests/test_get_active_pipelines_ids.py
|
|
27
29
|
tests/test_get_pipeline_config.py
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import pytest
|
|
3
|
+
import pytest_asyncio
|
|
4
|
+
from unittest.mock import patch, MagicMock
|
|
5
|
+
|
|
6
|
+
from airia import AiriaAsyncClient, AiriaClient
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TestBearerTokenAuthentication:
|
|
10
|
+
"""Tests for bearer token authentication functionality."""
|
|
11
|
+
|
|
12
|
+
def test_sync_client_with_bearer_token(self):
|
|
13
|
+
"""Test synchronous client initialization with bearer token."""
|
|
14
|
+
bearer_token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.test.token"
|
|
15
|
+
client = AiriaClient(bearer_token=bearer_token)
|
|
16
|
+
|
|
17
|
+
assert client.bearer_token == bearer_token
|
|
18
|
+
assert client.api_key is None
|
|
19
|
+
|
|
20
|
+
def test_sync_client_with_bearer_token_class_method(self):
|
|
21
|
+
"""Test synchronous client initialization using with_bearer_token class method."""
|
|
22
|
+
bearer_token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.test.token"
|
|
23
|
+
client = AiriaClient.with_bearer_token(bearer_token)
|
|
24
|
+
|
|
25
|
+
assert client.bearer_token == bearer_token
|
|
26
|
+
assert client.api_key is None
|
|
27
|
+
|
|
28
|
+
def test_explicit_api_key_and_bearer_token_conflict(self):
|
|
29
|
+
"""Test that providing both api_key and bearer_token explicitly raises an error."""
|
|
30
|
+
with pytest.raises(ValueError, match="Cannot provide both api_key and bearer_token"):
|
|
31
|
+
AiriaClient(api_key="test_key", bearer_token="test_token")
|
|
32
|
+
|
|
33
|
+
def test_no_auth_provided_raises_error(self):
|
|
34
|
+
"""Test that providing no authentication raises an error."""
|
|
35
|
+
with patch.dict(os.environ, {}, clear=True):
|
|
36
|
+
with pytest.raises(ValueError, match="Authentication required"):
|
|
37
|
+
AiriaClient()
|
|
38
|
+
|
|
39
|
+
def test_bearer_token_auth_header_generation(self):
|
|
40
|
+
"""Test that bearer token generates correct Authorization header."""
|
|
41
|
+
bearer_token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.test.token"
|
|
42
|
+
client = AiriaClient(bearer_token=bearer_token)
|
|
43
|
+
|
|
44
|
+
request_data = client._prepare_request("https://test.com")
|
|
45
|
+
|
|
46
|
+
assert "Authorization" in request_data.headers
|
|
47
|
+
assert request_data.headers["Authorization"] == f"Bearer {bearer_token}"
|
|
48
|
+
assert "X-API-KEY" not in request_data.headers
|
|
49
|
+
|
|
50
|
+
def test_api_key_auth_header_generation(self):
|
|
51
|
+
"""Test that API key generates correct X-API-KEY header."""
|
|
52
|
+
api_key = "test_api_key"
|
|
53
|
+
client = AiriaClient(api_key=api_key)
|
|
54
|
+
|
|
55
|
+
request_data = client._prepare_request("https://test.com")
|
|
56
|
+
|
|
57
|
+
assert "X-API-KEY" in request_data.headers
|
|
58
|
+
assert request_data.headers["X-API-KEY"] == api_key
|
|
59
|
+
assert "Authorization" not in request_data.headers
|
|
60
|
+
|
|
61
|
+
def test_bearer_token_logging_redaction(self):
|
|
62
|
+
"""Test that bearer token is redacted in logs."""
|
|
63
|
+
bearer_token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.test.token"
|
|
64
|
+
client = AiriaClient(bearer_token=bearer_token, log_requests=True)
|
|
65
|
+
|
|
66
|
+
# Mock the logger to capture log output
|
|
67
|
+
with patch.object(client, 'logger') as mock_logger:
|
|
68
|
+
request_data = client._prepare_request("https://test.com")
|
|
69
|
+
|
|
70
|
+
# Check that the log call was made and bearer token was redacted
|
|
71
|
+
mock_logger.info.assert_called_once()
|
|
72
|
+
log_call_args = mock_logger.info.call_args[0][0]
|
|
73
|
+
assert "[REDACTED]" in log_call_args
|
|
74
|
+
assert bearer_token not in log_call_args
|
|
75
|
+
|
|
76
|
+
def test_bearer_token_error_sanitization(self):
|
|
77
|
+
"""Test that bearer token is sanitized in error messages."""
|
|
78
|
+
bearer_token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.test.token"
|
|
79
|
+
client = AiriaClient(bearer_token=bearer_token)
|
|
80
|
+
|
|
81
|
+
# Test the sanitization logic directly
|
|
82
|
+
error_message = f"Invalid token: {bearer_token}"
|
|
83
|
+
sanitized_message = error_message
|
|
84
|
+
if client.bearer_token and client.bearer_token in sanitized_message:
|
|
85
|
+
sanitized_message = sanitized_message.replace(client.bearer_token, "[REDACTED]")
|
|
86
|
+
|
|
87
|
+
assert bearer_token not in sanitized_message
|
|
88
|
+
assert "[REDACTED]" in sanitized_message
|
|
89
|
+
|
|
90
|
+
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|