pygeai 0.6.0b11__py3-none-any.whl → 0.6.0b13__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 (138) 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/assistant/managers.py +1 -1
  30. pygeai/chat/managers.py +1 -1
  31. pygeai/cli/commands/analytics.py +525 -0
  32. pygeai/cli/commands/base.py +16 -0
  33. pygeai/cli/commands/common.py +28 -24
  34. pygeai/cli/commands/migrate.py +75 -6
  35. pygeai/cli/commands/organization.py +265 -0
  36. pygeai/cli/commands/validators.py +144 -1
  37. pygeai/cli/error_handler.py +41 -6
  38. pygeai/cli/geai.py +106 -18
  39. pygeai/cli/parsers.py +75 -31
  40. pygeai/cli/texts/help.py +75 -6
  41. pygeai/core/base/clients.py +18 -4
  42. pygeai/core/base/session.py +59 -7
  43. pygeai/core/common/config.py +25 -2
  44. pygeai/core/common/exceptions.py +64 -1
  45. pygeai/core/embeddings/managers.py +1 -1
  46. pygeai/core/files/managers.py +1 -1
  47. pygeai/core/rerank/managers.py +1 -1
  48. pygeai/core/services/rest.py +20 -2
  49. pygeai/evaluation/clients.py +5 -3
  50. pygeai/lab/agents/clients.py +3 -3
  51. pygeai/lab/agents/endpoints.py +2 -2
  52. pygeai/lab/agents/mappers.py +50 -2
  53. pygeai/lab/clients.py +5 -2
  54. pygeai/lab/managers.py +8 -10
  55. pygeai/lab/models.py +70 -2
  56. pygeai/lab/tools/clients.py +1 -59
  57. pygeai/migration/__init__.py +3 -1
  58. pygeai/migration/strategies.py +72 -3
  59. pygeai/organization/clients.py +110 -1
  60. pygeai/organization/endpoints.py +11 -7
  61. pygeai/organization/limits/managers.py +1 -1
  62. pygeai/organization/managers.py +135 -3
  63. pygeai/organization/mappers.py +28 -2
  64. pygeai/organization/responses.py +11 -1
  65. pygeai/tests/analytics/__init__.py +0 -0
  66. pygeai/tests/analytics/test_clients.py +86 -0
  67. pygeai/tests/analytics/test_managers.py +94 -0
  68. pygeai/tests/analytics/test_mappers.py +84 -0
  69. pygeai/tests/analytics/test_responses.py +73 -0
  70. pygeai/tests/auth/test_oauth.py +172 -0
  71. pygeai/tests/cli/commands/test_migrate.py +14 -1
  72. pygeai/tests/cli/commands/test_organization.py +69 -1
  73. pygeai/tests/cli/test_error_handler.py +4 -4
  74. pygeai/tests/cli/test_geai_driver.py +1 -1
  75. pygeai/tests/lab/agents/test_mappers.py +128 -1
  76. pygeai/tests/lab/test_models.py +2 -0
  77. pygeai/tests/lab/tools/test_clients.py +2 -31
  78. pygeai/tests/organization/test_clients.py +180 -1
  79. pygeai/tests/organization/test_managers.py +40 -0
  80. pygeai/tests/snippets/analytics/__init__.py +0 -0
  81. pygeai/tests/snippets/analytics/get_agent_usage_per_user.py +16 -0
  82. pygeai/tests/snippets/analytics/get_agents_created_and_modified.py +11 -0
  83. pygeai/tests/snippets/analytics/get_average_cost_per_request.py +10 -0
  84. pygeai/tests/snippets/analytics/get_overall_error_rate.py +10 -0
  85. pygeai/tests/snippets/analytics/get_top_10_agents_by_requests.py +12 -0
  86. pygeai/tests/snippets/analytics/get_total_active_users.py +10 -0
  87. pygeai/tests/snippets/analytics/get_total_cost.py +10 -0
  88. pygeai/tests/snippets/analytics/get_total_requests_per_day.py +12 -0
  89. pygeai/tests/snippets/analytics/get_total_tokens.py +12 -0
  90. pygeai/tests/snippets/chat/get_response_complete_example.py +67 -0
  91. pygeai/tests/snippets/chat/get_response_with_instructions.py +19 -0
  92. pygeai/tests/snippets/chat/get_response_with_metadata.py +24 -0
  93. pygeai/tests/snippets/chat/get_response_with_parallel_tools.py +58 -0
  94. pygeai/tests/snippets/chat/get_response_with_reasoning.py +21 -0
  95. pygeai/tests/snippets/chat/get_response_with_store.py +38 -0
  96. pygeai/tests/snippets/chat/get_response_with_truncation.py +24 -0
  97. pygeai/tests/snippets/lab/agents/create_agent_with_permissions.py +39 -0
  98. pygeai/tests/snippets/lab/agents/create_agent_with_properties.py +46 -0
  99. pygeai/tests/snippets/lab/agents/get_agent_with_new_fields.py +62 -0
  100. pygeai/tests/snippets/lab/agents/update_agent_properties.py +50 -0
  101. pygeai/tests/snippets/organization/add_project_member.py +10 -0
  102. pygeai/tests/snippets/organization/add_project_member_batch.py +44 -0
  103. {pygeai-0.6.0b11.dist-info → pygeai-0.6.0b13.dist-info}/METADATA +1 -1
  104. {pygeai-0.6.0b11.dist-info → pygeai-0.6.0b13.dist-info}/RECORD +108 -98
  105. pygeai/_docs/source/pygeai.tests.snippets.assistants.data_analyst.rst +0 -37
  106. pygeai/_docs/source/pygeai.tests.snippets.assistants.rag.rst +0 -85
  107. pygeai/_docs/source/pygeai.tests.snippets.assistants.rst +0 -78
  108. pygeai/_docs/source/pygeai.tests.snippets.auth.rst +0 -10
  109. pygeai/_docs/source/pygeai.tests.snippets.chat.rst +0 -125
  110. pygeai/_docs/source/pygeai.tests.snippets.dbg.rst +0 -45
  111. pygeai/_docs/source/pygeai.tests.snippets.embeddings.rst +0 -61
  112. pygeai/_docs/source/pygeai.tests.snippets.evaluation.dataset.rst +0 -197
  113. pygeai/_docs/source/pygeai.tests.snippets.evaluation.plan.rst +0 -133
  114. pygeai/_docs/source/pygeai.tests.snippets.evaluation.result.rst +0 -37
  115. pygeai/_docs/source/pygeai.tests.snippets.evaluation.rst +0 -20
  116. pygeai/_docs/source/pygeai.tests.snippets.extras.rst +0 -37
  117. pygeai/_docs/source/pygeai.tests.snippets.files.rst +0 -53
  118. pygeai/_docs/source/pygeai.tests.snippets.gam.rst +0 -21
  119. pygeai/_docs/source/pygeai.tests.snippets.lab.agents.rst +0 -93
  120. pygeai/_docs/source/pygeai.tests.snippets.lab.processes.jobs.rst +0 -21
  121. pygeai/_docs/source/pygeai.tests.snippets.lab.processes.kbs.rst +0 -45
  122. pygeai/_docs/source/pygeai.tests.snippets.lab.processes.rst +0 -46
  123. pygeai/_docs/source/pygeai.tests.snippets.lab.rst +0 -82
  124. pygeai/_docs/source/pygeai.tests.snippets.lab.samples.rst +0 -21
  125. pygeai/_docs/source/pygeai.tests.snippets.lab.strategies.rst +0 -45
  126. pygeai/_docs/source/pygeai.tests.snippets.lab.tools.rst +0 -85
  127. pygeai/_docs/source/pygeai.tests.snippets.lab.use_cases.rst +0 -117
  128. pygeai/_docs/source/pygeai.tests.snippets.migrate.rst +0 -10
  129. pygeai/_docs/source/pygeai.tests.snippets.organization.rst +0 -109
  130. pygeai/_docs/source/pygeai.tests.snippets.rag.rst +0 -85
  131. pygeai/_docs/source/pygeai.tests.snippets.rerank.rst +0 -21
  132. pygeai/_docs/source/pygeai.tests.snippets.rst +0 -32
  133. pygeai/_docs/source/pygeai.tests.snippets.secrets.rst +0 -10
  134. pygeai/_docs/source/pygeai.tests.snippets.usage_limit.rst +0 -77
  135. {pygeai-0.6.0b11.dist-info → pygeai-0.6.0b13.dist-info}/WHEEL +0 -0
  136. {pygeai-0.6.0b11.dist-info → pygeai-0.6.0b13.dist-info}/entry_points.txt +0 -0
  137. {pygeai-0.6.0b11.dist-info → pygeai-0.6.0b13.dist-info}/licenses/LICENSE +0 -0
  138. {pygeai-0.6.0b11.dist-info → pygeai-0.6.0b13.dist-info}/top_level.txt +0 -0
