pygeai 0.6.0b7__py3-none-any.whl → 0.6.0b10__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 (178) hide show
  1. pygeai/_docs/source/conf.py +78 -6
  2. pygeai/_docs/source/content/api_reference/embeddings.rst +31 -1
  3. pygeai/_docs/source/content/api_reference/evaluation.rst +590 -0
  4. pygeai/_docs/source/content/api_reference/feedback.rst +237 -0
  5. pygeai/_docs/source/content/api_reference/files.rst +592 -0
  6. pygeai/_docs/source/content/api_reference/gam.rst +401 -0
  7. pygeai/_docs/source/content/api_reference/proxy.rst +318 -0
  8. pygeai/_docs/source/content/api_reference/secrets.rst +495 -0
  9. pygeai/_docs/source/content/api_reference/usage_limits.rst +390 -0
  10. pygeai/_docs/source/content/api_reference.rst +7 -0
  11. pygeai/_docs/source/content/debugger.rst +376 -83
  12. pygeai/_docs/source/content/migration.rst +528 -0
  13. pygeai/_docs/source/content/modules.rst +1 -1
  14. pygeai/_docs/source/pygeai.cli.rst +8 -0
  15. pygeai/_docs/source/pygeai.tests.cli.rst +16 -0
  16. pygeai/_docs/source/pygeai.tests.core.embeddings.rst +16 -0
  17. pygeai/_docs/source/pygeai.tests.snippets.chat.rst +40 -0
  18. pygeai/_docs/source/pygeai.tests.snippets.dbg.rst +45 -0
  19. pygeai/_docs/source/pygeai.tests.snippets.embeddings.rst +40 -0
  20. pygeai/_docs/source/pygeai.tests.snippets.evaluation.dataset.rst +197 -0
  21. pygeai/_docs/source/pygeai.tests.snippets.evaluation.plan.rst +133 -0
  22. pygeai/_docs/source/pygeai.tests.snippets.evaluation.result.rst +37 -0
  23. pygeai/_docs/source/pygeai.tests.snippets.evaluation.rst +10 -0
  24. pygeai/_docs/source/pygeai.tests.snippets.rst +1 -0
  25. pygeai/admin/clients.py +5 -0
  26. pygeai/assistant/clients.py +7 -0
  27. pygeai/assistant/data_analyst/clients.py +2 -0
  28. pygeai/assistant/rag/clients.py +11 -0
  29. pygeai/chat/clients.py +191 -25
  30. pygeai/chat/endpoints.py +2 -1
  31. pygeai/cli/commands/chat.py +227 -1
  32. pygeai/cli/commands/embeddings.py +56 -8
  33. pygeai/cli/commands/migrate.py +994 -434
  34. pygeai/cli/error_handler.py +116 -0
  35. pygeai/cli/geai.py +28 -10
  36. pygeai/cli/parsers.py +8 -2
  37. pygeai/core/base/clients.py +3 -1
  38. pygeai/core/common/exceptions.py +11 -10
  39. pygeai/core/embeddings/__init__.py +19 -0
  40. pygeai/core/embeddings/clients.py +17 -2
  41. pygeai/core/embeddings/mappers.py +16 -2
  42. pygeai/core/embeddings/responses.py +9 -2
  43. pygeai/core/feedback/clients.py +1 -0
  44. pygeai/core/files/clients.py +5 -7
  45. pygeai/core/files/managers.py +42 -0
  46. pygeai/core/llm/clients.py +4 -0
  47. pygeai/core/plugins/clients.py +1 -0
  48. pygeai/core/rerank/clients.py +1 -0
  49. pygeai/core/secrets/clients.py +6 -0
  50. pygeai/core/services/rest.py +1 -1
  51. pygeai/dbg/__init__.py +3 -0
  52. pygeai/dbg/debugger.py +565 -70
  53. pygeai/evaluation/clients.py +1 -1
  54. pygeai/evaluation/dataset/clients.py +45 -44
  55. pygeai/evaluation/plan/clients.py +27 -26
  56. pygeai/evaluation/result/clients.py +37 -5
  57. pygeai/gam/clients.py +4 -0
  58. pygeai/health/clients.py +1 -0
  59. pygeai/lab/agents/clients.py +8 -1
  60. pygeai/lab/models.py +3 -3
  61. pygeai/lab/processes/clients.py +21 -0
  62. pygeai/lab/strategies/clients.py +4 -0
  63. pygeai/lab/tools/clients.py +1 -0
  64. pygeai/migration/__init__.py +31 -0
  65. pygeai/migration/strategies.py +404 -155
  66. pygeai/migration/tools.py +170 -3
  67. pygeai/organization/clients.py +13 -0
  68. pygeai/organization/limits/clients.py +15 -0
  69. pygeai/proxy/clients.py +3 -1
  70. pygeai/tests/admin/test_clients.py +16 -11
  71. pygeai/tests/assistants/rag/test_clients.py +35 -23
  72. pygeai/tests/assistants/test_clients.py +22 -15
  73. pygeai/tests/auth/test_clients.py +14 -6
  74. pygeai/tests/chat/test_clients.py +211 -1
  75. pygeai/tests/cli/commands/test_embeddings.py +32 -9
  76. pygeai/tests/cli/commands/test_evaluation.py +7 -0
  77. pygeai/tests/cli/commands/test_migrate.py +112 -243
  78. pygeai/tests/cli/test_error_handler.py +225 -0
  79. pygeai/tests/cli/test_geai_driver.py +154 -0
  80. pygeai/tests/cli/test_parsers.py +5 -5
  81. pygeai/tests/core/embeddings/test_clients.py +144 -0
  82. pygeai/tests/core/embeddings/test_managers.py +171 -0
  83. pygeai/tests/core/embeddings/test_mappers.py +142 -0
  84. pygeai/tests/core/feedback/test_clients.py +2 -0
  85. pygeai/tests/core/files/test_clients.py +1 -0
  86. pygeai/tests/core/llm/test_clients.py +14 -9
  87. pygeai/tests/core/plugins/test_clients.py +5 -3
  88. pygeai/tests/core/rerank/test_clients.py +1 -0
  89. pygeai/tests/core/secrets/test_clients.py +19 -13
  90. pygeai/tests/dbg/test_debugger.py +453 -75
  91. pygeai/tests/evaluation/dataset/test_clients.py +3 -1
  92. pygeai/tests/evaluation/plan/test_clients.py +4 -2
  93. pygeai/tests/evaluation/result/test_clients.py +7 -5
  94. pygeai/tests/gam/test_clients.py +1 -1
  95. pygeai/tests/health/test_clients.py +1 -0
  96. pygeai/tests/lab/agents/test_clients.py +9 -0
  97. pygeai/tests/lab/processes/test_clients.py +36 -0
  98. pygeai/tests/lab/processes/test_mappers.py +3 -0
  99. pygeai/tests/lab/strategies/test_clients.py +14 -9
  100. pygeai/tests/migration/test_strategies.py +45 -218
  101. pygeai/tests/migration/test_tools.py +133 -9
  102. pygeai/tests/organization/limits/test_clients.py +17 -0
  103. pygeai/tests/organization/test_clients.py +22 -0
  104. pygeai/tests/proxy/test_clients.py +2 -0
  105. pygeai/tests/proxy/test_integration.py +1 -0
  106. pygeai/tests/snippets/chat/chat_completion_with_reasoning_effort.py +18 -0
  107. pygeai/tests/snippets/chat/get_response.py +15 -0
  108. pygeai/tests/snippets/chat/get_response_streaming.py +20 -0
  109. pygeai/tests/snippets/chat/get_response_with_files.py +16 -0
  110. pygeai/tests/snippets/chat/get_response_with_tools.py +36 -0
  111. pygeai/tests/snippets/dbg/__init__.py +0 -0
  112. pygeai/tests/snippets/dbg/basic_debugging.py +32 -0
  113. pygeai/tests/snippets/dbg/breakpoint_management.py +48 -0
  114. pygeai/tests/snippets/dbg/stack_navigation.py +45 -0
  115. pygeai/tests/snippets/dbg/stepping_example.py +40 -0
  116. pygeai/tests/snippets/embeddings/cache_example.py +31 -0
  117. pygeai/tests/snippets/embeddings/cohere_example.py +41 -0
  118. pygeai/tests/snippets/embeddings/openai_base64_example.py +27 -0
  119. pygeai/tests/snippets/embeddings/openai_example.py +30 -0
  120. pygeai/tests/snippets/embeddings/similarity_example.py +42 -0
  121. pygeai/tests/snippets/evaluation/dataset/__init__.py +0 -0
  122. pygeai/tests/snippets/evaluation/dataset/complete_workflow_example.py +195 -0
  123. pygeai/tests/snippets/evaluation/dataset/create_dataset.py +26 -0
  124. pygeai/tests/snippets/evaluation/dataset/create_dataset_from_file.py +11 -0
  125. pygeai/tests/snippets/evaluation/dataset/create_dataset_row.py +17 -0
  126. pygeai/tests/snippets/evaluation/dataset/create_expected_source.py +18 -0
  127. pygeai/tests/snippets/evaluation/dataset/create_filter_variable.py +19 -0
  128. pygeai/tests/snippets/evaluation/dataset/delete_dataset.py +9 -0
  129. pygeai/tests/snippets/evaluation/dataset/delete_dataset_row.py +10 -0
  130. pygeai/tests/snippets/evaluation/dataset/delete_expected_source.py +15 -0
  131. pygeai/tests/snippets/evaluation/dataset/delete_filter_variable.py +15 -0
  132. pygeai/tests/snippets/evaluation/dataset/get_dataset.py +9 -0
  133. pygeai/tests/snippets/evaluation/dataset/get_dataset_row.py +10 -0
  134. pygeai/tests/snippets/evaluation/dataset/get_expected_source.py +15 -0
  135. pygeai/tests/snippets/evaluation/dataset/get_filter_variable.py +15 -0
  136. pygeai/tests/snippets/evaluation/dataset/list_dataset_rows.py +9 -0
  137. pygeai/tests/snippets/evaluation/dataset/list_datasets.py +6 -0
  138. pygeai/tests/snippets/evaluation/dataset/list_expected_sources.py +10 -0
  139. pygeai/tests/snippets/evaluation/dataset/list_filter_variables.py +10 -0
  140. pygeai/tests/snippets/evaluation/dataset/update_dataset.py +15 -0
  141. pygeai/tests/snippets/evaluation/dataset/update_dataset_row.py +20 -0
  142. pygeai/tests/snippets/evaluation/dataset/update_expected_source.py +18 -0
  143. pygeai/tests/snippets/evaluation/dataset/update_filter_variable.py +19 -0
  144. pygeai/tests/snippets/evaluation/dataset/upload_dataset_rows_file.py +10 -0
  145. pygeai/tests/snippets/evaluation/plan/__init__.py +0 -0
  146. pygeai/tests/snippets/evaluation/plan/add_plan_system_metric.py +13 -0
  147. pygeai/tests/snippets/evaluation/plan/complete_workflow_example.py +136 -0
  148. pygeai/tests/snippets/evaluation/plan/create_evaluation_plan.py +24 -0
  149. pygeai/tests/snippets/evaluation/plan/create_rag_evaluation_plan.py +22 -0
  150. pygeai/tests/snippets/evaluation/plan/delete_evaluation_plan.py +9 -0
  151. pygeai/tests/snippets/evaluation/plan/delete_plan_system_metric.py +13 -0
  152. pygeai/tests/snippets/evaluation/plan/execute_evaluation_plan.py +11 -0
  153. pygeai/tests/snippets/evaluation/plan/get_evaluation_plan.py +9 -0
  154. pygeai/tests/snippets/evaluation/plan/get_plan_system_metric.py +13 -0
  155. pygeai/tests/snippets/evaluation/plan/get_system_metric.py +9 -0
  156. pygeai/tests/snippets/evaluation/plan/list_evaluation_plans.py +7 -0
  157. pygeai/tests/snippets/evaluation/plan/list_plan_system_metrics.py +9 -0
  158. pygeai/tests/snippets/evaluation/plan/list_system_metrics.py +7 -0
  159. pygeai/tests/snippets/evaluation/plan/update_evaluation_plan.py +22 -0
  160. pygeai/tests/snippets/evaluation/plan/update_plan_system_metric.py +14 -0
  161. pygeai/tests/snippets/evaluation/result/__init__.py +0 -0
  162. pygeai/tests/snippets/evaluation/result/complete_workflow_example.py +150 -0
  163. pygeai/tests/snippets/evaluation/result/get_evaluation_result.py +26 -0
  164. pygeai/tests/snippets/evaluation/result/list_evaluation_results.py +17 -0
  165. pygeai/tests/snippets/migrate/__init__.py +45 -0
  166. pygeai/tests/snippets/migrate/agent_migration.py +110 -0
  167. pygeai/tests/snippets/migrate/assistant_migration.py +64 -0
  168. pygeai/tests/snippets/migrate/orchestrator_examples.py +179 -0
  169. pygeai/tests/snippets/migrate/process_migration.py +64 -0
  170. pygeai/tests/snippets/migrate/project_migration.py +42 -0
  171. pygeai/tests/snippets/migrate/tool_migration.py +64 -0
  172. pygeai/tests/snippets/organization/create_project.py +2 -2
  173. {pygeai-0.6.0b7.dist-info → pygeai-0.6.0b10.dist-info}/METADATA +1 -1
  174. {pygeai-0.6.0b7.dist-info → pygeai-0.6.0b10.dist-info}/RECORD +178 -96
  175. {pygeai-0.6.0b7.dist-info → pygeai-0.6.0b10.dist-info}/WHEEL +0 -0
  176. {pygeai-0.6.0b7.dist-info → pygeai-0.6.0b10.dist-info}/entry_points.txt +0 -0
  177. {pygeai-0.6.0b7.dist-info → pygeai-0.6.0b10.dist-info}/licenses/LICENSE +0 -0
  178. {pygeai-0.6.0b7.dist-info → pygeai-0.6.0b10.dist-info}/top_level.txt +0 -0
