airia 0.1.7__tar.gz → 0.1.9__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.
Files changed (31) hide show
  1. {airia-0.1.7 → airia-0.1.9}/PKG-INFO +1 -1
  2. {airia-0.1.7 → airia-0.1.9}/airia/client/async_client.py +121 -35
  3. {airia-0.1.7 → airia-0.1.9}/airia/client/base_client.py +50 -35
  4. {airia-0.1.7 → airia-0.1.9}/airia/client/sync_client.py +107 -21
  5. {airia-0.1.7 → airia-0.1.9}/airia/types/__init__.py +2 -0
  6. {airia-0.1.7 → airia-0.1.9}/airia/types/api/get_pipeline_config.py +40 -5
  7. airia-0.1.9/airia/types/api/get_projects.py +35 -0
  8. {airia-0.1.7 → airia-0.1.9}/airia/types/request_data.py +1 -0
  9. {airia-0.1.7 → airia-0.1.9}/airia.egg-info/PKG-INFO +1 -1
  10. {airia-0.1.7 → airia-0.1.9}/airia.egg-info/SOURCES.txt +2 -0
  11. {airia-0.1.7 → airia-0.1.9}/pyproject.toml +1 -1
  12. {airia-0.1.7 → airia-0.1.9}/tests/test_anthropic_gateway.py +1 -1
  13. {airia-0.1.7 → airia-0.1.9}/tests/test_execute_pipeline.py +1 -1
  14. {airia-0.1.7 → airia-0.1.9}/tests/test_get_active_pipelines_ids.py +29 -12
  15. {airia-0.1.7 → airia-0.1.9}/tests/test_get_pipeline_config.py +1 -1
  16. airia-0.1.9/tests/test_get_projects.py +203 -0
  17. {airia-0.1.7 → airia-0.1.9}/tests/test_openai_gateway.py +1 -1
  18. {airia-0.1.7 → airia-0.1.9}/LICENSE +0 -0
  19. {airia-0.1.7 → airia-0.1.9}/README.md +0 -0
  20. {airia-0.1.7 → airia-0.1.9}/airia/__init__.py +0 -0
  21. {airia-0.1.7 → airia-0.1.9}/airia/client/__init__.py +0 -0
  22. {airia-0.1.7 → airia-0.1.9}/airia/exceptions.py +0 -0
  23. {airia-0.1.7 → airia-0.1.9}/airia/logs.py +0 -0
  24. {airia-0.1.7 → airia-0.1.9}/airia/types/api/pipeline_execution.py +0 -0
  25. {airia-0.1.7 → airia-0.1.9}/airia/types/api_version.py +0 -0
  26. {airia-0.1.7 → airia-0.1.9}/airia/types/sse_messages.py +0 -0
  27. {airia-0.1.7 → airia-0.1.9}/airia/utils/sse_parser.py +0 -0
  28. {airia-0.1.7 → airia-0.1.9}/airia.egg-info/dependency_links.txt +0 -0
  29. {airia-0.1.7 → airia-0.1.9}/airia.egg-info/requires.txt +0 -0
  30. {airia-0.1.7 → airia-0.1.9}/airia.egg-info/top_level.txt +0 -0
  31. {airia-0.1.7 → airia-0.1.9}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: airia
3
- Version: 0.1.7
3
+ Version: 0.1.9
4
4
  Summary: Python SDK for Airia API
5
5
  Author-email: Airia LLC <support@airia.com>
6
6
  License: MIT
@@ -14,6 +14,7 @@ from ..types import (
14
14
  PipelineExecutionResponse,
15
15
  PipelineExecutionV1StreamedResponse,
16
16
  PipelineExecutionV2AsyncStreamedResponse,
17
+ ProjectItem,
17
18
  RequestData,
18
19
  )
19
20
  from ..utils.sse_parser import async_parse_sse_stream_chunked
@@ -199,6 +200,7 @@ class AiriaAsyncClient(AiriaBaseClient):
199
200
  method=method,
200
201
  url=request_data.url,
201
202
  json=request_data.payload,
203
+ params=request_data.params,
202
204
  headers=request_data.headers,
203
205
  timeout=self.timeout,
204
206
  ) as response:
@@ -250,6 +252,7 @@ class AiriaAsyncClient(AiriaBaseClient):
250
252
  method=method,
251
253
  url=request_data.url,
252
254
  json=request_data.payload,
255
+ params=request_data.params,
253
256
  headers=request_data.headers,
254
257
  timeout=self.timeout,
255
258
  chunked=True,
@@ -419,11 +422,11 @@ class AiriaAsyncClient(AiriaBaseClient):
419
422
  aiohttp.ClientError: For other request-related errors.
420
423
 
421
424
  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
- ... )
425
+ >>> client = AiriaAsyncClient(api_key="your_api_key")
426
+ ... response = await client.execute_pipeline(
427
+ ... pipeline_id="pipeline_123",
428
+ ... user_input="Tell me about quantum computing"
429
+ ... )
427
430
  >>> print(response.result)
