pygeai 0.6.0b13__py3-none-any.whl → 0.6.1__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 (217) hide show
  1. pygeai/__init__.py +1 -2
  2. pygeai/_docs/source/content/api_reference/project.rst +392 -0
  3. pygeai/_docs/source/content/authentication.rst +130 -1
  4. pygeai/_docs/source/content/debugger.rst +327 -157
  5. pygeai/_docs/source/content/migration.rst +391 -7
  6. pygeai/_docs/source/pygeai.core.common.rst +8 -0
  7. pygeai/_docs/source/pygeai.tests.auth.rst +56 -0
  8. pygeai/_docs/source/pygeai.tests.cli.rst +8 -0
  9. pygeai/admin/clients.py +1 -3
  10. pygeai/analytics/clients.py +1 -1
  11. pygeai/assistant/clients.py +2 -7
  12. pygeai/assistant/data/clients.py +0 -8
  13. pygeai/assistant/data_analyst/clients.py +0 -2
  14. pygeai/assistant/managers.py +1 -1
  15. pygeai/assistant/rag/clients.py +0 -2
  16. pygeai/assistant/rag/mappers.py +9 -11
  17. pygeai/auth/clients.py +26 -7
  18. pygeai/auth/endpoints.py +2 -1
  19. pygeai/chat/clients.py +2 -2
  20. pygeai/chat/managers.py +1 -1
  21. pygeai/cli/commands/admin.py +13 -25
  22. pygeai/cli/commands/analytics.py +56 -88
  23. pygeai/cli/commands/assistant.py +84 -138
  24. pygeai/cli/commands/auth.py +23 -46
  25. pygeai/cli/commands/base.py +0 -1
  26. pygeai/cli/commands/chat.py +218 -209
  27. pygeai/cli/commands/common.py +5 -5
  28. pygeai/cli/commands/configuration.py +79 -29
  29. pygeai/cli/commands/docs.py +3 -4
  30. pygeai/cli/commands/embeddings.py +13 -19
  31. pygeai/cli/commands/evaluation.py +133 -344
  32. pygeai/cli/commands/feedback.py +7 -15
  33. pygeai/cli/commands/files.py +26 -53
  34. pygeai/cli/commands/gam.py +28 -69
  35. pygeai/cli/commands/lab/ai_lab.py +96 -142
  36. pygeai/cli/commands/lab/common.py +1 -1
  37. pygeai/cli/commands/lab/spec.py +12 -32
  38. pygeai/cli/commands/llm.py +9 -18
  39. pygeai/cli/commands/migrate.py +43 -99
  40. pygeai/cli/commands/organization.py +223 -196
  41. pygeai/cli/commands/rag.py +35 -58
  42. pygeai/cli/commands/rerank.py +21 -25
  43. pygeai/cli/commands/secrets.py +39 -67
  44. pygeai/cli/commands/usage_limits.py +50 -136
  45. pygeai/cli/commands/validators.py +1 -1
  46. pygeai/cli/geai.py +32 -3
  47. pygeai/cli/geai_proxy.py +6 -2
  48. pygeai/cli/install_man.py +1 -1
  49. pygeai/cli/parsers.py +1 -1
  50. pygeai/core/base/clients.py +90 -21
  51. pygeai/core/base/mappers.py +39 -55
  52. pygeai/core/base/session.py +134 -22
  53. pygeai/core/common/config.py +50 -13
  54. pygeai/core/common/constants.py +8 -0
  55. pygeai/core/common/exceptions.py +6 -0
  56. pygeai/core/embeddings/clients.py +0 -1
  57. pygeai/core/embeddings/managers.py +0 -1
  58. pygeai/core/feedback/clients.py +0 -2
  59. pygeai/core/feedback/models.py +1 -1
  60. pygeai/core/files/clients.py +0 -3
  61. pygeai/core/files/managers.py +1 -1
  62. pygeai/core/files/mappers.py +4 -5
  63. pygeai/core/llm/clients.py +0 -1
  64. pygeai/core/models.py +4 -4
  65. pygeai/core/plugins/clients.py +0 -3
  66. pygeai/core/plugins/models.py +2 -2
  67. pygeai/core/rerank/clients.py +0 -2
  68. pygeai/core/secrets/clients.py +0 -2
  69. pygeai/core/services/rest.py +80 -14
  70. pygeai/core/singleton.py +24 -0
  71. pygeai/dbg/__init__.py +2 -2
  72. pygeai/dbg/debugger.py +276 -38
  73. pygeai/evaluation/clients.py +2 -4
  74. pygeai/evaluation/dataset/clients.py +0 -1
  75. pygeai/evaluation/plan/clients.py +0 -2
  76. pygeai/evaluation/result/clients.py +0 -2
  77. pygeai/gam/clients.py +1 -3
  78. pygeai/health/clients.py +1 -3
  79. pygeai/lab/clients.py +0 -1
  80. pygeai/lab/managers.py +0 -1
  81. pygeai/lab/models.py +0 -1
  82. pygeai/lab/strategies/clients.py +1 -2
  83. pygeai/lab/tools/clients.py +2 -2
  84. pygeai/lab/tools/mappers.py +1 -1
  85. pygeai/migration/strategies.py +5 -6
  86. pygeai/migration/tools.py +1 -1
  87. pygeai/organization/clients.py +118 -12
  88. pygeai/organization/endpoints.py +1 -0
  89. pygeai/organization/limits/clients.py +4 -6
  90. pygeai/organization/limits/managers.py +1 -4
  91. pygeai/organization/managers.py +2 -2
  92. pygeai/proxy/config.py +1 -0
  93. pygeai/proxy/managers.py +6 -5
  94. pygeai/tests/admin/test_clients.py +11 -11
  95. pygeai/tests/assistants/rag/test_clients.py +1 -1
  96. pygeai/tests/assistants/rag/test_models.py +1 -2
  97. pygeai/tests/assistants/test_clients.py +1 -1
  98. pygeai/tests/assistants/test_managers.py +1 -3
  99. pygeai/tests/auth/test_cli_configuration.py +252 -0
  100. pygeai/tests/auth/test_client_initialization.py +411 -0
  101. pygeai/tests/auth/test_clients.py +29 -27
  102. pygeai/tests/auth/test_config_manager.py +305 -0
  103. pygeai/tests/auth/test_header_injection.py +294 -0
  104. pygeai/tests/auth/test_oauth.py +3 -1
  105. pygeai/tests/auth/test_session_logging.py +119 -0
  106. pygeai/tests/auth/test_session_validation.py +408 -0
  107. pygeai/tests/auth/test_singleton_reset.py +201 -0
  108. pygeai/tests/chat/test_clients.py +1 -1
  109. pygeai/tests/chat/test_iris.py +1 -1
  110. pygeai/tests/chat/test_ui.py +0 -2
  111. pygeai/tests/cli/commands/lab/test_ai_lab.py +1 -3
  112. pygeai/tests/cli/commands/lab/test_common.py +0 -1
  113. pygeai/tests/cli/commands/test_chat.py +1 -1
  114. pygeai/tests/cli/commands/test_common.py +0 -1
  115. pygeai/tests/cli/commands/test_embeddings.py +2 -2
  116. pygeai/tests/cli/commands/test_evaluation.py +1 -9
  117. pygeai/tests/cli/commands/test_llm.py +1 -1
  118. pygeai/tests/cli/commands/test_migrate.py +1 -1
  119. pygeai/tests/cli/commands/test_rerank.py +0 -1
  120. pygeai/tests/cli/commands/test_secrets.py +1 -1
  121. pygeai/tests/cli/commands/test_show_help.py +0 -1
  122. pygeai/tests/cli/commands/test_validators.py +0 -1
  123. pygeai/tests/cli/test_credentials_flag.py +312 -0
  124. pygeai/tests/cli/test_error_handler.py +0 -1
  125. pygeai/tests/core/base/test_mappers.py +2 -2
  126. pygeai/tests/core/base/test_models.py +4 -4
  127. pygeai/tests/core/common/test_config.py +2 -7
  128. pygeai/tests/core/common/test_decorators.py +0 -1
  129. pygeai/tests/core/embeddings/test_managers.py +1 -1
  130. pygeai/tests/core/feedback/test_clients.py +2 -2
  131. pygeai/tests/core/files/test_clients.py +6 -6
  132. pygeai/tests/core/files/test_models.py +0 -1
  133. pygeai/tests/core/files/test_responses.py +0 -1
  134. pygeai/tests/core/llm/test_clients.py +1 -1
  135. pygeai/tests/core/plugins/test_clients.py +4 -4
  136. pygeai/tests/core/rerank/test_mappers.py +1 -3
  137. pygeai/tests/core/secrets/test_clients.py +2 -3
  138. pygeai/tests/core/services/test_rest.py +10 -10
  139. pygeai/tests/core/utils/test_console.py +0 -1
  140. pygeai/tests/dbg/test_debugger.py +95 -8
  141. pygeai/tests/evaluation/dataset/test_clients.py +24 -27
  142. pygeai/tests/evaluation/plan/test_clients.py +16 -18
  143. pygeai/tests/evaluation/result/test_clients.py +4 -5
  144. pygeai/tests/health/test_clients.py +2 -2
  145. pygeai/tests/integration/lab/agents/test_create_agent.py +1 -3
  146. pygeai/tests/integration/lab/agents/test_get_agent.py +1 -1
  147. pygeai/tests/integration/lab/processes/test_create_process.py +2 -2
  148. pygeai/tests/integration/lab/processes/test_create_task.py +2 -3
  149. pygeai/tests/integration/lab/processes/test_delete_process.py +0 -1
  150. pygeai/tests/integration/lab/processes/test_get_process.py +2 -4
  151. pygeai/tests/integration/lab/processes/test_list_process_instances.py +1 -3
  152. pygeai/tests/integration/lab/processes/test_update_process.py +3 -9
  153. pygeai/tests/integration/lab/reasoning_strategies/test_update_reasoning_strategy.py +1 -2
  154. pygeai/tests/integration/lab/tools/test_delete_tool.py +1 -1
  155. pygeai/tests/integration/lab/tools/test_list_tools.py +1 -1
  156. pygeai/tests/integration/lab/tools/test_update_tool.py +1 -1
  157. pygeai/tests/lab/agents/test_clients.py +17 -17
  158. pygeai/tests/lab/processes/test_clients.py +67 -67
  159. pygeai/tests/lab/processes/test_mappers.py +23 -23
  160. pygeai/tests/lab/spec/test_loader.py +0 -2
  161. pygeai/tests/lab/spec/test_parsers.py +1 -2
  162. pygeai/tests/lab/strategies/test_clients.py +10 -10
  163. pygeai/tests/lab/test_managers.py +3 -5
  164. pygeai/tests/lab/test_mappers.py +1 -4
  165. pygeai/tests/lab/tools/test_clients.py +21 -21
  166. pygeai/tests/lab/tools/test_mappers.py +0 -1
  167. pygeai/tests/organization/limits/test_clients.py +33 -33
  168. pygeai/tests/organization/limits/test_managers.py +7 -7
  169. pygeai/tests/organization/test_clients.py +78 -60
  170. pygeai/tests/proxy/test_clients.py +1 -1
  171. pygeai/tests/proxy/test_integration.py +1 -4
  172. pygeai/tests/proxy/test_managers.py +1 -2
  173. pygeai/tests/proxy/test_servers.py +1 -2
  174. pygeai/tests/snippets/assistants/rag/delete_rag_assistant.py +0 -1
  175. pygeai/tests/snippets/assistants/rag/get_documents.py +0 -1
  176. pygeai/tests/snippets/assistants/rag/get_rag_assistant_data.py +0 -1
  177. pygeai/tests/snippets/chat/get_request_status.py +0 -1
  178. pygeai/tests/snippets/dbg/file_debugging.py +72 -0
  179. pygeai/tests/snippets/dbg/module_debugging.py +60 -0
  180. pygeai/tests/snippets/embeddings/cohere_example.py +2 -2
  181. pygeai/tests/snippets/embeddings/openai_base64_example.py +1 -1
  182. pygeai/tests/snippets/evaluation/dataset/complete_workflow_example.py +8 -8
  183. pygeai/tests/snippets/evaluation/plan/complete_workflow_example.py +5 -5
  184. pygeai/tests/snippets/evaluation/result/complete_workflow_example.py +3 -3
  185. pygeai/tests/snippets/lab/agentic_flow_example_1.py +1 -1
  186. pygeai/tests/snippets/lab/agentic_flow_example_2.py +3 -4
  187. pygeai/tests/snippets/lab/agents/create_agent_with_permissions.py +2 -2
  188. pygeai/tests/snippets/lab/agents/delete_agent.py +1 -2
  189. pygeai/tests/snippets/lab/agents/get_agent.py +1 -1
  190. pygeai/tests/snippets/lab/agents/get_agent_with_new_fields.py +10 -10
  191. pygeai/tests/snippets/lab/agents/get_sharing_link.py +0 -1
  192. pygeai/tests/snippets/lab/agents/list_agents.py +1 -1
  193. pygeai/tests/snippets/lab/agents/publish_agent_revision.py +0 -1
  194. pygeai/tests/snippets/lab/agents/update_agent_properties.py +1 -1
  195. pygeai/tests/snippets/lab/crud_ui.py +3 -5
  196. pygeai/tests/snippets/lab/processes/kbs/get_kb.py +0 -1
  197. pygeai/tests/snippets/lab/processes/kbs/list_kbs.py +0 -1
  198. pygeai/tests/snippets/lab/processes/list_processes.py +1 -1
  199. pygeai/tests/snippets/lab/samples/summarize_files.py +0 -3
  200. pygeai/tests/snippets/lab/strategies/get_reasoning_strategy.py +0 -1
  201. pygeai/tests/snippets/lab/strategies/list_reasoning_strategies.py +1 -1
  202. pygeai/tests/snippets/lab/tools/get_tool.py +1 -1
  203. pygeai/tests/snippets/lab/tools/publish_tool_revision.py +0 -1
  204. pygeai/tests/snippets/lab/tools/set_parameters.py +1 -2
  205. pygeai/tests/snippets/lab/use_cases/c_code_fixer_agent_flow.py +2 -3
  206. pygeai/tests/snippets/lab/use_cases/file_summarizer_example_2.py +1 -1
  207. pygeai/tests/snippets/lab/use_cases/update_cli_expert.py +0 -1
  208. pygeai/tests/snippets/lab/use_cases/update_lab_expert.py +0 -1
  209. pygeai/tests/snippets/lab/use_cases/update_web_designer.py +0 -1
  210. pygeai/tests/snippets/lab/use_cases/update_web_reader.py +0 -1
  211. pygeai/tests/snippets/migrate/orchestrator_examples.py +1 -1
  212. {pygeai-0.6.0b13.dist-info → pygeai-0.6.1.dist-info}/METADATA +32 -7
  213. {pygeai-0.6.0b13.dist-info → pygeai-0.6.1.dist-info}/RECORD +217 -206
  214. {pygeai-0.6.0b13.dist-info → pygeai-0.6.1.dist-info}/WHEEL +0 -0
  215. {pygeai-0.6.0b13.dist-info → pygeai-0.6.1.dist-info}/entry_points.txt +0 -0
  216. {pygeai-0.6.0b13.dist-info → pygeai-0.6.1.dist-info}/licenses/LICENSE +0 -0
  217. {pygeai-0.6.0b13.dist-info → pygeai-0.6.1.dist-info}/top_level.txt +0 -0