@@ -1,22 +1,29 @@
1
1
  import sys
2
2
  from abc import ABC, abstractmethod
3
+ from typing import Optional
3
4
 
4
- from pygeai.core.models import Project
5
+ from pygeai import logger
6
+ from pygeai.core.models import Project, UsageLimit
5
7
  from pygeai.core.base.responses import ErrorListResponse
6
8
  from pygeai.core.utils.console import Console
9
+ from pygeai.core.files.managers import FileManager
10
+ from pygeai.core.files.models import File
7
11
  from pygeai.lab.managers import AILabManager
8
12
  from pygeai.lab.models import Agent, Tool, AgenticProcess, Task
13
+ from pygeai.organization.limits.managers import UsageLimitManager
9
14
  from pygeai.organization.managers import OrganizationManager
15
+ from pygeai.assistant.managers import AssistantManager
16
+ from pygeai.assistant.rag.models import RAGAssistant
10
17
 
11
18
 
12
19
  class MigrationStrategy(ABC):
13
20
 
14
21
  def __init__(
15
22
  self,
16
- from_api_key: str = None,
17
- from_instance: str = None,
18
- to_api_key: str = None,
19
- to_instance: str = None
23
+ from_api_key: str,
24
+ from_instance: str,
25
+ to_api_key: Optional[str] = None,
26
+ to_instance: Optional[str] = None
20
27
  ):
