pygeai 0.6.0b10__py3-none-any.whl → 0.6.0b12__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pygeai/_docs/source/content/ai_lab/cli.rst +4 -4
- pygeai/_docs/source/content/ai_lab/models.rst +169 -35
- pygeai/_docs/source/content/ai_lab/runner.rst +2 -2
- pygeai/_docs/source/content/ai_lab/spec.rst +9 -9
- pygeai/_docs/source/content/ai_lab/usage.rst +34 -34
- pygeai/_docs/source/content/ai_lab.rst +1 -1
- pygeai/_docs/source/content/analytics.rst +598 -0
- pygeai/_docs/source/content/api_reference/chat.rst +428 -2
- pygeai/_docs/source/content/api_reference/embeddings.rst +1 -1
- pygeai/_docs/source/content/api_reference/project.rst +184 -0
- pygeai/_docs/source/content/api_reference/rag.rst +2 -2
- pygeai/_docs/source/content/authentication.rst +295 -0
- pygeai/_docs/source/content/cli.rst +79 -2
- pygeai/_docs/source/content/debugger.rst +1 -1
- pygeai/_docs/source/content/migration.rst +19 -2
- pygeai/_docs/source/index.rst +2 -0
- pygeai/_docs/source/pygeai.analytics.rst +53 -0
- pygeai/_docs/source/pygeai.cli.commands.rst +8 -0
- pygeai/_docs/source/pygeai.rst +1 -0
- pygeai/_docs/source/pygeai.tests.analytics.rst +45 -0
- pygeai/_docs/source/pygeai.tests.auth.rst +8 -0
- pygeai/_docs/source/pygeai.tests.rst +1 -1
- pygeai/analytics/__init__.py +0 -0
- pygeai/analytics/clients.py +505 -0
- pygeai/analytics/endpoints.py +35 -0
- pygeai/analytics/managers.py +606 -0
- pygeai/analytics/mappers.py +207 -0
- pygeai/analytics/responses.py +240 -0
- pygeai/chat/clients.py +46 -1
- pygeai/chat/endpoints.py +1 -0
- pygeai/cli/commands/analytics.py +525 -0
- pygeai/cli/commands/base.py +16 -0
- pygeai/cli/commands/chat.py +95 -0
- pygeai/cli/commands/common.py +28 -24
- pygeai/cli/commands/migrate.py +75 -6
- pygeai/cli/commands/organization.py +265 -0
- pygeai/cli/commands/validators.py +144 -1
- pygeai/cli/error_handler.py +41 -6
- pygeai/cli/geai.py +99 -16
- pygeai/cli/parsers.py +75 -31
- pygeai/cli/texts/help.py +75 -6
- pygeai/core/base/clients.py +18 -4
- pygeai/core/base/session.py +46 -7
- pygeai/core/common/config.py +25 -2
- pygeai/core/common/exceptions.py +64 -1
- pygeai/core/services/rest.py +20 -2
- pygeai/evaluation/clients.py +5 -3
- pygeai/lab/agents/clients.py +3 -3
- pygeai/lab/agents/endpoints.py +2 -2
- pygeai/lab/agents/mappers.py +50 -2
- pygeai/lab/clients.py +5 -2
- pygeai/lab/managers.py +7 -9
- pygeai/lab/models.py +70 -2
- pygeai/lab/tools/clients.py +1 -59
- pygeai/migration/__init__.py +3 -1
- pygeai/migration/strategies.py +72 -3
- pygeai/organization/clients.py +110 -1
- pygeai/organization/endpoints.py +11 -7
- pygeai/organization/managers.py +134 -2
- pygeai/organization/mappers.py +28 -2
- pygeai/organization/responses.py +11 -1
- pygeai/tests/analytics/__init__.py +0 -0
- pygeai/tests/analytics/test_clients.py +86 -0
- pygeai/tests/analytics/test_managers.py +94 -0
- pygeai/tests/analytics/test_mappers.py +84 -0
- pygeai/tests/analytics/test_responses.py +73 -0
- pygeai/tests/auth/test_oauth.py +172 -0
- pygeai/tests/cli/commands/test_migrate.py +14 -1
- pygeai/tests/cli/commands/test_organization.py +69 -1
- pygeai/tests/cli/test_error_handler.py +4 -4
- pygeai/tests/cli/test_geai_driver.py +1 -1
- pygeai/tests/lab/agents/test_mappers.py +128 -1
- pygeai/tests/lab/test_models.py +2 -0
- pygeai/tests/lab/tools/test_clients.py +2 -31
- pygeai/tests/organization/test_clients.py +180 -1
- pygeai/tests/organization/test_managers.py +40 -0
- pygeai/tests/snippets/analytics/__init__.py +0 -0
- pygeai/tests/snippets/analytics/get_agent_usage_per_user.py +16 -0
- pygeai/tests/snippets/analytics/get_agents_created_and_modified.py +11 -0
- pygeai/tests/snippets/analytics/get_average_cost_per_request.py +10 -0
- pygeai/tests/snippets/analytics/get_overall_error_rate.py +10 -0
- pygeai/tests/snippets/analytics/get_top_10_agents_by_requests.py +12 -0
- pygeai/tests/snippets/analytics/get_total_active_users.py +10 -0
- pygeai/tests/snippets/analytics/get_total_cost.py +10 -0
- pygeai/tests/snippets/analytics/get_total_requests_per_day.py +12 -0
- pygeai/tests/snippets/analytics/get_total_tokens.py +12 -0
- pygeai/tests/snippets/chat/get_response_complete_example.py +67 -0
- pygeai/tests/snippets/chat/get_response_with_instructions.py +19 -0
- pygeai/tests/snippets/chat/get_response_with_metadata.py +24 -0
- pygeai/tests/snippets/chat/get_response_with_parallel_tools.py +58 -0
- pygeai/tests/snippets/chat/get_response_with_reasoning.py +21 -0
- pygeai/tests/snippets/chat/get_response_with_store.py +38 -0
- pygeai/tests/snippets/chat/get_response_with_truncation.py +24 -0
- pygeai/tests/snippets/lab/agents/create_agent_with_permissions.py +39 -0
- pygeai/tests/snippets/lab/agents/create_agent_with_properties.py +46 -0
- pygeai/tests/snippets/lab/agents/get_agent_with_new_fields.py +62 -0
- pygeai/tests/snippets/lab/agents/update_agent_properties.py +50 -0
- pygeai/tests/snippets/organization/add_project_member.py +10 -0
- pygeai/tests/snippets/organization/add_project_member_batch.py +44 -0
- {pygeai-0.6.0b10.dist-info → pygeai-0.6.0b12.dist-info}/METADATA +1 -1
- {pygeai-0.6.0b10.dist-info → pygeai-0.6.0b12.dist-info}/RECORD +105 -95
- pygeai/_docs/source/pygeai.tests.snippets.assistants.data_analyst.rst +0 -37
- pygeai/_docs/source/pygeai.tests.snippets.assistants.rag.rst +0 -85
- pygeai/_docs/source/pygeai.tests.snippets.assistants.rst +0 -78
- pygeai/_docs/source/pygeai.tests.snippets.auth.rst +0 -10
- pygeai/_docs/source/pygeai.tests.snippets.chat.rst +0 -125
- pygeai/_docs/source/pygeai.tests.snippets.dbg.rst +0 -45
- pygeai/_docs/source/pygeai.tests.snippets.embeddings.rst +0 -61
- pygeai/_docs/source/pygeai.tests.snippets.evaluation.dataset.rst +0 -197
- pygeai/_docs/source/pygeai.tests.snippets.evaluation.plan.rst +0 -133
- pygeai/_docs/source/pygeai.tests.snippets.evaluation.result.rst +0 -37
- pygeai/_docs/source/pygeai.tests.snippets.evaluation.rst +0 -20
- pygeai/_docs/source/pygeai.tests.snippets.extras.rst +0 -37
- pygeai/_docs/source/pygeai.tests.snippets.files.rst +0 -53
- pygeai/_docs/source/pygeai.tests.snippets.gam.rst +0 -21
- pygeai/_docs/source/pygeai.tests.snippets.lab.agents.rst +0 -93
- pygeai/_docs/source/pygeai.tests.snippets.lab.processes.jobs.rst +0 -21
- pygeai/_docs/source/pygeai.tests.snippets.lab.processes.kbs.rst +0 -45
- pygeai/_docs/source/pygeai.tests.snippets.lab.processes.rst +0 -46
- pygeai/_docs/source/pygeai.tests.snippets.lab.rst +0 -82
- pygeai/_docs/source/pygeai.tests.snippets.lab.samples.rst +0 -21
- pygeai/_docs/source/pygeai.tests.snippets.lab.strategies.rst +0 -45
- pygeai/_docs/source/pygeai.tests.snippets.lab.tools.rst +0 -85
- pygeai/_docs/source/pygeai.tests.snippets.lab.use_cases.rst +0 -117
- pygeai/_docs/source/pygeai.tests.snippets.migrate.rst +0 -10
- pygeai/_docs/source/pygeai.tests.snippets.organization.rst +0 -109
- pygeai/_docs/source/pygeai.tests.snippets.rag.rst +0 -85
- pygeai/_docs/source/pygeai.tests.snippets.rerank.rst +0 -21
- pygeai/_docs/source/pygeai.tests.snippets.rst +0 -32
- pygeai/_docs/source/pygeai.tests.snippets.secrets.rst +0 -10
- pygeai/_docs/source/pygeai.tests.snippets.usage_limit.rst +0 -77
- {pygeai-0.6.0b10.dist-info → pygeai-0.6.0b12.dist-info}/WHEEL +0 -0
- {pygeai-0.6.0b10.dist-info → pygeai-0.6.0b12.dist-info}/entry_points.txt +0 -0
- {pygeai-0.6.0b10.dist-info → pygeai-0.6.0b12.dist-info}/licenses/LICENSE +0 -0
- {pygeai-0.6.0b10.dist-info → pygeai-0.6.0b12.dist-info}/top_level.txt +0 -0
pygeai/cli/error_handler.py
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import traceback
|
|
2
2
|
from difflib import SequenceMatcher
|
|
3
|
-
from typing import List, Optional
|
|
3
|
+
from typing import List, Optional, Tuple
|
|
4
4
|
|
|
5
5
|
from pygeai import logger
|
|
6
6
|
from pygeai.cli.commands import Command, Option
|
|
7
7
|
from pygeai.core.utils.console import Console
|
|
8
8
|
|
|
9
9
|
|
|
10
|
+
FUZZY_MATCH_THRESHOLD = 0.6
|
|
11
|
+
MAX_FUZZY_SUGGESTIONS = 3
|
|
12
|
+
|
|
13
|
+
|
|
10
14
|
class ExitCode:
|
|
11
15
|
SUCCESS = 0
|
|
12
16
|
USER_INPUT_ERROR = 1
|
|
@@ -19,27 +23,58 @@ class ExitCode:
|
|
|
19
23
|
class ErrorHandler:
|
|
20
24
|
|
|
21
25
|
@staticmethod
|
|
22
|
-
def format_error(
|
|
23
|
-
|
|
26
|
+
def format_error(
|
|
27
|
+
error_type: str,
|
|
28
|
+
message: str,
|
|
29
|
+
suggestion: Optional[str] = None,
|
|
30
|
+
show_help: bool = True,
|
|
31
|
+
example: Optional[str] = None
|
|
32
|
+
) -> str:
|
|
33
|
+
"""
|
|
34
|
+
Formats an error message with optional suggestion and example.
|
|
35
|
+
|
|
36
|
+
:param error_type: str - Type of error (e.g., "Unknown Command").
|
|
37
|
+
:param message: str - The error message.
|
|
38
|
+
:param suggestion: Optional[str] - Suggested fix or next steps.
|
|
39
|
+
:param show_help: bool - Whether to show help command hint.
|
|
40
|
+
:param example: Optional[str] - Example of correct usage.
|
|
41
|
+
:return: str - Formatted error message.
|
|
42
|
+
"""
|
|
43
|
+
output = f"ERROR [{error_type}]: {message}"
|
|
24
44
|
|
|
25
45
|
if suggestion:
|
|
26
46
|
output += f"\n → {suggestion}"
|
|
27
47
|
|
|
48
|
+
if example:
|
|
49
|
+
output += f"\n\n Example:\n {example}"
|
|
50
|
+
|
|
28
51
|
if show_help:
|
|
29
52
|
output += "\n\nRun 'geai help' for usage information."
|
|
30
53
|
|
|
31
54
|
return output
|
|
32
55
|
|
|
33
56
|
@staticmethod
|
|
34
|
-
def find_similar_items(
|
|
35
|
-
|
|
57
|
+
def find_similar_items(
|
|
58
|
+
item: str,
|
|
59
|
+
available_items: List[str],
|
|
60
|
+
threshold: float = FUZZY_MATCH_THRESHOLD
|
|
61
|
+
) -> List[str]:
|
|
62
|
+
"""
|
|
63
|
+
Finds similar items using fuzzy string matching.
|
|
64
|
+
|
|
65
|
+
:param item: str - The item to match against.
|
|
66
|
+
:param available_items: List[str] - List of available items.
|
|
67
|
+
:param threshold: float - Minimum similarity ratio (0.0 to 1.0).
|
|
68
|
+
:return: List[str] - List of similar items, up to MAX_FUZZY_SUGGESTIONS.
|
|
69
|
+
"""
|
|
70
|
+
similarities: List[Tuple[str, float]] = []
|
|
36
71
|
for available in available_items:
|
|
37
72
|
ratio = SequenceMatcher(None, item.lower(), available.lower()).ratio()
|
|
38
73
|
if ratio >= threshold:
|
|
39
74
|
similarities.append((available, ratio))
|
|
40
75
|
|
|
41
76
|
similarities.sort(key=lambda x: x[1], reverse=True)
|
|
42
|
-
return [item[0] for item in similarities[:
|
|
77
|
+
return [item[0] for item in similarities[:MAX_FUZZY_SUGGESTIONS]]
|
|
43
78
|
|
|
44
79
|
@staticmethod
|
|
45
80
|
def get_available_commands(commands: List[Command]) -> List[str]:
|
pygeai/cli/geai.py
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import sys
|
|
2
|
+
import logging
|
|
3
|
+
from typing import List, Optional
|
|
2
4
|
|
|
3
5
|
from pygeai import logger
|
|
4
6
|
from pygeai.cli.commands.base import base_commands, base_options
|
|
@@ -12,18 +14,61 @@ from pygeai.core.common.exceptions import UnknownArgumentError, MissingRequireme
|
|
|
12
14
|
from pygeai.core.utils.console import Console
|
|
13
15
|
|
|
14
16
|
|
|
15
|
-
def
|
|
17
|
+
def setup_verbose_logging() -> None:
|
|
18
|
+
"""
|
|
19
|
+
Configure verbose logging for the CLI.
|
|
20
|
+
|
|
21
|
+
Sets up a console handler with DEBUG level logging and a formatted output
|
|
22
|
+
that includes timestamp, logger name, level, and message.
|
|
23
|
+
"""
|
|
24
|
+
if logger.handlers:
|
|
25
|
+
for handler in logger.handlers:
|
|
26
|
+
if not isinstance(handler, logging.NullHandler):
|
|
27
|
+
return
|
|
28
|
+
|
|
29
|
+
logger.setLevel(logging.DEBUG)
|
|
30
|
+
console_handler = logging.StreamHandler(sys.stderr)
|
|
31
|
+
console_handler.setLevel(logging.DEBUG)
|
|
32
|
+
formatter = logging.Formatter(
|
|
33
|
+
'%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
|
34
|
+
datefmt='%Y-%m-%d %H:%M:%S'
|
|
35
|
+
)
|
|
36
|
+
console_handler.setFormatter(formatter)
|
|
37
|
+
logger.addHandler(console_handler)
|
|
38
|
+
logger.propagate = False
|
|
39
|
+
logger.debug("Verbose mode enabled")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def main() -> int:
|
|
43
|
+
"""
|
|
44
|
+
Main entry point for the GEAI CLI application.
|
|
45
|
+
|
|
46
|
+
:return: int - Exit code indicating success or error.
|
|
47
|
+
"""
|
|
16
48
|
driver = CLIDriver()
|
|
17
49
|
return driver.main()
|
|
18
50
|
|
|
19
51
|
|
|
20
52
|
class CLIDriver:
|
|
21
|
-
|
|
22
|
-
|
|
53
|
+
"""
|
|
54
|
+
Main CLI driver for the GEAI command-line interface.
|
|
55
|
+
|
|
56
|
+
The CLIDriver orchestrates command parsing, execution, and error handling
|
|
57
|
+
for all GEAI CLI operations. It supports multi-profile session management
|
|
58
|
+
via the --alias flag and provides comprehensive error handling with
|
|
59
|
+
user-friendly messages.
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
def __init__(self, session=None) -> None:
|
|
23
63
|
"""
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
64
|
+
Initialize the CLI driver with optional session.
|
|
65
|
+
|
|
66
|
+
Sets up the session to be used while running commands, either with a
|
|
67
|
+
specified alias, environment variables, or function parameters.
|
|
68
|
+
Once the session is defined, it won't change during the execution.
|
|
69
|
+
|
|
70
|
+
:param session: Optional session object. If None, uses 'default' or
|
|
71
|
+
alias-specified session from command-line arguments.
|
|
27
72
|
"""
|
|
28
73
|
arguments = sys.argv
|
|
29
74
|
if "-a" in arguments or "--alias" in arguments:
|
|
@@ -32,9 +77,13 @@ class CLIDriver:
|
|
|
32
77
|
|
|
33
78
|
self.session = get_session("default") if session is None else session
|
|
34
79
|
|
|
35
|
-
def _get_alias(self, arguments:
|
|
80
|
+
def _get_alias(self, arguments: List[str]) -> str:
|
|
36
81
|
"""
|
|
37
|
-
Retrieves and removes alias and alias flag from argument list
|
|
82
|
+
Retrieves and removes alias and alias flag from argument list.
|
|
83
|
+
|
|
84
|
+
:param arguments: List[str] - Command line arguments.
|
|
85
|
+
:return: str - The alias value.
|
|
86
|
+
:raises ValueError: If alias flag is present but no value provided.
|
|
38
87
|
"""
|
|
39
88
|
alias_index = None
|
|
40
89
|
|
|
@@ -47,24 +96,46 @@ class CLIDriver:
|
|
|
47
96
|
alias = arguments.pop(alias_index)
|
|
48
97
|
return alias
|
|
49
98
|
|
|
50
|
-
def main(self, args=None):
|
|
99
|
+
def main(self, args: Optional[List[str]] = None) -> int:
|
|
51
100
|
"""
|
|
52
|
-
|
|
101
|
+
Execute the CLI command based on provided arguments.
|
|
102
|
+
|
|
103
|
+
If no argument is received, it defaults to help (first command in base_command list).
|
|
53
104
|
Otherwise, it parses the arguments received to identify the appropriate command and either
|
|
54
105
|
execute it or parse it again to detect subcommands.
|
|
106
|
+
|
|
107
|
+
:param args: Optional[List[str]] - Command line arguments. If None, uses sys.argv.
|
|
108
|
+
:return: int - Exit code (0 for success, non-zero for errors).
|
|
55
109
|
"""
|
|
56
110
|
try:
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
111
|
+
argv = sys.argv if args is None else args
|
|
112
|
+
|
|
113
|
+
if "--verbose" in argv or "-v" in argv:
|
|
114
|
+
setup_verbose_logging()
|
|
115
|
+
argv_copy = [a for a in argv if a not in ("--verbose", "-v")]
|
|
116
|
+
if args is None:
|
|
117
|
+
sys.argv = argv_copy
|
|
118
|
+
else:
|
|
119
|
+
args = argv_copy
|
|
120
|
+
argv = argv_copy
|
|
121
|
+
|
|
122
|
+
logger.debug(f"Running geai with: {' '.join(a for a in argv)}")
|
|
123
|
+
logger.debug(f"Session: {self.session.alias if hasattr(self.session, 'alias') else 'default'}")
|
|
124
|
+
|
|
125
|
+
if len(argv) > 1:
|
|
126
|
+
arg = argv[1] if args is None else args[1]
|
|
127
|
+
arguments = argv[2:] if args is None else args[2:]
|
|
128
|
+
|
|
129
|
+
logger.debug(f"Identifying command for argument: {arg}")
|
|
62
130
|
command = CommandParser(base_commands, base_options).identify_command(arg)
|
|
131
|
+
logger.debug(f"Command identified: {command.name}")
|
|
63
132
|
else:
|
|
133
|
+
logger.debug("No arguments provided, defaulting to help command")
|
|
64
134
|
command = base_commands[0]
|
|
65
135
|
arguments = []
|
|
66
136
|
|
|
67
137
|
self.process_command(command, arguments)
|
|
138
|
+
logger.debug(f"Command completed successfully")
|
|
68
139
|
return ExitCode.SUCCESS
|
|
69
140
|
except UnknownArgumentError as e:
|
|
70
141
|
if hasattr(e, 'available_commands') and e.available_commands:
|
|
@@ -102,27 +173,39 @@ class CLIDriver:
|
|
|
102
173
|
If the command has no action associated with it, it means it has subcommands, so it must be parsed again
|
|
103
174
|
to identify it.
|
|
104
175
|
"""
|
|
176
|
+
logger.debug(f"Processing command: {command.name}, arguments: {arguments}")
|
|
177
|
+
|
|
105
178
|
if command.action:
|
|
106
179
|
if command.additional_args == ArgumentsEnum.NOT_AVAILABLE:
|
|
180
|
+
logger.debug(f"Executing command {command.name} without arguments")
|
|
107
181
|
command.action()
|
|
108
182
|
else:
|
|
183
|
+
logger.debug(f"Extracting options for command {command.name}")
|
|
109
184
|
option_list = CommandParser(base_commands, command.options).extract_option_list(arguments)
|
|
185
|
+
logger.debug(f"Options extracted: {len(option_list)} items")
|
|
110
186
|
command.action(option_list)
|
|
111
187
|
elif command.subcommands:
|
|
112
188
|
subcommand_arg = arguments[0] if len(arguments) > 0 else None
|
|
113
189
|
subcommand_arguments = arguments[1:] if len(arguments) > 1 else []
|
|
114
|
-
|
|
190
|
+
|
|
191
|
+
logger.debug(f"Command has subcommands, identifying: {subcommand_arg}")
|
|
192
|
+
|
|
115
193
|
available_commands = command.subcommands
|
|
116
194
|
available_options = command.options
|
|
117
195
|
parser = CommandParser(available_commands, available_options)
|
|
118
196
|
|
|
119
197
|
if not subcommand_arg:
|
|
198
|
+
logger.debug(f"No subcommand specified, using default: {command.subcommands[0].name}")
|
|
120
199
|
subcommand = command.subcommands[0]
|
|
121
200
|
else:
|
|
122
201
|
subcommand = parser.identify_command(subcommand_arg)
|
|
202
|
+
logger.debug(f"Subcommand identified: {subcommand.name}")
|
|
123
203
|
|
|
124
204
|
if subcommand.additional_args == ArgumentsEnum.NOT_AVAILABLE:
|
|
205
|
+
logger.debug(f"Executing subcommand {subcommand.name} without arguments")
|
|
125
206
|
subcommand.action()
|
|
126
207
|
else:
|
|
208
|
+
logger.debug(f"Extracting options for subcommand {subcommand.name}")
|
|
127
209
|
option_list = CommandParser(None, subcommand.options).extract_option_list(subcommand_arguments)
|
|
210
|
+
logger.debug(f"Options extracted: {len(option_list)} items")
|
|
128
211
|
subcommand.action(option_list)
|
pygeai/cli/parsers.py
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
from typing import List, Tuple, Optional
|
|
2
|
+
|
|
3
|
+
from pygeai import logger
|
|
1
4
|
from pygeai.cli.commands.base import base_options
|
|
2
5
|
from pygeai.cli.commands import Command, Option
|
|
3
6
|
from pygeai.core.common.exceptions import UnknownArgumentError, MissingRequirementException
|
|
@@ -5,7 +8,17 @@ from pygeai.core.common.exceptions import UnknownArgumentError, MissingRequireme
|
|
|
5
8
|
|
|
6
9
|
class CommandParser:
|
|
7
10
|
|
|
8
|
-
def __init__(
|
|
11
|
+
def __init__(
|
|
12
|
+
self,
|
|
13
|
+
available_commands: Optional[List[Command]],
|
|
14
|
+
available_options: Optional[List[Option]]
|
|
15
|
+
) -> None:
|
|
16
|
+
"""
|
|
17
|
+
Initialize a CommandParser with available commands and options.
|
|
18
|
+
|
|
19
|
+
:param available_commands: Optional[List[Command]] - List of valid commands, or None.
|
|
20
|
+
:param available_options: Optional[List[Option]] - List of valid options, or None.
|
|
21
|
+
"""
|
|
9
22
|
self.available_commands = available_commands
|
|
10
23
|
self.available_options = available_options
|
|
11
24
|
|
|
@@ -13,26 +26,34 @@ class CommandParser:
|
|
|
13
26
|
"""
|
|
14
27
|
Analyzes the first argument and checks if it's a valid command.
|
|
15
28
|
|
|
16
|
-
:param
|
|
17
|
-
:return: The command
|
|
29
|
+
:param arg: str - The argument to be analyzed.
|
|
30
|
+
:return: Command - The identified command object.
|
|
31
|
+
:raises UnknownArgumentError: If the argument is not a valid command.
|
|
18
32
|
"""
|
|
33
|
+
logger.debug(f"Searching for command matching: {arg}")
|
|
19
34
|
command = self._get_associated_command(arg)
|
|
20
35
|
if not command:
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
36
|
+
logger.debug(f"No command found for: {arg}")
|
|
37
|
+
raise UnknownArgumentError(
|
|
38
|
+
f"'{arg}' is not a valid command.",
|
|
39
|
+
arg=arg,
|
|
40
|
+
available_commands=self.available_commands
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
logger.debug(f"Command found: {command.name} (identifiers: {command.identifiers})")
|
|
26
44
|
return command
|
|
27
45
|
|
|
28
|
-
def extract_option_list(self, arguments:
|
|
46
|
+
def extract_option_list(self, arguments: List[str]) -> List[Tuple[Option, str]]:
|
|
29
47
|
"""
|
|
30
|
-
Parses a list of arguments and returns the
|
|
48
|
+
Parses a list of arguments and returns the options being invoked.
|
|
31
49
|
|
|
32
|
-
:param arguments:
|
|
33
|
-
:return:
|
|
50
|
+
:param arguments: List[str] - The list of arguments received by the CLI utility.
|
|
51
|
+
:return: List[Tuple[Option, str]] - A list of tuples containing Option objects and their values.
|
|
52
|
+
:raises UnknownArgumentError: If an unknown option is provided.
|
|
53
|
+
:raises MissingRequirementException: If a required option argument is missing.
|
|
34
54
|
"""
|
|
35
|
-
|
|
55
|
+
logger.debug(f"Extracting options from arguments: {arguments}")
|
|
56
|
+
flag_list: List[Tuple[Option, str]] = []
|
|
36
57
|
|
|
37
58
|
complementary_arg = False
|
|
38
59
|
for i, arg in enumerate(arguments):
|
|
@@ -42,36 +63,59 @@ class CommandParser:
|
|
|
42
63
|
|
|
43
64
|
flag = self._get_associated_option(arg)
|
|
44
65
|
if not flag:
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
66
|
+
logger.debug(f"Unknown option: {arg}")
|
|
67
|
+
raise UnknownArgumentError(
|
|
68
|
+
f"'{arg}' is not a valid option.",
|
|
69
|
+
arg=arg,
|
|
70
|
+
available_options=self.available_options
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
logger.debug(f"Option found: {flag.name} (identifiers: {flag.identifiers})")
|
|
49
74
|
|
|
50
75
|
if flag.requires_args:
|
|
51
76
|
complementary_arg = True
|
|
52
77
|
try:
|
|
53
|
-
|
|
54
|
-
|
|
78
|
+
value = arguments[i + 1]
|
|
79
|
+
logger.debug(f"Option {flag.name} has value: {value}")
|
|
80
|
+
flag_list.append((flag, value))
|
|
81
|
+
except IndexError:
|
|
82
|
+
logger.debug(f"Missing required argument for option: {flag.name}")
|
|
55
83
|
raise MissingRequirementException(f"'{flag.name}' requires an argument.")
|
|
56
84
|
else:
|
|
57
|
-
|
|
85
|
+
logger.debug(f"Option {flag.name} is a flag (no value required)")
|
|
86
|
+
flag_list.append((flag, ""))
|
|
58
87
|
|
|
88
|
+
logger.debug(f"Total options parsed: {len(flag_list)}")
|
|
59
89
|
return flag_list
|
|
60
90
|
|
|
61
|
-
def _get_associated_command(self, arg: str) -> Command:
|
|
62
|
-
|
|
91
|
+
def _get_associated_command(self, arg: str) -> Optional[Command]:
|
|
92
|
+
"""
|
|
93
|
+
Finds the command associated with the given argument.
|
|
94
|
+
|
|
95
|
+
:param arg: str - The argument to search for.
|
|
96
|
+
:return: Optional[Command] - The associated command if found, None otherwise.
|
|
97
|
+
"""
|
|
98
|
+
if not self.available_commands:
|
|
99
|
+
return None
|
|
100
|
+
|
|
63
101
|
for command in self.available_commands:
|
|
64
102
|
if arg in command.identifiers:
|
|
65
|
-
|
|
66
|
-
|
|
103
|
+
return command
|
|
104
|
+
|
|
105
|
+
return None
|
|
67
106
|
|
|
68
|
-
|
|
107
|
+
def _get_associated_option(self, arg: str) -> Optional[Option]:
|
|
108
|
+
"""
|
|
109
|
+
Finds the option associated with the given argument.
|
|
69
110
|
|
|
70
|
-
|
|
71
|
-
|
|
111
|
+
:param arg: str - The argument to search for.
|
|
112
|
+
:return: Optional[Option] - The associated option if found, None otherwise.
|
|
113
|
+
"""
|
|
114
|
+
if not self.available_options:
|
|
115
|
+
return None
|
|
116
|
+
|
|
72
117
|
for option in self.available_options:
|
|
73
118
|
if arg in option.identifiers:
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
return associated_option
|
|
119
|
+
return option
|
|
120
|
+
|
|
121
|
+
return None
|
pygeai/cli/texts/help.py
CHANGED
|
@@ -340,12 +340,30 @@ DESCRIPTION
|
|
|
340
340
|
EXAMPLES
|
|
341
341
|
The command:
|
|
342
342
|
|
|
343
|
-
geai migrate
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
343
|
+
geai migrate clone-project \\
|
|
344
|
+
--from-api-key "source_api_key_123abc456def789ghi012jkl345mno678pqr901stu234" \\
|
|
345
|
+
--from-organization-api-key "org_key_abc123def456ghi789jkl012mno345pqr678" \\
|
|
346
|
+
--from-project-id "1a2b3c4d-5e6f-7g8h-9i0j-1k2l3m4n5o6p" \\
|
|
347
|
+
--from-instance "https://api.example.ai" \\
|
|
348
|
+
--to-project-name "Migrated Project - Complete Clone" \\
|
|
349
|
+
--admin-email "admin@example.com" \\
|
|
350
|
+
--all
|
|
351
|
+
|
|
352
|
+
will clone entire project within the same instance
|
|
353
|
+
|
|
354
|
+
The command:
|
|
355
|
+
|
|
356
|
+
geai migrate clone-project \\
|
|
357
|
+
--from-api-key "source_api_key_123abc456def789ghi012jkl345mno678pqr901stu234" \\
|
|
358
|
+
--from-organization-api-key "source_org_key_abc123def456ghi789jkl012mno345pqr678" \\
|
|
359
|
+
--from-project-id "1a2b3c4d-5e6f-7g8h-9i0j-1k2l3m4n5o6p" \\
|
|
360
|
+
--from-instance "https://api.test.example.ai" \\
|
|
361
|
+
--to-organization-api-key "dest_org_key_stu901vwx234yz567abc890def123ghi456jkl789" \\
|
|
362
|
+
--to-project-name "Migrated Project - Complete Clone" \\
|
|
363
|
+
--admin-email "admin@example.com" \\
|
|
364
|
+
--all
|
|
365
|
+
|
|
366
|
+
will clone entire project with ALL resources (cross-instance)
|
|
349
367
|
|
|
350
368
|
{AUTHORS_SECTION}
|
|
351
369
|
"""
|
|
@@ -782,3 +800,54 @@ EXAMPLES
|
|
|
782
800
|
|
|
783
801
|
{AUTHORS_SECTION}
|
|
784
802
|
"""
|
|
803
|
+
|
|
804
|
+
ANALYTICS_HELP_TEXT = f"""
|
|
805
|
+
GEAI CLI - ANALYTICS
|
|
806
|
+
--------------------
|
|
807
|
+
|
|
808
|
+
NAME
|
|
809
|
+
geai - Command Line Interface for Globant Enterprise AI
|
|
810
|
+
|
|
811
|
+
SYNOPSIS
|
|
812
|
+
geai analytics <subcommand> --[flag] [flag_arg]
|
|
813
|
+
|
|
814
|
+
DESCRIPTION
|
|
815
|
+
geai analytics is a command from geai cli utility, developed to retrieve analytics and metrics from GEAI.
|
|
816
|
+
|
|
817
|
+
Date Range Defaults:
|
|
818
|
+
If --start-date and --end-date are not specified, the commands will default to the previous month
|
|
819
|
+
(from the first day to the last day of the previous month).
|
|
820
|
+
|
|
821
|
+
The options are as follows:
|
|
822
|
+
{{available_commands}}
|
|
823
|
+
|
|
824
|
+
EXAMPLES
|
|
825
|
+
The command:
|
|
826
|
+
|
|
827
|
+
geai analytics agents-created --start-date "2024-01-01" --end-date "2024-01-31"
|
|
828
|
+
retrieves the total number of agents created and modified in January 2024.
|
|
829
|
+
|
|
830
|
+
The command:
|
|
831
|
+
|
|
832
|
+
geai analytics full-report
|
|
833
|
+
retrieves a comprehensive analytics report for the previous month.
|
|
834
|
+
|
|
835
|
+
The command:
|
|
836
|
+
|
|
837
|
+
geai analytics full-report --start-date "2024-01-01" --end-date "2024-01-31" --csv report.csv
|
|
838
|
+
retrieves a comprehensive analytics report for January 2024 and exports it to report.csv.
|
|
839
|
+
|
|
840
|
+
The command:
|
|
841
|
+
geai analytics total-cost -s "2024-01-01" -e "2024-01-31"
|
|
842
|
+
retrieves the total cost for January 2024.
|
|
843
|
+
|
|
844
|
+
The command:
|
|
845
|
+
geai analytics requests-per-day -s "2024-01-01" -e "2024-01-31"
|
|
846
|
+
retrieves the total requests per day for January 2024.
|
|
847
|
+
|
|
848
|
+
The command:
|
|
849
|
+
geai analytics top-agents -s "2024-01-01" -e "2024-01-31"
|
|
850
|
+
retrieves the top 10 agents by number of requests
|
|
851
|
+
|
|
852
|
+
{AUTHORS_SECTION}
|
|
853
|
+
"""
|
pygeai/core/base/clients.py
CHANGED
|
@@ -8,20 +8,29 @@ from pygeai.core.utils.validators import validate_status_code
|
|
|
8
8
|
|
|
9
9
|
class BaseClient(ABC):
|
|
10
10
|
|
|
11
|
-
def __init__(self, api_key: str = None, base_url: str = None, alias: str = None
|
|
11
|
+
def __init__(self, api_key: str = None, base_url: str = None, alias: str = None, *,
|
|
12
|
+
access_token: str = None, project_id: str = None):
|
|
12
13
|
"""
|
|
13
14
|
If commont settings are not specified, they're retrieved from default Session, based on the
|
|
14
15
|
credential files.
|
|
15
16
|
:param api_key: GEAI API KEY to access services
|
|
16
17
|
:param base_url: URL for GEAI instance to be used
|
|
18
|
+
:param alias: Alias to use from credentials file
|
|
19
|
+
:param access_token: OAuth access token (keyword-only)
|
|
20
|
+
:param project_id: Project ID for OAuth authentication (keyword-only)
|
|
17
21
|
"""
|
|
18
|
-
if
|
|
22
|
+
if access_token and not project_id:
|
|
23
|
+
raise MissingRequirementException("project_id is required when using access_token")
|
|
24
|
+
|
|
25
|
+
if not (api_key and base_url) and not (access_token and base_url) and alias:
|
|
19
26
|
self.__session = get_session(alias)
|
|
20
27
|
if not self.__session:
|
|
21
28
|
raise MissingRequirementException("API KEY and BASE URL must be defined in order to use this functionality")
|
|
22
|
-
elif api_key and base_url:
|
|
29
|
+
elif (api_key or access_token) and base_url:
|
|
23
30
|
self.__session = get_session()
|
|
24
31
|
self.__session.api_key = api_key
|
|
32
|
+
self.__session.access_token = access_token
|
|
33
|
+
self.__session.project_id = project_id
|
|
25
34
|
self.__session.base_url = base_url
|
|
26
35
|
else:
|
|
27
36
|
self.__session = get_session()
|
|
@@ -29,7 +38,12 @@ class BaseClient(ABC):
|
|
|
29
38
|
if self.session is None:
|
|
30
39
|
raise MissingRequirementException("Cannot access this functionality without setting API_KEY and BASE_URL")
|
|
31
40
|
|
|
32
|
-
self.
|
|
41
|
+
token = self.session.access_token if self.session.access_token else self.session.api_key
|
|
42
|
+
self.__api_service = ApiService(
|
|
43
|
+
base_url=self.session.base_url,
|
|
44
|
+
token=token,
|
|
45
|
+
project_id=self.session.project_id
|
|
46
|
+
)
|
|
33
47
|
|
|
34
48
|
@property
|
|
35
49
|
def session(self):
|
pygeai/core/base/session.py
CHANGED
|
@@ -19,6 +19,8 @@ class Session(metaclass=Singleton):
|
|
|
19
19
|
:param api_key: str - API key to interact with GEAI
|
|
20
20
|
:param base_url: str - Base URL of the GEAI instance
|
|
21
21
|
:param eval_url: Optional[str] - Optional evaluation endpoint URL
|
|
22
|
+
:param access_token: Optional[str] - OAuth access token (keyword-only)
|
|
23
|
+
:param project_id: Optional[str] - Project ID for OAuth authentication (keyword-only)
|
|
22
24
|
:return: Session - Instance of the Session class
|
|
23
25
|
:raises: ValueError - If required parameters are missing or invalid
|
|
24
26
|
"""
|
|
@@ -28,13 +30,22 @@ class Session(metaclass=Singleton):
|
|
|
28
30
|
api_key: str = None,
|
|
29
31
|
base_url: str = None,
|
|
30
32
|
eval_url: Optional[str] = None,
|
|
33
|
+
*,
|
|
34
|
+
access_token: Optional[str] = None,
|
|
35
|
+
project_id: Optional[str] = None,
|
|
36
|
+
alias: Optional[str] = None,
|
|
31
37
|
):
|
|
32
|
-
if not api_key
|
|
33
|
-
logger.warning("Cannot instantiate session without api_key
|
|
38
|
+
if not api_key and not access_token:
|
|
39
|
+
logger.warning("Cannot instantiate session without api_key or access_token")
|
|
40
|
+
if not base_url:
|
|
41
|
+
logger.warning("Cannot instantiate session without base_url")
|
|
34
42
|
|
|
35
43
|
self.__api_key = api_key
|
|
36
44
|
self.__base_url = base_url
|
|
37
45
|
self.__eval_url = eval_url
|
|
46
|
+
self.__access_token = access_token
|
|
47
|
+
self.__project_id = project_id
|
|
48
|
+
self.__alias = alias if alias else "default"
|
|
38
49
|
|
|
39
50
|
global _session
|
|
40
51
|
_session = self
|
|
@@ -63,6 +74,30 @@ class Session(metaclass=Singleton):
|
|
|
63
74
|
def eval_url(self, eval_url: str):
|
|
64
75
|
self.__eval_url = eval_url
|
|
65
76
|
|
|
77
|
+
@property
|
|
78
|
+
def access_token(self):
|
|
79
|
+
return self.__access_token
|
|
80
|
+
|
|
81
|
+
@access_token.setter
|
|
82
|
+
def access_token(self, access_token: str):
|
|
83
|
+
self.__access_token = access_token
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def project_id(self):
|
|
87
|
+
return self.__project_id
|
|
88
|
+
|
|
89
|
+
@project_id.setter
|
|
90
|
+
def project_id(self, project_id: str):
|
|
91
|
+
self.__project_id = project_id
|
|
92
|
+
|
|
93
|
+
@property
|
|
94
|
+
def alias(self):
|
|
95
|
+
return self.__alias
|
|
96
|
+
|
|
97
|
+
@alias.setter
|
|
98
|
+
def alias(self, alias: str):
|
|
99
|
+
self.__alias = alias
|
|
100
|
+
|
|
66
101
|
|
|
67
102
|
def get_session(alias: str = None) -> Session:
|
|
68
103
|
"""
|
|
@@ -80,13 +115,17 @@ def get_session(alias: str = None) -> Session:
|
|
|
80
115
|
api_key=settings.get_api_key(alias),
|
|
81
116
|
base_url=settings.get_base_url(alias),
|
|
82
117
|
eval_url=settings.get_eval_url(alias),
|
|
118
|
+
access_token=settings.get_access_token(alias),
|
|
119
|
+
project_id=settings.get_project_id(alias),
|
|
120
|
+
alias=alias,
|
|
83
121
|
)
|
|
84
122
|
elif _session is not None and alias:
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
123
|
+
_session.alias = alias
|
|
124
|
+
_session.api_key = settings.get_api_key(alias)
|
|
125
|
+
_session.base_url = settings.get_base_url(alias)
|
|
126
|
+
_session.eval_url = settings.get_eval_url(alias)
|
|
127
|
+
_session.access_token = settings.get_access_token(alias)
|
|
128
|
+
_session.project_id = settings.get_project_id(alias)
|
|
90
129
|
|
|
91
130
|
if alias:
|
|
92
131
|
logger.debug(f"Alias: {alias}")
|