428
431
  """
429
432
  request_data = self._pre_execute_pipeline(
@@ -462,9 +465,9 @@ class AiriaAsyncClient(AiriaBaseClient):
462
465
  self.base_url, f"{api_version}/StreamSocketConfig/GenerateUrl"
463
466
  )
464
467
  request_data = self._prepare_request(
465
- url,
466
- {"socketIdentifier": resp},
467
- request_data.headers["X-Correlation-ID"],
468
+ url=url,
469
+ payload={"socketIdentifier": resp},
470
+ correlation_id=request_data.headers["X-Correlation-ID"],
468
471
  )
469
472
  resp = await self._make_request("POST", request_data)
470
473
 
@@ -472,43 +475,126 @@ class AiriaAsyncClient(AiriaBaseClient):
472
475
 
473
476
  return PipelineExecutionV2AsyncStreamedResponse(stream=resp)
474
477
 
478
+ async def get_projects(
479
+ self,
480
+ correlation_id: Optional[str] = None,
481
+ api_version: str = ApiVersion.V1.value,
482
+ ) -> List[ProjectItem]:
483
+ """
484
+ Retrieve a list of all projects accessible to the authenticated user.
485
+
486
+ This method fetches comprehensive information about all projects that the
487
+ current user has access to, including project metadata, creation details,
488
+ and status information.
489
+
490
+ Args:
491
+ correlation_id (str, optional): A unique identifier for request tracing
492
+ and logging. If not provided, one will be automatically generated.
493
+ api_version (str, optional): The API version to use for the request.
494
+ Defaults to "v1". Valid versions are defined in ApiVersion enum.
495
+
496
+ Returns:
497
+ List[ProjectItem]: A list of ProjectItem objects containing project
498
+ information. Returns an empty list if no projects are accessible
499
+ or found.
500
+
501
+ Raises:
502
+ ValueError: If the provided api_version is not valid.
503
+ AiriaAPIError: If the API request fails, including cases where:
504
+ - Authentication fails (401)
505
+ - Access is forbidden (403)
506
+ - Server errors (5xx)
507
+
508
+ Example:
509
+ ```python
510
+ from airia import AiriaAsyncClient
511
+
512
+ client = AiriaAsyncClient(api_key="your_api_key")
513
+
514
+ # Get all accessible projects
515
+ projects = await client.get_projects()
516
+
517
+ for project in projects:
518
+ print(f"Project: {project.name}")
519
+ print(f"ID: {project.id}")
520
+ print(f"Description: {project.description}")
521
+ print(f"Created: {project.created_at}")
522
+ print("---")
523
+ ```
524
+
525
+ Note:
526
+ The returned projects are filtered based on the authenticated user's
527
+ permissions. Users will only see projects they have been granted
528
+ access to.
529
+ """
530
+ request_data = self._pre_get_projects(
531
+ correlation_id=correlation_id, api_version=api_version
532
+ )
533
+ resp = await self._make_request("GET", request_data)
534
+
535
+ if "items" not in resp or len(resp["items"]) == 0:
536
+ return []
537
+
538
+ return [ProjectItem(**item) for item in resp["items"]]
539
+
475
540
  async def get_active_pipelines_ids(
476
541
  self,
542
+ project_id: Optional[str] = None,
477
543
  correlation_id: Optional[str] = None,
478
544
  api_version: str = ApiVersion.V1.value,
479
545
  ) -> List[str]:
480
546
  """
481
547
  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
-
548
+
549
+ This method fetches the IDs of all active pipelines, optionally filtered by project.
550
+ Active pipelines are those that are currently deployed and available for execution.
551
+
487
552
  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
-
553
+ project_id (str, optional): The unique identifier of the project to filter
554
+ pipelines by. If not provided, returns active pipelines from all projects
555
+ accessible to the authenticated user.
556
+ correlation_id (str, optional): A unique identifier for request tracing
557
+ and logging. If not provided, one will be automatically generated.
558
+ api_version (str, optional): The API version to use for the request.
559
+ Defaults to "v1". Valid versions are defined in ApiVersion enum.
560
+
494
561
  Returns:
495
- List[str]: A list of active pipeline ID strings. Returns an empty list
496
- if no active pipelines are found.
497
-
562
+ List[str]: A list of pipeline IDs that are currently active. Returns an
563
+ empty list if no active pipelines are found.
564
+
498
565
  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
-
566
+ ValueError: If the provided api_version is not valid.
567
+ AiriaAPIError: If the API request fails, including cases where:
568
+ - The project_id doesn't exist (404)
569
+ - Authentication fails (401)
570
+ - Access is forbidden (403)
571
+ - Server errors (5xx)
572
+
503
573
  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}")