21
28
  self.from_api_key = from_api_key
22
29
  self.from_instance = from_instance
@@ -27,65 +34,196 @@ class MigrationStrategy(ABC):
27
34
  def migrate(self):
28
35
  pass
29
36
 
37
+ def get_display_info(self) -> str:
38
+ """
39
+ Return a human-readable description of what this strategy will migrate.
40
+ Used for progress tracking and logging.
41
+
42
+ :return: String description like "agent abc-123" or "project MyProject"
43
+ """
44
+ return self.__class__.__name__.replace("MigrationStrategy", "").lower()
45
+
30
46
 
31
47
  class ProjectMigrationStrategy(MigrationStrategy):
32
48
  """
33
49
  Migrate a project from a GEAI instance.
34
50
  The target project can be in another organization or the same one; in the same instance or another.
51
+
52
+ Note: This strategy requires organization scope API keys for both source and destination,
53
+ as it needs to create projects and manage usage limits using the Organization API.
54
+ See: https://docs.globant.ai/en/wiki?22,Organization+API
35
55
  """
36
56
 
37
57
  def __init__(
38
58
  self,
39
- from_api_key: str = None,
40
- from_instance: str = None,
41
- to_api_key: str = None,
42
- to_instance: str = None,
43
- from_project_id: str = None,
44
- to_project_name: str = None,
45
- admin_email: str = None
59
+ from_api_key: str,
60
+ from_instance: str,
61
+ from_project_id: str,
62
+ to_project_name: str,
63
+ admin_email: str,
64
+ to_api_key: Optional[str] = None,
65
+ to_instance: Optional[str] = None
46
66
  ):