pygeai/core/singleton.py CHANGED
@@ -8,3 +8,27 @@ class Singleton(type):
8
8
  cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
9
9
 
10
10
  return cls._instances[cls]
11
+
12
+ @classmethod
13
+ def reset_instance(mcs, cls):
14
+ """
15
+ Reset the singleton instance for a specific class.
16
+
17
+ This is intended for testing purposes to ensure proper isolation
18
+ between tests. In production code, singletons persist for the
19
+ application lifetime.
20
+
21
+ :param cls: The class whose singleton instance should be reset
22
+ """
23
+ if cls in mcs._instances:
24
+ del mcs._instances[cls]
25
+
26
+ @classmethod
27
+ def reset_all_instances(mcs):
28
+ """
29
+ Reset all singleton instances.
30
+
31
+ This is intended for testing purposes to ensure proper isolation
32
+ between tests. Use with caution as it clears all singleton caches.
33
+ """
34
+ mcs._instances.clear()
pygeai/dbg/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
- from pygeai.dbg.debugger import Debugger, Breakpoint
1
+ from pygeai.dbg.debugger import Debugger, Breakpoint, debug_file, debug_module
2
2
 
3
- __all__ = ['Debugger', 'Breakpoint']
3
+ __all__ = ['Debugger', 'Breakpoint', 'debug_file', 'debug_module']
pygeai/dbg/debugger.py CHANGED
@@ -1,11 +1,12 @@
1
1
  import logging