@@ -7,7 +7,20 @@ class GEAIException(Exception):
7
7
 
8
8
  class UnknownArgumentError(GEAIException):
9
9
  """Raised when an unknown or invalid command/option is provided"""
10
- pass
10
+
11
+ def __init__(self, message: str, arg: str = None, available_commands=None, available_options=None):
12
+ """
13
+ Initialize an UnknownArgumentError with context.
14
+
15
+ :param message: str - The error message.
16
+ :param arg: str - The unknown argument that was provided.
17
+ :param available_commands: list - Available commands for suggestion.
18
+ :param available_options: list - Available options for suggestion.
19
+ """
20
+ super().__init__(message)
21
+ self.arg = arg
22
+ self.available_commands = available_commands
23
+ self.available_options = available_options
11
24
 
12
25
 
13
26
  class MissingRequirementException(GEAIException):
@@ -20,6 +33,56 @@ class WrongArgumentError(GEAIException):
20
33
  pass
21
34
 
22
35
 
36
+ class ValidationError(WrongArgumentError):
37
+ """
38
+ Raised when input validation fails with detailed context.
39
+
40
+ Extends WrongArgumentError to provide structured validation errors
41
+ with field-specific information and examples.
42
+ """
43
+ def __init__(
44
+ self,
45
+ message: str,
46
+ field: str = None,
47
+ expected: str = None,
48
+ received: str = None,
49
+ example: str = None
50
+ ):
51
+ """
52
+ Initialize a ValidationError with detailed context.
53
+
54
+ :param message: str - The main error message.
55
+ :param field: str - Name of the field that failed validation.
56
+ :param expected: str - Description of what was expected.
57
+ :param received: str - Description of what was received.
58
+ :param example: str - Example of valid input.
59
+ """
60
+ super().__init__(message)
61
+ self.field = field
62
+ self.expected = expected
63
+ self.received = received
64
+ self.example = example
65
+
66
+ def __str__(self) -> str:
67
+ """
68
+ Format the error message with all available context.
69
+
70
+ :return: str - Formatted error message with field details.
71
+ """
72
+ parts = [super().__str__()]
73
+
74
+ if self.field:
75
+ parts.append(f" Field: {self.field}")
76
+ if self.expected:
77
+ parts.append(f" Expected: {self.expected}")
78
+ if self.received:
79
+ parts.append(f" Received: {self.received}")
80
+ if self.example:
81
+ parts.append(f" Example: {self.example}")
82
+
83
+ return "\n".join(parts)
84
+
85
+
23
86
  class ServerResponseError(GEAIException):