47
67
  super().__init__(from_api_key, from_instance, to_api_key, to_instance)
48
68
  self.from_project_id = from_project_id
49
69
  self.to_project_name = to_project_name
50
70
  self.admin_email = admin_email
51
- self.source_manager = OrganizationManager(
71
+ self._source_manager = OrganizationManager(
52
72
  api_key=self.from_api_key,
53
73
  base_url=self.from_instance
54
74
  )
55
- self.destination_manager = OrganizationManager(
75
+ self._destination_manager = OrganizationManager(
56
76
  api_key=self.to_api_key,
57
77
  base_url=self.to_instance
58
78
  )
59
79
 
80
+ def get_display_info(self) -> str:
81
+ return f"project '{self.to_project_name}'"
82
+
60
83
  def migrate(self):
61
- response = self.__migrate_project()
84
+ """
85
+ Execute the project migration from source to destination instance.
86
+
87
+ :return: The ID of the created project
88
+ :raises ValueError: If project data cannot be retrieved or migration fails
89
+ """
90
+ new_project = self._migrate_project()
91
+ project_name = getattr(new_project, 'name', 'Unknown')
92
+ project_id = getattr(new_project, 'id', None)
93
+ logger.info(f"Successfully migrated project {self.from_project_id} to {project_name}")
94
+ return project_id
95
+
96
+ def _migrate_project(self) -> Project:
97
+ """
98
+ Migrate the project data and create it in the destination instance.
99
+
100
+ :return: The newly created project
101
+ :raises ValueError: If project retrieval or creation fails
102
+ """
103
+ project_data = self._source_manager.get_project_data(project_id=self.from_project_id)
104
+
105
+ if not hasattr(project_data, "project"):
106
+ raise ValueError(f"Unable to retrieve project data for project {self.from_project_id}")
107
+
108
+ new_project = project_data.project
109
+ new_project.name = self.to_project_name
110
+ new_project.email = self.admin_email
111
+
112
+ logger.debug(f"Creating project with destination manager:")
113
+ logger.debug(f" - API Key (first 20 chars): {self.to_api_key[:20] if self.to_api_key else 'None'}...")
114
+ logger.debug(f" - Base URL: {self.to_instance}")
115
+ logger.debug(f" - Project Name: {self.to_project_name}")
116
+ logger.debug(f" - Admin Email: {self.admin_email}")
117
+
118
+ try:
119
+ response = self._destination_manager.create_project(new_project)
120
+ except Exception as e:
121
+ error_msg = f"Create project failed: {e}"
122
+ logger.error(error_msg)
123
+ logger.error(f" - Operation: Create project")
124
+ logger.error(f" - Base URL: {self.to_instance}")
125
+ logger.error(f" - API Key used (first 20 chars): {self.to_api_key[:20] if self.to_api_key else 'None'}...")
126
+ Console.write_stderr(f"\nDEBUG: Operation failed: Create project")
127
+ Console.write_stderr(f"DEBUG: Base URL: {self.to_instance}")
128
+ Console.write_stderr(f"DEBUG: API Key used (first 20 chars): {self.to_api_key[:20] if self.to_api_key else 'None'}...")
129
+ raise ValueError(error_msg) from e
130
+
62
131
  if isinstance(response, ErrorListResponse):
63
- Console.write_stderr(f"{response.to_dict()}")
64
- elif not response:
65
- Console.write_stderr(f"Unable to migrate project")
66
- else:
67
- new_project = response.project
132
+ error_detail = response.to_dict()
133
+ logger.error(f"Create project returned error response: {error_detail}")
134
+ logger.error(f" - Operation: Create project")
135
+ logger.error(f" - Base URL: {self.to_instance}")
136
+ logger.error(f" - API Key used (first 20 chars): {self.to_api_key[:20] if self.to_api_key else 'None'}...")
137
+ Console.write_stderr(f"\nDEBUG: Operation failed: Create project")
138
+ Console.write_stderr(f"DEBUG: Base URL: {self.to_instance}")
139
+ Console.write_stderr(f"DEBUG: API Key used (first 20 chars): {self.to_api_key[:20] if self.to_api_key else 'None'}...")
140
+ raise ValueError(f"Failed to create project: {error_detail}")
141
+
142
+ if not response or not hasattr(response, "project"):
143
+ raise ValueError("Project creation returned invalid response")
144
+
145
+ return response.project
146
+
147
+ def _migrate_assistants(self, new_project: Project):
148
+ """
149
+ Migrate assistants associated with the project.
150
+
151
+ :param new_project: The newly created project to migrate assistants to
152
+ """
153
+ pass
68
154
 
69
- self.__migrate_assistants(new_project)
70
155
 
71
- Console.write_stdout(f"Migrated project: \n{response}")
156
+ class _LabResourceMigrationStrategy(MigrationStrategy):
157
+ """
158
+ Base class for migrating AI Lab resources (agents, tools, processes, tasks).
159
+ Provides common functionality for resource migration.
160
+ """
72
161
 
73
- def __migrate_project(self):
74
- project_data = self.source_manager.get_project_data(project_id=self.from_project_id)
162
+ def __init__(
163
+ self,
164
+ from_api_key: str,
165
+ from_instance: str,
166
+ to_api_key: Optional[str] = None,
167
+ to_instance: Optional[str] = None
168
+ ):
169
+ super().__init__(from_api_key, from_instance, to_api_key, to_instance)
170
+ self._source_manager = AILabManager(
171
+ api_key=self.from_api_key,
172
+ base_url=self.from_instance
173
+ )
174
+ self._destination_manager = AILabManager(
175
+ api_key=self.to_api_key,
176
+ base_url=self.to_instance
177
+ )
75
178
 
76
- if hasattr(project_data, "project"):
77
- new_project = project_data.project
78
- new_project.name = self.to_project_name
79
- new_project.email = self.admin_email
80
- response = self.destination_manager.create_project(new_project)
179
+ @abstractmethod
180
+ def _get_resource(self, resource_id: str):
181
+ """Retrieve the resource from source instance"""
182
+ pass
81
183
 
82
- return response
184
+ @abstractmethod
185
+ def _create_resource(self, resource):
186
+ """Create the resource in destination instance"""
187
+ pass
83
188
 
84
- def __migrate_assistants(self, new_project: Project):
189
+ @abstractmethod
190
+ def _get_resource_name(self) -> str:
191
+ """Return the name of the resource type for logging/errors"""
85
192
  pass
86
193
 
194
+ def get_display_info(self) -> str:
195
+ resource_id = getattr(self, f"{self._get_resource_name()}_id", "unknown")
196
+ return f"{self._get_resource_name()} {resource_id}"
87
197
 
88
- class AgentMigrationStrategy(MigrationStrategy):
198
+ def migrate(self):
199
+ """
200
+ Execute the resource migration from source to destination instance.
201
+
202
+ :raises ValueError: If resource retrieval or creation fails
203
+ """
204
+ resource_id = getattr(self, f"{self._get_resource_name()}_id")
205
+ new_resource = self._migrate_resource(resource_id)
206
+ Console.write_stdout(f"New {self._get_resource_name()} detail: \n{new_resource}")
207
+ logger.info(f"Successfully migrated {self._get_resource_name()} {resource_id}")
208
+
209
+ def _migrate_resource(self, resource_id: str):
210
+ """
211
+ Retrieve resource from source and create in destination.
212
+
213
+ :param resource_id: The ID of the resource to migrate
214
+ :return: The newly created resource
215
+ :raises ValueError: If migration fails
216
+ """
217
+ try:
218
+ source_resource = self._get_resource(resource_id)
219
+ new_resource = self._create_resource(source_resource)
220
+ return new_resource
221
+ except Exception as e:
222
+ logger.error(f"{self._get_resource_name().capitalize()} migration failed: {e}")
223
+ raise ValueError(f"{self._get_resource_name().capitalize()} migration failed: {e}") from e
224
+
225
+
226
+ class AgentMigrationStrategy(_LabResourceMigrationStrategy):
89
227
  """
90
228
  Migrate an agent from a GEAI instance.
91
229
  The target project can be in another organization or the same one; in the same instance or another.
@@ -93,49 +231,29 @@ class AgentMigrationStrategy(MigrationStrategy):
93
231
 
94
232
  def __init__(
95
233
  self,
96
- from_api_key: str = None,
97
- from_instance: str = None,
98
- to_api_key: str = None,
99
- to_instance: str = None,
100
- from_project_id: str = None,
101
- to_project_id: str = None,
102
- agent_id: str = None
234
+ from_api_key: str,
235
+ from_instance: str,
236
+ agent_id: str,
237
+ to_api_key: Optional[str] = None,
238
+ to_instance: Optional[str] = None
103
239
  ):
104
240
  super().__init__(from_api_key, from_instance, to_api_key, to_instance)
105
- self.from_project_id = from_project_id
106
- self.to_project_id = to_project_id
107
241
  self.agent_id = agent_id
108
- self.source_manager = AILabManager(
109
- api_key=self.from_api_key,
110
- base_url=self.from_instance
111
- )
112
- self.destination_manager = AILabManager(
113
- api_key=self.to_api_key,
114
- base_url=self.to_instance
115
- )
116
242
 
117
- def migrate(self):
118
- new_agent = self.__migrate_agent()
119
- if isinstance(new_agent, ErrorListResponse):
120
- Console.write_stderr(f"{new_agent.to_dict()}")
121
- else:
122
- Console.write_stdout(f"New agent detail: \n{new_agent}")
123
-
124
- def __migrate_agent(self):
125
- new_agent = None
126
- try:
127
- source_agent = self.source_manager.get_agent(agent_id=self.agent_id)
128
- if not isinstance(source_agent, Agent):
129
- raise ValueError("Unable to retrieve requested agent.")
243
+ def _get_resource(self, resource_id: str) -> Agent:
244
+ agent = self._source_manager.get_agent(agent_id=resource_id)
245
+ if not isinstance(agent, Agent):
246
+ raise ValueError(f"Unable to retrieve agent {resource_id}")
247
+ return agent
130
248
 
131
- new_agent = self.destination_manager.create_agent(agent=source_agent)
132
- except Exception as e:
133
- Console.write_stderr(f"Agent migration failed: {e} \n")
249
+ def _create_resource(self, resource: Agent) -> Agent:
250
+ return self._destination_manager.create_agent(agent=resource)
134
251
 
135
- return new_agent
252
+ def _get_resource_name(self) -> str:
253
+ return "agent"
136
254
 
137
255
 
138
- class ToolMigrationStrategy(MigrationStrategy):
256
+ class ToolMigrationStrategy(_LabResourceMigrationStrategy):
139
257
  """
140
258
  Migrate a tool from a GEAI instance.
141
259
  The target project can be in another organization or the same one; in the same instance or another.
@@ -143,143 +261,274 @@ class ToolMigrationStrategy(MigrationStrategy):
143
261
 
144
262
  def __init__(
145
263
  self,
146
- from_api_key: str = None,
147
- from_instance: str = None,
148
- to_api_key: str = None,
149
- to_instance: str = None,
150
- from_project_id: str = None,
151
- to_project_id: str = None,
152
- tool_id: str = None
264
+ from_api_key: str,
265
+ from_instance: str,
266
+ tool_id: str,
267
+ to_api_key: Optional[str] = None,
268
+ to_instance: Optional[str] = None
153
269
  ):
154
270
  super().__init__(from_api_key, from_instance, to_api_key, to_instance)
155
- self.from_project_id = from_project_id
156
- self.to_project_id = to_project_id
157
271
  self.tool_id = tool_id
158
- self.source_manager = AILabManager(
272
+
273
+ def _get_resource(self, resource_id: str) -> Tool:
274
+ tool = self._source_manager.get_tool(tool_id=resource_id)
275
+ if not isinstance(tool, Tool):
276
+ raise ValueError(f"Unable to retrieve tool {resource_id}")
277
+ return tool
278
+
279
+ def _create_resource(self, resource: Tool) -> Tool:
280
+ return self._destination_manager.create_tool(tool=resource)
281
+
282
+ def _get_resource_name(self) -> str:
283
+ return "tool"
284
+
285
+
286
+ class AgenticProcessMigrationStrategy(_LabResourceMigrationStrategy):
287
+ """
288
+ Migrate an agentic process from a GEAI instance.
289
+ The target project can be in another organization or the same one; in the same instance or another.
290
+ """
291
+
292
+ def __init__(
293
+ self,
294
+ from_api_key: str,
295
+ from_instance: str,
296
+ process_id: str,
297
+ to_api_key: Optional[str] = None,
298
+ to_instance: Optional[str] = None
299
+ ):
300
+ super().__init__(from_api_key, from_instance, to_api_key, to_instance)
301
+ self.process_id = process_id
302
+
303
+ def _get_resource(self, resource_id: str) -> AgenticProcess:
304
+ process = self._source_manager.get_process(process_id=resource_id)
305
+ if not isinstance(process, AgenticProcess):
306
+ raise ValueError(f"Unable to retrieve process {resource_id}")
307
+ return process
308
+
309
+ def _create_resource(self, resource: AgenticProcess) -> AgenticProcess:
310
+ return self._destination_manager.create_process(process=resource)
311
+
312
+ def _get_resource_name(self) -> str:
313
+ return "process"
314
+
315
+
316
+ class TaskMigrationStrategy(_LabResourceMigrationStrategy):
317
+ """
318
+ Migrate a task from a GEAI instance.
319
+ The target project can be in another organization or the same one; in the same instance or another.
320
+ """
321
+
322
+ def __init__(
323
+ self,
324
+ from_api_key: str,
325
+ from_instance: str,
326
+ task_id: str,
327
+ to_api_key: Optional[str] = None,
328
+ to_instance: Optional[str] = None
329
+ ):
330
+ super().__init__(from_api_key, from_instance, to_api_key, to_instance)
331
+ self.task_id = task_id
332
+
333
+ def _get_resource(self, resource_id: str) -> Task:
334
+ task = self._source_manager.get_task(task_id=resource_id)
335
+ if not isinstance(task, Task):
336
+ raise ValueError(f"Unable to retrieve task {resource_id}")
337
+ return task
338
+
339
+ def _create_resource(self, resource: Task) -> Task:
340
+ return self._destination_manager.create_task(task=resource)
341
+
342
+ def _get_resource_name(self) -> str:
343
+ return "task"
344
+
345
+
346
+ class UsageLimitMigrationStrategy(MigrationStrategy):
347
+ """
348
+ Migrate usage limit from a GEAI organization.
349
+ The target organization can be in the same instance or another.
350
+ """
351
+
352
+ def __init__(
353
+ self,
354
+ from_api_key: str,
355
+ from_instance: str,
356
+ from_organization_id: str,
357
+ to_organization_id: str,
358
+ to_api_key: Optional[str] = None,
359
+ to_instance: Optional[str] = None
360
+ ):
361
+ super().__init__(from_api_key, from_instance, to_api_key, to_instance)
362
+ self.from_organization_id = from_organization_id
363
+ self.to_organization_id = to_organization_id
364
+ self._source_manager = UsageLimitManager(
159
365
  api_key=self.from_api_key,
160
- base_url=self.from_instance
366
+ base_url=self.from_instance,
367
+ organization_id=self.from_organization_id
161
368
  )
162
- self.destination_manager = AILabManager(
369
+ self._destination_manager = UsageLimitManager(
163
370
  api_key=self.to_api_key,
164
- base_url=self.to_instance
371
+ base_url=self.to_instance,
372
+ organization_id=self.to_organization_id
165
373
  )
166
374
 
375
+ def get_display_info(self) -> str:
376
+ return f"usage limits (org {self.from_organization_id})"
377
+
167
378
  def migrate(self):
168
- new_tool = self.__migrate_tool()
169
- if isinstance(new_tool, ErrorListResponse):
170
- Console.write_stderr(f"{new_tool.to_dict()}")
171
- else:
172
- Console.write_stdout(f"New tool detail: \n{new_tool}")
173
-
174
- def __migrate_tool(self):
175
- new_tool = None
379
+ """
380
+ Execute the usage limit migration from source to destination organization.
381
+
382
+ :raises ValueError: If usage limit retrieval or creation fails
383
+ """
384
+ new_limit = self._migrate_usage_limit()
385
+ logger.info(f"Successfully migrated usage limit from org {self.from_organization_id} to {self.to_organization_id}")
386
+
387
+ def _migrate_usage_limit(self) -> UsageLimit:
388
+ """
389
+ Retrieve latest usage limit from source and create in destination.
390
+
391
+ :return: The newly created usage limit
392
+ :raises ValueError: If migration fails
393
+ """
176
394
  try:
177
- source_tool = self.source_manager.get_tool(tool_id=self.tool_id)
178
- if not isinstance(source_tool, Tool):
179
- raise ValueError("Unable to retrieve requested tool.")
395
+ source_limit = self._source_manager.get_latest_usage_limit_from_organization()
396
+ if not isinstance(source_limit, UsageLimit):
397
+ raise ValueError("Unable to retrieve usage limit from source organization")
180
398
 
181
- new_tool = self.destination_manager.create_tool(tool=source_tool)
399
+ source_limit.id = None
400
+ new_limit = self._destination_manager.set_organization_usage_limit(source_limit)
401
+ return new_limit
182
402
  except Exception as e:
183
- Console.write_stderr(f"Tool migration failed: {e}")
403
+ logger.error(f"Usage limit migration failed: {e}")
404
+ raise ValueError(f"Usage limit migration failed: {e}") from e
184
405
 
185
- return new_tool
186
406
 
187
-
188
- class AgenticProcessMigrationStrategy(MigrationStrategy):
407
+ class RAGAssistantMigrationStrategy(MigrationStrategy):
189
408
  """
190
- Migrate an agentic process from a GEAI instance.
191
- The target project can be in another organization or the same one; in the same instance or another.
409
+ Migrate RAG assistant from a GEAI instance.
410
+ The target instance can be the same or another.
192
411
  """
193
412
 
194
413
  def __init__(
195
414
  self,
196
- from_api_key: str = None,
197
- from_instance: str = None,
198
- to_api_key: str = None,
199
- to_instance: str = None,
200
- from_project_id: str = None,
201
- to_project_id: str = None,
202
- process_id: str = None
415
+ from_api_key: str,
416
+ from_instance: str,
417
+ assistant_name: str,
418
+ to_api_key: Optional[str] = None,
419
+ to_instance: Optional[str] = None
203
420
  ):
204
421
  super().__init__(from_api_key, from_instance, to_api_key, to_instance)
205
- self.from_project_id = from_project_id
206
- self.to_project_id = to_project_id
207
- self.process_id = process_id
208
- self.source_manager = AILabManager(
422
+ self.assistant_name = assistant_name
423
+ self._source_manager = AssistantManager(
209
424
  api_key=self.from_api_key,
210
425
  base_url=self.from_instance
211
426
  )
212
- self.destination_manager = AILabManager(
427
+ self._destination_manager = AssistantManager(
213
428
  api_key=self.to_api_key,
214
429
  base_url=self.to_instance
215
430
  )
216
431
 
432
+ def get_display_info(self) -> str:
433
+ return f"RAG assistant '{self.assistant_name}'"
434
+
217
435
  def migrate(self):
218
- new_process = self.__migrate_process()
219
- if isinstance(new_process, ErrorListResponse):
220
- Console.write_stdout(f"{new_process.to_dict()}")
221
- else:
222
- Console.write_stdout(f"New process detail: \n{new_process}")
223
-
224
- def __migrate_process(self):
225
- new_process = None
436
+ """
437
+ Execute the RAG assistant migration from source to destination instance.
438
+
439
+ :raises ValueError: If assistant retrieval or creation fails
440
+ """
441
+ new_assistant = self._migrate_assistant()
442
+ logger.info(f"Successfully migrated RAG assistant {self.assistant_name}")
443
+
444
+ def _migrate_assistant(self) -> RAGAssistant:
445
+ """
446
+ Retrieve RAG assistant from source and create in destination.
447
+
448
+ :return: The newly created RAG assistant
449
+ :raises ValueError: If migration fails
450
+ """
226
451
  try:
227
- source_process = self.source_manager.get_process(process_id=self.process_id)
228
- if not isinstance(source_process, AgenticProcess):
229
- raise ValueError("Unable to retrieve requested process.")
452
+ source_assistant = self._source_manager.get_assistant_data(assistant_name=self.assistant_name)
453
+ if not isinstance(source_assistant, RAGAssistant):
454
+ raise ValueError(f"Assistant {self.assistant_name} is not a RAG assistant")
230
455
 
231
- new_process = self.destination_manager.create_process(process=source_process)
456
+ source_assistant.id = None
457
+ new_assistant = self._destination_manager.create_assistant(source_assistant)
458
+ return new_assistant
232
459
  except Exception as e:
233
- Console.write_stderr(f"Process migration failed: {e}")
234
-
235
- return new_process
460
+ logger.error(f"RAG assistant migration failed: {e}")
461
+ raise ValueError(f"RAG assistant migration failed: {e}") from e
236
462
 
237
463
 
238
- class TaskMigrationStrategy(MigrationStrategy):
464
+ class FileMigrationStrategy(MigrationStrategy):
239
465
  """
240
- Migrate a task from a GEAI instance.
466
+ Migrate file from a GEAI project.
241
467
  The target project can be in another organization or the same one; in the same instance or another.
242
468
  """
243
469
 
244
470
  def __init__(
245
471
  self,
246
- from_api_key: str = None,
247
- from_instance: str = None,
248
- to_api_key: str = None,
249
- to_instance: str = None,
250
- from_project_id: str = None,
251
- to_project_id: str = None,
252
- task_id: str = None
472
+ from_api_key: str,
473
+ from_instance: str,
474
+ from_organization_id: str,
475
+ from_project_id: str,
476
+ to_organization_id: str,
477
+ to_project_id: str,
478
+ file_id: str,
479
+ to_api_key: Optional[str] = None,
480
+ to_instance: Optional[str] = None
253
481
  ):
254
482
  super().__init__(from_api_key, from_instance, to_api_key, to_instance)
483
+ self.from_organization_id = from_organization_id
255
484
  self.from_project_id = from_project_id
485
+ self.to_organization_id = to_organization_id
256
486
  self.to_project_id = to_project_id
257
- self.task_id = task_id
258
- self.source_manager = AILabManager(
487
+ self.file_id = file_id
488
+ self._source_manager = FileManager(
259
489
  api_key=self.from_api_key,
260
- base_url=self.from_instance
490
+ base_url=self.from_instance,
491
+ organization_id=self.from_organization_id,
492
+ project_id=self.from_project_id
261
493
  )
262
- self.destination_manager = AILabManager(
494
+ self._destination_manager = FileManager(
263
495
  api_key=self.to_api_key,
264
- base_url=self.to_instance
496
+ base_url=self.to_instance,
497
+ organization_id=self.to_organization_id,
498
+ project_id=self.to_project_id
265
499
  )
266
500
 
501
+ def get_display_info(self) -> str:
502
+ return f"file {self.file_id}"
503
+
267
504
  def migrate(self):
268
- new_task = self.__migrate_task()
269
- if isinstance(new_task, ErrorListResponse):
270
- Console.write_stderr(f"{new_task.to_dict()}")
271
- else:
272
- Console.write_stdout(f"New task detail: \n{new_task}")
273
-
274
- def __migrate_task(self):
275
- new_task = None
505
+ """
506
+ Execute the file migration from source to destination project.
507
+
508
+ :raises ValueError: If file retrieval or upload fails
509
+ """
510
+ new_file = self._migrate_file()
511
+ logger.info(f"Successfully migrated file {self.file_id}")
512
+
513
+ def _migrate_file(self) -> File:
514
+ """
515
+ Retrieve file from source and upload to destination.
516
+
517
+ :return: The newly uploaded file
518
+ :raises ValueError: If migration fails
519
+ """
276
520
  try:
277
- source_task = self.source_manager.get_task(task_id=self.task_id)
278
- if not isinstance(source_task, Task):
279
- raise ValueError("Unable to retrieve requested task.")
280
-
281
- new_task = self.destination_manager.create_task(task=source_task)
521
+ source_file = self._source_manager.get_file_data(file_id=self.file_id)
522
+ if not isinstance(source_file, File):
523
+ raise ValueError(f"Unable to retrieve file {self.file_id}")
524
+
525
+ file_content = self._source_manager.get_file_content(file_id=self.file_id)
526
+ upload_response = self._destination_manager.upload_file_from_content(
527
+ file_name=source_file.name,
528
+ content=file_content,
529
+ folder=None
530
+ )
531
+ return upload_response.file
282
532
  except Exception as e:
283
- Console.write_stderr(f"Task migration failed: {e}")
284
-
285
- return new_task
533
+ logger.error(f"File migration failed: {e}")
534
+ raise ValueError(f"File migration failed: {e}") from e