pygeai 0.6.0b11__py3-none-any.whl → 0.6.0b12__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.
Files changed (132) hide show
  1. pygeai/_docs/source/content/ai_lab/cli.rst +4 -4
  2. pygeai/_docs/source/content/ai_lab/models.rst +169 -35
  3. pygeai/_docs/source/content/ai_lab/runner.rst +2 -2
  4. pygeai/_docs/source/content/ai_lab/spec.rst +9 -9
  5. pygeai/_docs/source/content/ai_lab/usage.rst +34 -34
  6. pygeai/_docs/source/content/ai_lab.rst +1 -1
  7. pygeai/_docs/source/content/analytics.rst +598 -0
  8. pygeai/_docs/source/content/api_reference/chat.rst +428 -2
  9. pygeai/_docs/source/content/api_reference/embeddings.rst +1 -1
  10. pygeai/_docs/source/content/api_reference/project.rst +184 -0
  11. pygeai/_docs/source/content/api_reference/rag.rst +2 -2
  12. pygeai/_docs/source/content/authentication.rst +295 -0
  13. pygeai/_docs/source/content/cli.rst +79 -2
  14. pygeai/_docs/source/content/debugger.rst +1 -1
  15. pygeai/_docs/source/content/migration.rst +19 -2
  16. pygeai/_docs/source/index.rst +2 -0
  17. pygeai/_docs/source/pygeai.analytics.rst +53 -0
  18. pygeai/_docs/source/pygeai.cli.commands.rst +8 -0
  19. pygeai/_docs/source/pygeai.rst +1 -0
  20. pygeai/_docs/source/pygeai.tests.analytics.rst +45 -0
  21. pygeai/_docs/source/pygeai.tests.auth.rst +8 -0
  22. pygeai/_docs/source/pygeai.tests.rst +1 -1
  23. pygeai/analytics/__init__.py +0 -0
  24. pygeai/analytics/clients.py +505 -0
  25. pygeai/analytics/endpoints.py +35 -0
  26. pygeai/analytics/managers.py +606 -0
  27. pygeai/analytics/mappers.py +207 -0
  28. pygeai/analytics/responses.py +240 -0
  29. pygeai/cli/commands/analytics.py +525 -0
  30. pygeai/cli/commands/base.py +16 -0
  31. pygeai/cli/commands/common.py +28 -24
  32. pygeai/cli/commands/migrate.py +75 -6
  33. pygeai/cli/commands/organization.py +265 -0
  34. pygeai/cli/commands/validators.py +144 -1
  35. pygeai/cli/error_handler.py +41 -6
  36. pygeai/cli/geai.py +99 -16
  37. pygeai/cli/parsers.py +75 -31
  38. pygeai/cli/texts/help.py +75 -6
  39. pygeai/core/base/clients.py +18 -4
  40. pygeai/core/base/session.py +46 -7
  41. pygeai/core/common/config.py +25 -2
  42. pygeai/core/common/exceptions.py +64 -1
  43. pygeai/core/services/rest.py +20 -2
  44. pygeai/evaluation/clients.py +5 -3
  45. pygeai/lab/agents/clients.py +3 -3
  46. pygeai/lab/agents/endpoints.py +2 -2
  47. pygeai/lab/agents/mappers.py +50 -2
  48. pygeai/lab/clients.py +5 -2
  49. pygeai/lab/managers.py +7 -9
  50. pygeai/lab/models.py +70 -2
  51. pygeai/lab/tools/clients.py +1 -59
  52. pygeai/migration/__init__.py +3 -1
  53. pygeai/migration/strategies.py +72 -3
  54. pygeai/organization/clients.py +110 -1
  55. pygeai/organization/endpoints.py +11 -7
  56. pygeai/organization/managers.py +134 -2
  57. pygeai/organization/mappers.py +28 -2
  58. pygeai/organization/responses.py +11 -1
  59. pygeai/tests/analytics/__init__.py +0 -0
  60. pygeai/tests/analytics/test_clients.py +86 -0
  61. pygeai/tests/analytics/test_managers.py +94 -0
  62. pygeai/tests/analytics/test_mappers.py +84 -0
  63. pygeai/tests/analytics/test_responses.py +73 -0
  64. pygeai/tests/auth/test_oauth.py +172 -0
  65. pygeai/tests/cli/commands/test_migrate.py +14 -1
  66. pygeai/tests/cli/commands/test_organization.py +69 -1
  67. pygeai/tests/cli/test_error_handler.py +4 -4
  68. pygeai/tests/cli/test_geai_driver.py +1 -1
  69. pygeai/tests/lab/agents/test_mappers.py +128 -1
  70. pygeai/tests/lab/test_models.py +2 -0
  71. pygeai/tests/lab/tools/test_clients.py +2 -31
  72. pygeai/tests/organization/test_clients.py +180 -1
  73. pygeai/tests/organization/test_managers.py +40 -0
  74. pygeai/tests/snippets/analytics/__init__.py +0 -0
  75. pygeai/tests/snippets/analytics/get_agent_usage_per_user.py +16 -0
  76. pygeai/tests/snippets/analytics/get_agents_created_and_modified.py +11 -0
  77. pygeai/tests/snippets/analytics/get_average_cost_per_request.py +10 -0
  78. pygeai/tests/snippets/analytics/get_overall_error_rate.py +10 -0
  79. pygeai/tests/snippets/analytics/get_top_10_agents_by_requests.py +12 -0
  80. pygeai/tests/snippets/analytics/get_total_active_users.py +10 -0
  81. pygeai/tests/snippets/analytics/get_total_cost.py +10 -0
  82. pygeai/tests/snippets/analytics/get_total_requests_per_day.py +12 -0
  83. pygeai/tests/snippets/analytics/get_total_tokens.py +12 -0
  84. pygeai/tests/snippets/chat/get_response_complete_example.py +67 -0
  85. pygeai/tests/snippets/chat/get_response_with_instructions.py +19 -0
  86. pygeai/tests/snippets/chat/get_response_with_metadata.py +24 -0
  87. pygeai/tests/snippets/chat/get_response_with_parallel_tools.py +58 -0
  88. pygeai/tests/snippets/chat/get_response_with_reasoning.py +21 -0
  89. pygeai/tests/snippets/chat/get_response_with_store.py +38 -0
  90. pygeai/tests/snippets/chat/get_response_with_truncation.py +24 -0
  91. pygeai/tests/snippets/lab/agents/create_agent_with_permissions.py +39 -0
  92. pygeai/tests/snippets/lab/agents/create_agent_with_properties.py +46 -0
  93. pygeai/tests/snippets/lab/agents/get_agent_with_new_fields.py +62 -0
  94. pygeai/tests/snippets/lab/agents/update_agent_properties.py +50 -0
  95. pygeai/tests/snippets/organization/add_project_member.py +10 -0
  96. pygeai/tests/snippets/organization/add_project_member_batch.py +44 -0
  97. {pygeai-0.6.0b11.dist-info → pygeai-0.6.0b12.dist-info}/METADATA +1 -1
  98. {pygeai-0.6.0b11.dist-info → pygeai-0.6.0b12.dist-info}/RECORD +102 -92
  99. pygeai/_docs/source/pygeai.tests.snippets.assistants.data_analyst.rst +0 -37
  100. pygeai/_docs/source/pygeai.tests.snippets.assistants.rag.rst +0 -85
  101. pygeai/_docs/source/pygeai.tests.snippets.assistants.rst +0 -78
  102. pygeai/_docs/source/pygeai.tests.snippets.auth.rst +0 -10
  103. pygeai/_docs/source/pygeai.tests.snippets.chat.rst +0 -125
  104. pygeai/_docs/source/pygeai.tests.snippets.dbg.rst +0 -45
  105. pygeai/_docs/source/pygeai.tests.snippets.embeddings.rst +0 -61
  106. pygeai/_docs/source/pygeai.tests.snippets.evaluation.dataset.rst +0 -197
  107. pygeai/_docs/source/pygeai.tests.snippets.evaluation.plan.rst +0 -133
  108. pygeai/_docs/source/pygeai.tests.snippets.evaluation.result.rst +0 -37
  109. pygeai/_docs/source/pygeai.tests.snippets.evaluation.rst +0 -20
  110. pygeai/_docs/source/pygeai.tests.snippets.extras.rst +0 -37
  111. pygeai/_docs/source/pygeai.tests.snippets.files.rst +0 -53
  112. pygeai/_docs/source/pygeai.tests.snippets.gam.rst +0 -21
  113. pygeai/_docs/source/pygeai.tests.snippets.lab.agents.rst +0 -93
  114. pygeai/_docs/source/pygeai.tests.snippets.lab.processes.jobs.rst +0 -21
  115. pygeai/_docs/source/pygeai.tests.snippets.lab.processes.kbs.rst +0 -45
  116. pygeai/_docs/source/pygeai.tests.snippets.lab.processes.rst +0 -46
  117. pygeai/_docs/source/pygeai.tests.snippets.lab.rst +0 -82
  118. pygeai/_docs/source/pygeai.tests.snippets.lab.samples.rst +0 -21
  119. pygeai/_docs/source/pygeai.tests.snippets.lab.strategies.rst +0 -45
  120. pygeai/_docs/source/pygeai.tests.snippets.lab.tools.rst +0 -85
  121. pygeai/_docs/source/pygeai.tests.snippets.lab.use_cases.rst +0 -117
  122. pygeai/_docs/source/pygeai.tests.snippets.migrate.rst +0 -10
  123. pygeai/_docs/source/pygeai.tests.snippets.organization.rst +0 -109
  124. pygeai/_docs/source/pygeai.tests.snippets.rag.rst +0 -85
  125. pygeai/_docs/source/pygeai.tests.snippets.rerank.rst +0 -21
  126. pygeai/_docs/source/pygeai.tests.snippets.rst +0 -32
  127. pygeai/_docs/source/pygeai.tests.snippets.secrets.rst +0 -10
  128. pygeai/_docs/source/pygeai.tests.snippets.usage_limit.rst +0 -77
  129. {pygeai-0.6.0b11.dist-info → pygeai-0.6.0b12.dist-info}/WHEEL +0 -0
  130. {pygeai-0.6.0b11.dist-info → pygeai-0.6.0b12.dist-info}/entry_points.txt +0 -0
  131. {pygeai-0.6.0b11.dist-info → pygeai-0.6.0b12.dist-info}/licenses/LICENSE +0 -0
  132. {pygeai-0.6.0b11.dist-info → pygeai-0.6.0b12.dist-info}/top_level.txt +0 -0
