airia 0.1.8__py3-none-any.whl → 0.1.10__py3-none-any.whl

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.
@@ -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,
@@ -14,6 +15,7 @@ from ..types import (
14
15
  PipelineExecutionResponse,
15
16
  PipelineExecutionV1StreamedResponse,
16
17
  PipelineExecutionV2AsyncStreamedResponse,
18
+ ProjectItem,
17
19
  RequestData,
18
20
  )
19
21
  from ..utils.sse_parser import async_parse_sse_stream_chunked
@@ -25,9 +27,10 @@ class AiriaAsyncClient(AiriaBaseClient):
25
27
 
26
28
  def __init__(
27
29
  self,
28
- base_url: str = "https://api.airia.ai/",
30
+ base_url: str = DEFAULT_BASE_URL,
29
31
  api_key: Optional[str] = None,
30
- timeout: float = 30.0,
32
+ bearer_token: Optional[str] = None,
33
+ timeout: float = DEFAULT_TIMEOUT,
31
34
  log_requests: bool = False,
32
35
  custom_logger: Optional["loguru.Logger"] = None,
33
36
  ):
@@ -37,6 +40,7 @@ class AiriaAsyncClient(AiriaBaseClient):
37
40
  Args:
38
41
  base_url: Base URL of the Airia API.
39
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).
40
44
  timeout: Request timeout in seconds.
41
45
  log_requests: Whether to log API requests and responses. Default is False.
42
46
  custom_logger: Optional custom logger object to use for logging. If not provided, will use a default logger when `log_requests` is True.
@@ -44,6 +48,7 @@ class AiriaAsyncClient(AiriaBaseClient):
44
48
  super().__init__(
45
49
  base_url=base_url,
46
50
  api_key=api_key,
51
+ bearer_token=bearer_token,
47
52
  timeout=timeout,
48
53
  log_requests=log_requests,
49
54
  custom_logger=custom_logger,
@@ -79,10 +84,10 @@ class AiriaAsyncClient(AiriaBaseClient):
79
84
  @classmethod
80
85
  def with_openai_gateway(
81
86
  cls,
82
- base_url: str = "https://api.airia.ai/",
83
- gateway_url: str = "https://gateway.airia.ai/openai/v1",
87
+ base_url: str = DEFAULT_BASE_URL,
88
+ gateway_url: str = DEFAULT_OPENAI_GATEWAY_URL,
84
89
  api_key: Optional[str] = None,
85
- timeout: float = 30.0,
90
+ timeout: float = DEFAULT_TIMEOUT,
86
91
  log_requests: bool = False,
87
92
  custom_logger: Optional["loguru.Logger"] = None,
88
93
  **kwargs,
@@ -101,22 +106,22 @@ class AiriaAsyncClient(AiriaBaseClient):
101
106
  """
102
107
  from openai import AsyncOpenAI
103
108
 
104
- api_key = cls._get_api_key(api_key)
109
+ client = cls(base_url=base_url, api_key=api_key, timeout=timeout, log_requests=log_requests, custom_logger=custom_logger)
105
110
  cls.openai = AsyncOpenAI(
106
- api_key=api_key,
111
+ api_key=client.api_key,
107
112
  base_url=gateway_url,
108
113
  **kwargs,
109
114
  )
110
115
 
111
- return cls(base_url, api_key, timeout, log_requests, custom_logger)
116
+ return client
112
117
 
113
118
  @classmethod
114
119
  def with_anthropic_gateway(
115
120
  cls,
116
- base_url: str = "https://api.airia.ai/",
117
- gateway_url: str = "https://gateway.airia.ai/anthropic",
121
+ base_url: str = DEFAULT_BASE_URL,
122
+ gateway_url: str = DEFAULT_ANTHROPIC_GATEWAY_URL,
118
123
  api_key: Optional[str] = None,
119
- timeout: float = 30.0,
124
+ timeout: float = DEFAULT_TIMEOUT,
120
125
  log_requests: bool = False,
121
126
  custom_logger: Optional["loguru.Logger"] = None,
122
127
  **kwargs,
@@ -135,14 +140,41 @@ class AiriaAsyncClient(AiriaBaseClient):
135
140
  """
136
141
  from anthropic import AsyncAnthropic
137
142
 
138
- api_key = cls._get_api_key(api_key)
143
+ client = cls(base_url=base_url, api_key=api_key, timeout=timeout, log_requests=log_requests, custom_logger=custom_logger)
139
144
  cls.anthropic = AsyncAnthropic(
140
- api_key=api_key,
145
+ api_key=client.api_key,
141
146
  base_url=gateway_url,
142
147
  **kwargs,
143
148
  )
144
149
 
145
- return cls(base_url, api_key, timeout, log_requests, custom_logger)
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
+ )
146
178
 
147
179
  def _handle_exception(
148
180
  self, e: aiohttp.ClientResponseError, url: str, correlation_id: str
@@ -158,12 +190,12 @@ class AiriaAsyncClient(AiriaBaseClient):
158
190
  # Extract error details from response
159
191
  error_message = e.message
160
192
 
161
- # Make sure API key is not included in error messages
162
- sanitized_message = (
163
- error_message.replace(self.api_key, "[REDACTED]")
164
- if self.api_key in error_message
165
- else error_message
166
- )
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]")
167
199
 
168
200
  # Raise custom exception with status code and sanitized message
169
201
  raise AiriaAPIError(status_code=e.status, message=sanitized_message) from e
@@ -199,6 +231,7 @@ class AiriaAsyncClient(AiriaBaseClient):
199
231
  method=method,
200
232
  url=request_data.url,
201
233
  json=request_data.payload,
234
+ params=request_data.params,
202
235
  headers=request_data.headers,
203
236
  timeout=self.timeout,
204
237
  ) as response:
@@ -250,6 +283,7 @@ class AiriaAsyncClient(AiriaBaseClient):
250
283
  method=method,