2
2
  import sys
3
3
  import inspect
4
- import readline
5
4
  import pprint
5
+ import argparse
6
6
  from types import FrameType
7
- from typing import Optional, Any, Callable, Set, Dict, List, Tuple
8
- from dataclasses import dataclass, field
7
+ from typing import Optional, Any, Callable, Dict, List, Tuple
8
+ from dataclasses import dataclass
9
+ from importlib import util
9
10
 
10
11
  from pygeai.cli.geai import main as geai
11
12
  from pygeai.core.utils.console import Console
@@ -62,16 +63,19 @@ class Debugger:
62
63
  - Readline support for command history
63
64
  """
64
65
 
65
- def __init__(self, target: Optional[Callable] = None, module_filter: str = "pygeai"):
66
+ def __init__(self, target: Optional[Callable] = None, module_filter: str = "pygeai", verbose: bool = False, log_level: str = 'DEBUG'):
66
67
  """
67
68
  Initialize the debugger.
68
69
 
69
- Args:
70
- target: The callable to debug. If None, defaults to pygeai.cli.geai.main
71
- module_filter: Only trace modules starting with this prefix (for performance)
70
+ :param target: Optional[Callable] - The callable to debug. If None, defaults to pygeai.cli.geai.main (optional).
71
+ :param module_filter: str - Only trace modules starting with this prefix (for performance). Empty string traces all modules. Use '__main__' to trace only the main script (default is 'pygeai').
72
+ :param verbose: bool - If True, enable logging for pygeai modules (default is False).
73
+ :param log_level: str - Log level for verbose mode: 'DEBUG', 'INFO', 'WARNING', 'ERROR' (default is 'DEBUG').
72
74
  """
73
75
  self.target = target or geai
74
76
  self.module_filter = module_filter
77
+ self.verbose = verbose
78
+ self.log_level = getattr(logging, log_level.upper(), logging.DEBUG)
75
79
  self._setup_logging()
76
80
  self.logger = logging.getLogger('geai.dbg')
77
81
 
@@ -106,9 +110,31 @@ class Debugger:
106
110
  console_handler.setFormatter(formatter)
107
111
  logger.addHandler(console_handler)
108
112
  logger.propagate = False
113
+
114
+ # If verbose mode, enable logging for pygeai modules at specified level
115
+ if self.verbose:
116
+ # Configure root logger to show at specified log level
117
+ root_logger = logging.getLogger()
118
+ if not root_logger.handlers:
119
+ root_logger.setLevel(self.log_level)
120
+ console_handler = logging.StreamHandler()
121
+ console_handler.setLevel(self.log_level)
122
+ formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
123
+ console_handler.setFormatter(formatter)
124
+ root_logger.addHandler(console_handler)
125
+
126
+ # Ensure pygeai logger propagates to root
127
+ pygeai_logger = logging.getLogger('pygeai')
128
+ pygeai_logger.setLevel(self.log_level)
129
+ pygeai_logger.propagate = True
109
130
 
110
131
  def _setup_readline(self):
111
132
  """Setup readline for command history and tab completion."""
133
+ if not util.find_spec("readline"):
134
+ self.logger.debug("Readline not available (not supported on this platform)")
135
+ return
136
+
137
+ import readline
112
138
  try:
113
139
  import os
114
140
  histfile = os.path.expanduser("~/.geai_dbg_history")
@@ -145,13 +171,10 @@ class Debugger:
145
171
  """
