airia 0.1.2__tar.gz → 0.1.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: airia
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Summary: Python SDK for Airia API
5
5
  Author-email: Airia LLC <support@airia.com>
6
6
  License: MIT
@@ -30,6 +30,10 @@ Dynamic: license-file
30
30
 
31
31
  # Airia Python API Library
32
32
 
33
+ [![PyPI version](https://badge.fury.io/py/airia.svg)](https://badge.fury.io/py/airia)
34
+ [![Python versions](https://img.shields.io/pypi/pyversions/airia.svg)](https://pypi.org/project/airia/)
35
+ [![License](https://img.shields.io/pypi/l/airia.svg)](https://pypi.org/project/airia/)
36
+
33
37
  Airia Python API Library that provides a clean and intuitive interface to interact with the Airia AI platform API. The library offers both synchronous and asynchronous clients for maximum flexibility in your applications.
34
38
 
35
39
  ## Features
@@ -43,13 +47,6 @@ Airia Python API Library that provides a clean and intuitive interface to intera
43
47
 
44
48
  ## Installation
45
49
 
46
- Clone the repository:
47
-
48
- ```bash
49
- git clone https://github.com/AiriaLLC/airia-python.git
50
- cd airia-python
51
- ```
52
-
53
50
  You can install the package using pip or uv:
54
51
 
55
52
  <table>
@@ -61,14 +58,14 @@ You can install the package using pip or uv:
61
58
  <td>
62
59
 
63
60
  ```bash
64
- pip install -e .
61
+ pip install airia
65
62
  ```
66
63
 
67
64
  </td>
68
65
  <td>
69
66
 
70
67
  ```bash
71
- uv sync --frozen
68
+ uv add airia
72
69
  ```
73
70
 
74
71
  </td>
@@ -89,21 +86,21 @@ The package supports optional dependencies for gateway functionality:
89
86
  <td>
90
87
 
91
88
  ```bash
92
- pip install -e .[openai]
89
+ pip install "airia[openai]"
93
90
  ```
94
91
 
95
92
  </td>
96
93
  <td>
97
94
 
98
95
  ```bash
99
- pip install -e .[anthropic]
96
+ pip install "airia[anthropic]"
100
97
  ```
101
98
 
102
99
  </td>
103
100
  <td>
104
101
 
105
102
  ```bash
106
- pip install -e .[all]
103
+ pip install "airia[all]"
107
104
  ```
108
105
 
109
106
  </td>
@@ -112,6 +109,15 @@ pip install -e .[all]
112
109
 
113
110
  ### Install with development dependencies
114
111
 
112
+ Clone the repository:
113
+
114
+ ```bash
115
+ git clone https://github.com/AiriaLLC/airia-python.git
116
+ cd airia-python
117
+ ```
118
+
119
+ Then, run one of the following commands:
120
+
115
121
  <table>
116
122
  <tr>
117
123
  <th>pip</th>
@@ -140,7 +146,7 @@ uv sync --frozen --group dev
140
146
 
141
147
  ## Building from Source
142
148
 
143
- To build the package from source:
149
+ First make sure you have already cloned the repository, then run one of the following commands:
144
150
 
145
151
  <table>
146
152
  <tr>
@@ -1,5 +1,9 @@
1
1
  # Airia Python API Library
2
2
 
3
+ [![PyPI version](https://badge.fury.io/py/airia.svg)](https://badge.fury.io/py/airia)
4
+ [![Python versions](https://img.shields.io/pypi/pyversions/airia.svg)](https://pypi.org/project/airia/)
5
+ [![License](https://img.shields.io/pypi/l/airia.svg)](https://pypi.org/project/airia/)
6
+
3
7
  Airia Python API Library that provides a clean and intuitive interface to interact with the Airia AI platform API. The library offers both synchronous and asynchronous clients for maximum flexibility in your applications.
4
8
 
5
9
  ## Features
@@ -13,13 +17,6 @@ Airia Python API Library that provides a clean and intuitive interface to intera
13
17
 
14
18
  ## Installation
15
19
 
16
- Clone the repository:
17
-
18
- ```bash
19
- git clone https://github.com/AiriaLLC/airia-python.git
20
- cd airia-python
21
- ```
22
-
23
20
  You can install the package using pip or uv:
24
21
 
25
22
  <table>
@@ -31,14 +28,14 @@ You can install the package using pip or uv:
31
28
  <td>
32
29
 
33
30
  ```bash
34
- pip install -e .
31
+ pip install airia
35
32
  ```
36
33
 
37
34
  </td>
38
35
  <td>
39
36
 
40
37
  ```bash
41
- uv sync --frozen
38
+ uv add airia
42
39
  ```
43
40
 
44
41
  </td>
@@ -59,21 +56,21 @@ The package supports optional dependencies for gateway functionality:
59
56
  <td>
60
57
 
61
58
  ```bash
62
- pip install -e .[openai]
59
+ pip install "airia[openai]"
63
60
  ```
64
61
 
65
62
  </td>
66
63
  <td>
67
64
 
68
65
  ```bash
69
- pip install -e .[anthropic]
66
+ pip install "airia[anthropic]"
70
67
  ```
71
68
 
72
69
  </td>
73
70
  <td>
74
71
 
75
72
  ```bash
76
- pip install -e .[all]
73
+ pip install "airia[all]"
77
74
  ```
78
75
 
79
76
  </td>
@@ -82,6 +79,15 @@ pip install -e .[all]
82
79
 
83
80
  ### Install with development dependencies
84
81
 
82
+ Clone the repository:
83
+
84
+ ```bash
85
+ git clone https://github.com/AiriaLLC/airia-python.git
86
+ cd airia-python
87
+ ```
88
+
89
+ Then, run one of the following commands:
90
+
85
91
  <table>
86
92
  <tr>
87
93
  <th>pip</th>
@@ -110,7 +116,7 @@ uv sync --frozen --group dev
110
116
 
111
117
  ## Building from Source
112
118
 
113
- To build the package from source:
119
+ First make sure you have already cloned the repository, then run one of the following commands:
114
120
 
115
121
  <table>
116
122
  <tr>
@@ -0,0 +1,4 @@
1
+ from .async_client import AiriaAsyncClient
2
+ from .sync_client import AiriaClient
3
+
4
+ __all__ = ["AiriaClient", "AiriaAsyncClient"]
@@ -0,0 +1,448 @@
1
+ from typing import Any, AsyncIterator, Dict, List, Literal, Optional, overload
2
+ from urllib.parse import urljoin
3
+
4
+ import aiohttp
5
+ import loguru
6
+
7
+ from ..exceptions import AiriaAPIError
8
+ from ..types import (
9
+ ApiVersion,
10
+ PipelineExecutionDebugResponse,
11
+ PipelineExecutionResponse,
12
+ PipelineExecutionV1StreamedResponse,
13
+ PipelineExecutionV2AsyncStreamedResponse,
14
+ RequestData,
15
+ )
16
+ from .base_client import AiriaBaseClient
17
+
18
+
19
+ class AiriaAsyncClient(AiriaBaseClient):
20
+ """Asynchronous client for interacting with the Airia API."""
21
+
22
+ def __init__(
23
+ self,
24
+ api_key: Optional[str] = None,
25
+ timeout: float = 30.0,
26
+ log_requests: bool = False,
27
+ custom_logger: Optional["loguru.Logger"] = None,
28
+ ):
29
+ """
30
+ Initialize the asynchronous Airia API client.
31
+
32
+ Args:
33
+ api_key: API key for authentication. If not provided, will attempt to use AIRIA_API_KEY environment variable.
34
+ timeout: Request timeout in seconds.
35
+ log_requests: Whether to log API requests and responses. Default is False.
36
+ custom_logger: Optional custom logger object to use for logging. If not provided, will use a default logger when `log_requests` is True.
37
+ """
38
+ super().__init__(api_key, timeout, log_requests, custom_logger)
39
+
40
+ # Session will be initialized in __aenter__
41
+ self.session = None
42
+ self.headers = {"Content-Type": "application/json"}
43
+
44
+ @classmethod
45
+ def with_openai_gateway(
46
+ cls,
47
+ api_key: Optional[str] = None,
48
+ timeout: float = 30.0,
49
+ log_requests: bool = False,
50
+ custom_logger: Optional["loguru.Logger"] = None,
51
+ **kwargs,
52
+ ):
53
+ """
54
+ Initialize the asynchronous Airia API client with AsyncOpenAI gateway capabilities.
55
+
56
+ Args:
57
+ api_key: API key for authentication. If not provided, will attempt to use AIRIA_API_KEY environment variable.
58
+ timeout: Request timeout in seconds.
59
+ log_requests: Whether to log API requests and responses. Default is False.
60
+ custom_logger: Optional custom logger object to use for logging. If not provided, will use a default logger when `log_requests` is True.
61
+ **kwargs: Additional keyword arguments to pass to the AsyncOpenAI client initialization.
62
+ """
63
+ from openai import AsyncOpenAI
64
+
65
+ api_key = cls._get_api_key(api_key)
66
+ cls.openai = AsyncOpenAI(
67
+ api_key=api_key,
68
+ base_url="https://gateway.airia.ai/openai/v1",
69
+ **kwargs,
70
+ )
71
+
72
+ return cls(api_key, timeout, log_requests, custom_logger)
73
+
74
+ @classmethod
75
+ def with_anthropic_gateway(
76
+ cls,
77
+ api_key: Optional[str] = None,
78
+ timeout: float = 30.0,
79
+ log_requests: bool = False,
80
+ custom_logger: Optional["loguru.Logger"] = None,
81
+ **kwargs,
82
+ ):
83
+ """
84
+ Initialize the asynchronous Airia API client with AsyncAnthropic gateway capabilities.
85
+
86
+ Args:
87
+ api_key: API key for authentication. If not provided, will attempt to use AIRIA_API_KEY environment variable.
88
+ timeout: Request timeout in seconds.
89
+ log_requests: Whether to log API requests and responses. Default is False.
90
+ custom_logger: Optional custom logger object to use for logging. If not provided, will use a default logger when `log_requests` is True.
91
+ **kwargs: Additional keyword arguments to pass to the AsyncAnthropic client initialization.
92
+ """
93
+ from anthropic import AsyncAnthropic
94
+
95
+ api_key = cls._get_api_key(api_key)
96
+ cls.anthropic = AsyncAnthropic(
97
+ api_key=api_key,
98
+ base_url="https://gateway.airia.ai/anthropic",
99
+ **kwargs,
100
+ )
101
+
102
+ return cls(api_key, timeout, log_requests, custom_logger)
103
+
104
+ async def __aenter__(self):
105
+ """Async context manager entry point."""
106
+ self.session = aiohttp.ClientSession(headers=self.headers)
107
+ return self
108
+
109
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
110
+ """Async context manager exit point."""
111
+ if self.session:
112
+ await self.session.close()
113
+ self.session = None
114
+
115
+ def _check_session(self):
116
+ """Check if the client session is initialized."""
117
+ if not self.session:
118
+ raise RuntimeError(
119
+ "Client session not initialized. Use async with AiriaAsyncClient() as client: ..."
120
+ )
121
+
122
+ def _handle_exception(
123
+ self, e: aiohttp.ClientResponseError, url: str, correlation_id: str
124
+ ):
125
+ # Log the error response if enabled
126
+ if self.log_requests:
127
+ self.logger.error(
128
+ f"API Error: {e.status} {e.message}\n"
129
+ f"URL: {url}\n"
130
+ f"Correlation ID: {correlation_id}"
131
+ )
132
+
133
+ # Extract error details from response
134
+ error_message = e.message
135
+
136
+ # Make sure API key is not included in error messages
137
+ sanitized_message = (
138
+ error_message.replace(self.api_key, "[REDACTED]")
139
+ if self.api_key in error_message
140
+ else error_message
141
+ )
142
+
143
+ # Raise custom exception with status code and sanitized message
144
+ raise AiriaAPIError(status_code=e.status, message=sanitized_message) from e
145
+
146
+ async def _make_request(
147
+ self, method: str, request_data: RequestData
148
+ ) -> Dict[str, Any]:
149
+ """
150
+ Makes an asynchronous HTTP request to the Airia API.
151
+
152
+ Args:
153
+ method (str): The HTTP method (e.g., 'GET', 'POST')
154
+ request_data: A dictionary containing the following request information:
155
+ - url: The endpoint URL for the request
156
+ - headers: HTTP headers to include in the request
157
+ - payload: The JSON payload/body for the request
158
+ - correlation_id: Unique identifier for request tracing
159
+
160
+ Returns:
161
+ resp ([Dict[str, Any]): The JSON response from the API as a dictionary.
162
+
163
+ Raises:
164
+ AiriaAPIError: If the API returns an error response, with details about the error
165
+ aiohttp.ClientResponseError: For HTTP-related errors
166
+
167
+ Note:
168
+ This is an internal method used by other client methods to make API requests.
169
+ It handles logging, error handling, and API key redaction in error messages.
170
+ """
171
+ try:
172
+ # Make the request
173
+ async with self.session.request(
174
+ method=method,
175
+ url=request_data.url,
176
+ json=request_data.payload,
177
+ headers=request_data.headers,
178
+ timeout=self.timeout,
179
+ ) as response:
180
+ # Log the response if enabled
181
+ if self.log_requests:
182
+ self.logger.info(
183
+ f"API Response: {response.status} {response.reason}\n"
184
+ f"URL: {request_data.url}\n"
185
+ f"Correlation ID: {request_data.correlation_id}"
186
+ )
187
+
188
+ # Check for HTTP errors
189
+ response.raise_for_status()
190
+
191
+ # Return the response as a dictionary
192
+ return await response.json()
193
+
194
+ except aiohttp.ClientResponseError as e:
195
+ self._handle_exception(e, request_data.url, request_data.correlation_id)
196
+
197
+ async def _make_request_stream(
198
+ self, method: str, request_data: RequestData
199
+ ) -> AsyncIterator[str]:
200
+ """
201
+ Makes an asynchronous HTTP request to the Airia API.
202
+
203
+ Args:
204
+ method (str): The HTTP method (e.g., 'GET', 'POST')
205
+ request_data: A dictionary containing the following request information:
206
+ - url: The endpoint URL for the request
207
+ - headers: HTTP headers to include in the request
208
+ - payload: The JSON payload/body for the request
209
+ - correlation_id: Unique identifier for request tracing
210
+
211
+ Yields:
212
+ resp AsyncIterator[str]]: yields chunks of the response as they are received.
213
+
214
+ Raises:
215
+ AiriaAPIError: If the API returns an error response, with details about the error
216
+ aiohttp.ClientResponseError: For HTTP-related errors
217
+
218
+ Note:
219
+ This is an internal method used by other client methods to make API requests.
220
+ It handles logging, error handling, and API key redaction in error messages.
221
+ """
222
+ try:
223
+ # Make the request
224
+ async with self.session.request(
225
+ method=method,
226
+ url=request_data.url,
227
+ json=request_data.payload,
228
+ headers=request_data.headers,
229
+ timeout=self.timeout,
230
+ chunked=True,
231
+ ) as response:
232
+ # Log the response if enabled
233
+ if self.log_requests:
234
+ self.logger.info(
235
+ f"API Response: {response.status} {response.reason}\n"
236
+ f"URL: {request_data.url}\n"
237
+ f"Correlation ID: {request_data.correlation_id}"
238
+ )
239
+
240
+ # Check for HTTP errors
241
+ response.raise_for_status()
242
+
243
+ # Yields the response content as a stream if streaming
244
+ async for chunk in response.content.iter_any():
245
+ yield chunk.decode("utf-8")
246
+
247
+ except aiohttp.ClientResponseError as e:
248
+ self._handle_exception(e, request_data.url, request_data.correlation_id)
249
+
250
+ @overload
251
+ async def execute_pipeline(
252
+ self,
253
+ pipeline_id: str,
254
+ user_input: str,
255
+ debug: Literal[False] = False,
256
+ user_id: Optional[str] = None,
257
+ conversation_id: Optional[str] = None,
258
+ async_output: Literal[False] = False,
259
+ include_tools_response: bool = False,
260
+ images: Optional[List[str]] = None,
261
+ files: Optional[List[str]] = None,
262
+ data_source_folders: Optional[Dict[str, Any]] = None,
263
+ data_source_files: Optional[Dict[str, Any]] = None,
264
+ in_memory_messages: Optional[List[Dict[str, str]]] = None,
265
+ current_date_time: Optional[str] = None,
266
+ save_history: bool = True,
267
+ additional_info: Optional[List[Any]] = None,
268
+ prompt_variables: Optional[Dict[str, Any]] = None,
269
+ correlation_id: Optional[str] = None,
270
+ api_version: str = ApiVersion.V2.value,
271
+ ) -> PipelineExecutionResponse: ...
272
+
273
+ @overload
274
+ async def execute_pipeline(
275
+ self,
276
+ pipeline_id: str,
277
+ user_input: str,
278
+ debug: Literal[True] = True,
279
+ user_id: Optional[str] = None,
280
+ conversation_id: Optional[str] = None,
281
+ async_output: Literal[False] = False,
282
+ include_tools_response: bool = False,
283
+ images: Optional[List[str]] = None,
284
+ files: Optional[List[str]] = None,
285
+ data_source_folders: Optional[Dict[str, Any]] = None,
286
+ data_source_files: Optional[Dict[str, Any]] = None,
287
+ in_memory_messages: Optional[List[Dict[str, str]]] = None,
288
+ current_date_time: Optional[str] = None,
289
+ save_history: bool = True,
290
+ additional_info: Optional[List[Any]] = None,
291
+ prompt_variables: Optional[Dict[str, Any]] = None,
292
+ correlation_id: Optional[str] = None,
293
+ api_version: str = ApiVersion.V2.value,
294
+ ) -> PipelineExecutionDebugResponse: ...
295
+
296
+ @overload
297
+ async def execute_pipeline(
298
+ self,
299
+ pipeline_id: str,
300
+ user_input: str,
301
+ debug: bool = False,
302
+ user_id: Optional[str] = None,
303
+ conversation_id: Optional[str] = None,
304
+ async_output: Literal[True] = True,
305
+ include_tools_response: bool = False,
306
+ images: Optional[List[str]] = None,
307
+ files: Optional[List[str]] = None,
308
+ data_source_folders: Optional[Dict[str, Any]] = None,
309
+ data_source_files: Optional[Dict[str, Any]] = None,
310
+ in_memory_messages: Optional[List[Dict[str, str]]] = None,
311
+ current_date_time: Optional[str] = None,
312
+ save_history: bool = True,
313
+ additional_info: Optional[List[Any]] = None,
314
+ prompt_variables: Optional[Dict[str, Any]] = None,
315
+ correlation_id: Optional[str] = None,
316
+ api_version: Literal["v2"] = ApiVersion.V2.value,
317
+ ) -> PipelineExecutionV2AsyncStreamedResponse: ...
318
+
319
+ @overload
320
+ async def execute_pipeline(
321
+ self,
322
+ pipeline_id: str,
323
+ user_input: str,
324
+ debug: bool = False,
325
+ user_id: Optional[str] = None,
326
+ conversation_id: Optional[str] = None,
327
+ async_output: Literal[True] = True,
328
+ include_tools_response: bool = False,
329
+ images: Optional[List[str]] = None,
330
+ files: Optional[List[str]] = None,
331
+ data_source_folders: Optional[Dict[str, Any]] = None,
332
+ data_source_files: Optional[Dict[str, Any]] = None,
333
+ in_memory_messages: Optional[List[Dict[str, str]]] = None,
334
+ current_date_time: Optional[str] = None,
335
+ save_history: bool = True,
336
+ additional_info: Optional[List[Any]] = None,
337
+ prompt_variables: Optional[Dict[str, Any]] = None,
338
+ correlation_id: Optional[str] = None,
339
+ api_version: Literal["v1"] = ApiVersion.V1.value,
340
+ ) -> PipelineExecutionV1StreamedResponse: ...
341
+
342
+ async def execute_pipeline(
343
+ self,
344
+ pipeline_id: str,
345
+ user_input: str,
346
+ debug: bool = False,
347
+ user_id: Optional[str] = None,
348
+ conversation_id: Optional[str] = None,
349
+ async_output: bool = False,
350
+ include_tools_response: bool = False,
351
+ images: Optional[List[str]] = None,
352
+ files: Optional[List[str]] = None,
353
+ data_source_folders: Optional[Dict[str, Any]] = None,
354
+ data_source_files: Optional[Dict[str, Any]] = None,
355
+ in_memory_messages: Optional[List[Dict[str, str]]] = None,
356
+ current_date_time: Optional[str] = None,
357
+ save_history: bool = True,
358
+ additional_info: Optional[List[Any]] = None,
359
+ prompt_variables: Optional[Dict[str, Any]] = None,
360
+ correlation_id: Optional[str] = None,
361
+ api_version: str = ApiVersion.V2.value,
362
+ ) -> Dict[str, Any]:
363
+ """
364
+ Execute a pipeline with the provided input asynchronously.
365
+
366
+ Args:
367
+ pipeline_id: The ID of the pipeline to execute.
368
+ user_input: input text to process.
369
+ debug: Whether debug mode execution is enabled. Default is False.
370
+ user_id: Optional ID of the user making the request (guid).
371
+ conversation_id: Optional conversation ID (guid).
372
+ async_output: Whether to stream the response. Default is False.
373
+ include_tools_response: Whether to return the initial LLM tool result. Default is False.
374
+ images: Optional list of images formatted as base64 strings.
375
+ files: Optional list of files formatted as base64 strings.
376
+ data_source_folders: Optional data source folders information.
377
+ data_source_files: Optional data source files information.
378
+ in_memory_messages: Optional list of in-memory messages, each with a role and message.
379
+ current_date_time: Optional current date and time in ISO format.
380
+ save_history: Whether to save the userInput and output to conversation history. Default is True.
381
+ additional_info: Optional additional information.
382
+ prompt_variables: Optional variables to be used in the prompt.
383
+ correlation_id: Optional correlation ID for request tracing. If not provided,
384
+ one will be generated automatically.
385
+ api_version: API version to use. Default is `v2`
386
+
387
+ Returns:
388
+ The API response as a dictionary.
389
+
390
+ Raises:
391
+ AiriaAPIError: If the API request fails with details about the error.
392
+ aiohttp.ClientError: For other request-related errors.
393
+
394
+ Example:
395
+ >>> async with AiriaAsyncClient(api_key="your_api_key") as client:
396
+ ... response = await client.execute_pipeline(
397
+ ... pipeline_id="pipeline_123",
398
+ ... user_input="Tell me about quantum computing"
399
+ ... )
400
+ >>> print(response.result)
401
+ """
402
+ self._check_session()
403
+
404
+ request_data = self._pre_execute_pipeline(
405
+ pipeline_id=pipeline_id,
406
+ user_input=user_input,
407
+ debug=debug,
408
+ user_id=user_id,
409
+ conversation_id=conversation_id,
410
+ async_output=async_output,
411
+ include_tools_response=include_tools_response,
412
+ images=images,
413
+ files=files,
414
+ data_source_folders=data_source_folders,
415
+ data_source_files=data_source_files,
416
+ in_memory_messages=in_memory_messages,
417
+ current_date_time=current_date_time,
418
+ save_history=save_history,
419
+ additional_info=additional_info,
420
+ prompt_variables=prompt_variables,
421
+ correlation_id=correlation_id,
422
+ api_version=api_version,
423
+ )
424
+ stream = async_output and api_version == ApiVersion.V2.value
425
+ if stream:
426
+ resp = self._make_request_stream(method="POST", request_data=request_data)
427
+ else:
428
+ resp = await self._make_request("POST", request_data)
429
+
430
+ if not async_output:
431
+ if not debug:
432
+ return PipelineExecutionResponse(**resp)
433
+ return PipelineExecutionDebugResponse(**resp)
434
+
435
+ if api_version == ApiVersion.V1.value:
436
+ url = urljoin(
437
+ self.base_url, f"{api_version}/StreamSocketConfig/GenerateUrl"
438
+ )
439
+ request_data = self._prepare_request(
440
+ url,
441
+ {"socketIdentifier": resp},
442
+ request_data.headers["X-Correlation-ID"],
443
+ )
444
+ resp = await self._make_request("POST", request_data)
445
+
446
+ return PipelineExecutionV1StreamedResponse(**resp)
447
+
448
+ return PipelineExecutionV2AsyncStreamedResponse(stream=resp)