24
87
  """Raised when the server returns an error response"""
25
88
  pass
@@ -10,7 +10,7 @@ from pygeai.core.handlers import ErrorHandler
10
10
 
11
11
  class EmbeddingsManager:
12
12
 
13
- def __init__(self, api_key: str = None, base_url: str = None, alias: str = "default"):
13
+ def __init__(self, api_key: str = None, base_url: str = None, alias: str = None):
14
14
  self.__client = EmbeddingsClient(api_key, base_url, alias)
15
15
 
16
16
  def generate_embeddings(
@@ -24,7 +24,7 @@ class FileManager:
24
24
  self,
25
25
  api_key: str = None,
26
26
  base_url: str = None,
27
- alias: str = "default",
27
+ alias: str = None,
28
28
  organization_id: str = None,
29
29
  project_id: str = None
30
30
  ):
@@ -8,7 +8,7 @@ from pygeai.core.rerank.models import RerankResponse
8
8
 
9
9
  class RerankManager:
10
10
 
11
- def __init__(self, api_key: str = None, base_url: str = None, alias: str = "default"):
11
+ def __init__(self, api_key: str = None, base_url: str = None, alias: str = None):
12
12
  self.__client = RerankClient(api_key, base_url, alias)
13
13
 
14
14
  def rerank_chunks(
@@ -15,19 +15,22 @@ FAILED_REQUEST_RESPONSE = ResponseMock(
15
15
 
16
16
  class ApiService:
17
17
  """
18
- Generic service for interacting with RESTful APIs.
18
+ Generic service for interacting with REST APIs.
19
19
 
20
20
  :param base_url: str - The base URL of the API.
21
21
  :param username: str - Username for basic authentication (optional).
22
22
  :param password: str - Password for basic authentication (optional).
23
23
  :param token: str - Bearer token for authentication (optional).
24
+ :param project_id: str - Project ID for OAuth authentication (optional, keyword-only).
24
25
  """
25
26
 
26
- def __init__(self, base_url, username: str = None, password: str = None, token: str = None):
27
+ def __init__(self, base_url, username: str = None, password: str = None, token: str = None, *,
28
+ project_id: str = None):
27
29
  self._base_url = base_url
28
30
  self._username = username
29
31
  self._password = password
30
32
  self._token = token
33
+ self._project_id = project_id
31
34
 
32
35
  @property
33
36
  def base_url(self):
@@ -45,6 +48,18 @@ class ApiService:
45
48
  def token(self):
46
49
  return self._token
47
50
 
51
+ @token.setter
52
+ def token(self, token: str):
53
+ self._token = token
54
+
55
+ @property
56
+ def project_id(self):
57
+ return self._project_id
58
+
59
+ @project_id.setter
60
+ def project_id(self, project_id: str):
61
+ self._project_id = project_id
62
+
48
63
  def get(self, endpoint: str, params: dict = None, headers: dict = None, verify: bool = True):
49
64
  """
50
65
  Sends a GET request to the specified API endpoint.
@@ -403,4 +418,7 @@ class ApiService:
403
418
  if "Authorization" not in headers:
404
419
  headers["Authorization"] = f"Bearer {self.token}"
405
420
 
421
+ if self.project_id and "ProjectId" not in headers:
422
+ headers["ProjectId"] = self.project_id
423
+
406
424
  return headers
@@ -7,11 +7,13 @@ from pygeai.core.utils.validators import validate_status_code
7
7
 
8
8
  class EvaluationClient(BaseClient):
9
9
 
10
- def __init__(self, api_key: str = None, base_url: str = None, alias: str = "default", eval_url: str = None):
11
- super().__init__(api_key, base_url, alias)
10
+ def __init__(self, api_key: str = None, base_url: str = None, alias: str = None, eval_url: str = None, *,
11
+ access_token: str = None, project_id: str = None):
12
+ super().__init__(api_key, base_url, alias, access_token=access_token, project_id=project_id)
12
13
  eval_url = self.session.eval_url if not eval_url else eval_url
13
14
  if not eval_url:
14
15
  raise MissingRequirementException("EVAL URL must be defined in order to use the Evaluation module.")
15
16
 
16
17
  self.session.eval_url = eval_url
17
- self.api_service = ApiService(base_url=self.session.eval_url, token=self.session.api_key)
18
+ token = self.session.access_token if self.session.access_token else self.session.api_key
19
+ self.api_service = ApiService(base_url=self.session.eval_url, token=token, project_id=self.session.project_id)
@@ -5,7 +5,7 @@ from pygeai.core.common.exceptions import MissingRequirementException, InvalidAP
5
5
  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.agents.endpoints import CREATE_AGENT_V2, LIST_AGENTS_V2, GET_AGENT_V2, CREATE_SHARING_LINK_V2, \
8
- PUBLISH_AGENT_REVISION_V2, DELETE_AGENT_V2, UPDATE_AGENT_V2, UPSERT_AGENT_V2, EXPORT_AGENT_V4, IMPORT_AGENT_V4
8
+ PUBLISH_AGENT_REVISION_V2, DELETE_AGENT_V2, UPDATE_AGENT_V2, UPSERT_AGENT_V2, EXPORT_AGENT_V2, IMPORT_AGENT_V2
9
9
  from pygeai.lab.constants import VALID_ACCESS_SCOPES
10
10
  from pygeai.lab.clients import AILabClient
11
11
 
@@ -380,7 +380,7 @@ class AgentClient(AILabClient):
380
380
  if not agent_id:
381
381
  raise MissingRequirementException("agent_id must be specified in order to export the agent")
382
382
 
383
- endpoint = EXPORT_AGENT_V4.format(agentId=agent_id)
383
+ endpoint = EXPORT_AGENT_V2.format(agentId=agent_id)
384
384
  headers = {
385
385
  "Authorization": self.api_service.token,
386
386
  "ProjectId": self.project_id
@@ -410,7 +410,7 @@ class AgentClient(AILabClient):
410
410
  if not data:
411
411
  raise MissingRequirementException("data for spec must be specified in order to import the agent")
412
412
 
413
- endpoint = IMPORT_AGENT_V4
413
+ endpoint = IMPORT_AGENT_V2
414
414
  headers = {
415
415
  "Authorization": self.api_service.token,
416
416
  "ProjectId": self.project_id
@@ -8,5 +8,5 @@ UPDATE_AGENT_V2 = "v2/agents/{agentId}" # PUT -> Update agent
8
8
  UPSERT_AGENT_V2 = "v2/agents/{agentId}/upsert" # PUT -> Update or Insert agent
9
9
  EXPORT_AGENT_V2 = "v2/agents/{agentId}/export" # GET -> Export agent
10
10
  IMPORT_AGENT_V2 = "v2/agents/import" # POST -> Import agent
11
- EXPORT_AGENT_V4 = "v4/agents/{agentId}/export" # GET -> Export agent
12
- IMPORT_AGENT_V4 = "v4/agents/import" # POST -> Import agent
11
+ # EXPORT_AGENT_V4 = "v4/agents/{agentId}/export" # GET -> Export agent
12
+ # IMPORT_AGENT_V4 = "v4/agents/import" # POST -> Import agent
@@ -1,7 +1,7 @@
1
1
  from typing import Optional, List
2
2
 
3
3
  from pygeai.lab.models import Agent, AgentList, SharingLink, AgentData, Prompt, PromptOutput, PromptExample, LlmConfig, \
4
- Sampling, ModelList, Model, ResourcePoolList, ResourcePool, ResourcePoolAgent, ResourcePoolTool
4
+ Sampling, ModelList, Model, ResourcePoolList, ResourcePool, ResourcePoolAgent, ResourcePoolTool, Permission, Property
5
5
 
6
6
 
7
7
  class AgentMapper:
@@ -38,6 +38,8 @@ class AgentMapper:
38
38
  :return: Agent - The mapped `Agent` object.
39
39
  """
40
40
  agent_data_data = data.get("agentData")
41
+ permissions_data = data.get("permissions")
42
+ effective_permissions_data = data.get("effectivePermissions")
41
43
  return Agent(
42
44
  id=data.get("id"),
43
45
  status=data.get("status"),
@@ -51,6 +53,9 @@ class AgentMapper:
51
53
  is_readonly=data.get("isReadonly"),
52
54
  revision=data.get("revision"),
53
55
  version=data.get("version"),
56
+ sharing_scope=data.get("sharingScope"),
57
+ permissions=cls._map_to_permission(permissions_data) if permissions_data else None,
58
+ effective_permissions=cls._map_to_permission(effective_permissions_data) if effective_permissions_data else None,
54
59
  agent_data=cls._map_agent_data(agent_data_data) if agent_data_data else None
55
60
  )
56
61
 
@@ -67,12 +72,14 @@ class AgentMapper:
67
72
  models_list = data.get("models")
68
73
  strategy_name = data.get("strategyName")
69
74
  resource_pool_list = data.get("resourcePools")
75
+ properties_list = data.get("properties")
70
76
  return AgentData(
71
77
  prompt=cls._map_to_prompt(prompt_data) if prompt_data else None,
72
78
  llm_config=cls._map_to_llm_config(llm_config_data) if llm_config_data else None,
73
79
  strategy_name=strategy_name,
74
80
  models=cls._map_to_model_list(models_list) if models_list else None,
75
- resource_pools=cls._map_to_resource_pool_list(resource_pool_list) if resource_pool_list else None
81
+ resource_pools=cls._map_to_resource_pool_list(resource_pool_list) if resource_pool_list else None,
82
+ properties=cls._map_to_property_list(properties_list) if properties_list else None
76
83
  )
77
84
 
78
85
  @classmethod
@@ -269,3 +276,44 @@ class AgentMapper:
269
276
  api_token=data.get('apiToken'),
270
277
  shared_link=data.get('sharedLink'),
271
278
  )
279
+
280
+ @classmethod
281
+ def _map_to_permission(cls, data: dict) -> Permission:
282
+ """
283
+ Maps a dictionary to a `Permission` object.
284
+
285
+ :param data: dict - The dictionary containing permission details.
286
+ :return: Permission - The mapped `Permission` object, or None if data is None.
287
+ """
288
+ if data is None:
289
+ return None
290
+ return Permission(
291
+ chat_sharing=data.get('chatSharing'),
292
+ external_execution=data.get('externalExecution')
293
+ )
294
+
295
+ @classmethod
296
+ def _map_to_property_list(cls, data: List[dict]) -> List[Property]:
297
+ """
298
+ Maps a list of dictionaries to a list of `Property` objects.
299
+
300
+ :param data: List[dict] - The list of dictionaries containing property details.
301
+ :return: List[Property] - The mapped list of `Property` objects, or None if data is None.
302
+ """
303
+ if data is None:
304
+ return None
305
+ return [cls._map_to_property(prop) for prop in data]
306
+
307
+ @classmethod
308
+ def _map_to_property(cls, data: dict) -> Property:
309
+ """
310
+ Maps a dictionary to a `Property` object.
311
+
312
+ :param data: dict - The dictionary containing property details.
313
+ :return: Property - The mapped `Property` object.
314
+ """
315
+ return Property(
316
+ data_type=data.get('dataType'),
317
+ key=data.get('key'),
318
+ value=data.get('value')
319
+ )
pygeai/lab/clients.py CHANGED
@@ -7,9 +7,12 @@ from pygeai.core.utils.validators import validate_status_code
7
7
 
8
8
  class AILabClient(BaseClient):
9
9
 
10
- def __init__(self, api_key: str = None, base_url: str = None, alias: str = None, project_id: str = None):
11
- super().__init__(api_key, base_url, alias)
10
+ def __init__(self, api_key: str = None, base_url: str = None, alias: str = None, *,
11
+ access_token: str = None, project_id: str = None):
12
+ super().__init__(api_key, base_url, alias, access_token=access_token, project_id=project_id)
12
13
  self.project_id = project_id if project_id else self.__get_project_id(api_key, base_url, alias)
14
+ if self.project_id and not self.api_service.project_id:
15
+ self.api_service.project_id = self.project_id
13
16
 
14
17
  def __get_project_id(self, api_key: str = None, base_url: str = None, alias: str = None):
15
18
  response = None
pygeai/lab/managers.py CHANGED
@@ -22,7 +22,7 @@ from pygeai.lab.tools.mappers import ToolMapper
22
22
 
23
23
  class AILabManager:
24
24
 
25
- def __init__(self, api_key: str = None, base_url: str = None, alias: str = "default", project_id: str = None):
25
+ def __init__(self, api_key: str = None, base_url: str = None, alias: str = None, project_id: str = None):
26
26
  self.__agent_client = AgentClient(api_key=api_key, base_url=base_url, alias=alias, project_id=project_id)
27
27
  self.__tool_client = ToolClient(api_key=api_key, base_url=base_url, alias=alias, project_id=project_id)
28
28
  self.__reasoning_strategy_client = ReasoningStrategyClient(api_key=api_key, base_url=base_url, alias=alias, project_id=project_id)
@@ -91,7 +91,7 @@ class AILabManager:
91
91
  agent_data_prompt=agent.agent_data.prompt.to_dict() if agent.agent_data is not None else None,
92
92
  agent_data_strategy_name=agent.agent_data.strategy_name if agent.agent_data is not None else None,
93
93
  agent_data_llm_config=agent.agent_data.llm_config.to_dict() if agent.agent_data is not None else None,
94
- agent_data_models=agent.agent_data.models.to_dict() if agent.agent_data is not None else None,
94
+ agent_data_models=agent.agent_data.models.to_dict() if agent.agent_data and agent.agent_data.models else None,
95
95
  agent_data_resource_pools=agent.agent_data.resource_pools.to_dict() if agent.agent_data and agent.agent_data.resource_pools else None,
96
96
  automatic_publish=automatic_publish
97
97
  )