251
284
  url=request_data.url,
252
285
  json=request_data.payload,
286
+ params=request_data.params,
253
287
  headers=request_data.headers,
254
288
  timeout=self.timeout,
255
289
  chunked=True,
@@ -419,11 +453,11 @@ class AiriaAsyncClient(AiriaBaseClient):
419
453
  aiohttp.ClientError: For other request-related errors.
420
454
 
421
455
  Example:
422
- >>> async with AiriaAsyncClient(api_key="your_api_key") as client:
423
- ... response = await client.execute_pipeline(
424
- ... pipeline_id="pipeline_123",
425
- ... user_input="Tell me about quantum computing"
426
- ... )
456
+ >>> client = AiriaAsyncClient(api_key="your_api_key")
457
+ ... response = await client.execute_pipeline(
458
+ ... pipeline_id="pipeline_123",
459
+ ... user_input="Tell me about quantum computing"
460
+ ... )
427
461
  >>> print(response.result)
428
462
  """
429
463
  request_data = self._pre_execute_pipeline(
@@ -462,9 +496,9 @@ class AiriaAsyncClient(AiriaBaseClient):
462
496
  self.base_url, f"{api_version}/StreamSocketConfig/GenerateUrl"
463
497
  )
464
498
  request_data = self._prepare_request(
465
- url,
466
- {"socketIdentifier": resp},
467
- request_data.headers["X-Correlation-ID"],
499
+ url=url,
500
+ payload={"socketIdentifier": resp},
501
+ correlation_id=request_data.headers["X-Correlation-ID"],
468
502
  )
469
503
  resp = await self._make_request("POST", request_data)
470
504
 
@@ -472,43 +506,126 @@ class AiriaAsyncClient(AiriaBaseClient):
472
506
 
473
507
  return PipelineExecutionV2AsyncStreamedResponse(stream=resp)
474
508
 
509
+ async def get_projects(
510
+ self,
511
+ correlation_id: Optional[str] = None,
512
+ api_version: str = ApiVersion.V1.value,
513
+ ) -> List[ProjectItem]:
514
+ """
515
+ Retrieve a list of all projects accessible to the authenticated user.
516
+
517
+ This method fetches comprehensive information about all projects that the
518
+ current user has access to, including project metadata, creation details,
519
+ and status information.
520
+
521
+ Args:
522
+ correlation_id (str, optional): A unique identifier for request tracing
523
+ and logging. If not provided, one will be automatically generated.
524
+ api_version (str, optional): The API version to use for the request.
525
+ Defaults to "v1". Valid versions are defined in ApiVersion enum.
526
+
527
+ Returns:
528
+ List[ProjectItem]: A list of ProjectItem objects containing project
529
+ information. Returns an empty list if no projects are accessible
530
+ or found.
531
+
532
+ Raises:
533
+ ValueError: If the provided api_version is not valid.
534
+ AiriaAPIError: If the API request fails, including cases where:
535
+ - Authentication fails (401)
536
+ - Access is forbidden (403)
537
+ - Server errors (5xx)
538
+
539
+ Example:
540
+ ```python
541
+ from airia import AiriaAsyncClient
542
+
543
+ client = AiriaAsyncClient(api_key="your_api_key")
544
+
545
+ # Get all accessible projects
546
+ projects = await client.get_projects()
547
+
548
+ for project in projects:
549
+ print(f"Project: {project.name}")
550
+ print(f"ID: {project.id}")
551
+ print(f"Description: {project.description}")
552
+ print(f"Created: {project.created_at}")
553
+ print("---")
554
+ ```
555
+
556
+ Note:
557
+ The returned projects are filtered based on the authenticated user's
558
+ permissions. Users will only see projects they have been granted
559
+ access to.
560
+ """
561
+ request_data = self._pre_get_projects(
562
+ correlation_id=correlation_id, api_version=api_version
563
+ )
564
+ resp = await self._make_request("GET", request_data)
565
+
566
+ if "items" not in resp or len(resp["items"]) == 0:
567
+ return []
568
+
569
+ return [ProjectItem(**item) for item in resp["items"]]
570
+
475
571
  async def get_active_pipelines_ids(
476
572
  self,
573
+ project_id: Optional[str] = None,
477
574
  correlation_id: Optional[str] = None,
478
575
  api_version: str = ApiVersion.V1.value,
479
576
  ) -> List[str]:
480
577
  """
481
578
  Retrieve a list of active pipeline IDs.
482
-
483
- This method fetches all currently active pipeline IDs from the Airia API.
484
- These IDs can be used with other methods like execute_pipeline() or
485
- get_pipeline_config().
486
-
579
+
580
+ This method fetches the IDs of all active pipelines, optionally filtered by project.
581
+ Active pipelines are those that are currently deployed and available for execution.
582
+
487
583
  Args:
488
- api_version (str, optional): API version to use for the request.
489
- Must be one of the supported versions. Defaults to "v1".
490
- correlation_id (str, optional): Unique identifier for request tracing
491
- and logging. If not provided, a new UUID will be automatically
492
- generated.
493
-
584
+ project_id (str, optional): The unique identifier of the project to filter
585
+ pipelines by. If not provided, returns active pipelines from all projects
586
+ accessible to the authenticated user.
587
+ correlation_id (str, optional): A unique identifier for request tracing
588
+ and logging. If not provided, one will be automatically generated.
589
+ api_version (str, optional): The API version to use for the request.
590
+ Defaults to "v1". Valid versions are defined in ApiVersion enum.
591
+
494
592
  Returns:
495
- List[str]: A list of active pipeline ID strings. Returns an empty list
496
- if no active pipelines are found.
497
-
593
+ List[str]: A list of pipeline IDs that are currently active. Returns an
594
+ empty list if no active pipelines are found.
595
+
498
596
  Raises:
