pygeai 0.6.0b11__py3-none-any.whl → 0.6.0b13__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. pygeai/_docs/source/content/ai_lab/cli.rst +4 -4
  2. pygeai/_docs/source/content/ai_lab/models.rst +169 -35
  3. pygeai/_docs/source/content/ai_lab/runner.rst +2 -2
  4. pygeai/_docs/source/content/ai_lab/spec.rst +9 -9
  5. pygeai/_docs/source/content/ai_lab/usage.rst +34 -34
  6. pygeai/_docs/source/content/ai_lab.rst +1 -1
  7. pygeai/_docs/source/content/analytics.rst +598 -0
  8. pygeai/_docs/source/content/api_reference/chat.rst +428 -2
  9. pygeai/_docs/source/content/api_reference/embeddings.rst +1 -1
  10. pygeai/_docs/source/content/api_reference/project.rst +184 -0
  11. pygeai/_docs/source/content/api_reference/rag.rst +2 -2
  12. pygeai/_docs/source/content/authentication.rst +295 -0
  13. pygeai/_docs/source/content/cli.rst +79 -2
  14. pygeai/_docs/source/content/debugger.rst +1 -1
  15. pygeai/_docs/source/content/migration.rst +19 -2
  16. pygeai/_docs/source/index.rst +2 -0
  17. pygeai/_docs/source/pygeai.analytics.rst +53 -0
  18. pygeai/_docs/source/pygeai.cli.commands.rst +8 -0
  19. pygeai/_docs/source/pygeai.rst +1 -0
  20. pygeai/_docs/source/pygeai.tests.analytics.rst +45 -0
  21. pygeai/_docs/source/pygeai.tests.auth.rst +8 -0
  22. pygeai/_docs/source/pygeai.tests.rst +1 -1
  23. pygeai/analytics/__init__.py +0 -0
  24. pygeai/analytics/clients.py +505 -0
  25. pygeai/analytics/endpoints.py +35 -0
  26. pygeai/analytics/managers.py +606 -0
  27. pygeai/analytics/mappers.py +207 -0
  28. pygeai/analytics/responses.py +240 -0
  29. pygeai/assistant/managers.py +1 -1
  30. pygeai/chat/managers.py +1 -1
  31. pygeai/cli/commands/analytics.py +525 -0
  32. pygeai/cli/commands/base.py +16 -0
  33. pygeai/cli/commands/common.py +28 -24
  34. pygeai/cli/commands/migrate.py +75 -6
  35. pygeai/cli/commands/organization.py +265 -0
  36. pygeai/cli/commands/validators.py +144 -1
  37. pygeai/cli/error_handler.py +41 -6
  38. pygeai/cli/geai.py +106 -18
  39. pygeai/cli/parsers.py +75 -31
  40. pygeai/cli/texts/help.py +75 -6
  41. pygeai/core/base/clients.py +18 -4
  42. pygeai/core/base/session.py +59 -7
  43. pygeai/core/common/config.py +25 -2
  44. pygeai/core/common/exceptions.py +64 -1
  45. pygeai/core/embeddings/managers.py +1 -1
  46. pygeai/core/files/managers.py +1 -1
  47. pygeai/core/rerank/managers.py +1 -1
  48. pygeai/core/services/rest.py +20 -2
  49. pygeai/evaluation/clients.py +5 -3
  50. pygeai/lab/agents/clients.py +3 -3
  51. pygeai/lab/agents/endpoints.py +2 -2
  52. pygeai/lab/agents/mappers.py +50 -2
  53. pygeai/lab/clients.py +5 -2
  54. pygeai/lab/managers.py +8 -10
  55. pygeai/lab/models.py +70 -2
  56. pygeai/lab/tools/clients.py +1 -59
  57. pygeai/migration/__init__.py +3 -1
  58. pygeai/migration/strategies.py +72 -3
  59. pygeai/organization/clients.py +110 -1
  60. pygeai/organization/endpoints.py +11 -7
  61. pygeai/organization/limits/managers.py +1 -1
  62. pygeai/organization/managers.py +135 -3
  63. pygeai/organization/mappers.py +28 -2
  64. pygeai/organization/responses.py +11 -1
  65. pygeai/tests/analytics/__init__.py +0 -0
  66. pygeai/tests/analytics/test_clients.py +86 -0
  67. pygeai/tests/analytics/test_managers.py +94 -0
  68. pygeai/tests/analytics/test_mappers.py +84 -0
  69. pygeai/tests/analytics/test_responses.py +73 -0
  70. pygeai/tests/auth/test_oauth.py +172 -0
  71. pygeai/tests/cli/commands/test_migrate.py +14 -1
  72. pygeai/tests/cli/commands/test_organization.py +69 -1
  73. pygeai/tests/cli/test_error_handler.py +4 -4
  74. pygeai/tests/cli/test_geai_driver.py +1 -1
  75. pygeai/tests/lab/agents/test_mappers.py +128 -1
  76. pygeai/tests/lab/test_models.py +2 -0
  77. pygeai/tests/lab/tools/test_clients.py +2 -31
  78. pygeai/tests/organization/test_clients.py +180 -1
  79. pygeai/tests/organization/test_managers.py +40 -0
  80. pygeai/tests/snippets/analytics/__init__.py +0 -0
  81. pygeai/tests/snippets/analytics/get_agent_usage_per_user.py +16 -0
  82. pygeai/tests/snippets/analytics/get_agents_created_and_modified.py +11 -0
  83. pygeai/tests/snippets/analytics/get_average_cost_per_request.py +10 -0
  84. pygeai/tests/snippets/analytics/get_overall_error_rate.py +10 -0
  85. pygeai/tests/snippets/analytics/get_top_10_agents_by_requests.py +12 -0
  86. pygeai/tests/snippets/analytics/get_total_active_users.py +10 -0
  87. pygeai/tests/snippets/analytics/get_total_cost.py +10 -0
  88. pygeai/tests/snippets/analytics/get_total_requests_per_day.py +12 -0
  89. pygeai/tests/snippets/analytics/get_total_tokens.py +12 -0
  90. pygeai/tests/snippets/chat/get_response_complete_example.py +67 -0
  91. pygeai/tests/snippets/chat/get_response_with_instructions.py +19 -0
  92. pygeai/tests/snippets/chat/get_response_with_metadata.py +24 -0
  93. pygeai/tests/snippets/chat/get_response_with_parallel_tools.py +58 -0
  94. pygeai/tests/snippets/chat/get_response_with_reasoning.py +21 -0
  95. pygeai/tests/snippets/chat/get_response_with_store.py +38 -0
  96. pygeai/tests/snippets/chat/get_response_with_truncation.py +24 -0
  97. pygeai/tests/snippets/lab/agents/create_agent_with_permissions.py +39 -0
  98. pygeai/tests/snippets/lab/agents/create_agent_with_properties.py +46 -0
  99. pygeai/tests/snippets/lab/agents/get_agent_with_new_fields.py +62 -0
  100. pygeai/tests/snippets/lab/agents/update_agent_properties.py +50 -0
  101. pygeai/tests/snippets/organization/add_project_member.py +10 -0
  102. pygeai/tests/snippets/organization/add_project_member_batch.py +44 -0
  103. {pygeai-0.6.0b11.dist-info → pygeai-0.6.0b13.dist-info}/METADATA +1 -1
  104. {pygeai-0.6.0b11.dist-info → pygeai-0.6.0b13.dist-info}/RECORD +108 -98
  105. pygeai/_docs/source/pygeai.tests.snippets.assistants.data_analyst.rst +0 -37
  106. pygeai/_docs/source/pygeai.tests.snippets.assistants.rag.rst +0 -85
  107. pygeai/_docs/source/pygeai.tests.snippets.assistants.rst +0 -78
  108. pygeai/_docs/source/pygeai.tests.snippets.auth.rst +0 -10
  109. pygeai/_docs/source/pygeai.tests.snippets.chat.rst +0 -125
  110. pygeai/_docs/source/pygeai.tests.snippets.dbg.rst +0 -45
  111. pygeai/_docs/source/pygeai.tests.snippets.embeddings.rst +0 -61
  112. pygeai/_docs/source/pygeai.tests.snippets.evaluation.dataset.rst +0 -197
  113. pygeai/_docs/source/pygeai.tests.snippets.evaluation.plan.rst +0 -133
  114. pygeai/_docs/source/pygeai.tests.snippets.evaluation.result.rst +0 -37
  115. pygeai/_docs/source/pygeai.tests.snippets.evaluation.rst +0 -20
  116. pygeai/_docs/source/pygeai.tests.snippets.extras.rst +0 -37
  117. pygeai/_docs/source/pygeai.tests.snippets.files.rst +0 -53
  118. pygeai/_docs/source/pygeai.tests.snippets.gam.rst +0 -21
  119. pygeai/_docs/source/pygeai.tests.snippets.lab.agents.rst +0 -93
  120. pygeai/_docs/source/pygeai.tests.snippets.lab.processes.jobs.rst +0 -21
  121. pygeai/_docs/source/pygeai.tests.snippets.lab.processes.kbs.rst +0 -45
  122. pygeai/_docs/source/pygeai.tests.snippets.lab.processes.rst +0 -46
  123. pygeai/_docs/source/pygeai.tests.snippets.lab.rst +0 -82
  124. pygeai/_docs/source/pygeai.tests.snippets.lab.samples.rst +0 -21
  125. pygeai/_docs/source/pygeai.tests.snippets.lab.strategies.rst +0 -45
  126. pygeai/_docs/source/pygeai.tests.snippets.lab.tools.rst +0 -85
  127. pygeai/_docs/source/pygeai.tests.snippets.lab.use_cases.rst +0 -117
  128. pygeai/_docs/source/pygeai.tests.snippets.migrate.rst +0 -10
  129. pygeai/_docs/source/pygeai.tests.snippets.organization.rst +0 -109
  130. pygeai/_docs/source/pygeai.tests.snippets.rag.rst +0 -85
  131. pygeai/_docs/source/pygeai.tests.snippets.rerank.rst +0 -21
  132. pygeai/_docs/source/pygeai.tests.snippets.rst +0 -32
  133. pygeai/_docs/source/pygeai.tests.snippets.secrets.rst +0 -10
  134. pygeai/_docs/source/pygeai.tests.snippets.usage_limit.rst +0 -77
  135. {pygeai-0.6.0b11.dist-info → pygeai-0.6.0b13.dist-info}/WHEEL +0 -0
  136. {pygeai-0.6.0b11.dist-info → pygeai-0.6.0b13.dist-info}/entry_points.txt +0 -0
  137. {pygeai-0.6.0b11.dist-info → pygeai-0.6.0b13.dist-info}/licenses/LICENSE +0 -0
  138. {pygeai-0.6.0b11.dist-info → pygeai-0.6.0b13.dist-info}/top_level.txt +0 -0
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 main():
16
- driver = CLIDriver()
17
- return driver.main()
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
- def __init__(self, session=None):
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
- Sets session to be used while running the command, either with a specified alias,
25
- environment variables or function parameters.
26
- Once the session is defined, it won't change during the curse of the execution.
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: list):
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
- If not argument is received, it defaults to help (first command in base_command list).
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
- logger.debug(f"Running geai with: {' '.join(a for a in sys.argv)}")
58
- if len(sys.argv) > 1:
59
- arg = sys.argv[1] if args is None else args[1]
60
- arguments = sys.argv[2:] if args is None else args[2:]
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__(self, available_commands, available_options):
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 first_argument: The first argument to be analyzed.
17
- :return: The command to be run.
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
- error = UnknownArgumentError(f"'{arg}' is not a valid command.")
22
- error.arg = arg
23
- error.available_commands = self.available_commands
24
- raise error
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: list) -> list[(Option, str)]:
46
+ def extract_option_list(self, arguments: List[str]) -> List[Tuple[Option, str]]:
29
47
  """
30
- Parses a list of arguments and returns the commands being invoked.
48
+ Parses a list of arguments and returns the options being invoked.
31
49
 
32
- :param arguments: list - The list of arguments received by the CLI utility.
33
- :return: list - A list of flags and their associated arguments.
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
- flag_list = []
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
- error = UnknownArgumentError(f"'{arg}' is not a valid option.")
46
- error.arg = arg
47
- error.available_options = self.available_options
48
- raise error
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
- flag_list.append((flag, arguments[i + 1]))
54
- except IndexError as e:
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
- flag_list.append([flag, []])
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
- associated_command = None
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
- associated_command = command
66
- break
103
+ return command
104
+
105
+ return None
67
106
 
68
- return associated_command
107
+ def _get_associated_option(self, arg: str) -> Optional[Option]:
108
+ """
109
+ Finds the option associated with the given argument.
69
110
 
70
- def _get_associated_option(self, arg: str) -> Option:
71
- associated_option = None
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
- associated_option = option
75
- break
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 agent --agent-id "UUID" --target-project "TARGET_UUID"
344
- migrates an agent to a different project.
345
-
346
- The command:
347
- geai migrate process --process-id "UUID" --target-project "TARGET_UUID"
348
- migrates an agentic process to another project
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
+ """
@@ -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 not (api_key and base_url) and alias:
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.__api_service = ApiService(base_url=self.session.base_url, token=self.session.api_key)
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):
@@ -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 or not base_url:
33
- logger.warning("Cannot instantiate session without api_key and base_url")
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
- new_api_key = settings.get_api_key(alias)
86
- if new_api_key != _session.api_key:
87
- _session.api_key = new_api_key
88
- _session.base_url = settings.get_base_url(alias)
89
- _session.eval_url = settings.get_eval_url(alias)
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
+ )
@@ -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 URLS
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()