@@ -137,8 +137,9 @@ class AILabManager:
137
137
  description=agent.description,
138
138
  agent_data_prompt=agent.agent_data.prompt.to_dict() if agent.agent_data is not None else None,
139
139
  agent_data_llm_config=agent.agent_data.llm_config.to_dict() if agent.agent_data is not None else None,
140
- agent_data_strategy_name=agent.agent_data.strategy_name if agent.agent_data is not None else None,
141
- agent_data_models=agent.agent_data.models.to_dict() if agent.agent_data is not None else None,
140
+ agent_data_strategy_name=agent.agent_data.strategy_name if agent.agent_data and agent.agent_data.strategy_name else None,
141
+ agent_data_models=agent.agent_data.models.to_dict() if agent.agent_data and agent.agent_data.models else None,
142
+ agent_data_resource_pools=agent.agent_data.resource_pools.to_dict() if agent.agent_data and agent.agent_data.resource_pools else None,
142
143
  automatic_publish=automatic_publish,
143
144
  upsert=upsert
144
145
  )
@@ -264,8 +265,7 @@ class AILabManager:
264
265
  logger.error(f"Error received while deleting agent: {error}")
265
266
  raise APIError(f"Error received while deleting agent: {error}")
266
267
 
267
- response_data = response_data if response_data else "Agent deleted successfully"
268
- result = ResponseMapper.map_to_empty_response(response_data)
268
+ result = ResponseMapper.map_to_empty_response(response_data or "Agent deleted successfully")
269
269
  return result