@@ -6,7 +6,7 @@ from pygeai.core.utils.validators import validate_status_code
6
6
  from pygeai.core.utils.parsers import parse_json_response
7
7
  from pygeai.lab.constants import VALID_SCOPES, VALID_ACCESS_SCOPES, VALID_REPORT_EVENTS
8
8
  from pygeai.lab.tools.endpoints import CREATE_TOOL_V2, LIST_TOOLS_V2, GET_TOOL_V2, UPDATE_TOOL_V2, UPSERT_TOOL_V2, \
9
- PUBLISH_TOOL_REVISION_V2, GET_PARAMETER_V2, SET_PARAMETER_V2, DELETE_TOOL_V2, EXPORT_TOOL_V4
9
+ PUBLISH_TOOL_REVISION_V2, GET_PARAMETER_V2, SET_PARAMETER_V2, DELETE_TOOL_V2
10
10
  from pygeai.lab.clients import AILabClient
11
11
 
12
12
 
@@ -103,23 +103,14 @@ class ToolClient(AILabClient):
103
103
  if automatic_publish:
104
104
  endpoint = f"{endpoint}?automaticPublish=true"
105
105
 
106
- headers = {
107
- "Accept": "application/json",
108
- "Content-Type": "application/json",
109
- "Authorization": self.api_service.token,
110
- "ProjectId": self.project_id
111
- }
112
-
113
106
  response = self.api_service.post(
114
107
  endpoint=endpoint,
115
- headers=headers,
116
108
  data=data
117
109
  )