499
- ValueError: If the provided API version is not supported.
500
- AiriaAPIError: If the API request fails, including network errors,
501
- authentication failures, or server errors.
502
-
597
+ ValueError: If the provided api_version is not valid.
598
+ AiriaAPIError: If the API request fails, including cases where:
599
+ - The project_id doesn't exist (404)
600
+ - Authentication fails (401)
601
+ - Access is forbidden (403)
602
+ - Server errors (5xx)
603
+
503
604
  Example:
504
- >>> client = AiriaClient(api_key="your_api_key")
505
- >>> pipeline_ids = client.get_active_pipelines_ids()
506
- >>> print(f"Found {len(pipeline_ids)} active pipelines")
507
- >>> for pipeline_id in pipeline_ids:
508
- ... print(f"Pipeline ID: {pipeline_id}")
605
+ ```python
606
+ from airia import AiriaAsyncClient
607
+
608
+ client = AiriaAsyncClient(api_key="your_api_key")
609
+
610
+ # Get all active pipeline IDs
611
+ pipeline_ids = await client.get_active_pipelines_ids()
612
+ print(f"Found {len(pipeline_ids)} active pipelines")
613
+
614
+ # Get active pipeline IDs for a specific project
615
+ project_pipelines = await client.get_active_pipelines_ids(
616
+ project_id="your_project_id"
617
+ )
618
+ print(f"Project has {len(project_pipelines)} active pipelines")
619
+ ```
620
+
621
+ Note:
622
+ Only pipelines with active versions are returned. Inactive or archived
623
+ pipelines are not included in the results.
509
624
  """
510
625
  request_data = self._pre_get_active_pipelines_ids(
511
- correlation_id=correlation_id, api_version=api_version
626
+ project_id=project_id,
627
+ correlation_id=correlation_id,
628
+ api_version=api_version,
512
629
  )
513
630
  resp = await self._make_request("GET", request_data)
514
631
 
@@ -553,12 +670,12 @@ class AiriaAsyncClient(AiriaBaseClient):
553
670
 
554
671
  Example:
555
672
  ```python
556
- from airia import AiriaClient
673
+ from airia import AiriaAsyncClient
557
674
 
558
- client = AiriaClient(api_key="your_api_key")
675
+ client = AiriaAsyncClient(api_key="your_api_key")
559
676
 
560
677
  # Get pipeline configuration
561
- config = client.get_pipeline_config(
678
+ config = await client.get_pipeline_config(
562
679
  pipeline_id="your_pipeline_id"
563
680
  )
564
681
 
@@ -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 = "https://api.airia.ai/",
21
+ base_url: str = DEFAULT_BASE_URL,
21
22
  api_key: Optional[str] = None,
22
- timeout: float = 30.0,
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 API key: parameter takes precedence over environment variable
36
- self.api_key = self.__class__._get_api_key(api_key)
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,90 +47,91 @@ class AiriaBaseClient:
44
47
  self.logger = configure_logging() if custom_logger is None else custom_logger
45
48
 
46
49
  @staticmethod
47
- def _get_api_key(api_key: Optional[str] = None):
50
+ def _resolve_auth_credentials(api_key: Optional[str] = None, bearer_token: Optional[str] = None):
48
51
  """
49
- Get the API key from either the provided parameter or environment variable.
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
- str: The resolved API key.
59
+ tuple: (api_key, bearer_token) - exactly one will be non-None
56
60
 
57
61
  Raises:
