pygeai 0.6.0b7__py3-none-any.whl → 0.6.0b11__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.
- pygeai/_docs/source/conf.py +78 -6
- pygeai/_docs/source/content/api_reference/embeddings.rst +31 -1
- pygeai/_docs/source/content/api_reference/evaluation.rst +590 -0
- pygeai/_docs/source/content/api_reference/feedback.rst +237 -0
- pygeai/_docs/source/content/api_reference/files.rst +592 -0
- pygeai/_docs/source/content/api_reference/gam.rst +401 -0
- pygeai/_docs/source/content/api_reference/proxy.rst +318 -0
- pygeai/_docs/source/content/api_reference/secrets.rst +495 -0
- pygeai/_docs/source/content/api_reference/usage_limits.rst +390 -0
- pygeai/_docs/source/content/api_reference.rst +7 -0
- pygeai/_docs/source/content/debugger.rst +376 -83
- pygeai/_docs/source/content/migration.rst +528 -0
- pygeai/_docs/source/content/modules.rst +1 -1
- pygeai/_docs/source/pygeai.cli.rst +8 -0
- pygeai/_docs/source/pygeai.tests.cli.rst +16 -0
- pygeai/_docs/source/pygeai.tests.core.embeddings.rst +16 -0
- pygeai/_docs/source/pygeai.tests.snippets.chat.rst +40 -0
- pygeai/_docs/source/pygeai.tests.snippets.dbg.rst +45 -0
- pygeai/_docs/source/pygeai.tests.snippets.embeddings.rst +40 -0
- pygeai/_docs/source/pygeai.tests.snippets.evaluation.dataset.rst +197 -0
- pygeai/_docs/source/pygeai.tests.snippets.evaluation.plan.rst +133 -0
- pygeai/_docs/source/pygeai.tests.snippets.evaluation.result.rst +37 -0
- pygeai/_docs/source/pygeai.tests.snippets.evaluation.rst +10 -0
- pygeai/_docs/source/pygeai.tests.snippets.rst +1 -0
- pygeai/admin/clients.py +5 -0
- pygeai/assistant/clients.py +7 -0
- pygeai/assistant/data_analyst/clients.py +2 -0
- pygeai/assistant/rag/clients.py +11 -0
- pygeai/chat/clients.py +236 -25
- pygeai/chat/endpoints.py +3 -1
- pygeai/cli/commands/chat.py +322 -1
- pygeai/cli/commands/embeddings.py +56 -8
- pygeai/cli/commands/migrate.py +994 -434
- pygeai/cli/error_handler.py +116 -0
- pygeai/cli/geai.py +28 -10
- pygeai/cli/parsers.py +8 -2
- pygeai/core/base/clients.py +3 -1
- pygeai/core/common/exceptions.py +11 -10
- pygeai/core/embeddings/__init__.py +19 -0
- pygeai/core/embeddings/clients.py +17 -2
- pygeai/core/embeddings/mappers.py +16 -2
- pygeai/core/embeddings/responses.py +9 -2
- pygeai/core/feedback/clients.py +1 -0
- pygeai/core/files/clients.py +5 -7
- pygeai/core/files/managers.py +42 -0
- pygeai/core/llm/clients.py +4 -0
- pygeai/core/plugins/clients.py +1 -0
- pygeai/core/rerank/clients.py +1 -0
- pygeai/core/secrets/clients.py +6 -0
- pygeai/core/services/rest.py +1 -1
- pygeai/dbg/__init__.py +3 -0
- pygeai/dbg/debugger.py +565 -70
- pygeai/evaluation/clients.py +1 -1
- pygeai/evaluation/dataset/clients.py +45 -44
- pygeai/evaluation/plan/clients.py +27 -26
- pygeai/evaluation/result/clients.py +37 -5
- pygeai/gam/clients.py +4 -0
- pygeai/health/clients.py +1 -0
- pygeai/lab/agents/clients.py +8 -1
- pygeai/lab/models.py +3 -3
- pygeai/lab/processes/clients.py +21 -0
- pygeai/lab/strategies/clients.py +4 -0
- pygeai/lab/tools/clients.py +1 -0
- pygeai/migration/__init__.py +31 -0
- pygeai/migration/strategies.py +404 -155
- pygeai/migration/tools.py +170 -3
- pygeai/organization/clients.py +13 -0
- pygeai/organization/limits/clients.py +15 -0
- pygeai/proxy/clients.py +3 -1
- pygeai/tests/admin/test_clients.py +16 -11
- pygeai/tests/assistants/rag/test_clients.py +35 -23
- pygeai/tests/assistants/test_clients.py +22 -15
- pygeai/tests/auth/test_clients.py +14 -6
- pygeai/tests/chat/test_clients.py +211 -1
- pygeai/tests/cli/commands/test_embeddings.py +32 -9
- pygeai/tests/cli/commands/test_evaluation.py +7 -0
- pygeai/tests/cli/commands/test_migrate.py +112 -243
- pygeai/tests/cli/test_error_handler.py +225 -0
- pygeai/tests/cli/test_geai_driver.py +154 -0
- pygeai/tests/cli/test_parsers.py +5 -5
- pygeai/tests/core/embeddings/test_clients.py +144 -0
- pygeai/tests/core/embeddings/test_managers.py +171 -0
- pygeai/tests/core/embeddings/test_mappers.py +142 -0
- pygeai/tests/core/feedback/test_clients.py +2 -0
- pygeai/tests/core/files/test_clients.py +1 -0
- pygeai/tests/core/llm/test_clients.py +14 -9
- pygeai/tests/core/plugins/test_clients.py +5 -3
- pygeai/tests/core/rerank/test_clients.py +1 -0
- pygeai/tests/core/secrets/test_clients.py +19 -13
- pygeai/tests/dbg/test_debugger.py +453 -75
- pygeai/tests/evaluation/dataset/test_clients.py +3 -1
- pygeai/tests/evaluation/plan/test_clients.py +4 -2
- pygeai/tests/evaluation/result/test_clients.py +7 -5
- pygeai/tests/gam/test_clients.py +1 -1
- pygeai/tests/health/test_clients.py +1 -0
- pygeai/tests/lab/agents/test_clients.py +9 -0
- pygeai/tests/lab/processes/test_clients.py +36 -0
- pygeai/tests/lab/processes/test_mappers.py +3 -0
- pygeai/tests/lab/strategies/test_clients.py +14 -9
- pygeai/tests/migration/test_strategies.py +45 -218
- pygeai/tests/migration/test_tools.py +133 -9
- pygeai/tests/organization/limits/test_clients.py +17 -0
- pygeai/tests/organization/test_clients.py +22 -0
- pygeai/tests/proxy/test_clients.py +2 -0
- pygeai/tests/proxy/test_integration.py +1 -0
- pygeai/tests/snippets/chat/chat_completion_with_reasoning_effort.py +18 -0
- pygeai/tests/snippets/chat/get_response.py +15 -0
- pygeai/tests/snippets/chat/get_response_streaming.py +20 -0
- pygeai/tests/snippets/chat/get_response_with_files.py +16 -0
- pygeai/tests/snippets/chat/get_response_with_tools.py +36 -0
- pygeai/tests/snippets/dbg/__init__.py +0 -0
- pygeai/tests/snippets/dbg/basic_debugging.py +32 -0
- pygeai/tests/snippets/dbg/breakpoint_management.py +48 -0
- pygeai/tests/snippets/dbg/stack_navigation.py +45 -0
- pygeai/tests/snippets/dbg/stepping_example.py +40 -0
- pygeai/tests/snippets/embeddings/cache_example.py +31 -0
- pygeai/tests/snippets/embeddings/cohere_example.py +41 -0
- pygeai/tests/snippets/embeddings/openai_base64_example.py +27 -0
- pygeai/tests/snippets/embeddings/openai_example.py +30 -0
- pygeai/tests/snippets/embeddings/similarity_example.py +42 -0
- pygeai/tests/snippets/evaluation/dataset/__init__.py +0 -0
- pygeai/tests/snippets/evaluation/dataset/complete_workflow_example.py +195 -0
- pygeai/tests/snippets/evaluation/dataset/create_dataset.py +26 -0
- pygeai/tests/snippets/evaluation/dataset/create_dataset_from_file.py +11 -0
- pygeai/tests/snippets/evaluation/dataset/create_dataset_row.py +17 -0
- pygeai/tests/snippets/evaluation/dataset/create_expected_source.py +18 -0
- pygeai/tests/snippets/evaluation/dataset/create_filter_variable.py +19 -0
- pygeai/tests/snippets/evaluation/dataset/delete_dataset.py +9 -0
- pygeai/tests/snippets/evaluation/dataset/delete_dataset_row.py +10 -0
- pygeai/tests/snippets/evaluation/dataset/delete_expected_source.py +15 -0
- pygeai/tests/snippets/evaluation/dataset/delete_filter_variable.py +15 -0
- pygeai/tests/snippets/evaluation/dataset/get_dataset.py +9 -0
- pygeai/tests/snippets/evaluation/dataset/get_dataset_row.py +10 -0
- pygeai/tests/snippets/evaluation/dataset/get_expected_source.py +15 -0
- pygeai/tests/snippets/evaluation/dataset/get_filter_variable.py +15 -0
- pygeai/tests/snippets/evaluation/dataset/list_dataset_rows.py +9 -0
- pygeai/tests/snippets/evaluation/dataset/list_datasets.py +6 -0
- pygeai/tests/snippets/evaluation/dataset/list_expected_sources.py +10 -0
- pygeai/tests/snippets/evaluation/dataset/list_filter_variables.py +10 -0
- pygeai/tests/snippets/evaluation/dataset/update_dataset.py +15 -0
- pygeai/tests/snippets/evaluation/dataset/update_dataset_row.py +20 -0
- pygeai/tests/snippets/evaluation/dataset/update_expected_source.py +18 -0
- pygeai/tests/snippets/evaluation/dataset/update_filter_variable.py +19 -0
- pygeai/tests/snippets/evaluation/dataset/upload_dataset_rows_file.py +10 -0
- pygeai/tests/snippets/evaluation/plan/__init__.py +0 -0
- pygeai/tests/snippets/evaluation/plan/add_plan_system_metric.py +13 -0
- pygeai/tests/snippets/evaluation/plan/complete_workflow_example.py +136 -0
- pygeai/tests/snippets/evaluation/plan/create_evaluation_plan.py +24 -0
- pygeai/tests/snippets/evaluation/plan/create_rag_evaluation_plan.py +22 -0
- pygeai/tests/snippets/evaluation/plan/delete_evaluation_plan.py +9 -0
- pygeai/tests/snippets/evaluation/plan/delete_plan_system_metric.py +13 -0
- pygeai/tests/snippets/evaluation/plan/execute_evaluation_plan.py +11 -0
- pygeai/tests/snippets/evaluation/plan/get_evaluation_plan.py +9 -0
- pygeai/tests/snippets/evaluation/plan/get_plan_system_metric.py +13 -0
- pygeai/tests/snippets/evaluation/plan/get_system_metric.py +9 -0
- pygeai/tests/snippets/evaluation/plan/list_evaluation_plans.py +7 -0
- pygeai/tests/snippets/evaluation/plan/list_plan_system_metrics.py +9 -0
- pygeai/tests/snippets/evaluation/plan/list_system_metrics.py +7 -0
- pygeai/tests/snippets/evaluation/plan/update_evaluation_plan.py +22 -0
- pygeai/tests/snippets/evaluation/plan/update_plan_system_metric.py +14 -0
- pygeai/tests/snippets/evaluation/result/__init__.py +0 -0
- pygeai/tests/snippets/evaluation/result/complete_workflow_example.py +150 -0
- pygeai/tests/snippets/evaluation/result/get_evaluation_result.py +26 -0
- pygeai/tests/snippets/evaluation/result/list_evaluation_results.py +17 -0
- pygeai/tests/snippets/migrate/__init__.py +45 -0
- pygeai/tests/snippets/migrate/agent_migration.py +110 -0
- pygeai/tests/snippets/migrate/assistant_migration.py +64 -0
- pygeai/tests/snippets/migrate/orchestrator_examples.py +179 -0
- pygeai/tests/snippets/migrate/process_migration.py +64 -0
- pygeai/tests/snippets/migrate/project_migration.py +42 -0
- pygeai/tests/snippets/migrate/tool_migration.py +64 -0
- pygeai/tests/snippets/organization/create_project.py +2 -2
- {pygeai-0.6.0b7.dist-info → pygeai-0.6.0b11.dist-info}/METADATA +1 -1
- {pygeai-0.6.0b7.dist-info → pygeai-0.6.0b11.dist-info}/RECORD +178 -96
- {pygeai-0.6.0b7.dist-info → pygeai-0.6.0b11.dist-info}/WHEEL +0 -0
- {pygeai-0.6.0b7.dist-info → pygeai-0.6.0b11.dist-info}/entry_points.txt +0 -0
- {pygeai-0.6.0b7.dist-info → pygeai-0.6.0b11.dist-info}/licenses/LICENSE +0 -0
- {pygeai-0.6.0b7.dist-info → pygeai-0.6.0b11.dist-info}/top_level.txt +0 -0
pygeai/migration/tools.py
CHANGED
|
@@ -1,13 +1,180 @@
|
|
|
1
|
+
from typing import List, Dict, Optional
|
|
2
|
+
from dataclasses import dataclass, field
|
|
3
|
+
|
|
4
|
+
from pygeai import logger
|
|
1
5
|
from pygeai.migration.strategies import MigrationStrategy
|
|
6
|
+
from pygeai.core.utils.console import Console
|
|
2
7
|
|
|
3
8
|
|
|
4
9
|
class MigrationTool:
|
|
10
|
+
"""
|
|
11
|
+
Orchestrates migration operations using configurable strategies.
|
|
12
|
+
|
|
13
|
+
This class provides a flexible way to execute migrations with support for:
|
|
14
|
+
- Batch migrations of multiple resources
|
|
15
|
+
- Dependency ordering
|
|
16
|
+
- Progress tracking
|
|
17
|
+
- Dry-run mode
|
|
18
|
+
- Rollback capabilities
|
|
19
|
+
"""
|
|
5
20
|
|
|
6
21
|
def __init__(self, strategy: MigrationStrategy):
|
|
7
|
-
self.
|
|
22
|
+
self._strategy = strategy
|
|
8
23
|
|
|
9
24
|
def set_strategy(self, strategy: MigrationStrategy):
|
|
10
|
-
|
|
25
|
+
"""
|
|
26
|
+
Update the migration strategy.
|
|
27
|
+
|
|
28
|
+
:param strategy: The new migration strategy to use
|
|
29
|
+
"""
|
|
30
|
+
self._strategy = strategy
|
|
11
31
|
|
|
12
32
|
def run_migration(self):
|
|
13
|
-
|
|
33
|
+
"""
|
|
34
|
+
Execute the configured migration strategy.
|
|
35
|
+
|
|
36
|
+
:return: The result from the migration strategy (if any)
|
|
37
|
+
:raises ValueError: If migration fails
|
|
38
|
+
"""
|
|
39
|
+
logger.info(f"Starting migration with strategy: {self._strategy.__class__.__name__}")
|
|
40
|
+
return self._strategy.migrate()
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass
|
|
44
|
+
class MigrationPlan:
|
|
45
|
+
"""
|
|
46
|
+
Defines a migration plan with multiple strategies and execution order.
|
|
47
|
+
|
|
48
|
+
:param strategies: List of migration strategies to execute
|
|
49
|
+
:param dependencies: Map of strategy index to list of dependent strategy indices
|
|
50
|
+
:param dry_run: If True, validate without executing migrations
|
|
51
|
+
:param stop_on_error: If True, stop execution on first error
|
|
52
|
+
"""
|
|
53
|
+
strategies: List[MigrationStrategy]
|
|
54
|
+
dependencies: Dict[int, List[int]] = field(default_factory=dict)
|
|
55
|
+
dry_run: bool = False
|
|
56
|
+
stop_on_error: bool = True
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class MigrationOrchestrator:
|
|
60
|
+
"""
|
|
61
|
+
Advanced orchestration for complex migration scenarios.
|
|
62
|
+
|
|
63
|
+
Handles batch migrations, dependency resolution, progress tracking,
|
|
64
|
+
and rollback on failure.
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
def __init__(self, plan: MigrationPlan):
|
|
68
|
+
self._plan = plan
|
|
69
|
+
self._completed: List[int] = []
|
|
70
|
+
self._failed: List[int] = []
|
|
71
|
+
|
|
72
|
+
def execute(self) -> Dict[str, any]:
|
|
73
|
+
"""
|
|
74
|
+
Execute the migration plan respecting dependencies.
|
|
75
|
+
|
|
76
|
+
:return: Summary of migration results
|
|
77
|
+
:raises ValueError: If migration fails and stop_on_error is True
|
|
78
|
+
"""
|
|
79
|
+
logger.info(f"Executing migration plan with {len(self._plan.strategies)} strategies")
|
|
80
|
+
|
|
81
|
+
if self._plan.dry_run:
|
|
82
|
+
return self._validate_plan()
|
|
83
|
+
|
|
84
|
+
execution_order = self._resolve_dependencies()
|
|
85
|
+
total_strategies = len(execution_order)
|
|
86
|
+
|
|
87
|
+
Console.write_stdout("")
|
|
88
|
+
Console.write_stdout("=" * 60)
|
|
89
|
+
Console.write_stdout(f"Migration Progress: 0/{total_strategies} completed")
|
|
90
|
+
Console.write_stdout("=" * 60)
|
|
91
|
+
|
|
92
|
+
for position, idx in enumerate(execution_order, 1):
|
|
93
|
+
strategy = self._plan.strategies[idx]
|
|
94
|
+
display_info = strategy.get_display_info()
|
|
95
|
+
|
|
96
|
+
Console.write_stdout(f"\n[{position}/{total_strategies}] Migrating {display_info}...")
|
|
97
|
+
|
|
98
|
+
try:
|
|
99
|
+
logger.info(f"Executing strategy {idx + 1}/{len(self._plan.strategies)}: {strategy.__class__.__name__}")
|
|
100
|
+
strategy.migrate()
|
|
101
|
+
self._completed.append(idx)
|
|
102
|
+
Console.write_stdout(f"✓ Successfully migrated {display_info}")
|
|
103
|
+
except Exception as e:
|
|
104
|
+
logger.error(f"Strategy {idx} failed: {e}")
|
|
105
|
+
self._failed.append(idx)
|
|
106
|
+
Console.write_stdout(f"✗ Failed to migrate {display_info}: {e}")
|
|
107
|
+
if self._plan.stop_on_error:
|
|
108
|
+
raise ValueError(f"Migration failed at strategy {idx}: {e}") from e
|
|
109
|
+
|
|
110
|
+
Console.write_stdout("")
|
|
111
|
+
Console.write_stdout("=" * 60)
|
|
112
|
+
Console.write_stdout(f"Migration Complete: {len(self._completed)}/{total_strategies} successful")
|
|
113
|
+
Console.write_stdout("=" * 60)
|
|
114
|
+
|
|
115
|
+
return self._generate_summary()
|
|
116
|
+
|
|
117
|
+
def _resolve_dependencies(self) -> List[int]:
|
|
118
|
+
"""
|
|
119
|
+
Resolve strategy execution order based on dependencies.
|
|
120
|
+
|
|
121
|
+
:return: Ordered list of strategy indices
|
|
122
|
+
:raises ValueError: If circular dependencies detected
|
|
123
|
+
"""
|
|
124
|
+
visited = set()
|
|
125
|
+
order = []
|
|
126
|
+
|
|
127
|
+
def visit(idx: int, path: set):
|
|
128
|
+
if idx in path:
|
|
129
|
+
raise ValueError(f"Circular dependency detected at strategy {idx}")
|
|
130
|
+
if idx in visited:
|
|
131
|
+
return
|
|
132
|
+
|
|
133
|
+
path.add(idx)
|
|
134
|
+
for dep_idx in self._plan.dependencies.get(idx, []):
|
|
135
|
+
visit(dep_idx, path)
|
|
136
|
+
path.remove(idx)
|
|
137
|
+
|
|
138
|
+
visited.add(idx)
|
|
139
|
+
order.append(idx)
|
|
140
|
+
|
|
141
|
+
for idx in range(len(self._plan.strategies)):
|
|
142
|
+
visit(idx, set())
|
|
143
|
+
|
|
144
|
+
return order
|
|
145
|
+
|
|
146
|
+
def _validate_plan(self) -> Dict[str, any]:
|
|
147
|
+
"""
|
|
148
|
+
Validate the migration plan without executing.
|
|
149
|
+
|
|
150
|
+
:return: Validation results
|
|
151
|
+
"""
|
|
152
|
+
logger.info("Validating migration plan (dry-run mode)")
|
|
153
|
+
try:
|
|
154
|
+
execution_order = self._resolve_dependencies()
|
|
155
|
+
return {
|
|
156
|
+
"valid": True,
|
|
157
|
+
"execution_order": execution_order,
|
|
158
|
+
"total_strategies": len(self._plan.strategies)
|
|
159
|
+
}
|
|
160
|
+
except Exception as e:
|
|
161
|
+
logger.error(f"Validation failed: {e}")
|
|
162
|
+
return {
|
|
163
|
+
"valid": False,
|
|
164
|
+
"error": str(e)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
def _generate_summary(self) -> Dict[str, any]:
|
|
168
|
+
"""
|
|
169
|
+
Generate a summary of migration results.
|
|
170
|
+
|
|
171
|
+
:return: Summary dictionary with completed, failed, and pending migrations
|
|
172
|
+
"""
|
|
173
|
+
return {
|
|
174
|
+
"total": len(self._plan.strategies),
|
|
175
|
+
"completed": len(self._completed),
|
|
176
|
+
"failed": len(self._failed),
|
|
177
|
+
"success_rate": len(self._completed) / len(self._plan.strategies) if self._plan.strategies else 0,
|
|
178
|
+
"completed_indices": self._completed,
|
|
179
|
+
"failed_indices": self._failed
|
|
180
|
+
}
|
pygeai/organization/clients.py
CHANGED
|
@@ -25,6 +25,7 @@ class OrganizationClient(BaseClient):
|
|
|
25
25
|
:return: AssistantListResponse - The API response containing the list of assistants and the project.
|
|
26
26
|
"""
|
|
27
27
|
response = self.api_service.get(endpoint=GET_ASSISTANT_LIST_V1, params={"detail": detail})
|
|
28
|
+
validate_status_code(response)
|
|
28
29
|
return parse_json_response(response, "get assistant list")
|
|
29
30
|
|
|
30
31
|
def get_project_list(
|
|
@@ -56,6 +57,7 @@ class OrganizationClient(BaseClient):
|
|
|
56
57
|
"detail": detail
|
|
57
58
|
}
|
|
58
59
|
)
|
|
60
|
+
validate_status_code(response)
|
|
59
61
|
return parse_json_response(response, "get project list")
|
|
60
62
|
|
|
61
63
|
def get_project_data(
|
|
@@ -72,6 +74,7 @@ class OrganizationClient(BaseClient):
|
|
|
72
74
|
response = self.api_service.get(
|
|
73
75
|
endpoint=endpoint
|
|
74
76
|
)
|
|
77
|
+
validate_status_code(response)
|
|
75
78
|
return parse_json_response(response, "get project data for ID", project_id=project_id)
|
|
76
79
|
|
|
77
80
|
def create_project(
|
|
@@ -109,6 +112,7 @@ class OrganizationClient(BaseClient):
|
|
|
109
112
|
"description": description
|
|
110
113
|
}
|
|
111
114
|
)
|
|
115
|
+
validate_status_code(response)
|
|
112
116
|
return parse_json_response(response, "create project with name", name=name)
|
|
113
117
|
|
|
114
118
|
def update_project(
|
|
@@ -133,6 +137,7 @@ class OrganizationClient(BaseClient):
|
|
|
133
137
|
"description": description
|
|
134
138
|
}
|
|
135
139
|
)
|
|
140
|
+
validate_status_code(response)
|
|
136
141
|
return parse_json_response(response, "update project with ID", project_id=project_id)
|
|
137
142
|
|
|
138
143
|
def delete_project(
|
|
@@ -147,6 +152,7 @@ class OrganizationClient(BaseClient):
|
|
|
147
152
|
"""
|
|
148
153
|
endpoint = DELETE_PROJECT_V1.format(id=project_id)
|
|
149
154
|
response = self.api_service.delete(endpoint=endpoint)
|
|
155
|
+
validate_status_code(response)
|
|
150
156
|
return parse_json_response(response, "delete project with ID", project_id=project_id)
|
|
151
157
|
|
|
152
158
|
def get_project_tokens(
|
|
@@ -161,6 +167,7 @@ class OrganizationClient(BaseClient):
|
|
|
161
167
|
"""
|
|
162
168
|
endpoint = GET_PROJECT_TOKENS_V1.format(id=project_id)
|
|
163
169
|
response = self.api_service.get(endpoint=endpoint)
|
|
170
|
+
validate_status_code(response)
|
|
164
171
|
return parse_json_response(response, "get tokens for project with ID", project_id=project_id)
|
|
165
172
|
|
|
166
173
|
def export_request_data(
|
|
@@ -188,6 +195,7 @@ class OrganizationClient(BaseClient):
|
|
|
188
195
|
"count": count
|
|
189
196
|
}
|
|
190
197
|
)
|
|
198
|
+
validate_status_code(response)
|
|
191
199
|
return parse_json_response(response, "export request data")
|
|
192
200
|
|
|
193
201
|
def get_memberships(
|
|
@@ -223,6 +231,7 @@ class OrganizationClient(BaseClient):
|
|
|
223
231
|
params["roleTypes"] = role_types
|
|
224
232
|
|
|
225
233
|
response = self.api_service.get(endpoint=GET_MEMBERSHIPS_V2, params=params)
|
|
234
|
+
validate_status_code(response)
|
|
226
235
|
return parse_json_response(response, "get memberships")
|
|
227
236
|
|
|
228
237
|
def get_project_memberships(
|
|
@@ -258,6 +267,7 @@ class OrganizationClient(BaseClient):
|
|
|
258
267
|
params["roleTypes"] = role_types
|
|
259
268
|
|
|
260
269
|
response = self.api_service.get(endpoint=GET_PROJECT_MEMBERSHIPS_V2, params=params)
|
|
270
|
+
validate_status_code(response)
|
|
261
271
|
return parse_json_response(response, "get project memberships")
|
|
262
272
|
|
|
263
273
|
def get_project_roles(
|
|
@@ -271,6 +281,7 @@ class OrganizationClient(BaseClient):
|
|
|
271
281
|
:return: dict - The API response containing the list of roles for the project, in JSON format.
|
|
272
282
|
"""
|
|
273
283
|
response = self.api_service.get(endpoint=GET_PROJECT_ROLES_V2, params={"projectId": project_id})
|
|
284
|
+
validate_status_code(response)
|
|
274
285
|
return parse_json_response(response, "get project roles for project", project_id=project_id)
|
|
275
286
|
|
|
276
287
|
def get_project_members(
|
|
@@ -284,6 +295,7 @@ class OrganizationClient(BaseClient):
|
|
|
284
295
|
:return: dict - The API response containing the list of members with their roles, in JSON format.
|
|
285
296
|
"""
|
|
286
297
|
response = self.api_service.get(endpoint=GET_PROJECT_MEMBERS_V2, params={"projectId": project_id})
|
|
298
|
+
validate_status_code(response)
|
|
287
299
|
return parse_json_response(response, "get project members for project", project_id=project_id)
|
|
288
300
|
|
|
289
301
|
def get_organization_members(
|
|
@@ -297,4 +309,5 @@ class OrganizationClient(BaseClient):
|
|
|
297
309
|
:return: dict - The API response containing the list of members with their roles, in JSON format.
|
|
298
310
|
"""
|
|
299
311
|
response = self.api_service.get(endpoint=GET_ORGANIZATION_MEMBERS_V2, params={"organizationId": organization_id})
|
|
312
|
+
validate_status_code(response)
|
|
300
313
|
return parse_json_response(response, "get organization members for organization", organization_id=organization_id)
|
|
@@ -34,6 +34,7 @@ class UsageLimitClient(BaseClient):
|
|
|
34
34
|
endpoint=endpoint,
|
|
35
35
|
data=usage_limit
|
|
36
36
|
)
|
|
37
|
+
validate_status_code(response)
|
|
37
38
|
return parse_json_response(response, f"set usage limit for organization", organization=organization)
|
|
38
39
|
|
|
39
40
|
def get_organization_latest_usage_limit(self, organization: str) -> dict:
|
|
@@ -45,6 +46,7 @@ class UsageLimitClient(BaseClient):
|
|
|
45
46
|
"""
|
|
46
47
|
endpoint = GET_ORGANIZATION_LATEST_USAGE_LIMIT_V2.format(organization=organization)
|
|
47
48
|
response = self.api_service.get(endpoint=endpoint)
|
|
49
|
+
validate_status_code(response)
|
|
48
50
|
return parse_json_response(response, f"get latest usage limit for organization", organization=organization)
|
|
49
51
|
|
|
50
52
|
def get_all_usage_limits_from_organization(self, organization: str) -> dict:
|
|
@@ -56,6 +58,7 @@ class UsageLimitClient(BaseClient):
|
|
|
56
58
|
"""
|
|
57
59
|
endpoint = GET_ALL_ORGANIZATION_USAGE_LIMITS_V2.format(organization=organization)
|
|
58
60
|
response = self.api_service.get(endpoint=endpoint)
|
|
61
|
+
validate_status_code(response)
|
|
59
62
|
return parse_json_response(response, f"get all usage limits for organization", organization=organization)
|
|
60
63
|
|
|
61
64
|
def delete_usage_limit_from_organization(self, organization: str, limit_id: str) -> dict:
|
|
@@ -68,6 +71,7 @@ class UsageLimitClient(BaseClient):
|
|
|
68
71
|
"""
|
|
69
72
|
endpoint = DELETE_ORGANIZATION_USAGE_LIMIT_V2.format(organization=organization, id=limit_id)
|
|
70
73
|
response = self.api_service.delete(endpoint=endpoint)
|
|
74
|
+
validate_status_code(response)
|
|
71
75
|
return parse_json_response(response, f"delete usage limit with ID '{limit_id}' from organization", organization=organization)
|
|
72
76
|
|
|
73
77
|
def set_organization_hard_limit(self, organization: str, limit_id: str, hard_limit: float) -> dict:
|
|
@@ -86,6 +90,7 @@ class UsageLimitClient(BaseClient):
|
|
|
86
90
|
"hardLimit": hard_limit
|
|
87
91
|
}
|
|
88
92
|
)
|
|
93
|
+
validate_status_code(response)
|
|
89
94
|
return parse_json_response(response, f"set hard limit for usage limit ID '{limit_id}' in organization", organization=organization)
|
|
90
95
|
|
|
91
96
|
def set_organization_soft_limit(self, organization: str, limit_id: str, soft_limit: float) -> dict:
|
|
@@ -104,6 +109,7 @@ class UsageLimitClient(BaseClient):
|
|
|
104
109
|
"softLimit": soft_limit
|
|
105
110
|
}
|
|
106
111
|
)
|
|
112
|
+
validate_status_code(response)
|
|
107
113
|
return parse_json_response(response, f"set soft limit for usage limit ID '{limit_id}' in organization", organization=organization)
|
|
108
114
|
|
|
109
115
|
def set_organization_renewal_status(self, organization: str, limit_id: str, renewal_status: str) -> dict:
|
|
@@ -122,6 +128,7 @@ class UsageLimitClient(BaseClient):
|
|
|
122
128
|
"renewalStatus": renewal_status
|
|
123
129
|
}
|
|
124
130
|
)
|
|
131
|
+
validate_status_code(response)
|
|
125
132
|
return parse_json_response(response, f"set renewal status for usage limit ID '{limit_id}' in organization", organization=organization)
|
|
126
133
|
|
|
127
134
|
def set_project_usage_limit(self, organization: str, project: str, usage_limit: dict) -> dict:
|
|
@@ -145,6 +152,7 @@ class UsageLimitClient(BaseClient):
|
|
|
145
152
|
endpoint=endpoint,
|
|
146
153
|
data=usage_limit
|
|
147
154
|
)
|
|
155
|
+
validate_status_code(response)
|
|
148
156
|
return parse_json_response(response, f"set usage limit for project '{project}' in organization", organization=organization)
|
|
149
157
|
|
|
150
158
|
def get_all_usage_limits_from_project(self, organization: str, project: str) -> dict:
|
|
@@ -157,6 +165,7 @@ class UsageLimitClient(BaseClient):
|
|
|
157
165
|
"""
|
|
158
166
|
endpoint = GET_ALL_PROJECT_USAGE_LIMIT_V2.format(organization=organization, project=project)
|
|
159
167
|
response = self.api_service.get(endpoint=endpoint)
|
|
168
|
+
validate_status_code(response)
|
|
160
169
|
return parse_json_response(response, f"get all usage limits for project '{project}' in organization", organization=organization)
|
|
161
170
|
|
|
162
171
|
def get_latest_usage_limit_from_project(self, organization: str, project: str) -> dict:
|
|
@@ -169,6 +178,7 @@ class UsageLimitClient(BaseClient):
|
|
|
169
178
|
"""
|
|
170
179
|
endpoint = GET_LATEST_PROJECT_USAGE_LIMIT_V2.format(organization=organization, project=project)
|
|
171
180
|
response = self.api_service.get(endpoint=endpoint)
|
|
181
|
+
validate_status_code(response)
|
|
172
182
|
return parse_json_response(response, f"get latest usage limit for project '{project}' in organization", organization=organization)
|
|
173
183
|
|
|
174
184
|
def get_active_usage_limit_from_project(self, organization: str, project: str) -> dict:
|
|
@@ -181,6 +191,7 @@ class UsageLimitClient(BaseClient):
|
|
|
181
191
|
"""
|
|
182
192
|
endpoint = GET_PROJECT_ACTIVE_USAGE_LIMIT_V2.format(organization=organization, project=project)
|
|
183
193
|
response = self.api_service.get(endpoint=endpoint)
|
|
194
|
+
validate_status_code(response)
|
|
184
195
|
return parse_json_response(response, f"get active usage limit for project '{project}' in organization", organization=organization)
|
|
185
196
|
|
|
186
197
|
def delete_usage_limit_from_project(self, organization: str, project: str, limit_id: str) -> dict:
|
|
@@ -194,6 +205,7 @@ class UsageLimitClient(BaseClient):
|
|
|
194
205
|
"""
|
|
195
206
|
endpoint = DELETE_PROJECT_USAGE_LIMIT_V2.format(organization=organization, project=project, id=limit_id)
|
|
196
207
|
response = self.api_service.delete(endpoint=endpoint)
|
|
208
|
+
validate_status_code(response)
|
|
197
209
|
return parse_json_response(response, f"delete usage limit with ID '{limit_id}' for project '{project}' in organization", organization=organization)
|
|
198
210
|
|
|
199
211
|
def set_hard_limit_for_active_usage_limit_from_project(
|
|
@@ -219,6 +231,7 @@ class UsageLimitClient(BaseClient):
|
|
|
219
231
|
"hardLimit": hard_limit
|
|
220
232
|
}
|
|
221
233
|
)
|
|
234
|
+
validate_status_code(response)
|
|
222
235
|
return parse_json_response(response, f"set hard limit for usage limit ID '{limit_id}' for project '{project}' in organization", organization=organization)
|
|
223
236
|
|
|
224
237
|
def set_soft_limit_for_active_usage_limit_from_project(
|
|
@@ -244,6 +257,7 @@ class UsageLimitClient(BaseClient):
|
|
|
244
257
|
"softLimit": soft_limit
|
|
245
258
|
}
|
|
246
259
|
)
|
|
260
|
+
validate_status_code(response)
|
|
247
261
|
return parse_json_response(response, f"set soft limit for usage limit ID '{limit_id}' for project '{project}' in organization", organization=organization)
|
|
248
262
|
|
|
249
263
|
def set_project_renewal_status(self, organization: str, project: str, limit_id: str, renewal_status: str) -> dict:
|
|
@@ -263,4 +277,5 @@ class UsageLimitClient(BaseClient):
|
|
|
263
277
|
"renewalStatus": renewal_status
|
|
264
278
|
}
|
|
265
279
|
)
|
|
280
|
+
validate_status_code(response)
|
|
266
281
|
return parse_json_response(response, f"set renewal status for usage limit ID '{limit_id}' for project '{project}' in organization", organization=organization)
|
pygeai/proxy/clients.py
CHANGED
|
@@ -5,6 +5,7 @@ import requests
|
|
|
5
5
|
from urllib3.exceptions import MaxRetryError
|
|
6
6
|
from pygeai.proxy.tool import ProxiedTool
|
|
7
7
|
from pygeai.core.utils.validators import validate_status_code
|
|
8
|
+
from pygeai.core.utils.parsers import parse_json_response
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
@dataclass
|
|
@@ -151,7 +152,8 @@ class ProxyClient:
|
|
|
151
152
|
response = self.session.request(method, url, **kwargs)
|
|
152
153
|
response.raise_for_status()
|
|
153
154
|
try:
|
|
154
|
-
|
|
155
|
+
validate_status_code(response)
|
|
156
|
+
return parse_json_response(response, "unknown operation")
|
|
155
157
|
except ValueError:
|
|
156
158
|
return response.text
|
|
157
159
|
except requests.exceptions.Timeout:
|
|
@@ -3,7 +3,7 @@ from unittest.mock import patch, MagicMock
|
|
|
3
3
|
from json import JSONDecodeError
|
|
4
4
|
|
|
5
5
|
from pygeai.admin.clients import AdminClient
|
|
6
|
-
from pygeai.core.common.exceptions import InvalidAPIResponseException
|
|
6
|
+
from pygeai.core.common.exceptions import InvalidAPIResponseException, APIResponseError, APIResponseError
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class TestAdminClient(unittest.TestCase):
|
|
@@ -18,6 +18,7 @@ class TestAdminClient(unittest.TestCase):
|
|
|
18
18
|
@patch('pygeai.core.services.rest.ApiService.get')
|
|
19
19
|
def test_validate_api_token_success(self, mock_get):
|
|
20
20
|
self.mock_response.json.return_value = {"organizationId": "org-123", "projectId": "proj-123"}
|
|
21
|
+
self.mock_response.status_code = 200
|
|
21
22
|
mock_get.return_value = self.mock_response
|
|
22
23
|
|
|
23
24
|
result = self.client.validate_api_token()
|
|
@@ -32,13 +33,14 @@ class TestAdminClient(unittest.TestCase):
|
|
|
32
33
|
self.mock_response.text = "Invalid response"
|
|
33
34
|
mock_get.return_value = self.mock_response
|
|
34
35
|
|
|
35
|
-
with self.assertRaises(
|
|
36
|
+
with self.assertRaises(APIResponseError) as context:
|
|
36
37
|
self.client.validate_api_token()
|
|
37
|
-
self.assertIn("Unable to validate API token", str(context.exception))
|
|
38
|
+
self.assertIn("API returned an error", str(context.exception)) # "Unable to validate API token", str(context.exception))
|
|
38
39
|
|
|
39
40
|
@patch('pygeai.core.services.rest.ApiService.get')
|
|
40
41
|
def test_get_authorized_organizations_success(self, mock_get):
|
|
41
42
|
self.mock_response.json.return_value = {"organizations": ["org1", "org2"]}
|
|
43
|
+
self.mock_response.status_code = 200
|
|
42
44
|
mock_get.return_value = self.mock_response
|
|
43
45
|
|
|
44
46
|
result = self.client.get_authorized_organizations()
|
|
@@ -53,13 +55,14 @@ class TestAdminClient(unittest.TestCase):
|
|
|
53
55
|
self.mock_response.text = "Invalid response"
|
|
54
56
|
mock_get.return_value = self.mock_response
|
|
55
57
|
|
|
56
|
-
with self.assertRaises(
|
|
58
|
+
with self.assertRaises(APIResponseError) as context:
|
|
57
59
|
self.client.get_authorized_organizations()
|
|
58
|
-
self.assertIn("Unable to retrieve authorized organizations", str(context.exception))
|
|
60
|
+
self.assertIn("API returned an error", str(context.exception)) # "Unable to retrieve authorized organizations", str(context.exception))
|
|
59
61
|
|
|
60
62
|
@patch('pygeai.core.services.rest.ApiService.get')
|
|
61
63
|
def test_get_authorized_projects_by_organization_success(self, mock_get):
|
|
62
64
|
self.mock_response.json.return_value = {"projects": ["proj1", "proj2"]}
|
|
65
|
+
self.mock_response.status_code = 200
|
|
63
66
|
mock_get.return_value = self.mock_response
|
|
64
67
|
|
|
65
68
|
result = self.client.get_authorized_projects_by_organization("org-123")
|
|
@@ -76,13 +79,14 @@ class TestAdminClient(unittest.TestCase):
|
|
|
76
79
|
self.mock_response.text = "Invalid response"
|
|
77
80
|
mock_get.return_value = self.mock_response
|
|
78
81
|
|
|
79
|
-
with self.assertRaises(
|
|
82
|
+
with self.assertRaises(APIResponseError) as context:
|
|
80
83
|
self.client.get_authorized_projects_by_organization("org-123")
|
|
81
|
-
self.assertIn("Unable to retrieve authorized projects for organization", str(context.exception))
|
|
84
|
+
self.assertIn("API returned an error", str(context.exception)) # "Unable to retrieve authorized projects for organization", str(context.exception))
|
|
82
85
|
|
|
83
86
|
@patch('pygeai.core.services.rest.ApiService.get')
|
|
84
87
|
def test_get_project_visibility_success(self, mock_get):
|
|
85
88
|
self.mock_response.json.return_value = {}
|
|
89
|
+
self.mock_response.status_code = 200
|
|
86
90
|
mock_get.return_value = self.mock_response
|
|
87
91
|
|
|
88
92
|
result = self.client.get_project_visibility(
|
|
@@ -105,13 +109,14 @@ class TestAdminClient(unittest.TestCase):
|
|
|
105
109
|
self.mock_response.text = "Forbidden"
|
|
106
110
|
mock_get.return_value = self.mock_response
|
|
107
111
|
|
|
108
|
-
with self.assertRaises(
|
|
112
|
+
with self.assertRaises(APIResponseError) as context:
|
|
109
113
|
self.client.get_project_visibility("org-123", "proj-456", "token-789")
|
|
110
|
-
self.assertIn("Unable to retrieve project visibility", str(context.exception))
|
|
114
|
+
self.assertIn("API returned an error", str(context.exception)) # "Unable to retrieve project visibility", str(context.exception))
|
|
111
115
|
|
|
112
116
|
@patch('pygeai.core.services.rest.ApiService.get')
|
|
113
117
|
def test_get_project_api_token_success(self, mock_get):
|
|
114
118
|
self.mock_response.json.return_value = {"apiToken": "api-token-123"}
|
|
119
|
+
self.mock_response.status_code = 200
|
|
115
120
|
mock_get.return_value = self.mock_response
|
|
116
121
|
|
|
117
122
|
result = self.client.get_project_api_token(
|
|
@@ -134,9 +139,9 @@ class TestAdminClient(unittest.TestCase):
|
|
|
134
139
|
self.mock_response.text = "Unauthorized"
|
|
135
140
|
mock_get.return_value = self.mock_response
|
|
136
141
|
|
|
137
|
-
with self.assertRaises(
|
|
142
|
+
with self.assertRaises(APIResponseError) as context:
|
|
138
143
|
self.client.get_project_api_token("org-123", "proj-456", "token-789")
|
|
139
|
-
self.assertIn("Unable to retrieve project API token", str(context.exception))
|
|
144
|
+
self.assertIn("API returned an error", str(context.exception)) # "Unable to retrieve project API token", str(context.exception))
|
|
140
145
|
|
|
141
146
|
|
|
142
147
|
if __name__ == '__main__':
|