574
+ ```python
575
+ from airia import AiriaAsyncClient
576
+
577
+ client = AiriaAsyncClient(api_key="your_api_key")
578
+
579
+ # Get all active pipeline IDs
580
+ pipeline_ids = await client.get_active_pipelines_ids()
581
+ print(f"Found {len(pipeline_ids)} active pipelines")
582
+
583
+ # Get active pipeline IDs for a specific project
584
+ project_pipelines = await client.get_active_pipelines_ids(
585
+ project_id="your_project_id"
586
+ )
587
+ print(f"Project has {len(project_pipelines)} active pipelines")
588
+ ```
589
+
590
+ Note:
591
+ Only pipelines with active versions are returned. Inactive or archived
592
+ pipelines are not included in the results.
509
593
  """
510
594
  request_data = self._pre_get_active_pipelines_ids(
511
- correlation_id=correlation_id, api_version=api_version
595
+ project_id=project_id,
596
+ correlation_id=correlation_id,
597
+ api_version=api_version,
512
598
  )
513
599
  resp = await self._make_request("GET", request_data)
514
600
 
@@ -553,12 +639,12 @@ class AiriaAsyncClient(AiriaBaseClient):
553
639
 
554
640
  Example:
555
641
  ```python
556
- from airia import AiriaClient
642
+ from airia import AiriaAsyncClient
557
643
 
558
- client = AiriaClient(api_key="your_api_key")
644
+ client = AiriaAsyncClient(api_key="your_api_key")
559
645
 
560
646
  # Get pipeline configuration
561
- config = client.get_pipeline_config(
647
+ config = await client.get_pipeline_config(
562
648
  pipeline_id="your_pipeline_id"
563
649
  )
564
650
 
@@ -70,29 +70,9 @@ class AiriaBaseClient:
70
70
  self,
71
71
  url: str,
72
72
  payload: Optional[Dict[str, Any]] = None,
73
+ params: Optional[Dict[str, Any]] = None,
73
74
  correlation_id: Optional[str] = None,
74
75
  ):
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
76
  # Set correlation ID if provided or generate a new one
97
77
  correlation_id = set_correlation_id(correlation_id)
98
78
 
@@ -105,29 +85,27 @@ class AiriaBaseClient:
105
85
 
106
86
  # Log the request if enabled
107
87
  if self.log_requests:
108
- # Create a sanitized copy of headers for logging
88
+ # Create a sanitized copy of headers and params for logging
109
89
  log_headers = headers.copy()
90
+ log_params = params.copy() if params is not None else {}
110
91
 
111
92
  # Filter out sensitive headers
112
93
  if "X-API-KEY" in log_headers:
113
94
  log_headers["X-API-KEY"] = "[REDACTED]"
114
95
 
115
96
  # 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)
97
+ log_payload = payload.copy() if payload is not None else {}
98
+ if "images" in log_payload and log_payload["images"] is not None:
99
+ log_payload["images"] = f"{len(log_payload['images'])} images"
100
+ if "files" in log_payload and log_payload["files"] is not None:
101
+ log_payload["files"] = f"{len(log_payload['files'])} files"
102
+ log_payload = json.dumps(log_payload)
126
103
 
127
104
  self.logger.info(
128
105
  f"API Request: POST {url}\n"
129
106
  f"Headers: {json.dumps(log_headers)}\n"
130
107
  f"Payload: {log_payload}"
108
+ f"Params: {json.dumps(log_params)}\n"
131
109
  )
132
110
 
133
111
  return RequestData(
@@ -135,6 +113,7 @@ class AiriaBaseClient:
135
113
  "url": url,
136
114
  "payload": payload,
137
115
  "headers": headers,
116
+ "params": params,
138
117
  "correlation_id": correlation_id,
139
118
  }
140
119
  )
@@ -184,12 +163,29 @@ class AiriaBaseClient:
184
163
  "promptVariables": prompt_variables,
185
164
  }
186
165
 
187
- request_data = self._prepare_request(url, payload, correlation_id)
166
+ request_data = self._prepare_request(
167
+ url=url, payload=payload, correlation_id=correlation_id
168
+ )
169
+
170
+ return request_data
171
+
172
+ def _pre_get_projects(
173
+ self,
174
+ correlation_id: Optional[str] = None,
175
+ api_version: str = ApiVersion.V1.value,
176
+ ):
177
+ if api_version not in ApiVersion.as_list():
178
+ raise ValueError(
179
+ f"Invalid API version: {api_version}. Valid versions are: {', '.join(ApiVersion.as_list())}"
180
+ )
181
+ url = urljoin(self.base_url, f"{api_version}/Project/paginated")
182
+ request_data = self._prepare_request(url, correlation_id=correlation_id)
188
183
 
189
184
  return request_data
190
185
 
191
186
  def _pre_get_active_pipelines_ids(
192
187
  self,
188
+ project_id: Optional[str] = None,
193
189
  correlation_id: Optional[str] = None,
194
190
  api_version: str = ApiVersion.V1.value,
195
191
  ):
@@ -198,7 +194,10 @@ class AiriaBaseClient:
198
194
  f"Invalid API version: {api_version}. Valid versions are: {', '.join(ApiVersion.as_list())}"
199
195
  )
200
196
  url = urljoin(self.base_url, f"{api_version}/PipelinesConfig")
201
- request_data = self._prepare_request(url, correlation_id=correlation_id)
197
+ params = {"projectId": project_id} if project_id is not None else None
198
+ request_data = self._prepare_request(
199
+ url, params=params, correlation_id=correlation_id
200
+ )
202
201
 
203
202
  return request_data
204
203
 
@@ -212,7 +211,23 @@ class AiriaBaseClient:
212
211
  raise ValueError(
213
212
  f"Invalid API version: {api_version}. Valid versions are: {', '.join(ApiVersion.as_list())}"
214
213
  )
215
- url = urljoin(self.base_url, f"{api_version}/PipelinesConfig/export/{pipeline_id}")
214
+ url = urljoin(
215
+ self.base_url, f"{api_version}/PipelinesConfig/export/{pipeline_id}"
216
+ )
217
+ request_data = self._prepare_request(url, correlation_id=correlation_id)
218
+
219
+ return request_data
220
+
221
+ def _pre_get_projects(
222
+ self,
223
+ correlation_id: Optional[str] = None,
224
+ api_version: str = ApiVersion.V1.value,
225
+ ):
226
+ if api_version not in ApiVersion.as_list():
227
+ raise ValueError(
228
+ f"Invalid API version: {api_version}. Valid versions are: {', '.join(ApiVersion.as_list())}"
229
+ )
230
+ url = urljoin(self.base_url, f"{api_version}/Project/paginated")
216
231
  request_data = self._prepare_request(url, correlation_id=correlation_id)
217
232
 
218
233
  return request_data
@@ -12,6 +12,7 @@ from ..types import (
12
12
  PipelineExecutionResponse,
13
13
  PipelineExecutionV1StreamedResponse,
14
14
  PipelineExecutionV2StreamedResponse,
15
+ ProjectItem,
15
16
  RequestData,
16
17
  )
17
18
  from ..utils.sse_parser import parse_sse_stream_chunked
@@ -181,6 +182,7 @@ class AiriaClient(AiriaBaseClient):
181
182
  method=method,
182
183
  url=request_data.url,
183
184
  json=request_data.payload,
185
+ params=request_data.params,
184
186
  headers=request_data.headers,
185
187
  timeout=self.timeout,
186
188
  )
@@ -231,6 +233,7 @@ class AiriaClient(AiriaBaseClient):
231
233
  response = self.session.request(
232
234
  method=method,
233
235
  url=request_data.url,
236
+ params=request_data.params,
234
237
  json=request_data.payload,
235
238
  headers=request_data.headers,
236
239
  timeout=self.timeout,
@@ -445,8 +448,8 @@ class AiriaClient(AiriaBaseClient):
445
448
  )
446
449
  request_data = self._prepare_request(
447
450
  url,
448
- {"socketIdentifier": resp},
449
- request_data.headers["X-Correlation-ID"],
451
+ payload={"socketIdentifier": resp},
452
+ correlation_id=request_data.headers["X-Correlation-ID"],
450
453
  )
451
454
  resp = self._make_request("POST", request_data)
452
455
 
@@ -454,43 +457,126 @@ class AiriaClient(AiriaBaseClient):
454
457
 
455
458
  return PipelineExecutionV2StreamedResponse(stream=resp)
456
459
 
460
+ def get_projects(
461
+ self,
462
+ correlation_id: Optional[str] = None,
463
+ api_version: str = ApiVersion.V1.value,
464
+ ) -> List[ProjectItem]:
465
+ """
466
+ Retrieve a list of all projects accessible to the authenticated user.
467
+
468
+ This method fetches comprehensive information about all projects that the
469
+ current user has access to, including project metadata, creation details,
470
+ and status information.
471
+
472
+ Args:
473
+ correlation_id (str, optional): A unique identifier for request tracing
474
+ and logging. If not provided, one will be automatically generated.
475
+ api_version (str, optional): The API version to use for the request.
476
+ Defaults to "v1". Valid versions are defined in ApiVersion enum.
477
+
478
+ Returns:
479
+ List[ProjectItem]: A list of ProjectItem objects containing project
480
+ information. Returns an empty list if no projects are accessible
481
+ or found.
482
+
483
+ Raises:
484
+ ValueError: If the provided api_version is not valid.
485
+ AiriaAPIError: If the API request fails, including cases where:
486
+ - Authentication fails (401)
487
+ - Access is forbidden (403)
488
+ - Server errors (5xx)
489
+
490
+ Example:
491
+ ```python
492
+ from airia import AiriaClient
493
+
494
+ client = AiriaClient(api_key="your_api_key")
495
+
496
+ # Get all accessible projects
497
+ projects = client.get_projects()
498
+
499
+ for project in projects:
500
+ print(f"Project: {project.name}")
501
+ print(f"ID: {project.id}")
502
+ print(f"Description: {project.description}")
503
+ print(f"Created: {project.created_at}")
504
+ print("---")
505
+ ```
506
+
507
+ Note:
508
+ The returned projects are filtered based on the authenticated user's
509
+ permissions. Users will only see projects they have been granted
510
+ access to.
511
+ """
512
+ request_data = self._pre_get_projects(
513
+ correlation_id=correlation_id, api_version=api_version
514
+ )
515
+ resp = self._make_request("GET", request_data)
516
+
517
+ if "items" not in resp or len(resp["items"]) == 0:
518
+ return []
519
+
520
+ return [ProjectItem(**item) for item in resp["items"]]
521
+
457
522
  def get_active_pipelines_ids(
458
523
  self,
524
+ project_id: Optional[str] = None,
459
525
  correlation_id: Optional[str] = None,
460
526
  api_version: str = ApiVersion.V1.value,
461
527
  ) -> List[str]:
462
528
  """