58
- ValueError: If no API key is provided through either method.
62
+ ValueError: If no authentication method is provided or if both are provided.
59
63
  """
60
- api_key = api_key or os.environ.get("AIRIA_API_KEY")
61
-
62
- if not api_key:
64
+ # Check for explicit conflict first
65
+ if api_key and bearer_token:
63
66
  raise ValueError(
64
- "API key must be provided either as a parameter or through the AIRIA_API_KEY environment variable."
67
+ "Cannot provide both api_key and bearer_token. Please use only one authentication method."
65
68
  )
66
-
67
- return api_key
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,
71
90
  url: str,
72
91
  payload: Optional[Dict[str, Any]] = None,
92
+ params: Optional[Dict[str, Any]] = None,
73
93
  correlation_id: Optional[str] = None,
74
94
  ):
75
- """
76
- Prepare the request parameters for an API call.
77
-
78
- Args:
79
- url (str): The endpoint URL for the API request.
80
- payload (Optional[Dict[str, Any]]): The request payload/body to be sent.
81
- correlation_id (Optional[str]): A unique identifier for tracing the request. If None, one will be generated.
82
-
83
- Returns:
84
- dict: A dictionary containing the prepared request parameters with the following keys:
85
- - url: The request URL
86
- - payload: The request payload
87
- - headers: Request headers including API key and correlation ID
88
- - correlation_id: The correlation ID used for the request
89
-
90
- Note:
91
- This method handles:
92
- - Setting/generating correlation IDs
93
- - Adding authentication headers
94
- - Logging requests (if enabled) with sensitive information redacted
95
- """
96
95
  # Set correlation ID if provided or generate a new one
97
96
  correlation_id = set_correlation_id(correlation_id)
98
97
 
99
- # Add the X-API-KEY header and correlation ID
98
+ # Set up base headers
100
99
  headers = {
101
- "X-API-KEY": self.api_key,
102
100
  "X-Correlation-ID": correlation_id,
103
101
  "Content-Type": "application/json",
104
102
  }
105
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
+
106
110
  # Log the request if enabled
107
111
  if self.log_requests:
108
- # Create a sanitized copy of headers for logging
112
+ # Create a sanitized copy of headers and params for logging
109
113
  log_headers = headers.copy()
114
+ log_params = params.copy() if params is not None else {}
110
115
 
111
116
  # Filter out sensitive headers
112
117
  if "X-API-KEY" in log_headers:
113
118
  log_headers["X-API-KEY"] = "[REDACTED]"
119
+ if "Authorization" in log_headers:
120
+ log_headers["Authorization"] = "[REDACTED]"
114
121
 
115
122
  # Process payload for logging
116
- log_payload = None
117
- if payload is not None:
118
- log_payload = payload.copy()
119
-
120
- if "images" in log_payload and log_payload["images"] is not None:
121
- log_payload["images"] = f"{len(log_payload['images'])} images"
122
- if "files" in log_payload and log_payload["files"] is not None:
123
- log_payload["files"] = f"{len(log_payload['files'])} files"
124
-
125
- log_payload = json.dumps(log_payload)
123
+ log_payload = payload.copy() if payload is not None else {}
124
+ if "images" in log_payload and log_payload["images"] is not None:
125
+ log_payload["images"] = f"{len(log_payload['images'])} images"
126
+ if "files" in log_payload and log_payload["files"] is not None:
127
+ log_payload["files"] = f"{len(log_payload['files'])} files"
128
+ log_payload = json.dumps(log_payload)
126
129
 
127
130
  self.logger.info(
128
131
  f"API Request: POST {url}\n"
129
132
  f"Headers: {json.dumps(log_headers)}\n"
130
133
  f"Payload: {log_payload}"
134
+ f"Params: {json.dumps(log_params)}\n"
131
135
  )
132
136
 
133
137
  return RequestData(
@@ -135,6 +139,7 @@ class AiriaBaseClient:
135
139
  "url": url,
136
140
  "payload": payload,
137
141
  "headers": headers,
142
+ "params": params,
138
143
  "correlation_id": correlation_id,
139
144
  }
140
145
  )
@@ -184,12 +189,29 @@ class AiriaBaseClient:
184
189
  "promptVariables": prompt_variables,
185
190
  }
186
191
 
187
- request_data = self._prepare_request(url, payload, correlation_id)
192
+ request_data = self._prepare_request(
193
+ url=url, payload=payload, correlation_id=correlation_id
194
+ )
195
+
196
+ return request_data
197
+
198
+ def _pre_get_projects(
199
+ self,
200
+ correlation_id: Optional[str] = None,
201
+ api_version: str = ApiVersion.V1.value,
202
+ ):
203
+ if api_version not in ApiVersion.as_list():
204
+ raise ValueError(
205
+ f"Invalid API version: {api_version}. Valid versions are: {', '.join(ApiVersion.as_list())}"
206
+ )
207
+ url = urljoin(self.base_url, f"{api_version}/Project/paginated")
208
+ request_data = self._prepare_request(url, correlation_id=correlation_id)
188
209
 
189
210
  return request_data
190
211
 
191
212
  def _pre_get_active_pipelines_ids(
192
213
  self,
214
+ project_id: Optional[str] = None,
193
215
  correlation_id: Optional[str] = None,
194
216
  api_version: str = ApiVersion.V1.value,
195
217
  ):
@@ -198,7 +220,10 @@ class AiriaBaseClient:
198
220
  f"Invalid API version: {api_version}. Valid versions are: {', '.join(ApiVersion.as_list())}"
199
221
  )
200
222
  url = urljoin(self.base_url, f"{api_version}/PipelinesConfig")
201
- request_data = self._prepare_request(url, correlation_id=correlation_id)
223
+ params = {"projectId": project_id} if project_id is not None else None
224
+ request_data = self._prepare_request(
225
+ url, params=params, correlation_id=correlation_id
226
+ )
202
227
 
203
228
  return request_data
204
229
 
@@ -212,7 +237,23 @@ class AiriaBaseClient:
212
237
  raise ValueError(
213
238
  f"Invalid API version: {api_version}. Valid versions are: {', '.join(ApiVersion.as_list())}"
214
239
  )
215
- url = urljoin(self.base_url, f"{api_version}/PipelinesConfig/export/{pipeline_id}")
240
+ url = urljoin(
241
+ self.base_url, f"{api_version}/PipelinesConfig/export/{pipeline_id}"
242
+ )
243
+ request_data = self._prepare_request(url, correlation_id=correlation_id)
244
+
245
+ return request_data
246
+
247
+ def _pre_get_projects(
248
+ self,
249
+ correlation_id: Optional[str] = None,
250
+ api_version: str = ApiVersion.V1.value,
251
+ ):
252
+ if api_version not in ApiVersion.as_list():
253
+ raise ValueError(
254
+ f"Invalid API version: {api_version}. Valid versions are: {', '.join(ApiVersion.as_list())}"
255
+ )
256
+ url = urljoin(self.base_url, f"{api_version}/Project/paginated")
216
257
  request_data = self._prepare_request(url, correlation_id=correlation_id)
217
258
 
218
259
  return request_data
@@ -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,
@@ -12,6 +13,7 @@ from ..types import (
12
13
  PipelineExecutionResponse,
13
14
  PipelineExecutionV1StreamedResponse,
14
15
  PipelineExecutionV2StreamedResponse,
16
+ ProjectItem,
15
17
  RequestData,
16
18
  )
17
19
  from ..utils.sse_parser import parse_sse_stream_chunked
@@ -23,9 +25,10 @@ class AiriaClient(AiriaBaseClient):
23
25
 
24
26
  def __init__(
25
27
  self,
26
- base_url: str = "https://api.airia.ai/",
28
+ base_url: str = DEFAULT_BASE_URL,
27
29
  api_key: Optional[str] = None,
28
- timeout: float = 30.0,
30
+ bearer_token: Optional[str] = None,
31
+ timeout: float = DEFAULT_TIMEOUT,
29
32
  log_requests: bool = False,
30
33
  custom_logger: Optional["loguru.Logger"] = None,
31
34
  ):
@@ -35,6 +38,7 @@ class AiriaClient(AiriaBaseClient):
35
38
  Args:
36
39
  base_url: Base URL of the Airia API.
37
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).
38
42
  timeout: Request timeout in seconds.
39
43
  log_requests: Whether to log API requests and responses. Default is False.
40
44
  custom_logger: Optional custom logger object to use for logging. If not provided, will use a default logger when `log_requests` is True.
@@ -42,6 +46,7 @@ class AiriaClient(AiriaBaseClient):
42
46
  super().__init__(
43
47
  base_url=base_url,
44
48
  api_key=api_key,
49
+ bearer_token=bearer_token,
45
50
  timeout=timeout,
46
51
  log_requests=log_requests,
47
52
  custom_logger=custom_logger,
@@ -54,10 +59,10 @@ class AiriaClient(AiriaBaseClient):
54
59
  @classmethod
55
60
  def with_openai_gateway(
56
61
  cls,
57
- base_url: str = "https://api.airia.ai/",
58
- gateway_url: str = "https://gateway.airia.ai/openai/v1",
62
+ base_url: str = DEFAULT_BASE_URL,
63
+ gateway_url: str = DEFAULT_OPENAI_GATEWAY_URL,
59
64
  api_key: Optional[str] = None,
60
- timeout: float = 30.0,
65
+ timeout: float = DEFAULT_TIMEOUT,
61
66
  log_requests: bool = False,
62
67
  custom_logger: Optional["loguru.Logger"] = None,
63
68
  **kwargs,
@@ -76,22 +81,22 @@ class AiriaClient(AiriaBaseClient):
76
81
  """