270
270
 
271
271
  def create_tool(
@@ -431,8 +431,7 @@ class AILabManager:
431
431
  logger.error(f"Error received while deleting tool: {error}")
432
432
  raise APIError(f"Error received while deleting tool: {error}")
433
433
 
434
- response_data = response_data if response_data else "Tool deleted successfully"
435
- result = ResponseMapper.map_to_empty_response(response_data)
434
+ result = ResponseMapper.map_to_empty_response(response_data or "Tool deleted successfully")
436
435
  return result
437
436
 
438
437
  def list_tools(
@@ -1518,8 +1517,7 @@ class AILabManager:
1518
1517
  logger.error(f"Error received while deleting knowledge base: {error}")
1519
1518
  raise APIError(f"Error received while deleting knowledge base: {error}")
1520
1519
 
1521
- response_data = response_data if response_data else "Knowledge base deleted successfully"
1522
- result = ResponseMapper.map_to_empty_response(response_data)
1520
+ result = ResponseMapper.map_to_empty_response(response_data or "Knowledge base deleted successfully")
1523
1521
  return result
1524
1522
 
1525
1523
  def list_jobs(
pygeai/lab/models.py CHANGED
@@ -385,6 +385,38 @@ class ResourcePoolList(CustomBaseModel):
385
385
  self.resource_pools.append(item)
386
386
 
387
387
 
388
+ class Permission(CustomBaseModel):
389
+ """
390
+ Represents permission settings for an agent.
391
+
392
+ :param chat_sharing: Literal["none", "organization", "project"] - Chat sharing permission level.
393
+ :param external_execution: Literal["none", "organization", "project"] - External execution permission level.
394
+ """
395
+ chat_sharing: Optional[Literal["none", "organization", "project"]] = Field(None, alias="chatSharing", description="Chat sharing permission level")
396
+ external_execution: Optional[Literal["none", "organization", "project"]] = Field(None, alias="externalExecution", description="External execution permission level")
397
+
398
+ def to_dict(self):
399
+ """Convert Permission to dictionary with API aliases."""
400
+ return self.model_dump(by_alias=True, exclude_none=True)
401
+
402
+
403
+ class Property(CustomBaseModel):
404
+ """
405
+ Represents a property key-value pair with data type.
406
+
407
+ :param data_type: str - Data type of the property (e.g., "String", "Number", "Boolean").
408
+ :param key: str - Property key identifier.
409
+ :param value: str - Property value.
410
+ """
411
+ data_type: str = Field(..., alias="dataType", description="Data type of the property")
412
+ key: str = Field(..., alias="key", description="Property key identifier")
413
+ value: str = Field(..., alias="value", description="Property value")
414
+
415
+ def to_dict(self):
416
+ """Convert Property to dictionary with API aliases."""
417
+ return self.model_dump(by_alias=True, exclude_none=True)
418
+
419
+
388
420
  class AgentData(CustomBaseModel):
389
421
  """
390
422
  Represents the detailed configuration data for an agent.
@@ -400,6 +432,7 @@ class AgentData(CustomBaseModel):
400
432
  strategy_name: Optional[str] = Field("Dynamic Prompting", alias="strategyName")
401
433
  models: Optional[Union[ModelList, List[Model]]] = Field(None, alias="models")
402
434
  resource_pools: Optional[ResourcePoolList] = Field(None, alias="resourcePools", description="List of resource pools organizing tools and helper agents")
435
+ properties: Optional[List[Property]] = Field(None, alias="properties", description="List of agent properties")
403
436
 
404
437
  @field_validator("prompt", mode="before")
405
438
  @classmethod
@@ -418,6 +451,8 @@ class AgentData(CustomBaseModel):
418
451
  @field_validator("models", mode="before")
419
452
  @classmethod
420
453
  def normalize_models(cls, value):
454
+ if value is None:
455
+ return None
421
456
  if isinstance(value, ModelList):
422
457
  return value
423
458
  elif isinstance(value, list):
@@ -433,6 +468,13 @@ class AgentData(CustomBaseModel):
433
468
  return ResourcePoolList(resource_pools=[ResourcePool.model_validate(item) if isinstance(item, dict) else item for item in value])
434
469
  return value
435
470
 
471
+ @field_validator("properties", mode="before")
472
+ @classmethod
473
+ def normalize_properties(cls, value):
474
+ if isinstance(value, list):
475
+ return [Property.model_validate(item) if isinstance(item, dict) else item for item in value]
476
+ return value
477
+
436
478
  @model_validator(mode="after")
437
479
  def validate_resource_pools_unique_names(self):
438
480
  if self.resource_pools:
@@ -451,8 +493,10 @@ class AgentData(CustomBaseModel):
451
493
  result = {
452
494
  "prompt": self.prompt.to_dict(),
453
495
  "llmConfig": self.llm_config.to_dict(),
454
- "models": self.models.to_dict(),
455
- "resourcePools": [pool.to_dict() for pool in self.resource_pools] if self.resource_pools else None
496
+ "strategyName": self.strategy_name,
497
+ "models": self.models.to_dict() if self.models else None,
498
+ "resourcePools": [pool.to_dict() for pool in self.resource_pools] if self.resource_pools else None,
499
+ "properties": [prop.to_dict() for prop in self.properties] if self.properties else None
456
500
  }
457
501
  return {k: v for k, v in result.items() if v is not None}
458
502
 
@@ -490,6 +534,9 @@ class Agent(CustomBaseModel):
490
534
  is_readonly: Optional[bool] = Field(False, alias="isReadonly")
491
535
  revision: Optional[int] = Field(None, alias="revision")
492
536
  version: Optional[Union[int | float]] = Field(None, alias="version")
537
+ sharing_scope: Optional[str] = Field(None, alias="sharingScope", description="Sharing scope of the agent")
538
+ permissions: Optional[Permission] = Field(None, alias="permissions", description="Permission settings")
539
+ effective_permissions: Optional[Permission] = Field(None, alias="effectivePermissions", description="Effective permission settings")
493
540
  agent_data: Optional[AgentData] = Field(None, alias="agentData")
494
541
 
495
542
  @field_validator("name")
@@ -528,6 +575,20 @@ class Agent(CustomBaseModel):
528
575
  return AgentData.model_validate(value)
529
576
  return value
530
577
 
578
+ @field_validator("permissions", mode="before")
579
+ @classmethod
580
+ def normalize_permissions(cls, value):
581
+ if isinstance(value, dict):
582
+ return Permission.model_validate(value)
583
+ return value
584
+
585
+ @field_validator("effective_permissions", mode="before")
586
+ @classmethod
587
+ def normalize_effective_permissions(cls, value):
588
+ if isinstance(value, dict):
589
+ return Permission.model_validate(value)
590
+ return value
591
+
531
592
  @model_validator(mode="after")
532
593
  def check_public_name(self):
533
594
  """
@@ -553,6 +614,9 @@ class Agent(CustomBaseModel):
553
614
  "isReadonly": self.is_readonly,
554
615
  "revision": self.revision,
555
616
  "version": self.version,
617
+ "sharingScope": self.sharing_scope,
618
+ "permissions": self.permissions.to_dict() if self.permissions else None,
619
+ "effectivePermissions": self.effective_permissions.to_dict() if self.effective_permissions else None,
556
620
  "agentData": self.agent_data.to_dict() if self.agent_data else None
557
621
  }
558
622
  return {k: v for k, v in result.items() if v is not None}
@@ -918,6 +982,7 @@ class KnowledgeBase(CustomBaseModel):
918
982
  artifact_type_name: Optional[List[str]] = Field(None, alias="artifactTypeName", description="List of artifact type names")
919
983
  artifacts: Optional[List[str]] = Field(None, description="List of artifact identifiers")
920
984
  metadata: Optional[List[str]] = Field(None, description="List of metadata identifiers")
985
+ created_at: Optional[str] = Field(None, alias="createdAt", description="Timestamp when the knowledge base was created")
921
986
 
922
987
  @field_validator("artifacts")
923
988
  @classmethod
@@ -1269,6 +1334,7 @@ class Task(CustomBaseModel):
1269
1334
  :param artifact_types: Optional[List[dict]] - List of artifact types with 'name', 'description', 'isRequired', 'usageType', and 'artifactVariableKey'.
1270
1335
  :param is_draft: Optional[bool] - Whether the task is in draft mode.
1271
1336
  :param revision: Optional[int] - Revision number of the task.
1337
+ :param version: Optional[int] - Version number of the task.
1272
1338
  :param status: Optional[str] - Current status of the task (e.g., 'active').
1273
1339
  """
1274
1340
  name: str = Field(..., description="Name of the task")
@@ -1279,6 +1345,7 @@ class Task(CustomBaseModel):
1279
1345
  artifact_types: Optional[Union[List[Dict[str, Any]], List[ArtifactType], ArtifactTypeList]] = Field(None, alias="artifactTypes", description="List of artifact types for the task")
1280
1346
  is_draft: Optional[bool] = Field(None, alias="isDraft", description="Whether the task is a draft")
1281
1347
  revision: Optional[int] = Field(None, description="Revision number of the task")
1348
+ version: Optional[int] = Field(None, description="Version number of the task")
1282
1349
  status: Optional[str] = Field(None, description="Status of the task (e.g., 'active')")
1283
1350
 
1284
1351
  @field_validator("name")
@@ -1367,6 +1434,7 @@ class Task(CustomBaseModel):
1367
1434
  "artifactTypes": self.artifact_types.to_dict() if self.artifact_types else None,
1368
1435
  "isDraft": self.is_draft,
1369
1436
  "revision": self.revision,
1437
+ "version": self.version,
1370
1438
  "status": self.status
1371
1439
  }
1372
1440
  return {k: v for k, v in result.items() if v is not None}