146
172
  Add a breakpoint by module and/or function name.
147
173
 
148
- Args:
149
- module: Module name to break on (None for any)
150
- function_name: Function name to break on (None for any)
151
- condition: Optional condition expression (Python code)
152
-
153
- Returns:
154
- The created Breakpoint object
174
+ :param module: Optional[str] - Module name to break on. None for any module (optional).
175
+ :param function_name: Optional[str] - Function name to break on. None for any function (optional).
176
+ :param condition: Optional[str] - Optional condition expression in Python code. Breakpoint only triggers if condition evaluates to True (optional).
177
+ :return: Breakpoint - The created Breakpoint object.
155
178
  """
156
179
  key = (module, function_name)
157
180
  if key in self.breakpoints:
@@ -206,6 +229,8 @@ class Debugger:
206
229
  """Check if we should trace this module based on filter."""
207
230
  if not module:
208
231
  return False
232
+ if not self.module_filter:
233
+ return True
209
234
  return module.startswith(self.module_filter)
210
235
 
211
236
  def _check_condition(self, bp: Breakpoint, frame: FrameType) -> bool:
@@ -214,7 +239,6 @@ class Debugger:
214
239
  return True
215
240
 
216
241
  try:
217
- # Evaluate condition in frame's context
218
242
  result = eval(bp.condition, frame.f_globals, frame.f_locals)
219
243
  return bool(result)
220
244
  except Exception as e:
@@ -234,17 +258,14 @@ class Debugger:
234
258
  """Trace function calls to intercept execution."""
235
259
  module = frame.f_globals.get('__name__')
236
260
 
237
- # Performance optimization: only trace filtered modules
238
261
  if not self._should_trace_module(module):
239
262
  return None
240
263
 
241
264
  function_name = frame.f_code.co_name
242
265
 
243
- # Handle different event types
244
266
  if event == 'call':
245
267
  self.current_depth += 1
246
268
 
247
- # Check breakpoints
248
269
  should_break = False
249
270
  for bp in self.breakpoints.values():
250
271
  if bp.matches(module, function_name):
@@ -265,7 +286,6 @@ class Debugger:
265
286
  elif event == 'return':
266
287
  self.current_depth -= 1
267
288
 
268
- # Handle step-out (return from function)
269
289
  if self.step_mode == 'return' and frame == self.step_frame:
270
290
  self.step_mode = None
271
291
  self.paused = True
@@ -276,7 +296,6 @@ class Debugger:
276
296
  self.paused = False
277
297
 
278
298
  elif event == 'line':
279
- # Handle step and next
280
299
  if self.step_mode == 'step':
281
300
  self.step_mode = None
282
301
  self.paused = True
@@ -372,7 +391,6 @@ class Debugger:
372
391
  cmd = parts[0]
373
392
  args = parts[1] if len(parts) > 1 else ""
374
393
 
375
- # Control flow commands
376
394
  if cmd in ('continue', 'c'):
377
395
  break
378
396
  elif cmd in ('quit', 'q'):
@@ -383,7 +401,6 @@ class Debugger:
383
401
  sys.settrace(None)
384
402
  break
385
403
 
386
- # Stepping commands
387
404
  elif cmd in ('step', 's'):
388
405
  self.step_mode = 'step'
389
406
  self.step_depth = self.current_depth
@@ -397,7 +414,6 @@ class Debugger:
397
414
  self.step_frame = frame
398
415
  break
399
416
 
400
- # Stack navigation
401
417
  elif cmd in ('up', 'u'):
402
418
  self._move_frame(1)
403
419
  elif cmd in ('down', 'd'):
@@ -405,12 +421,10 @@ class Debugger:
405
421
  elif cmd in ('where', 'w', 'bt', 'backtrace'):
406
422
  self._print_stack_trace()
407
423
 
408
- # Source display
409
424
  elif cmd in ('list', 'l'):
410
425
  context = int(args) if args.isdigit() else 10
411
426
  self._print_source(self.current_frame, context)
412
427
 
413
- # Variable inspection
414
428
  elif cmd in ('print', 'p'):
415
429
  if not args:
416
430
  Console.write_stdout("Usage: p <expression>")
@@ -437,7 +451,6 @@ class Debugger:
437
451
 
438
452
  elif cmd in ('globals', 'glob'):
439
453
  Console.write_stdout("\nGlobal variables:")
440
- # Filter out builtins
441
454
  filtered = {k: v for k, v in self.current_frame.f_globals.items()
442
455
  if not k.startswith('__')}
443
456
  pprint.pprint(filtered)
@@ -448,10 +461,8 @@ class Debugger:
448
461
  for arg in arginfo.args:
449
462
  Console.write_stdout(f" {arg} = {repr(self.current_frame.f_locals.get(arg))}")
450
463
 
451
- # Breakpoint management
452
464
  elif cmd in ('break', 'b'):
453
465
  if not args:
454
- # List breakpoints
455
466
  bps = self.list_breakpoints()
456
467
  if bps:
457
468
  Console.write_stdout("\nBreakpoints:")
@@ -460,7 +471,6 @@ class Debugger:
460
471
  else:
461
472
  Console.write_stdout("No breakpoints set.")
462
473
  else:
463
- # Parse breakpoint specification: module:function or just function
464
474
  if ':' in args:
465
475
  mod, func = args.split(':', 1)
466
476
  mod = mod.strip() or None
@@ -509,7 +519,6 @@ class Debugger:
509
519
  func = args.strip()
510
520
  self.disable_breakpoint(module=mod, function_name=func)
511
521
 
512
- # Legacy commands
513
522
  elif cmd in ('breakpoint-module', 'bm'):
514
523
  self.logger.info("Adding breakpoint on module")
515
524
  module_name = input("(geai-dbg) Enter module name (or press Enter for any module): ").strip()
@@ -528,20 +537,17 @@ class Debugger:
528
537
  self.logger.info("Listing available modules")
529
538
  modules = [m for m in sys.modules if m.startswith(self.module_filter)]
530
539
  Console.write_stdout(f"\nAvailable modules ({len(modules)}):")
531
- for mod in sorted(modules)[:50]: # Limit output
540
+ for mod in sorted(modules)[:50]:
532
541
  Console.write_stdout(f" {mod}")
533
542
  if len(modules) > 50:
534
543
  Console.write_stdout(f" ... and {len(modules) - 50} more")
535
544
 
536
- # Help
537
545
  elif cmd in ('help', 'h', '?'):
538
546
  self._print_help()
539
547
 
540
- # Execute arbitrary Python code
541
548
  else:
542
549
  self.logger.info(f"Executing interactive command: {command}")
543
550
  try:
544
- # Try exec first, if it fails try eval and print result
545
551
  try:
546
552
  exec(command, self.current_frame.f_globals, self.current_frame.f_locals)
547
553
  except SyntaxError:
@@ -626,11 +632,243 @@ Examples:
626
632
  sys.settrace(None)
627
633
 
628
634
 
635
+ def debug_file(
636
+ filepath: str,
637
+ args: Optional[List[str]] = None,
638
+ module_filter: Optional[str] = None,
639
+ breakpoint_specs: Optional[List[Tuple[Optional[str], Optional[str]]]] = None,
640
+ verbose: bool = False,
641
+ log_level: str = 'DEBUG'
642
+ ) -> Debugger:
643
+ """
644
+ Debug a Python file by executing it under the debugger.
645
+
646
+ :param filepath: str - Path to the Python file to debug (required).
647
+ :param args: Optional[List[str]] - Command-line arguments to pass to the script (optional).
648
+ :param module_filter: Optional[str] - Module prefix to trace. None auto-detects based on file content (optional).
649
+ :param breakpoint_specs: Optional[List[Tuple[Optional[str], Optional[str]]]] - List of (module, function) tuples for initial breakpoints (optional).
650
+ :param verbose: bool - Enable verbose logging (default False).
651
+ :param log_level: str - Log level for verbose mode: 'DEBUG', 'INFO', 'WARNING', 'ERROR' (default 'DEBUG').
652
+ :return: Debugger - Configured Debugger instance ready to run.
653
+ :raises FileNotFoundError: If the specified file doesn't exist.
654
+ """
655
+ import os
656
+
657
+ if not os.path.isfile(filepath):
658
+ raise FileNotFoundError(f"File not found: {filepath}")
659
+
660
+ import runpy
661
+
662
+ old_argv = sys.argv.copy()
663
+ sys.argv = [filepath] + (args or [])
664
+
665
+ def target():
666
+ try:
667
+ # Use runpy to properly execute as __main__ module
668
+ # This ensures unittest.main() and similar tools work correctly
669
+ runpy.run_path(filepath, run_name='__main__')
670
+ finally:
671
+ sys.argv = old_argv
672
+
673
+ # Auto-detect module filter based on file content
674
+ if module_filter is None:
675
+ with open(filepath, 'r') as f:
676
+ content = f.read()
677
+ if 'pygeai' in content or 'from pygeai' in content or 'import pygeai' in content:
678
+ module_filter = ''
679
+ else:
680
+ module_filter = '__main__'
681
+
682
+ dbg = Debugger(target=target, module_filter=module_filter, verbose=verbose, log_level=log_level)
683
+
684
+ if breakpoint_specs:
685
+ for module, function in breakpoint_specs:
686
+ dbg.add_breakpoint(module=module, function_name=function)
687
+
688
+ return dbg
689
+
690
+
691
+ def debug_module(
692
+ module_name: str,
693
+ function_name: str = 'main',
694
+ args: Optional[List[str]] = None,
695
+ module_filter: Optional[str] = None
696
+ ) -> Debugger:
697
+ """
698
+ Debug a specific module and function by importing and executing it under the debugger.
699
+
700
+ :param module_name: str - Fully qualified module name, e.g., 'pygeai.cli.geai' (required).
701
+ :param function_name: str - Function to call within the module (default is 'main').
702
+ :param args: Optional[List[str]] - Command-line arguments to pass (optional).
703
+ :param module_filter: Optional[str] - Module prefix to trace. None defaults to first part of module_name (optional).
704
+ :return: Debugger - Configured Debugger instance with an automatic breakpoint on the target function.
705
+ :raises ImportError: If the module or function cannot be imported.
706
+ """
707
+ import importlib
708
+
709
+ old_argv = sys.argv.copy()
710
+ sys.argv = [module_name] + (args or [])
711
+
712
+ try:
713
+ module = importlib.import_module(module_name)
714
+ func = getattr(module, function_name)
715
+ except (ImportError, AttributeError) as e:
716
+ raise ImportError(f"Cannot import {module_name}.{function_name}: {e}")
717
+
718
+ def target():
719
+ try:
720
+ func()
721
+ finally:
722
+ sys.argv = old_argv
723
+
724
+ if module_filter is None:
725
+ module_filter = module_name.split('.')[0]
726
+
727
+ dbg = Debugger(target=target, module_filter=module_filter)
728
+ dbg.add_breakpoint(module=module_name, function_name=function_name)
729
+
730
+ return dbg
731
+
732
+
629
733
  def main():
630
- """Entry point for geai-dbg command."""
631
- dbg = Debugger()
632
- dbg.add_breakpoint(module='pygeai.cli.geai', function_name='main')
633
- dbg.run()
734
+ """
735
+ Entry point for geai-dbg command.
736
+
737
+ Usage:
738
+ geai-dbg Debug geai CLI (default)
739
+ geai-dbg script.py [args...] Debug a Python file
740
+ geai-dbg -m module:func [args...] Debug a module function
741
+ geai-dbg -h Show help
742
+ """
743
+ parser = argparse.ArgumentParser(
744
+ prog='geai-dbg',
745
+ description='Interactive debugger for PyGEAI applications',
746
+ formatter_class=argparse.RawDescriptionHelpFormatter,
747
+ epilog="""
748
+ Examples:
749
+ geai-dbg Debug the geai CLI
750
+ geai-dbg script.py arg1 arg2 Debug a Python file with arguments
751
+ geai-dbg -v script.py Debug with verbose logging (DEBUG level)
752
+ geai-dbg -v --log-level INFO script.py Debug with INFO level logging
753
+ geai-dbg -m pygeai.cli.geai:main Debug a specific module function
754
+ geai-dbg -b main script.py Break on 'main' function
755
+ geai-dbg --filter pygeai script.py Only trace pygeai modules
756
+ """
757
+ )
758
+
759
+ parser.add_argument(
760
+ 'target',
761
+ nargs='?',
762
+ default=None,
763
+ help='Python file to debug (omit to debug geai CLI)'
764
+ )
765
+
766
+ parser.add_argument(
767
+ 'args',
768
+ nargs='*',
769
+ help='Arguments to pass to the target'
770
+ )
771
+
772
+ parser.add_argument(
773
+ '-m', '--module',
774
+ metavar='MODULE:FUNC',
775
+ help='Debug a module function (format: module.path:function_name)'
776
+ )
777
+
778
+ parser.add_argument(
779
+ '-b', '--break',
780
+ dest='breakpoints',
781
+ action='append',
782
+ metavar='BREAKPOINT',
783
+ help='Set initial breakpoint (format: [module:]function, can be repeated)'
784
+ )
785
+
786
+ parser.add_argument(
787
+ '--filter',
788
+ metavar='PREFIX',
789
+ help='Module prefix to trace (default: auto-detect)'
790
+ )
791
+
792
+ parser.add_argument(
793
+ '--trace-all',
794
+ action='store_true',
795
+ help='Trace all modules (warning: may be slow)'
796
+ )
797
+
798
+ parser.add_argument(
799
+ '-v', '--verbose',
800
+ action='store_true',
801
+ help='Enable verbose logging for pygeai modules'
802
+ )
803
+
804
+ parser.add_argument(
805
+ '--log-level',
806
+ choices=['DEBUG', 'INFO', 'WARNING', 'ERROR'],
807
+ default='DEBUG',
808
+ help='Log level for verbose mode (default: DEBUG)'
809
+ )
810
+
811
+ parsed_args = parser.parse_args()
812
+
813
+ try:
814
+ if parsed_args.module:
815
+ if ':' in parsed_args.module:
816
+ module_name, func_name = parsed_args.module.split(':', 1)
817
+ else:
818
+ module_name = parsed_args.module
819
+ func_name = 'main'
820
+
821
+ dbg = debug_module(
822
+ module_name=module_name,
823
+ function_name=func_name,
824
+ args=parsed_args.args,
825
+ module_filter='' if parsed_args.trace_all else parsed_args.filter
826
+ )
827
+
828
+ elif parsed_args.target:
829
+ breakpoint_specs = []
830
+ if parsed_args.breakpoints:
831
+ for bp in parsed_args.breakpoints:
832
+ if ':' in bp:
833
+ mod, func = bp.split(':', 1)
834
+ breakpoint_specs.append((mod or None, func or None))
835
+ else:
836
+ breakpoint_specs.append((None, bp))
837
+
838
+ dbg = debug_file(
839
+ filepath=parsed_args.target,
840
+ args=parsed_args.args,
841
+ module_filter='' if parsed_args.trace_all else parsed_args.filter,
842
+ breakpoint_specs=breakpoint_specs if breakpoint_specs else None,
843
+ verbose=parsed_args.verbose,
844
+ log_level=parsed_args.log_level
845
+ )
846
+
847
+ else:
848
+ dbg = Debugger(module_filter='pygeai')
849
+ dbg.add_breakpoint(module='pygeai.cli.geai', function_name='main')
850
+
851
+ if parsed_args.breakpoints and not parsed_args.target:
852
+ for bp in parsed_args.breakpoints:
853
+ if ':' in bp:
854
+ mod, func = bp.split(':', 1)
855
+ dbg.add_breakpoint(module=mod or None, function_name=func or None)
856
+ else:
857
+ dbg.add_breakpoint(function_name=bp)
858
+
859
+ Console.write_stdout("GEAI Debugger started. Type 'h' for help, 'c' to continue, 'q' to quit.")
860
+
861
+ dbg.run()
862
+
863
+ except FileNotFoundError as e:
864
+ Console.write_stdout(f"Error: {e}")
865
+ sys.exit(1)
866
+ except ImportError as e:
867
+ Console.write_stdout(f"Error: {e}")
868
+ sys.exit(1)
869
+ except Exception as e:
870
+ logging.getLogger('geai.dbg').error(f"Debugger failed: {e}", exc_info=True)
871
+ sys.exit(1)
634
872
 
635
873
 
636
874
  if __name__ == "__main__":
@@ -1,8 +1,6 @@
1
1
  from pygeai.core.base.clients import BaseClient
2
- from pygeai.core.base.session import Session
3
2
  from pygeai.core.common.exceptions import MissingRequirementException
4
- from pygeai.core.services.rest import ApiService
5
- from pygeai.core.utils.validators import validate_status_code
3
+ from pygeai.core.services.rest import GEAIApiService
6
4
 
7
5
 
8
6
  class EvaluationClient(BaseClient):
@@ -16,4 +14,4 @@ class EvaluationClient(BaseClient):
16
14
 
17
15
  self.session.eval_url = eval_url
18
16
  token = self.session.access_token if self.session.access_token else self.session.api_key
19
- self.api_service = ApiService(base_url=self.session.eval_url, token=token, project_id=self.session.project_id)
17
+ self.api_service = GEAIApiService(base_url=self.session.eval_url, token=token, project_id=self.session.project_id)
@@ -1,4 +1,3 @@
1
- import json
2
1
  from pathlib import Path
3
2
 
4
3
  from pygeai.evaluation.clients import EvaluationClient
@@ -1,5 +1,3 @@
1
- import json
2
-
3
1
  from pygeai.evaluation.clients import EvaluationClient
4
2
  from pygeai.evaluation.plan.endpoints import LIST_EVALUATION_PLANS, CREATE_EVALUATION_PLAN, GET_EVALUATION_PLAN, \
5
3
  UPDATE_EVALUATION_PLAN, DELETE_EVALUATION_PLAN, LIST_EVALUATION_PLAN_SYSTEM_METRICS, \
@@ -1,5 +1,3 @@
1
- import json
2
-
3
1
  from pygeai.evaluation.clients import EvaluationClient
4
2
  from pygeai.evaluation.result.endpoints import LIST_EVALUATION_RESULTS, GET_EVALUATION_RESULT
5
3
  from pygeai.core.utils.validators import validate_status_code
pygeai/gam/clients.py CHANGED
@@ -1,7 +1,5 @@
1
-
2
- from pygeai import logger
3
1
  from pygeai.core.base.clients import BaseClient
4
- from pygeai.core.common.exceptions import MissingRequirementException, InvalidAPIResponseException
2
+ from pygeai.core.common.exceptions import MissingRequirementException
5
3
  from pygeai.core.utils.validators import validate_status_code
6
4
  from pygeai.core.utils.parsers import parse_json_response
7
5
  from pygeai.gam.endpoints import GET_ACCESS_TOKEN_V2, GET_USER_INFO_V2, IDP_SIGNIN_V1
pygeai/health/clients.py CHANGED
@@ -1,7 +1,5 @@
1
-
2
- from pygeai import logger
3
1
  from pygeai.core.base.clients import BaseClient
4
- from pygeai.core.common.exceptions import InvalidAPIResponseException
2
+
5
3
  from pygeai.core.utils.validators import validate_status_code
6
4
  from pygeai.core.utils.parsers import parse_json_response
7
5
  from pygeai.health.endpoints import STATUS_CHECK_V1
pygeai/lab/clients.py CHANGED
@@ -2,7 +2,6 @@ from pygeai import logger
2
2
  from pygeai.admin.clients import AdminClient
3
3
  from pygeai.core.base.clients import BaseClient
4
4
  from pygeai.core.common.exceptions import APIError
5
- from pygeai.core.utils.validators import validate_status_code
6
5
 
7
6
 
8
7
  class AILabClient(BaseClient):
pygeai/lab/managers.py CHANGED
@@ -1,7 +1,6 @@
1
1
  from typing import Union, Optional, List
2
2
 
3
3
  from pygeai import logger
4
- from pygeai.admin.clients import AdminClient
5
4
  from pygeai.core.base.mappers import ResponseMapper
6
5
  from pygeai.core.base.responses import EmptyResponse
7
6
  from pygeai.core.common.exceptions import APIError, MissingRequirementException
pygeai/lab/models.py CHANGED
@@ -1,4 +1,3 @@
1
- import re
2
1
  from typing import Literal, Optional, List, Dict, Any, Union, Iterator
3
2
 
4
3
  from pydantic import model_validator, Field, field_validator
@@ -1,6 +1,5 @@
1
-
2
1
  from pygeai import logger
3
- from pygeai.core.common.exceptions import InvalidAPIResponseException
2
+
4
3
  from pygeai.core.utils.validators import validate_status_code
5
4
  from pygeai.core.utils.parsers import parse_json_response
6
5
  from pygeai.lab.clients import AILabClient
@@ -1,7 +1,7 @@
1
1
  import json
2
2
 
3
3
  from pygeai import logger
4
- from pygeai.core.common.exceptions import InvalidAPIResponseException, MissingRequirementException, APIResponseError
4
+ from pygeai.core.common.exceptions import InvalidAPIResponseException
5
5
  from pygeai.core.utils.validators import validate_status_code
6
6
  from pygeai.core.utils.parsers import parse_json_response
7
7
  from pygeai.lab.constants import VALID_SCOPES, VALID_ACCESS_SCOPES, VALID_REPORT_EVENTS
@@ -278,7 +278,7 @@ class ToolClient(AILabClient):
278
278
  :raises Exception: May be raised by `api_service.put` for network issues, authentication errors, or server-side problems (not caught here).
279
279
  """