77
82
  from openai import OpenAI
78
83
 
79
- api_key = cls._get_api_key(api_key)
84
+ client = cls(base_url=base_url, api_key=api_key, timeout=timeout, log_requests=log_requests, custom_logger=custom_logger)
80
85
  cls.openai = OpenAI(
81
- api_key=api_key,
86
+ api_key=client.api_key,
82
87
  base_url=gateway_url,
83
88
  **kwargs,
84
89
  )
85
90
 
86
- return cls(base_url, api_key, timeout, log_requests, custom_logger)
91
+ return client
87
92
 
88
93
  @classmethod
89
94
  def with_anthropic_gateway(
90
95
  cls,
91
- base_url: str = "https://api.airia.ai/",
92
- gateway_url: str = "https://gateway.airia.ai/anthropic",
96
+ base_url: str = DEFAULT_BASE_URL,
97
+ gateway_url: str = DEFAULT_ANTHROPIC_GATEWAY_URL,
93
98
  api_key: Optional[str] = None,
94
- timeout: float = 30.0,
99
+ timeout: float = DEFAULT_TIMEOUT,
95
100
  log_requests: bool = False,
96
101
  custom_logger: Optional["loguru.Logger"] = None,
97
102
  **kwargs,
@@ -110,14 +115,41 @@ class AiriaClient(AiriaBaseClient):
110
115
  """
111
116
  from anthropic import Anthropic
112
117
 
113
- api_key = cls._get_api_key(api_key)
118
+ client = cls(base_url=base_url, api_key=api_key, timeout=timeout, log_requests=log_requests, custom_logger=custom_logger)
114
119
  cls.anthropic = Anthropic(
115
- api_key=api_key,
120
+ api_key=client.api_key,
116
121
  base_url=gateway_url,
117
122
  **kwargs,
118
123
  )
119
124
 
120
- return cls(base_url, api_key, timeout, log_requests, custom_logger)
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
+ )
121
153
 
122
154
  def _handle_exception(self, e: requests.HTTPError, url: str, correlation_id: str):
123
155
  # Log the error response if enabled
@@ -140,12 +172,12 @@ class AiriaClient(AiriaBaseClient):
140
172
  # If JSON parsing fails or expected keys are missing
141
173
  error_message = f"API request failed: {str(e)}"
142
174
 
143
- # Make sure API key is not included in error messages
144
- sanitized_message = (
145
- error_message.replace(self.api_key, "[REDACTED]")
146
- if self.api_key in error_message
147
- else error_message
148
- )
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]")
149
181
 
150
182
  # Raise custom exception with status code and sanitized message
151
183
  raise AiriaAPIError(
@@ -181,6 +213,7 @@ class AiriaClient(AiriaBaseClient):
181
213
  method=method,
182
214
  url=request_data.url,
183
215
  json=request_data.payload,
216
+ params=request_data.params,
184
217
  headers=request_data.headers,
185
218
  timeout=self.timeout,
186
219
  )
@@ -231,6 +264,7 @@ class AiriaClient(AiriaBaseClient):
231
264
  response = self.session.request(
232
265
  method=method,
233
266
  url=request_data.url,
267
+ params=request_data.params,
234
268
  json=request_data.payload,
235
269
  headers=request_data.headers,
236
270
  timeout=self.timeout,
@@ -445,8 +479,8 @@ class AiriaClient(AiriaBaseClient):
445
479
  )
446
480
  request_data = self._prepare_request(
447
481
  url,
448
- {"socketIdentifier": resp},
449
- request_data.headers["X-Correlation-ID"],
482
+ payload={"socketIdentifier": resp},
483
+ correlation_id=request_data.headers["X-Correlation-ID"],
450
484
  )
451
485
  resp = self._make_request("POST", request_data)
452
486
 
@@ -454,43 +488,126 @@ class AiriaClient(AiriaBaseClient):
454
488
 
455
489
  return PipelineExecutionV2StreamedResponse(stream=resp)
456
490
 
491
+ def get_projects(
492
+ self,
493
+ correlation_id: Optional[str] = None,
494
+ api_version: str = ApiVersion.V1.value,
495
+ ) -> List[ProjectItem]:
496
+ """
497
+ Retrieve a list of all projects accessible to the authenticated user.
498
+
499
+ This method fetches comprehensive information about all projects that the
500
+ current user has access to, including project metadata, creation details,
501
+ and status information.
502
+
503
+ Args:
504
+ correlation_id (str, optional): A unique identifier for request tracing
505
+ and logging. If not provided, one will be automatically generated.
506
+ api_version (str, optional): The API version to use for the request.
507
+ Defaults to "v1". Valid versions are defined in ApiVersion enum.
508
+
509
+ Returns:
510
+ List[ProjectItem]: A list of ProjectItem objects containing project
511
+ information. Returns an empty list if no projects are accessible
512
+ or found.
513
+
514
+ Raises:
515
+ ValueError: If the provided api_version is not valid.
516
+ AiriaAPIError: If the API request fails, including cases where:
517
+ - Authentication fails (401)
518
+ - Access is forbidden (403)
519
+ - Server errors (5xx)
520
+
521
+ Example:
522
+ ```python
523
+ from airia import AiriaClient
524
+
525
+ client = AiriaClient(api_key="your_api_key")
526
+
527
+ # Get all accessible projects
528
+ projects = client.get_projects()
529
+
530
+ for project in projects:
531
+ print(f"Project: {project.name}")
532
+ print(f"ID: {project.id}")
533
+ print(f"Description: {project.description}")
534
+ print(f"Created: {project.created_at}")
535
+ print("---")
536
+ ```
537
+
538
+ Note:
539
+ The returned projects are filtered based on the authenticated user's
540
+ permissions. Users will only see projects they have been granted
541
+ access to.
542
+ """
543
+ request_data = self._pre_get_projects(
544
+ correlation_id=correlation_id, api_version=api_version
545
+ )
546
+ resp = self._make_request("GET", request_data)
547
+
548
+ if "items" not in resp or len(resp["items"]) == 0:
549
+ return []
550
+
551
+ return [ProjectItem(**item) for item in resp["items"]]
552
+
457
553
  def get_active_pipelines_ids(
458
554
  self,
555
+ project_id: Optional[str] = None,
459
556
  correlation_id: Optional[str] = None,
460
557
  api_version: str = ApiVersion.V1.value,
461
558
  ) -> List[str]:
462
559
  """
463
560
  Retrieve a list of active pipeline IDs.
464
561
 
465
- This method fetches all currently active pipeline IDs from the Airia API.
466
- These IDs can be used with other methods like execute_pipeline() or
467
- get_pipeline_config().
562
+ This method fetches the IDs of all active pipelines, optionally filtered by project.
563
+ Active pipelines are those that are currently deployed and available for execution.
468
564
 
469
565
  Args:
470
- api_version (str, optional): API version to use for the request.
471
- Must be one of the supported versions. Defaults to "v1".
472
- correlation_id (str, optional): Unique identifier for request tracing
473
- and logging. If not provided, a new UUID will be automatically
474
- generated.
566
+ project_id (str, optional): The unique identifier of the project to filter
567
+ pipelines by. If not provided, returns active pipelines from all projects
568
+ accessible to the authenticated user.
569
+ correlation_id (str, optional): A unique identifier for request tracing
570
+ and logging. If not provided, one will be automatically generated.
571
+ api_version (str, optional): The API version to use for the request.
572
+ Defaults to "v1". Valid versions are defined in ApiVersion enum.
475
573
 
476
574
  Returns:
477
- List[str]: A list of active pipeline ID strings. Returns an empty list
478
- if no active pipelines are found.
575
+ List[str]: A list of pipeline IDs that are currently active. Returns an
576
+ empty list if no active pipelines are found.
479
577
 
480
578
  Raises:
481
- ValueError: If the provided API version is not supported.
482
- AiriaAPIError: If the API request fails, including network errors,
483
- authentication failures, or server errors.
579
+ ValueError: If the provided api_version is not valid.
580
+ AiriaAPIError: If the API request fails, including cases where:
581
+ - The project_id doesn't exist (404)
582
+ - Authentication fails (401)
583
+ - Access is forbidden (403)
584
+ - Server errors (5xx)
484
585
 
485
586
  Example:
486
- >>> client = AiriaClient(api_key="your_api_key")
487
- >>> pipeline_ids = client.get_active_pipelines_ids()
488
- >>> print(f"Found {len(pipeline_ids)} active pipelines")
489
- >>> for pipeline_id in pipeline_ids:
490
- ... print(f"Pipeline ID: {pipeline_id}")
587
+ ```python
588
+ from airia import AiriaClient
589
+
590
+ client = AiriaClient(api_key="your_api_key")
591
+
592
+ # Get all active pipeline IDs
593
+ pipeline_ids = client.get_active_pipelines_ids()
594
+ print(f"Found {len(pipeline_ids)} active pipelines")
595
+
596
+ # Get active pipeline IDs for a specific project
597
+ project_pipelines = client.get_active_pipelines_ids(
598
+ project_id="your_project_id"
599
+ )
600
+ print(f"Project has {len(project_pipelines)} active pipelines")
601
+ ```
602
+
603
+ Note:
604
+ Only pipelines with active versions are returned. Inactive or archived
605
+ pipelines are not included in the results.
491
606
  """
492
607
  request_data = self._pre_get_active_pipelines_ids(
493
- correlation_id=correlation_id, api_version=api_version
608
+ project_id=project_id,
609
+ correlation_id=correlation_id,
610
+ api_version=api_version,
494
611
  )
495
612
  resp = self._make_request("GET", request_data)
496
613
 
airia/constants.py ADDED
@@ -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
airia/types/__init__.py CHANGED
@@ -1,3 +1,4 @@
1
+ from .api.get_projects import ProjectItem
1
2
  from .api.get_pipeline_config import GetPipelineConfigResponse
2
3
  from .api.pipeline_execution import (
3
4
  PipelineExecutionDebugResponse,
@@ -43,6 +44,7 @@ __all__ = [
43
44
  "PipelineExecutionV2AsyncStreamedResponse",
44
45
  "PipelineExecutionV2StreamedResponse",
45
46
  "GetPipelineConfigResponse",
47
+ "ProjectItem",
46
48
  "RequestData",
47
49
  "AgentPingMessage",
48
50
  "AgentStartMessage",
@@ -101,7 +101,9 @@ class Model(BaseModel):
101
101
  url: str
102
102
  input_type: str = Field(alias="inputType")
103
103
  provider: str
104
- credentials_definition: CredentialsDefinition = Field(alias="credentialsDefinition")
104
+ credentials_definition: Optional[CredentialsDefinition] = Field(
105
+ alias="credentialsDefinition"
106
+ )
105
107
  deployment_type: str = Field(alias="deploymentType")
106
108
  source_type: str = Field(alias="sourceType")
107
109
  connection_string: Optional[str] = Field(alias="connectionString", default=None)
@@ -135,10 +137,41 @@ class Router(BaseModel):
135
137
  router_config: Dict[str, Dict[str, Any]] = Field(alias="routerConfig")
136
138
 
137
139
 
140
+ class ChunkingConfig(BaseModel):
141
+ id: str
142
+ chunk_size: int = Field(alias="chunkSize")
143
+ chunk_overlap: int = Field(alias="chunkOverlap")
144
+ strategy_type: str = Field(alias="strategyType")
145
+
146
+
147
+ class DataSourceFile(BaseModel):
148
+ data_source_id: str = Field(alias="dataSourceId")
149
+ file_path: Optional[str] = Field(None, alias="filePath")
150
+ input_token: Optional[str] = Field(None, alias="inputToken")
151
+ file_count: Optional[int] = Field(None, alias="fileCount")
152
+
153
+
154
+ class DataSource(BaseModel):
155
+ id: str = Field(alias="id")
156
+ name: Optional[str] = None
157
+ execution_name: Optional[str] = Field(None, alias="executionName")
158
+ chunking_config: ChunkingConfig = Field(alias="chunkingConfig")
159
+ data_source_type: str = Field(alias="dataSourceType")
160
+ database_type: str = Field(alias="databaseType")
161
+ embedding_provider: str = Field(alias="embeddingProvider")
162
+ is_user_specific: bool = Field(alias="isUserSpecific")
163
+ files: Optional[List[DataSourceFile]] = None
164
+ configuration_json: Optional[str] = Field(None, alias="configurationJson")
165
+ credentials: Optional[CredentialsDefinition]
166
+ is_image_processing_enabled: bool = Field(alias="isImageProcessingEnabled")
167
+
168
+
138
169
  class GetPipelineConfigResponse(BaseModel):
139
170
  metadata: Metadata
140
171
  agent: Agent
141
- data_sources: Optional[List[Any]] = Field(alias="dataSources", default_factory=list)
172
+ data_sources: Optional[List[DataSource]] = Field(
173
+ alias="dataSources", default_factory=list
174
+ )
142
175
  prompts: Optional[List[Prompt]] = Field(default_factory=list)
143
176
  tools: Optional[List[Tool]] = Field(default_factory=list)
144
177
  models: Optional[List[Model]] = Field(default_factory=list)
@@ -0,0 +1,35 @@
1
+ from datetime import datetime
2
+ from typing import Any, List, Optional
3
+
4
+ from pydantic import BaseModel, Field
5
+
6
+
7
+ class Pipeline(BaseModel):
8
+ id: str
9
+ name: str
10
+
11
+
12
+ class ProjectItem(BaseModel):
13
+ tenant_id: str = Field(alias="tenantId")
14
+ created_at: datetime = Field(alias="createdAt")
15
+ require_classification: bool = Field(alias="requireClassification")
16
+ budget_amount: Optional[Any] = Field(None, alias="budgetAmount")
17
+ budget_period: Optional[Any] = Field(None, alias="budgetPeriod")
18
+ budget_alert: Optional[Any] = Field(None, alias="budgetAlert")
19
+ budget_stop: bool = Field(alias="budgetStop")
20
+ used_budget_amount: Optional[Any] = Field(None, alias="usedBudgetAmount")
21
+ resume_ends_at: Optional[datetime] = Field(None, alias="resumeEndsAt")
22
+ updated_at: datetime = Field(alias="updatedAt")
23
+ pipelines: List[Pipeline]
24
+ models: Optional[Any] = None
25
+ data_sources: List[Any] = Field(alias="dataSources")
26
+ prompts: Optional[Any] = None
27
+ api_keys: Optional[Any] = Field(alias="apiKeys")
28
+ memories: Optional[Any] = None
29
+ project_icon: Optional[str] = Field(None, alias="projectIcon")
30
+ project_icon_id: Optional[str] = Field(None, alias="projectIconId")
31
+ description: Optional[str] = None
32
+ project_type: str = Field(alias="projectType")
33
+ classifications: Optional[Any] = None
34
+ id: str
35
+ name: str
@@ -6,5 +6,6 @@ from pydantic import BaseModel
6
6
  class RequestData(BaseModel):
7
7
  url: str
8
8
  payload: Optional[Dict[str, Any]]
9
+ params: Optional[Dict[str, Any]]
9
10
  headers: Dict[str, Any]
10
11
  correlation_id: str
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: airia
3
- Version: 0.1.8
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
- - **API Key Management**: Flexible API key configuration via parameters or environment variables
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, # Or set AIRIA_API_KEY environment variable
187
- timeout=30.0, # Request timeout in seconds (default: 30.0)
188
- log_requests=False, # Enable request/response logging (default: False)
189
- custom_logger=None # Use custom logger (default: None - uses built-in)
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(
@@ -0,0 +1,21 @@
1
+ airia/__init__.py,sha256=T39gO8E5T5zxlw-JP78ruxOu7-LeKOJCJzz6t40kdQo,231
2
+ airia/constants.py,sha256=fHZNUbvEP_x567uFI1qnA2DT8dOzQ07ojdawdPwIrIA,292
3
+ airia/exceptions.py,sha256=4Z55n-cRJrtTa5-pZBIK2oZD4-Z99aUtKx_kfTFYY5o,1146
4
+ airia/logs.py,sha256=17YZ4IuzOF0m5bgofj9-QYlJ2BYR2kRZbBVQfFSLFEk,5441
5
+ airia/client/__init__.py,sha256=6gSQ9bl7j79q1HPE0o5py3IRdkwWWuU_7J4h05Dd2o8,127
6
+ airia/client/async_client.py,sha256=9MJI4wVwFOcXVaeGN_JQjqnUO0zE12kbXJLTVP1rOe0,28599
7
+ airia/client/base_client.py,sha256=04l7qQ-pvcq6bFOEMPh0x_6izJGTl0VnAxcFQgcFhOk,9859
8
+ airia/client/sync_client.py,sha256=RdCOyzxPoEsj_x3JibmU4yPIqs07b_izyJ5PmbAj6Uw,27656
9
+ airia/types/__init__.py,sha256=Tr1xm5bQrE7yvw0Bo-goNmeh7Rl0QiXUabg7dsJ55dk,2227
10
+ airia/types/api_version.py,sha256=Uzom6O2ZG92HN_Z2h-lTydmO2XYX9RVs4Yi4DJmXytE,255
11
+ airia/types/request_data.py,sha256=WUvf7fixhBPWLxKbc2VY3Y32BOUbLmXktY58mINuq3A,243
12
+ airia/types/sse_messages.py,sha256=wSdowY07AjEO8R73SJrFPJtkfIBS4satUpNytjKQq2U,8305
13
+ airia/types/api/get_pipeline_config.py,sha256=BLz4HS94EYmdwq37KHda2XJ5KV7tJPP43jHmPbO8-NE,6647
14
+ airia/types/api/get_projects.py,sha256=kWmlN0VMgmQ9WP9ms245MAAEmsHhkpwWUQJ_XJrhIRI,1362
15
+ airia/types/api/pipeline_execution.py,sha256=4zGM5W4aXiAibAdgWQCIZBC_blW6QYAlAHoVUMbnCF0,752
16
+ airia/utils/sse_parser.py,sha256=h3TcBvXqUIngTqgY6yYxZCGLnC1eI6meQzYr13aFAb8,2791
17
+ airia-0.1.10.dist-info/licenses/LICENSE,sha256=R3ClUMMKPRItIcZ0svzyj2taZZnFYw568YDNzN9KQ1Q,1066
18
+ airia-0.1.10.dist-info/METADATA,sha256=xh195iJ9x3GO3kv2tqX2A9yj5lzjsvvi5dJGymn9L1U,16682
19
+ airia-0.1.10.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
20
+ airia-0.1.10.dist-info/top_level.txt,sha256=qUQEKfs_hdOYTwjKj1JZbRhS5YeXDNaKQaVTrzabS6w,6
21
+ airia-0.1.10.dist-info/RECORD,,
@@ -1,19 +0,0 @@
1
- airia/__init__.py,sha256=T39gO8E5T5zxlw-JP78ruxOu7-LeKOJCJzz6t40kdQo,231
2
- airia/exceptions.py,sha256=4Z55n-cRJrtTa5-pZBIK2oZD4-Z99aUtKx_kfTFYY5o,1146
3
- airia/logs.py,sha256=17YZ4IuzOF0m5bgofj9-QYlJ2BYR2kRZbBVQfFSLFEk,5441
4
- airia/client/__init__.py,sha256=6gSQ9bl7j79q1HPE0o5py3IRdkwWWuU_7J4h05Dd2o8,127
5
- airia/client/async_client.py,sha256=QtnH1MNfF_ONx2YHsTz7Y4pFBTrggHWz0LLJiITREwc,23694
6
- airia/client/base_client.py,sha256=YWhQle7cmtiuc5BwiavW-NYjTC6kXyclpse4irwkMuI,7969
7
- airia/client/sync_client.py,sha256=iUqRn7Bv5hi-Qcg3Vgfc-Z1gx5T42f9Vq4pUTPsyG5M,22750
8
- airia/types/__init__.py,sha256=s6_uMrrKzhk2wg1lpaRRIl4AQ8hUxKbCNLfz-VK7tAs,2166
9
- airia/types/api_version.py,sha256=Uzom6O2ZG92HN_Z2h-lTydmO2XYX9RVs4Yi4DJmXytE,255
10
- airia/types/request_data.py,sha256=m8lBwFliK2_kZU2TgeLdhiO_7v826lVRhA5_8NYB_NM,206
11
- airia/types/sse_messages.py,sha256=wSdowY07AjEO8R73SJrFPJtkfIBS4satUpNytjKQq2U,8305
12
- airia/types/api/get_pipeline_config.py,sha256=HAKgcpiJKeG_TuWtxo28CeWiodhlsVBwgbpzDgd1Eoo,5401
13
- airia/types/api/pipeline_execution.py,sha256=4zGM5W4aXiAibAdgWQCIZBC_blW6QYAlAHoVUMbnCF0,752
14
- airia/utils/sse_parser.py,sha256=h3TcBvXqUIngTqgY6yYxZCGLnC1eI6meQzYr13aFAb8,2791
15
- airia-0.1.8.dist-info/licenses/LICENSE,sha256=R3ClUMMKPRItIcZ0svzyj2taZZnFYw568YDNzN9KQ1Q,1066
16
- airia-0.1.8.dist-info/METADATA,sha256=Toz3tWc8psnI2QbUrhjfzomljqp5BSrlaJH11mohHos,13218
17
- airia-0.1.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
18
- airia-0.1.8.dist-info/top_level.txt,sha256=qUQEKfs_hdOYTwjKj1JZbRhS5YeXDNaKQaVTrzabS6w,6
19
- airia-0.1.8.dist-info/RECORD,,
File without changes