118
110
 
119
111
  validate_status_code(response)
120
112
  return parse_json_response(response, f"create tool for project {self.project_id}")
121
113
 
122
-
123
114
  def list_tools(
124
115
  self,
125
116
  id: str = "",
@@ -141,10 +132,6 @@ class ToolClient(AILabClient):
141
132
  :return: dict or str - JSON response containing the list of tools if successful, otherwise the raw response text.
142
133
  """
143
134
  endpoint = LIST_TOOLS_V2
144
- headers = {
145
- "Authorization": self.api_service.token,
146
- "ProjectId": self.project_id
147
- }
148
135
 
149
136
  if scope and scope not in VALID_SCOPES:
150
137
  raise ValueError(f"Scope must be one of {', '.join(VALID_SCOPES)}.")
@@ -153,7 +140,6 @@ class ToolClient(AILabClient):
153
140
 
154
141
  response = self.api_service.get(
155
142
  endpoint=endpoint,
156
- headers=headers,
157
143
  params={
158
144
  "id": id,
159
145
  "count": count,
@@ -166,7 +152,6 @@ class ToolClient(AILabClient):
166
152
  validate_status_code(response)
167
153
  return parse_json_response(response, f"list tools for project {self.project_id}")
168
154
 
169
-
170
155
  def get_tool(
171
156
  self,
172
157
  tool_id: str,
@@ -184,16 +169,10 @@ class ToolClient(AILabClient):
184
169
  :return: dict or str - JSON response containing the tool details if successful, otherwise the raw response text.
185
170
  """
186
171
  endpoint = GET_TOOL_V2.format(toolId=tool_id)
187
- headers = {
188
- "Authorization": self.api_service.token,
189
- "ProjectId": self.project_id
190
- }
191
-
192
172
  logger.debug(f"Retrieving detail of tool with ID: {tool_id}")
193
173
 
194
174
  response = self.api_service.get(
195
175
  endpoint=endpoint,
196
- headers=headers,
197
176
  params={
198
177
  "revision": revision,
199
178
  "version": version,
@@ -222,10 +201,6 @@ class ToolClient(AILabClient):
222
201
  raise ValueError("Either tool_id or tool_name must be provided.")
223
202
 
224
203
  endpoint = DELETE_TOOL_V2.format(toolId=tool_id if tool_id else tool_name)
225
- headers = {
226
- "Authorization": self.api_service.token,
227
- "ProjectId": self.project_id
228
- }
229
204
 
230
205
  if tool_id:
231
206
  logger.debug(f"Deleting tool with ID {tool_id}")
@@ -234,7 +209,6 @@ class ToolClient(AILabClient):
234
209
 
235
210
  response = self.api_service.delete(
236
211
  endpoint=endpoint,
237
- headers=headers
238
212
  )
239
213
  validate_status_code(response)
240
214
 
@@ -345,19 +319,13 @@ class ToolClient(AILabClient):
345
319
  if automatic_publish:
346
320
  endpoint = f"{endpoint}?automaticPublish=true"
347
321
 
348
- headers = {
349
- "Authorization": self.api_service.token,
350
- "ProjectId": self.project_id
351
- }
352
322
  response = self.api_service.put(
353
323
  endpoint=endpoint,
354
- headers=headers,
355
324
  data=data
356
325
  )
357
326
  validate_status_code(response)
358
327
  return parse_json_response(response, f"update tool {tool_id} in project {self.project_id}")
359
328
 
360
-
361
329
  def publish_tool_revision(
362
330
  self,
363
331
  tool_id: str,
@@ -371,16 +339,10 @@ class ToolClient(AILabClient):
371
339
  :return: dict or str - JSON response containing the result of the publish operation if successful, otherwise the raw response text.
372
340
  """
373
341
  endpoint = PUBLISH_TOOL_REVISION_V2.format(toolId=tool_id)
374
- headers = {
375
- "Authorization": self.api_service.token,
376
- "ProjectId": self.project_id
377
- }
378
-
379
342
  logger.debug(f"Publishing revision {revision} for tool with ID {tool_id}")
380
343
 
381
344
  response = self.api_service.post(
382
345
  endpoint=endpoint,
383
- headers=headers,
384
346
  data={
385
347
  "revision": revision,
386
348
  }
@@ -417,14 +379,8 @@ class ToolClient(AILabClient):
417
379
  logger.debug(f"Retrieving parameter for tool with name '{tool_public_name}'")
418
380
 
419
381
  endpoint = GET_PARAMETER_V2.format(toolPublicName=tool_public_name) if tool_public_name else GET_PARAMETER_V2.format(toolPublicName=tool_id)
420
- headers = {
421
- "Authorization": self.api_service.token,
422
- "ProjectId": self.project_id
423
- }
424
-
425
382
  response = self.api_service.get(
426
383
  endpoint=endpoint,
427
- headers=headers,
428
384
  params={
429
385
  "revision": revision,
430
386
  "version": version,
@@ -435,7 +391,6 @@ class ToolClient(AILabClient):
435
391
  tool_identifier = tool_id or tool_public_name
436
392
  return parse_json_response(response, f"retrieve parameters for tool {tool_identifier} in project {self.project_id}")
437
393
 
438
-
439
394
  def set_parameter(
440
395
  self,
441
396
  tool_id: str = None,
@@ -459,12 +414,6 @@ class ToolClient(AILabClient):
459
414
  raise ValueError("Parameters list must be provided and non-empty.")
460
415
 
461
416
  endpoint = SET_PARAMETER_V2.format(toolPublicName=tool_public_name) if tool_public_name else SET_PARAMETER_V2.format(toolPublicName=tool_id)
462
- headers = {
463
- "Authorization": self.api_service.token,
464
- "ProjectId": self.project_id,
465
- "Content-Type": "application/json",
466
- "Accept": "application/json"
467
- }
468
417
 
469
418
  data = {
470
419
  "parameterDefinition": {
@@ -479,7 +428,6 @@ class ToolClient(AILabClient):
479
428
 
480
429
  response = self.api_service.post(
481
430
  endpoint=endpoint,
482
- headers=headers,
483
431
  data=data
484
432
  )
485
433
  if response.status_code != 204:
@@ -506,16 +454,10 @@ class ToolClient(AILabClient):
506
454
  raise MissingRequirementException("tool_id must be specified in order to retrieve the tool")
507
455
 
508
456
  endpoint = EXPORT_TOOL_V4.format(toolId=tool_id)
509
- headers = {
510
- "Authorization": self.api_service.token,
511
- "ProjectId": self.project_id
512
- }
513
-
514
457
  logger.debug(f"Exporting tool with ID {tool_id}")
515
458
 
516
459
  response = self.api_service.get(
517
460
  endpoint=endpoint,
518
- headers=headers,
519
461
  )
520
462
  validate_status_code(response)
521
463
  return parse_json_response(response, f"export tool {tool_id} for project {self.project_id}")
@@ -7,7 +7,8 @@ from pygeai.migration.strategies import (
7
7
  TaskMigrationStrategy,
8
8
  UsageLimitMigrationStrategy,
9
9
  RAGAssistantMigrationStrategy,
10
- FileMigrationStrategy
10
+ FileMigrationStrategy,
11
+ SecretMigrationStrategy
11
12
  )
12
13
  from pygeai.migration.tools import (
13
14
  MigrationTool,
@@ -25,6 +26,7 @@ __all__ = [
25
26
  "UsageLimitMigrationStrategy",
26
27
  "RAGAssistantMigrationStrategy",
27
28
  "FileMigrationStrategy",
29
+ "SecretMigrationStrategy",
28
30
  "MigrationTool",
29
31
  "MigrationPlan",
30
32
  "MigrationOrchestrator"
@@ -3,6 +3,7 @@ from abc import ABC, abstractmethod
3
3
  from typing import Optional
4
4
 
5
5
  from pygeai import logger
6
+ from pygeai.core.files.responses import UploadFileResponse
6
7
  from pygeai.core.models import Project, UsageLimit
7
8
  from pygeai.core.base.responses import ErrorListResponse
8
9
  from pygeai.core.utils.console import Console
@@ -14,6 +15,7 @@ from pygeai.organization.limits.managers import UsageLimitManager
14
15
  from pygeai.organization.managers import OrganizationManager
15
16
  from pygeai.assistant.managers import AssistantManager
16
17
  from pygeai.assistant.rag.models import RAGAssistant
18
+ from pygeai.core.secrets.clients import SecretClient
17
19
 
18
20
 
19
21
  class MigrationStrategy(ABC):
@@ -507,10 +509,10 @@ class FileMigrationStrategy(MigrationStrategy):
507
509
 
508
510
  :raises ValueError: If file retrieval or upload fails
509
511
  """
510
- new_file = self._migrate_file()
512
+ upload_file_response = self._migrate_file()
511
513
  logger.info(f"Successfully migrated file {self.file_id}")
512
514
 
513
- def _migrate_file(self) -> File:
515
+ def _migrate_file(self) -> UploadFileResponse:
514
516
  """
515
517
  Retrieve file from source and upload to destination.
516
518
 
@@ -528,7 +530,74 @@ class FileMigrationStrategy(MigrationStrategy):
528
530
  content=file_content,
529
531
  folder=None
530
532
  )
531
- return upload_response.file
533
+ return upload_response
532
534
  except Exception as e:
533
535
  logger.error(f"File migration failed: {e}")
534
536
  raise ValueError(f"File migration failed: {e}") from e
537
+
538
+
539
+ class SecretMigrationStrategy(MigrationStrategy):
540
+ """
541
+ Migrate secret from a GEAI project.
542
+ The target project can be in another organization or the same one; in the same instance or another.
543
+ """
544
+
545
+ def __init__(
546
+ self,
547
+ from_api_key: str,
548
+ from_instance: str,
549
+ secret_id: str,
550
+ to_api_key: Optional[str] = None,
551
+ to_instance: Optional[str] = None
552
+ ):
553
+ super().__init__(from_api_key, from_instance, to_api_key, to_instance)
554
+ self.secret_id = secret_id
555
+ self._source_client = SecretClient(
556
+ api_key=self.from_api_key,
557
+ base_url=self.from_instance
558
+ )
559
+ self._destination_client = SecretClient(
560
+ api_key=self.to_api_key,
561
+ base_url=self.to_instance
562
+ )
563
+
564
+ def get_display_info(self) -> str:
565
+ return f"secret {self.secret_id}"
566
+
567
+ def migrate(self):
568
+ """
569
+ Execute the secret migration from source to destination project.
570
+
571
+ :raises ValueError: If secret retrieval or creation fails
572
+ """
573
+ new_secret = self._migrate_secret()
574
+ logger.info(f"Successfully migrated secret {self.secret_id}")
575
+
576
+ def _migrate_secret(self) -> dict:
577
+ """
578
+ Retrieve secret from source and create in destination.
579
+
580
+ :return: The newly created secret
581
+ :raises ValueError: If migration fails
582
+ """
583
+ try:
584
+ source_secret = self._source_client.get_secret(secret_id=self.secret_id)
585
+ if not isinstance(source_secret, dict):
586
+ raise ValueError(f"Unable to retrieve secret {self.secret_id}")
587
+
588
+ secret_name = source_secret.get("name")
589
+ secret_string = source_secret.get("secretString")
590
+ secret_description = source_secret.get("description")
591
+
592
+ if not secret_name or not secret_string:
593
+ raise ValueError(f"Secret {self.secret_id} missing required fields (name or secretString)")
594
+
595
+ new_secret = self._destination_client.create_secret(
596
+ name=secret_name,
597
+ secret_string=secret_string,
598
+ description=secret_description
599
+ )
600
+ return new_secret
601
+ except Exception as e:
602
+ logger.error(f"Secret migration failed: {e}")
603
+ raise ValueError(f"Secret migration failed: {e}") from e
@@ -4,7 +4,8 @@ from pygeai.core.common.exceptions import InvalidAPIResponseException
4
4
  from pygeai.organization.endpoints import GET_ASSISTANT_LIST_V1, GET_PROJECT_LIST_V1, GET_PROJECT_V1, \
5
5
  CREATE_PROJECT_V1, UPDATE_PROJECT_V1, DELETE_PROJECT_V1, GET_PROJECT_TOKENS_V1, GET_REQUEST_DATA_V1, \
6
6
  GET_MEMBERSHIPS_V2, GET_PROJECT_MEMBERSHIPS_V2, GET_PROJECT_ROLES_V2, GET_PROJECT_MEMBERS_V2, \
7
- GET_ORGANIZATION_MEMBERS_V2
7
+ GET_ORGANIZATION_MEMBERS_V2, ADD_PROJECT_MEMBER_V2, CREATE_ORGANIZATION_V2, GET_ORGANIZATION_LIST_V2, \
8
+ DELETE_ORGANIZATION_V2
8
9
  from pygeai.core.base.clients import BaseClient
9
10
  from pygeai.core.utils.validators import validate_status_code
10
11
  from pygeai.core.utils.parsers import parse_json_response
@@ -311,3 +312,111 @@ class OrganizationClient(BaseClient):
311
312
  response = self.api_service.get(endpoint=GET_ORGANIZATION_MEMBERS_V2, params={"organizationId": organization_id})
312
313
  validate_status_code(response)
313
314
  return parse_json_response(response, "get organization members for organization", organization_id=organization_id)
315
+
316
+ def add_project_member(
317
+ self,
318
+ project_id: str,
319
+ user_email: str,
320
+ roles: list
321
+ ) -> dict:
322
+ """
323
+ Adds a user to a project by sending an invitation with the specified roles.
324
+
325
+ :param project_id: str - The unique identifier (GUID) of the project (required). Will be sent as header.
326
+ :param user_email: str - The email address of the user to invite (required).
327
+ :param roles: list - A list of role names or GUIDs to assign to the user (required).
328
+ :return: dict - The API response confirming the invitation was sent, in JSON format.
329
+ """
330
+ headers = {"project-id": project_id}
331
+ data = {
332
+ "userEmail": user_email,
333
+ "roles": roles
334
+ }
335
+ response = self.api_service.post(
336
+ endpoint=ADD_PROJECT_MEMBER_V2,
337
+ data=data,
338
+ headers=headers
339
+ )
340
+ validate_status_code(response)
341
+ return parse_json_response(response, "add project member", user_email=user_email)
342
+
343
+ def create_organization(
344
+ self,
345
+ name: str,
346
+ administrator_user_email: str
347
+ ) -> dict:
348
+ """
349
+ Creates a new organization with the provided details.
350
+
351
+ This endpoint requires an OAuth access token from the System Administrator role.
352
+
353
+ :param name: str - The name of the new organization (required).
354
+ :param administrator_user_email: str - The email address of the organization administrator (required).
355
+ :return: dict - The API response with details of the created organization in JSON format.
356
+ """
357
+ response = self.api_service.post(
358
+ endpoint=CREATE_ORGANIZATION_V2,
359
+ data={
360
+ "name": name,
361
+ "administratorUserEmail": administrator_user_email
362
+ }
363
+ )
364
+ validate_status_code(response)
365
+ return parse_json_response(response, "create organization with name", name=name)
366
+
367
+ def get_organization_list(
368
+ self,
369
+ start_page: int = None,
370
+ page_size: int = None,
371
+ order_key: str = None,
372
+ order_direction: str = "desc",
373
+ filter_key: str = None,
374
+ filter_value: str = None
375
+ ) -> dict:
376
+ """
377
+ Retrieves a list of organizations based on the specified search criteria.
378
+
379
+ This endpoint requires an OAuth access token from the System Administrator role.
380
+
381
+ :param start_page: int - The page number for pagination (optional).
382
+ :param page_size: int - The number of items per page (optional).
383
+ :param order_key: str - Field for sorting. Only 'name' is supported (optional).
384
+ :param order_direction: str - Sort direction: 'asc' or 'desc' (default is 'desc').
385
+ :param filter_key: str - Field for filtering. Only 'name' is supported (optional).
386
+ :param filter_value: str - Value to filter by (optional).
387
+ :return: dict - The API response containing the list of organizations in JSON format.
388
+ """
389
+ params = {
390
+ "orderDirection": order_direction
391
+ }
392
+ if start_page is not None:
393
+ params["startPage"] = start_page
394
+ if page_size is not None:
395
+ params["pageSize"] = page_size
396
+ if order_key:
397
+ params["orderKey"] = order_key
398
+ if filter_key:
399
+ params["filterKey"] = filter_key
400
+ if filter_value:
401
+ params["filterValue"] = filter_value
402
+
403
+ response = self.api_service.get(endpoint=GET_ORGANIZATION_LIST_V2, params=params)
404
+ validate_status_code(response)
405
+ return parse_json_response(response, "get organization list")
406
+
407
+ def delete_organization(
408
+ self,
409
+ organization_id: str
410
+ ) -> dict:
411
+ """
412
+ Deletes an existing organization using its unique identifier.
413
+
414
+ This endpoint requires an OAuth access token from the System Administrator role.
415
+
416
+ :param organization_id: str - The unique identifier (GUID) of the organization to delete (required).
417
+ :return: dict - The API response confirming the deletion of the organization, in JSON format.
418
+ """
419
+ endpoint = DELETE_ORGANIZATION_V2.format(organizationId=organization_id)
420
+ response = self.api_service.delete(endpoint=endpoint)
421
+ validate_status_code(response)
422
+ return parse_json_response(response, "delete organization with ID", organization_id=organization_id)
@@ -1,13 +1,17 @@
1
1
  GET_ASSISTANT_LIST_V1 = "v1/organization/assistants" # GET - Gets the list of assistants
2
- GET_PROJECT_LIST_V1 = "v1/organization/projects" # GET - Gets the list of projects
3
- GET_PROJECT_V1 = "v1/organization/project/{id}" # GET - Gets project details
4
- CREATE_PROJECT_V1 = "v1/organization/project" # POST - Creates a project
5
- UPDATE_PROJECT_V1 = "v1/organization/project/{id}" # PUT - Updates a project
6
- DELETE_PROJECT_V1 = "v1/organization/project/{id}" # DELETE - Deletes a project
7
- GET_PROJECT_TOKENS_V1 = "v1/organization/project/{id}/tokens" # GET - Gets the list of Tokens for the project
8
- GET_REQUEST_DATA_V1 = "v1/organization/request/export" # GET - Exports request data
2
+ GET_PROJECT_LIST_V1 = "v1/organization/projects" # GET - Gets the list of projects
3
+ GET_PROJECT_V1 = "v1/organization/project/{id}" # GET - Gets project details
4
+ CREATE_PROJECT_V1 = "v1/organization/project" # POST - Creates a project
5
+ UPDATE_PROJECT_V1 = "v1/organization/project/{id}" # PUT - Updates a project
6
+ DELETE_PROJECT_V1 = "v1/organization/project/{id}" # DELETE - Deletes a project
7
+ GET_PROJECT_TOKENS_V1 = "v1/organization/project/{id}/tokens" # GET - Gets the list of Tokens for the project
8
+ GET_REQUEST_DATA_V1 = "v1/organization/request/export" # GET - Exports request data
9
9
  GET_MEMBERSHIPS_V2 = "v2/accessControl/memberships" # GET - Lists Organizations and Projects a user belongs to with their Roles
10
10
  GET_PROJECT_MEMBERSHIPS_V2 = "v2/accessControl/projects/memberships" # GET - Lists Projects and Roles for a user within a specific Organization
11
11
  GET_PROJECT_ROLES_V2 = "v2/accessControl/projects/roles" # GET - Lists all Roles supported by a specific Project
12
12
  GET_PROJECT_MEMBERS_V2 = "v2/accessControl/projects/members" # GET - Lists all members and their Roles for a specific Project
13
13
  GET_ORGANIZATION_MEMBERS_V2 = "v2/accessControl/organizations/members" # GET - Lists all members and their Roles for a specific Organization
14
+ ADD_PROJECT_MEMBER_V2 = "v2/accessControl/projects/members" # POST - Adds a member to a project by sending an invitation
15
+ CREATE_ORGANIZATION_V2 = "v2/admin/organizations" # POST - Creates a new Organization
16
+ GET_ORGANIZATION_LIST_V2 = "v2/admin/organizations" # GET - Retrieves all the organizations available based on a given search criteria
17
+ DELETE_ORGANIZATION_V2 = "v2/admin/organizations/{organizationId}" # DELETE - Deletes the created Organization
@@ -1,13 +1,14 @@
1
1
  from pygeai import logger
2
2
  from pygeai.core.base.mappers import ErrorMapper, ResponseMapper
3
3
  from pygeai.core.handlers import ErrorHandler
4
- from pygeai.core.models import Project
4
+ from pygeai.core.models import Project, Organization
5
5
  from pygeai.core.base.responses import EmptyResponse
6
6
  from pygeai.organization.clients import OrganizationClient
7
7
  from pygeai.organization.mappers import OrganizationResponseMapper
8
8
  from pygeai.organization.responses import AssistantListResponse, ProjectListResponse, ProjectDataResponse, \
9
9
  ProjectTokensResponse, ProjectItemListResponse, MembershipsResponse, ProjectMembershipsResponse, \
10
- ProjectRolesResponse, ProjectMembersResponse, OrganizationMembersResponse
10
+ ProjectRolesResponse, ProjectMembersResponse, OrganizationMembersResponse, OrganizationListResponse, \
11
+ OrganizationDataResponse
11
12
  from pygeai.core.common.exceptions import APIError
12
13
 
13
14
 
@@ -403,3 +404,134 @@ class OrganizationManager:
403
404
 
404
405
  result = OrganizationResponseMapper.map_to_organization_members_response(response_data)
405
406
  return result
407
+
408
+ def add_project_member(
409
+ self,
410
+ project_id: str,
411
+ user_email: str,
412
+ roles: list
413
+ ) -> EmptyResponse:
414
+ """
415
+ Adds a user to a project by sending an invitation with the specified roles.
416
+
417
+ This method calls `OrganizationClient.add_project_member` to invite a user to the project
418
+ and maps the response to an `EmptyResponse`.
419
+
420
+ :param project_id: str - The unique identifier of the project.
421
+ :param user_email: str - The email address of the user to invite.
422
+ :param roles: list - A list of role names or GUIDs to assign to the user.
423
+ :return: EmptyResponse - An empty response indicating successful invitation.
424
+ :raises APIError: If the API returns errors.
425
+ """
426
+ response_data = self.__organization_client.add_project_member(
427
+ project_id=project_id,
428
+ user_email=user_email,
429
+ roles=roles
430
+ )
431
+ if ErrorHandler.has_errors(response_data):
432
+ error = ErrorHandler.extract_error(response_data)
433
+ logger.error(f"Error received while adding project member: {error}")
434
+ raise APIError(f"Error received while adding project member: {error}")
435
+
436
+ result = ResponseMapper.map_to_empty_response(response_data or "Invitation sent successfully")
437
+ return result
438
+
439
+ def create_organization(
440
+ self,
441
+ name: str,
442
+ administrator_user_email: str
443
+ ) -> OrganizationDataResponse:
444
+ """
445
+ Creates a new organization with the given details.
446
+
447
+ This endpoint requires an OAuth access token from the System Administrator role.
448
+
449
+ This method calls `OrganizationClient.create_organization` to create a new organization and maps the response
450
+ using `OrganizationResponseMapper` into an `OrganizationDataResponse` object.
451
+
452
+ :param name: str - The name of the new organization (required).
453
+ :param administrator_user_email: str - The email address of the organization administrator (required).
454
+ :return: OrganizationDataResponse - The mapped response containing the created organization details.
455
+ :raises APIError: If the API returns errors.
456
+ """
457
+ response_data = self.__organization_client.create_organization(
458
+ name=name,
459
+ administrator_user_email=administrator_user_email
460
+ )
461
+
462
+ if ErrorHandler.has_errors(response_data):
463
+ error = ErrorHandler.extract_error(response_data)
464
+ logger.error(f"Error received while creating organization: {error}")
465
+ raise APIError(f"Error received while creating organization: {error}")
466
+
467
+ result = OrganizationResponseMapper.map_to_organization_data_response(response_data)
468
+ return result
469
+
470
+ def get_organization_list(
471
+ self,
472
+ start_page: int = None,
473
+ page_size: int = None,
474
+ order_key: str = None,
475
+ order_direction: str = "desc",
476
+ filter_key: str = None,
477
+ filter_value: str = None
478
+ ) -> OrganizationListResponse:
479
+ """
480
+ Retrieves a list of organizations based on the specified search criteria.
481
+
482
+ This endpoint requires an OAuth access token from the System Administrator role.
483
+
484
+ This method calls `OrganizationClient.get_organization_list` to fetch organization data
485
+ and maps the response using `OrganizationResponseMapper` into an `OrganizationListResponse` object.
486
+
487
+ :param start_page: int, optional - The page number for pagination.
488
+ :param page_size: int, optional - The number of items per page.
489
+ :param order_key: str, optional - Field for sorting. Only 'name' is supported.
490
+ :param order_direction: str - Sort direction: 'asc' or 'desc' (default is 'desc').
491
+ :param filter_key: str, optional - Field for filtering. Only 'name' is supported.
492
+ :param filter_value: str, optional - Value to filter by.
493
+ :return: OrganizationListResponse - The mapped response containing the list of organizations.
494
+ :raises APIError: If the API returns errors.
495
+ """
496
+ response_data = self.__organization_client.get_organization_list(
497
+ start_page=start_page,
498
+ page_size=page_size,
499
+ order_key=order_key,
500
+ order_direction=order_direction,
501
+ filter_key=filter_key,
502
+ filter_value=filter_value
503
+ )
504
+ if ErrorHandler.has_errors(response_data):
505
+ error = ErrorHandler.extract_error(response_data)
506
+ logger.error(f"Error received while retrieving organization list: {error}")
507
+ raise APIError(f"Error received while retrieving organization list: {error}")
508
+
509
+ result = OrganizationResponseMapper.map_to_organization_list_response(response_data)
510
+ return result
511
+
512
+ def delete_organization(
513
+ self,
514
+ organization_id: str
515
+ ) -> EmptyResponse:
516
+ """
517
+ Deletes an organization by its unique identifier.
518
+
519
+ This endpoint requires an OAuth access token from the System Administrator role.
520
+
521
+ This method calls `OrganizationClient.delete_organization` to remove an organization and maps the response
522
+ using `ResponseMapper.map_to_empty_response`.
523
+
524
+ :param organization_id: str - The unique identifier of the organization to be deleted.
525
+ :return: EmptyResponse - An empty response indicating successful deletion.
526
+ :raises APIError: If the API returns errors.
527
+ """
528
+ response_data = self.__organization_client.delete_organization(
529
+ organization_id=organization_id
530
+ )
531
+ if ErrorHandler.has_errors(response_data):
532
+ error = ErrorHandler.extract_error(response_data)
533
+ logger.error(f"Error received while deleting organization: {error}")
534
+ raise APIError(f"Error received while deleting organization: {error}")
535
+
536
+ result = ResponseMapper.map_to_empty_response(response_data or "Organization deleted successfully")
537
+ return result
@@ -1,8 +1,9 @@
1
1
  from pygeai.core.base.mappers import ModelMapper
2
- from pygeai.core.models import Assistant, Project, Role, Member, OrganizationMembership, ProjectMembership
2
+ from pygeai.core.models import Assistant, Project, Role, Member, OrganizationMembership, ProjectMembership, Organization
3
3
  from pygeai.organization.responses import AssistantListResponse, ProjectListResponse, ProjectDataResponse, \
4
4
  ProjectTokensResponse, ProjectItemListResponse, MembershipsResponse, ProjectMembershipsResponse, \
5
- ProjectRolesResponse, ProjectMembersResponse, OrganizationMembersResponse
5
+ ProjectRolesResponse, ProjectMembersResponse, OrganizationMembersResponse, OrganizationListResponse, \
6
+ OrganizationDataResponse
6
7
 
7
8
 
8
9
  class OrganizationResponseMapper:
@@ -150,3 +151,28 @@ class OrganizationResponseMapper:
150
151
  members=members
151
152
  )
152
153
 
154
+ @classmethod
155
+ def map_to_organization_list_response(cls, data: dict) -> OrganizationListResponse:
156
+ count = data.get("count", 0)
157
+ pages = data.get("pages", 0)
158
+ organizations_data = data.get("organizations", [])
159
+ organizations = []
160
+
161
+ for org_data in organizations_data:
162
+ organization = Organization.model_validate(org_data)
163
+ organizations.append(organization)
164
+
165
+ return OrganizationListResponse(
166
+ count=count,
167
+ pages=pages,
168
+ organizations=organizations
169
+ )
170
+
171
+ @classmethod
172
+ def map_to_organization_data_response(cls, data: dict) -> OrganizationDataResponse:
173
+ organization = Organization.model_validate(data)
174
+
175
+ return OrganizationDataResponse(
176
+ organization=organization
177
+ )
178
+
@@ -1,7 +1,7 @@
1
1
  from pydantic.main import BaseModel
2
2
 
3
3
  from pygeai.core.models import Assistant, Project, ProjectToken, \
4
- RequestItem, Role, Member, OrganizationMembership, ProjectMembership
4
+ RequestItem, Role, Member, OrganizationMembership, ProjectMembership, Organization
5
5
 
6
6
 
7
7
  class AssistantListResponse(BaseModel):
@@ -69,3 +69,13 @@ class ProjectMembersResponse(BaseModel):
69
69
 
70
70
  class OrganizationMembersResponse(BaseModel):
71
71
  members: list[Member]
72
+
73
+
74
+ class OrganizationListResponse(BaseModel):
75
+ count: int
76
+ pages: int
77
+ organizations: list[Organization]
78
+
79
+
80
+ class OrganizationDataResponse(BaseModel):
81
+ organization: Organization
File without changes