280
280
  if not (tool_id or name):
281
- raise ValueError(f"Either tool ID or tool Name must be defined in order to update tool.")
281
+ raise ValueError("Either tool ID or tool Name must be defined in order to update tool.")
282
282
  if scope and scope not in VALID_SCOPES:
283
283
  raise ValueError(f"Scope must be one of {', '.join(VALID_SCOPES)}.")
284
284
  if access_scope and access_scope not in VALID_ACCESS_SCOPES:
@@ -69,7 +69,7 @@ class ToolMapper:
69
69
  if isinstance(open_api_json, str):
70
70
  try:
71
71
  open_api_json = json.loads(open_api_json)
72
- except JSONDecodeError as e:
72
+ except JSONDecodeError:
73
73
  raise ValueError("open_api_json must be a valid JSON string or a dict")
74
74
 
75
75
  report_events = tool_data.get("reportEvents", "None")
@@ -1,4 +1,3 @@
1
- import sys
2
1
  from abc import ABC, abstractmethod
3
2
  from typing import Optional
4
3
 
@@ -111,7 +110,7 @@ class ProjectMigrationStrategy(MigrationStrategy):
111
110
  new_project.name = self.to_project_name
112
111
  new_project.email = self.admin_email
113
112
 
114
- logger.debug(f"Creating project with destination manager:")
113
+ logger.debug("Creating project with destination manager:")
115
114
  logger.debug(f" - API Key (first 20 chars): {self.to_api_key[:20] if self.to_api_key else 'None'}...")