463
529
  Retrieve a list of active pipeline IDs.
464
530
 
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().
531
+ This method fetches the IDs of all active pipelines, optionally filtered by project.
532
+ Active pipelines are those that are currently deployed and available for execution.
468
533
 
469
534
  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.
535
+ project_id (str, optional): The unique identifier of the project to filter
536
+ pipelines by. If not provided, returns active pipelines from all projects
537
+ accessible to the authenticated user.
538
+ correlation_id (str, optional): A unique identifier for request tracing
539
+ and logging. If not provided, one will be automatically generated.
540
+ api_version (str, optional): The API version to use for the request.
541
+ Defaults to "v1". Valid versions are defined in ApiVersion enum.
475
542
 
476
543
  Returns:
477
- List[str]: A list of active pipeline ID strings. Returns an empty list
478
- if no active pipelines are found.
544
+ List[str]: A list of pipeline IDs that are currently active. Returns an
545
+ empty list if no active pipelines are found.
479
546
 
480
547
  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.
548
+ ValueError: If the provided api_version is not valid.
549
+ AiriaAPIError: If the API request fails, including cases where:
550
+ - The project_id doesn't exist (404)
551
+ - Authentication fails (401)
552
+ - Access is forbidden (403)
553
+ - Server errors (5xx)
484
554
 
