pygeai 0.6.0b11__py3-none-any.whl → 0.6.0b13__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/assistant/managers.py +1 -1
- pygeai/chat/managers.py +1 -1
- pygeai/cli/commands/analytics.py +525 -0
- pygeai/cli/commands/base.py +16 -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 +106 -18
- 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 +59 -7
- pygeai/core/common/config.py +25 -2
- pygeai/core/common/exceptions.py +64 -1
- pygeai/core/embeddings/managers.py +1 -1
- pygeai/core/files/managers.py +1 -1
- pygeai/core/rerank/managers.py +1 -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 +8 -10
- 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/limits/managers.py +1 -1
- pygeai/organization/managers.py +135 -3
- 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.0b11.dist-info → pygeai-0.6.0b13.dist-info}/METADATA +1 -1
- {pygeai-0.6.0b11.dist-info → pygeai-0.6.0b13.dist-info}/RECORD +108 -98
- 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.0b11.dist-info → pygeai-0.6.0b13.dist-info}/WHEEL +0 -0
- {pygeai-0.6.0b11.dist-info → pygeai-0.6.0b13.dist-info}/entry_points.txt +0 -0
- {pygeai-0.6.0b11.dist-info → pygeai-0.6.0b13.dist-info}/licenses/LICENSE +0 -0
- {pygeai-0.6.0b11.dist-info → pygeai-0.6.0b13.dist-info}/top_level.txt +0 -0
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,66 @@ from pygeai.core.common.exceptions import UnknownArgumentError, MissingRequireme
|
|
|
12
14
|
from pygeai.core.utils.console import Console
|
|
13
15
|
|
|
14
16
|
|
|
15
|
-
def
|
|
16
|
-
|
|
17
|
-
|
|
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
|
+
"""
|
|
48
|
+
try:
|
|
49
|
+
driver = CLIDriver()
|
|
50
|
+
return driver.main()
|
|
51
|
+
except MissingRequirementException as e:
|
|
52
|
+
error_msg = ErrorHandler.handle_missing_requirement(str(e))
|
|
53
|
+
Console.write_stderr(error_msg)
|
|
54
|
+
return ExitCode.MISSING_REQUIREMENT
|
|
18
55
|
|
|
19
56
|
|
|
20
57
|
class CLIDriver:
|
|
21
|
-
|
|
22
|
-
|
|
58
|
+
"""
|
|
59
|
+
Main CLI driver for the GEAI command-line interface.
|
|
60
|
+
|
|
61
|
+
The CLIDriver orchestrates command parsing, execution, and error handling
|
|
62
|
+
for all GEAI CLI operations. It supports multi-profile session management
|
|
63
|
+
via the --alias flag and provides comprehensive error handling with
|
|
64
|
+
user-friendly messages.
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
def __init__(self, session=None) -> None:
|
|
23
68
|
"""
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
69
|
+
Initialize the CLI driver with optional session.
|
|
70
|
+
|
|
71
|
+
Sets up the session to be used while running commands, either with a
|
|
72
|
+
specified alias, environment variables, or function parameters.
|
|
73
|
+
Once the session is defined, it won't change during the execution.
|
|
74
|
+
|
|
75
|
+
:param session: Optional session object. If None, uses 'default' or
|
|
76
|
+
alias-specified session from command-line arguments.
|
|
27
77
|
"""
|
|
28
78
|
arguments = sys.argv
|
|
29
79
|
if "-a" in arguments or "--alias" in arguments:
|
|
@@ -32,9 +82,13 @@ class CLIDriver:
|
|
|
32
82
|
|
|
33
83
|
self.session = get_session("default") if session is None else session
|
|
34
84
|
|
|
35
|
-
def _get_alias(self, arguments:
|
|
85
|
+
def _get_alias(self, arguments: List[str]) -> str:
|
|
36
86
|
"""
|
|
37
|
-
Retrieves and removes alias and alias flag from argument list
|
|
87
|
+
Retrieves and removes alias and alias flag from argument list.
|
|
88
|
+
|
|
89
|
+
:param arguments: List[str] - Command line arguments.
|
|
90
|
+
:return: str - The alias value.
|
|
91
|
+
:raises ValueError: If alias flag is present but no value provided.
|
|
38
92
|
"""
|
|
39
93
|
alias_index = None
|
|
40
94
|
|
|
@@ -47,24 +101,46 @@ class CLIDriver:
|
|
|
47
101
|
alias = arguments.pop(alias_index)
|
|
48
102
|
return alias
|
|
49
103
|
|
|
50
|
-
def main(self, args=None):
|
|
104
|
+
def main(self, args: Optional[List[str]] = None) -> int:
|
|
51
105
|
"""
|
|
52
|
-
|
|
106
|
+
Execute the CLI command based on provided arguments.
|
|
107
|
+
|
|
108
|
+
If no argument is received, it defaults to help (first command in base_command list).
|
|
53
109
|
Otherwise, it parses the arguments received to identify the appropriate command and either
|
|
54
110
|
execute it or parse it again to detect subcommands.
|
|
111
|
+
|
|
112
|
+
:param args: Optional[List[str]] - Command line arguments. If None, uses sys.argv.
|
|
113
|
+
:return: int - Exit code (0 for success, non-zero for errors).
|
|
55
114
|
"""
|
|
56
115
|
try:
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
116
|
+
argv = sys.argv if args is None else args
|
|
117
|
+
|
|
118
|
+
if "--verbose" in argv or "-v" in argv:
|
|
119
|
+
setup_verbose_logging()
|
|
120
|
+
argv_copy = [a for a in argv if a not in ("--verbose", "-v")]
|
|
121
|
+
if args is None:
|
|
122
|
+
sys.argv = argv_copy
|
|
123
|
+
else:
|
|
124
|
+
args = argv_copy
|
|
125
|
+
argv = argv_copy
|
|
126
|
+
|
|
127
|
+
logger.debug(f"Running geai with: {' '.join(a for a in argv)}")
|
|
128
|
+
logger.debug(f"Session: {self.session.alias if hasattr(self.session, 'alias') else 'default'}")
|
|
129
|
+
|
|
130
|
+
if len(argv) > 1:
|
|
131
|
+
arg = argv[1] if args is None else args[1]
|
|
132
|
+
arguments = argv[2:] if args is None else args[2:]
|
|
133
|
+
|
|
134
|
+
logger.debug(f"Identifying command for argument: {arg}")
|
|
62
135
|
command = CommandParser(base_commands, base_options).identify_command(arg)
|
|
136
|
+
logger.debug(f"Command identified: {command.name}")
|
|
63
137
|
else:
|
|
138
|
+
logger.debug("No arguments provided, defaulting to help command")
|
|
64
139
|
command = base_commands[0]
|
|
65
140
|
arguments = []
|
|
66
141
|
|
|
67
142
|
self.process_command(command, arguments)
|
|
143
|
+
logger.debug(f"Command completed successfully")
|
|
68
144
|
return ExitCode.SUCCESS
|
|
69
145
|
except UnknownArgumentError as e:
|
|
70
146
|
if hasattr(e, 'available_commands') and e.available_commands:
|
|
@@ -102,27 +178,39 @@ class CLIDriver:
|
|
|
102
178
|
If the command has no action associated with it, it means it has subcommands, so it must be parsed again
|
|
103
179
|
to identify it.
|
|
104
180
|
"""
|
|
181
|
+
logger.debug(f"Processing command: {command.name}, arguments: {arguments}")
|
|
182
|
+
|
|
105
183
|
if command.action:
|
|
106
184
|
if command.additional_args == ArgumentsEnum.NOT_AVAILABLE:
|
|
185
|
+
logger.debug(f"Executing command {command.name} without arguments")
|
|
107
186
|
command.action()
|
|
108
187
|
else:
|
|
188
|
+
logger.debug(f"Extracting options for command {command.name}")
|
|
109
189
|
option_list = CommandParser(base_commands, command.options).extract_option_list(arguments)
|
|
190
|
+
logger.debug(f"Options extracted: {len(option_list)} items")
|
|
110
191
|
command.action(option_list)
|
|
111
192
|
elif command.subcommands:
|
|
112
193
|
subcommand_arg = arguments[0] if len(arguments) > 0 else None
|
|
113
194
|
subcommand_arguments = arguments[1:] if len(arguments) > 1 else []
|
|
114
|
-
|
|
195
|
+
|
|
196
|
+
logger.debug(f"Command has subcommands, identifying: {subcommand_arg}")
|
|
197
|
+
|
|
115
198
|
available_commands = command.subcommands
|
|
116
199
|
available_options = command.options
|
|
117
200
|
parser = CommandParser(available_commands, available_options)
|
|
118
201
|
|
|
119
202
|
if not subcommand_arg:
|
|
203
|
+
logger.debug(f"No subcommand specified, using default: {command.subcommands[0].name}")
|
|
120
204
|
subcommand = command.subcommands[0]
|
|
121
205
|
else:
|
|
122
206
|
subcommand = parser.identify_command(subcommand_arg)
|
|
207
|
+
logger.debug(f"Subcommand identified: {subcommand.name}")
|
|
123
208
|
|
|
124
209
|
if subcommand.additional_args == ArgumentsEnum.NOT_AVAILABLE:
|
|
210
|
+
logger.debug(f"Executing subcommand {subcommand.name} without arguments")
|
|
125
211
|
subcommand.action()
|
|
126
212
|
else:
|
|
213
|
+
logger.debug(f"Extracting options for subcommand {subcommand.name}")
|
|
127
214
|
option_list = CommandParser(None, subcommand.options).extract_option_list(subcommand_arguments)
|
|
215
|
+
logger.debug(f"Options extracted: {len(option_list)} items")
|
|
128
216
|
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
|
"""
|
|
@@ -75,18 +110,26 @@ def get_session(alias: str = None) -> Session:
|
|
|
75
110
|
if _session is None:
|
|
76
111
|
if not alias:
|
|
77
112
|
alias = "default"
|
|
113
|
+
|
|
114
|
+
_validate_alias(alias)
|
|
78
115
|
|
|
79
116
|
_session = Session(
|
|
80
117
|
api_key=settings.get_api_key(alias),
|
|
81
118
|
base_url=settings.get_base_url(alias),
|
|
82
119
|
eval_url=settings.get_eval_url(alias),
|
|
120
|
+
access_token=settings.get_access_token(alias),
|
|
121
|
+
project_id=settings.get_project_id(alias),
|
|
122
|
+
alias=alias,
|
|
83
123
|
)
|
|
84
124
|
elif _session is not None and alias:
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
125
|
+
_validate_alias(alias)
|
|
126
|
+
|
|
127
|
+
_session.alias = alias
|
|
128
|
+
_session.api_key = settings.get_api_key(alias)
|
|
129
|
+
_session.base_url = settings.get_base_url(alias)
|
|
130
|
+
_session.eval_url = settings.get_eval_url(alias)
|
|
131
|
+
_session.access_token = settings.get_access_token(alias)
|
|
132
|
+
_session.project_id = settings.get_project_id(alias)
|
|
90
133
|
|
|
91
134
|
if alias:
|
|
92
135
|
logger.debug(f"Alias: {alias}")
|
|
@@ -96,3 +139,12 @@ def get_session(alias: str = None) -> Session:
|
|
|
96
139
|
except ValueError as e:
|
|
97
140
|
logger.warning(f"Warning: API_KEY and/or BASE_URL not set. {e}")
|
|
98
141
|
sys.stdout.write("Warning: API_KEY and/or BASE_URL not set. Please run geai configure to set them up.\n")
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _validate_alias(alias: str):
|
|
145
|
+
# Validate alias exists
|
|
146
|
+
available_aliases = settings.list_aliases()
|
|
147
|
+
if alias not in available_aliases:
|
|
148
|
+
raise MissingRequirementException(
|
|
149
|
+
f"The profile '{alias}' doesn't exist. Use 'geai configure --list' to see available profiles."
|
|
150
|
+
)
|
pygeai/core/common/config.py
CHANGED
|
@@ -42,9 +42,13 @@ class SettingsManager:
|
|
|
42
42
|
|
|
43
43
|
if setting_key not in self.config[alias]:
|
|
44
44
|
sys.stdout.write(f"'{setting_key}' not found in alias '{alias}' in the credentials file. Adding empty value.\n")
|
|
45
|
-
# SET ADDITIONAL
|
|
45
|
+
# SET ADDITIONAL VARS
|
|
46
46
|
if setting_key.lower() == "GEAI_API_EVAL_URL".lower():
|
|
47
47
|
self.set_eval_url("", alias)
|
|
48
|
+
if setting_key.lower() == "GEAI_OAUTH_ACCESS_TOKEN".lower():
|
|
49
|
+
self.set_access_token("", alias)
|
|
50
|
+
if setting_key.lower() == "GEAI_PROJECT_ID".lower():
|
|
51
|
+
self.set_project_id("", alias)
|
|
48
52
|
|
|
49
53
|
return self.config[alias].get(setting_key, "")
|
|
50
54
|
|
|
@@ -78,6 +82,26 @@ class SettingsManager:
|
|
|
78
82
|
def set_base_url(self, base_url, alias: str = "default"):
|
|
79
83
|
self.set_setting_value("GEAI_API_BASE_URL", base_url, alias)
|
|
80
84
|
|
|
85
|
+
def get_access_token(self, alias: str = "default"):
|
|
86
|
+
access_token = os.environ.get("GEAI_OAUTH_ACCESS_TOKEN") if not alias or alias == "default" else None
|
|
87
|
+
if not access_token:
|
|
88
|
+
access_token = self.get_setting_value("GEAI_OAUTH_ACCESS_TOKEN", alias)
|
|
89
|
+
|
|
90
|
+
return access_token
|
|
91
|
+
|
|
92
|
+
def set_access_token(self, access_token, alias: str = "default"):
|
|
93
|
+
self.set_setting_value("GEAI_OAUTH_ACCESS_TOKEN", access_token, alias)
|
|
94
|
+
|
|
95
|
+
def get_project_id(self, alias: str = "default"):
|
|
96
|
+
project_id = os.environ.get("GEAI_PROJECT_ID") if not alias or alias == "default" else None
|
|
97
|
+
if not project_id:
|
|
98
|
+
project_id = self.get_setting_value("GEAI_PROJECT_ID", alias)
|
|
99
|
+
|
|
100
|
+
return project_id
|
|
101
|
+
|
|
102
|
+
def set_project_id(self, project_id, alias: str = "default"):
|
|
103
|
+
self.set_setting_value("GEAI_PROJECT_ID", project_id, alias)
|
|
104
|
+
|
|
81
105
|
def get_eval_url(self, alias: str = "default"):
|
|
82
106
|
eval_url = os.environ.get("GEAI_API_EVAL_URL") if not alias or alias == "default" else None
|
|
83
107
|
if not eval_url:
|
|
@@ -106,7 +130,6 @@ class SettingsManager:
|
|
|
106
130
|
logger.warning(f"Alias '{alias}' not found in the credentials file.")
|
|
107
131
|
|
|
108
132
|
|
|
109
|
-
|
|
110
133
|
@lru_cache()
|
|
111
134
|
def get_settings():
|
|
112
135
|
return SettingsManager()
|