116
115
  logger.debug(f" - Base URL: {self.to_instance}")
117
116
  logger.debug(f" - Project Name: {self.to_project_name}")
@@ -122,10 +121,10 @@ class ProjectMigrationStrategy(MigrationStrategy):
122
121
  except Exception as e:
123
122
  error_msg = f"Create project failed: {e}"
124
123
  logger.error(error_msg)
125
- logger.error(f" - Operation: Create project")
124
+ logger.error(" - Operation: Create project")
126
125
  logger.error(f" - Base URL: {self.to_instance}")
127
126
  logger.error(f" - API Key used (first 20 chars): {self.to_api_key[:20] if self.to_api_key else 'None'}...")
128
- Console.write_stderr(f"\nDEBUG: Operation failed: Create project")
127
+ Console.write_stderr("\nDEBUG: Operation failed: Create project")
129
128
  Console.write_stderr(f"DEBUG: Base URL: {self.to_instance}")
130
129
  Console.write_stderr(f"DEBUG: API Key used (first 20 chars): {self.to_api_key[:20] if self.to_api_key else 'None'}...")
131
130
  raise ValueError(error_msg) from e
@@ -133,10 +132,10 @@ class ProjectMigrationStrategy(MigrationStrategy):
133
132
  if isinstance(response, ErrorListResponse):
134
133
  error_detail = response.to_dict()
135
134
  logger.error(f"Create project returned error response: {error_detail}")
136
- logger.error(f" - Operation: Create project")
135
+ logger.error(" - Operation: Create project")
137
136
  logger.error(f" - Base URL: {self.to_instance}")
138
137
  logger.error(f" - API Key used (first 20 chars): {self.to_api_key[:20] if self.to_api_key else 'None'}...")
139
- Console.write_stderr(f"\nDEBUG: Operation failed: Create project")
138
+ Console.write_stderr("\nDEBUG: Operation failed: Create project")
140
139
  Console.write_stderr(f"DEBUG: Base URL: {self.to_instance}")
141
140
  Console.write_stderr(f"DEBUG: API Key used (first 20 chars): {self.to_api_key[:20] if self.to_api_key else 'None'}...")
142
141
  raise ValueError(f"Failed to create project: {error_detail}")