485
555
  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}")
556
+ ```python
557
+ from airia import AiriaClient
558
+
559
+ client = AiriaClient(api_key="your_api_key")
560
+
561
+ # Get all active pipeline IDs
562
+ pipeline_ids = client.get_active_pipelines_ids()
563
+ print(f"Found {len(pipeline_ids)} active pipelines")
564
+
565
+ # Get active pipeline IDs for a specific project
566
+ project_pipelines = client.get_active_pipelines_ids(
567
+ project_id="your_project_id"
568
+ )
569
+ print(f"Project has {len(project_pipelines)} active pipelines")
570
+ ```
571
+
572
+ Note:
573
+ Only pipelines with active versions are returned. Inactive or archived
574
+ pipelines are not included in the results.
491
575
  """
492
576
  request_data = self._pre_get_active_pipelines_ids(
493
- correlation_id=correlation_id, api_version=api_version
577
+ project_id=project_id,
578
+ correlation_id=correlation_id,
579
+ api_version=api_version,
494
580
  )
495
581
  resp = self._make_request("GET", request_data)
496
582
 
@@ -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",
@@ -75,7 +75,9 @@ class Tool(BaseModel):
75
75
  tool_description: str = Field(alias="toolDescription")
76
76
  purpose: str
77
77
  api_endpoint: str = Field(alias="apiEndpoint")
78
- credentials_definition: CredentialsDefinition = Field(alias="credentialsDefinition")
78
+ credentials_definition: Optional[CredentialsDefinition] = Field(
79
+ alias="credentialsDefinition"
80
+ )
79
81
  headers_definition: List[HeaderDefinition] = Field(alias="headersDefinition")
80
82
  body: str
81
83
  parameters_definition: List[ParameterDefinition] = Field(
@@ -99,7 +101,9 @@ class Model(BaseModel):
99
101
  url: str
100
102
  input_type: str = Field(alias="inputType")
101
103
  provider: str
102
- credentials_definition: CredentialsDefinition = Field(alias="credentialsDefinition")
104
+ credentials_definition: Optional[CredentialsDefinition] = Field(
105
+ alias="credentialsDefinition"
106
+ )
103
107
  deployment_type: str = Field(alias="deploymentType")
104
108
  source_type: str = Field(alias="sourceType")
105
109
  connection_string: Optional[str] = Field(alias="connectionString", default=None)
@@ -110,14 +114,14 @@ class Model(BaseModel):
110
114
  uploaded_container_id: Optional[str] = Field(
111
115
  alias="uploadedContainerId", default=None
112
116
  )
113
- library_model_id: str = Field(alias="libraryModelId")
117
+ library_model_id: Optional[str] = Field(alias="libraryModelId")
114
118
  input_token_price: str = Field(alias="inputTokenPrice")
115
119
  output_token_price: str = Field(alias="outputTokenPrice")
116
120
  token_units: int = Field(alias="tokenUnits")
117
121
  has_tool_support: bool = Field(alias="hasToolSupport")
118
122
  allow_airia_credentials: bool = Field(alias="allowAiriaCredentials")
119
123
  allow_byok_credentials: bool = Field(alias="allowBYOKCredentials")
120
- author: str
124
+ author: Optional[str]
121
125
  price_type: str = Field(alias="priceType")
122
126
 
123
127
 
@@ -133,10 +137,41 @@ class Router(BaseModel):
133
137
  router_config: Dict[str, Dict[str, Any]] = Field(alias="routerConfig")
134
138
 
135
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
+
136
169
  class GetPipelineConfigResponse(BaseModel):
137
170
  metadata: Metadata
138
171
  agent: Agent
139
- 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
+ )
140
175
  prompts: Optional[List[Prompt]] = Field(default_factory=list)
141
176
  tools: Optional[List[Tool]] = Field(default_factory=list)
142
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.7
3
+ Version: 0.1.9
4
4
  Summary: Python SDK for Airia API
5
5
  Author-email: Airia LLC <support@airia.com>
6
6
  License: MIT
@@ -18,10 +18,12 @@ airia/types/api_version.py
18
18
  airia/types/request_data.py
19
19
  airia/types/sse_messages.py
20
20
  airia/types/api/get_pipeline_config.py
21
+ airia/types/api/get_projects.py
21
22
  airia/types/api/pipeline_execution.py
22
23
  airia/utils/sse_parser.py
23
24
  tests/test_anthropic_gateway.py
24
25
  tests/test_execute_pipeline.py
25
26
  tests/test_get_active_pipelines_ids.py
26
27
  tests/test_get_pipeline_config.py
28
+ tests/test_get_projects.py
27
29
  tests/test_openai_gateway.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "airia"
7
- version = "0.1.7"
7
+ version = "0.1.9"
8
8
  description = "Python SDK for Airia API"
9
9
  license = { text = "MIT" }
10
10
  authors = [{ name = "Airia LLC", email = "support@airia.com" }]
@@ -4,7 +4,7 @@ from dotenv import load_dotenv
4
4
 
5
5
  from airia import AiriaAsyncClient, AiriaClient
6
6
 
7
- load_dotenv()
7
+ load_dotenv(override=True)
8
8
 
9
9
 
10
10
  # Fixtures for the sync client
@@ -6,7 +6,7 @@ from dotenv import load_dotenv
6
6
 
7
7
  from airia import AiriaAsyncClient, AiriaClient
8
8
 
9
- load_dotenv()
9
+ load_dotenv(override=True)
10
10
  PYTHON_PIPELINE = "0134da17-c5a5-4730-a576-92f8eaf0926f"
11
11
  GPT_4O_MINI_PIPELINE = "367969f1-7a11-40f0-bab6-c8f901fdb537"
12
12
  USER_INPUT = "test"
@@ -56,6 +56,20 @@ class TestSyncGetActivePipelineIds:
56
56
  assert isinstance(result, list)
57
57
  assert len(result) == 0
58
58
 
59
+ def test_get_active_pipelines_ids_with_custom_project_id(
60
+ self, sync_client: AiriaClient
61
+ ):
62
+ """Test using custom Project ID."""
63
+ with patch.object(sync_client, "_make_request") as mock_request:
64
+ mock_request.return_value = SAMPLE_PIPELINE_IDS
65
+
66
+ result = sync_client.get_active_pipelines_ids(
67
+ project_id="custom-project-id"
68
+ )
69
+
70
+ assert result == ["pipeline-id-1", "pipeline-id-2", "pipeline-id-3"]
71
+ mock_request.assert_called_once()
72
+
59
73
  def test_get_active_pipelines_ids_with_custom_api_version(
60
74
  self, sync_client: AiriaClient
61
75
  ):
@@ -104,18 +118,6 @@ class TestSyncGetActivePipelineIds:
104
118
  assert "Pipelines not found" in str(exc_info.value)
105
119
  assert exc_info.value.status_code == 404
106
120
 
107
- def test_get_active_pipelines_ids_network_error(self, sync_client: AiriaClient):
108
- """Test error handling for network errors."""
109
- with patch.object(sync_client, "_make_request") as mock_request:
110
- mock_request.side_effect = AiriaAPIError(
111
- status_code=500, message="Internal server error"
112
- )
113
-
114
- with pytest.raises(AiriaAPIError) as exc_info:
115
- sync_client.get_active_pipelines_ids()
116
-
117
- assert exc_info.value.status_code == 500
118
-
119
121
 
120
122
  class TestAsyncGetActivePipelineIds:
121
123
  """Test cases for asynchronous get_active_pipelines_ids method."""
@@ -166,6 +168,21 @@ class TestAsyncGetActivePipelineIds:
166
168
  assert result == ["pipeline-id-1", "pipeline-id-2", "pipeline-id-3"]
167
169
  mock_request.assert_called_once()
168
170
 
171
+ @pytest.mark.asyncio
172
+ async def test_get_active_pipelines_ids_with_custom_project_id(
173
+ self, async_client: AiriaClient
174
+ ):
175
+ """Test using custom Project ID."""
176
+ with patch.object(async_client, "_make_request") as mock_request:
177
+ mock_request.return_value = SAMPLE_PIPELINE_IDS
178
+
179
+ result = await async_client.get_active_pipelines_ids(
180
+ project_id="custom-project-id"
181
+ )
182
+
183
+ assert result == ["pipeline-id-1", "pipeline-id-2", "pipeline-id-3"]
184
+ mock_request.assert_called_once()
185
+
169
186
  @pytest.mark.asyncio
170
187
  async def test_get_active_pipelines_ids_invalid_api_version(
171
188
  self, async_client: AiriaAsyncClient
@@ -8,7 +8,7 @@ from airia import AiriaAPIError, AiriaAsyncClient, AiriaClient
8
8
  from airia.types import GetPipelineConfigResponse
9
9
 
10
10
  # Load environment variables for testing
11
- load_dotenv()
11
+ load_dotenv(override=True)
12
12
  PYTHON_PIPELINE = "0134da17-c5a5-4730-a576-92f8eaf0926f"
13
13
 
14
14
 
@@ -0,0 +1,203 @@
1
+ from unittest.mock import patch
2
+
3
+ import pytest
4
+ import pytest_asyncio
5
+ from dotenv import load_dotenv
6
+
7
+ from airia import AiriaAsyncClient, AiriaClient
8
+ from airia.exceptions import AiriaAPIError
9
+
10
+ # Load environment variables from .env file
11
+ load_dotenv(override=True)
12
+
13
+ # Test constants
14
+ SAMPLE_PROJECTS = {
15
+ "items": [
16
+ {
17
+ "tenantId": "2ce49ae0-c3ff-421a-91b7-830d0c73b348",
18
+ "createdAt": "2025-01-22T20:00:19.8504590Z",
19
+ "requireClassification": False,
20
+ "budgetAmount": None,
21
+ "budgetPeriod": None,
22
+ "budgetAlert": None,
23
+ "budgetStop": False,
24
+ "usedBudgetAmount": None,
25
+ "resumeEndsAt": None,
26
+ "updatedAt": "2025-01-22T20:00:19.8509410Z",
27
+ "pipelines": [
28
+ {
29
+ "id": "940275e2-7eae-4bc9-9830-4ef595727b87",
30
+ "name": "Cloudkit Records",
31
+ },
32
+ {"id": "b8c24cc9-39de-4e76-b7dd-debac381d1a8", "name": "RAG_test"},
33
+ {
34
+ "id": "17554b31-2370-4d2d-91de-4fe4805b9ef9",
35
+ "name": "Omni Agent - Test Agent to be Described",
36
+ },
37
+ ],
38
+ "models": None,
39
+ "dataSources": [],
40
+ "prompts": None,
41
+ "apiKeys": None,
42
+ "memories": None,
43
+ "projectIcon": "https://airiaimagesprod.blob.core.windows.net/airia-default/8a8f75f5-bd6c-470b-bf23-e82c58d81912?sv=2025-05-05&spr=https&st=2025-06-17T20%3A23%3A39Z&se=2025-06-18T20%3A23%3A39Z&sr=b&sp=r&sig=TE7B5sgtCp%2BMpeQizbuhZShOXRcGPol%2BGitQdKGDBKg%3D",
44
+ "projectIconId": "8a8f75f5-bd6c-470b-bf23-e82c58d81912",
45
+ "description": "Agents Developed by AI Core Team",
46
+ "projectType": "Standard",
47
+ "classifications": None,
48
+ "id": "01948f99-f78a-7415-a187-b250c6e04458",
49
+ "name": "AI Core",
50
+ }
51
+ ],
52
+ "totalCount": 1,
53
+ }
54
+
55
+
56
+ @pytest.fixture
57
+ def sync_client():
58
+ return AiriaClient(log_requests=True)
59
+
60
+
61
+ @pytest_asyncio.fixture
62
+ async def async_client():
63
+ return AiriaAsyncClient(log_requests=True)
64
+
65
+
66
+ class TestSyncGetProjectsIds:
67
+ """Test cases for synchronous get_projects method."""
68
+
69
+ def test_get_projects_success(self, sync_client: AiriaClient):
70
+ """Test successful retrieval of projectss."""
71
+ with patch.object(sync_client, "_make_request") as mock_request:
72
+ mock_request.return_value = SAMPLE_PROJECTS
73
+
74
+ result = sync_client.get_projects()
75
+
76
+ assert isinstance(result, list)
77
+ assert len(result) == 1
78
+ mock_request.assert_called_once()
79
+
80
+ def test_get_projects_empty_dict(self, sync_client: AiriaClient):
81
+ """Test handling of empty project response."""
82
+ with patch.object(sync_client, "_make_request") as mock_request:
83
+ mock_request.return_value = {}
84
+
85
+ result = sync_client.get_projects()
86
+
87
+ assert result == []
88
+ assert isinstance(result, list)
89
+ assert len(result) == 0
90
+
91
+ def test_get_projects_with_custom_api_version(self, sync_client: AiriaClient):
92
+ """Test using custom API version."""
93
+ with patch.object(sync_client, "_make_request") as mock_request:
94
+ mock_request.return_value = SAMPLE_PROJECTS
95
+
96
+ result = sync_client.get_projects(api_version="v1")
97
+
98
+ assert isinstance(result, list)
99
+ assert len(result) == 1
100
+ mock_request.assert_called_once()
101
+
102
+ def test_get_projects_with_correlation_id(self, sync_client: AiriaClient):
103
+ """Test using custom correlation ID."""
104
+ custom_correlation_id = "test-correlation-123"
105
+
106
+ with patch.object(sync_client, "_make_request") as mock_request:
107
+ mock_request.return_value = SAMPLE_PROJECTS
108
+
109
+ result = sync_client.get_projects(correlation_id=custom_correlation_id)
110
+
111
+ assert isinstance(result, list)
112
+ assert len(result) == 1
113
+ mock_request.assert_called_once()
114
+
115
+ def test_get_projects_invalid_api_version(
116
+ self, sync_client: AiriaClient
117
+ ):
118
+ """Test error handling for invalid API version."""
119
+ with pytest.raises(ValueError, match="Invalid API version"):
120
+ sync_client.get_projects(api_version="invalid")
121
+
122
+ def test_get_projects_api_error(self, sync_client: AiriaClient):
123
+ """Test error handling for API errors."""
124
+ with patch.object(sync_client, "_make_request") as mock_request:
125
+ mock_request.side_effect = AiriaAPIError(
126
+ status_code=404, message="Projects not found"
127
+ )
128
+
129
+ with pytest.raises(AiriaAPIError) as exc_info:
130
+ sync_client.get_projects()
131
+
132
+ assert "Projects not found" in str(exc_info.value)
133
+ assert exc_info.value.status_code == 404
134
+
135
+
136
+ class TestAsyncGetActivePipelineIds:
137
+ """Test cases for asynchronous get_active_pipelines_ids method."""
138
+
139
+ @pytest.mark.asyncio
140
+ async def test_get_projects_success(self, async_client: AiriaAsyncClient):
141
+ """Test successful retrieval of projects."""
142
+ with patch.object(async_client, "_make_request") as mock_request:
143
+ mock_request.return_value = SAMPLE_PROJECTS
144
+
145
+ result = await async_client.get_projects()
146
+
147
+ assert isinstance(result, list)
148
+ assert len(result) == 1
149
+ mock_request.assert_called_once()
150
+
151
+ @pytest.mark.asyncio
152
+ async def test_get_projects_empty_list(self, async_client: AiriaAsyncClient):
153
+ """Test handling of empty project list response."""
154
+ with patch.object(async_client, "_make_request") as mock_request:
155
+ mock_request.return_value = []
156
+
157
+ result = await async_client.get_projects()
158
+
159
+ assert result == []
160
+ assert isinstance(result, list)
161
+ assert len(result) == 0
162
+
163
+ @pytest.mark.asyncio
164
+ async def test_get_projects_with_custom_params(
165
+ self, async_client: AiriaAsyncClient
166
+ ):
167
+ """Test using custom API version and correlation ID."""
168
+ custom_correlation_id = "async-test-correlation-456"
169
+
170
+ with patch.object(async_client, "_make_request") as mock_request:
171
+ mock_request.return_value = SAMPLE_PROJECTS
172
+
173
+ result = await async_client.get_projects(
174
+ api_version="v1", correlation_id=custom_correlation_id
175
+ )
176
+
177
+ assert isinstance(result, list)
178
+ assert len(result) == 1
179
+ mock_request.assert_called_once()
180
+
181
+ @pytest.mark.asyncio
182
+ async def test_get_projects_invalid_api_version(
183
+ self, async_client: AiriaAsyncClient
184
+ ):
185
+ """Test error handling for invalid API version."""
186
+ with pytest.raises(ValueError, match="Invalid API version"):
187
+ await async_client.get_projects(api_version="invalid")
188
+
189
+ @pytest.mark.asyncio
190
+ async def test_get_projects_api_error(
191
+ self, async_client: AiriaAsyncClient
192
+ ):
193
+ """Test error handling for API errors."""
194
+ with patch.object(async_client, "_make_request") as mock_request:
195
+ mock_request.side_effect = AiriaAPIError(
196
+ status_code=403, message="Access forbidden"
197
+ )
198
+
199
+ with pytest.raises(AiriaAPIError) as exc_info:
200
+ await async_client.get_projects()
201
+
202
+ assert "Access forbidden" in str(exc_info.value)
203
+ assert exc_info.value.status_code == 403
@@ -4,7 +4,7 @@ from dotenv import load_dotenv
4
4
 
5
5
  from airia import AiriaAsyncClient, AiriaClient
6
6
 
7
- load_dotenv()
7
+ load_dotenv(override=True)
8
8
 
9
9
 
10
10
  # Fixtures for the sync client
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