praisonAI 2.3.78__tar.gz → 2.3.80__tar.gz

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 (125) hide show
  1. {praisonai-2.3.78 → praisonai-2.3.80}/PKG-INFO +1 -1
  2. {praisonai-2.3.78 → praisonai-2.3.80}/PraisonAI.egg-info/PKG-INFO +1 -1
  3. {praisonai-2.3.78 → praisonai-2.3.80}/PraisonAI.egg-info/SOURCES.txt +1 -0
  4. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/cli/features/compare.py +27 -2
  5. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/cli/features/eval.py +54 -3
  6. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/cli/features/fast_context.py +46 -10
  7. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/cli/features/handoff.py +35 -16
  8. praisonai-2.3.80/praisonai/cli/features/image.py +376 -0
  9. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/cli/features/metrics.py +44 -0
  10. praisonai-2.3.80/praisonai/cli/features/profile.py +226 -0
  11. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/cli/main.py +54 -6
  12. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/deploy.py +1 -1
  13. praisonai-2.3.80/praisonai/version.py +1 -0
  14. praisonai-2.3.78/praisonai/cli/features/image.py +0 -207
  15. praisonai-2.3.78/praisonai/version.py +0 -1
  16. {praisonai-2.3.78 → praisonai-2.3.80}/PraisonAI.egg-info/dependency_links.txt +0 -0
  17. {praisonai-2.3.78 → praisonai-2.3.80}/PraisonAI.egg-info/entry_points.txt +0 -0
  18. {praisonai-2.3.78 → praisonai-2.3.80}/PraisonAI.egg-info/requires.txt +0 -0
  19. {praisonai-2.3.78 → praisonai-2.3.80}/PraisonAI.egg-info/top_level.txt +0 -0
  20. {praisonai-2.3.78 → praisonai-2.3.80}/README.md +0 -0
  21. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/__init__.py +0 -0
  22. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/__main__.py +0 -0
  23. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/agent_scheduler.py +0 -0
  24. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/agents_generator.py +0 -0
  25. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/api/call.py +0 -0
  26. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/auto.py +0 -0
  27. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/chainlit_ui.py +0 -0
  28. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/cli/__init__.py +0 -0
  29. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/cli/features/__init__.py +0 -0
  30. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/cli/features/agent_scheduler.py +0 -0
  31. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/cli/features/at_mentions.py +0 -0
  32. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/cli/features/auto_memory.py +0 -0
  33. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/cli/features/autonomy_mode.py +0 -0
  34. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/cli/features/base.py +0 -0
  35. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/cli/features/cost_tracker.py +0 -0
  36. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/cli/features/external_agents.py +0 -0
  37. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/cli/features/flow_display.py +0 -0
  38. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/cli/features/git_integration.py +0 -0
  39. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/cli/features/guardrail.py +0 -0
  40. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/cli/features/interactive_tui.py +0 -0
  41. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/cli/features/knowledge.py +0 -0
  42. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/cli/features/mcp.py +0 -0
  43. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/cli/features/message_queue.py +0 -0
  44. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/cli/features/n8n.py +0 -0
  45. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/cli/features/profiling.py +0 -0
  46. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/cli/features/repo_map.py +0 -0
  47. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/cli/features/router.py +0 -0
  48. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/cli/features/sandbox_executor.py +0 -0
  49. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/cli/features/session.py +0 -0
  50. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/cli/features/skills.py +0 -0
  51. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/cli/features/slash_commands.py +0 -0
  52. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/cli/features/telemetry.py +0 -0
  53. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/cli/features/todo.py +0 -0
  54. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/cli/features/tools.py +0 -0
  55. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/cli/features/workflow.py +0 -0
  56. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/cli/schedule_cli.py +0 -0
  57. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/code/__init__.py +0 -0
  58. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/code/agent_tools.py +0 -0
  59. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/code/diff/__init__.py +0 -0
  60. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/code/diff/diff_strategy.py +0 -0
  61. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/code/tools/__init__.py +0 -0
  62. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/code/tools/apply_diff.py +0 -0
  63. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/code/tools/execute_command.py +0 -0
  64. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/code/tools/list_files.py +0 -0
  65. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/code/tools/read_file.py +0 -0
  66. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/code/tools/search_replace.py +0 -0
  67. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/code/tools/write_file.py +0 -0
  68. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/code/utils/__init__.py +0 -0
  69. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/code/utils/file_utils.py +0 -0
  70. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/code/utils/ignore_utils.py +0 -0
  71. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/code/utils/text_utils.py +0 -0
  72. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/inbuilt_tools/__init__.py +0 -0
  73. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/inbuilt_tools/autogen_tools.py +0 -0
  74. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/inc/__init__.py +0 -0
  75. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/inc/config.py +0 -0
  76. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/inc/models.py +0 -0
  77. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/integrations/__init__.py +0 -0
  78. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/integrations/base.py +0 -0
  79. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/integrations/claude_code.py +0 -0
  80. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/integrations/codex_cli.py +0 -0
  81. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/integrations/cursor_cli.py +0 -0
  82. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/integrations/gemini_cli.py +0 -0
  83. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/profiler.py +0 -0
  84. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/scheduler/__init__.py +0 -0
  85. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/scheduler/agent_scheduler.py +0 -0
  86. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/scheduler/base.py +0 -0
  87. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/scheduler/daemon_manager.py +0 -0
  88. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/scheduler/state_manager.py +0 -0
  89. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/scheduler/yaml_loader.py +0 -0
  90. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/scheduler.py +0 -0
  91. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/setup/__init__.py +0 -0
  92. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/setup/build.py +0 -0
  93. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/setup/post_install.py +0 -0
  94. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/setup/setup_conda_env.py +0 -0
  95. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/setup.py +0 -0
  96. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/test.py +0 -0
  97. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/train.py +0 -0
  98. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/train_vision.py +0 -0
  99. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/ui/agents.py +0 -0
  100. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/ui/callbacks.py +0 -0
  101. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/ui/chat.py +0 -0
  102. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/ui/code.py +0 -0
  103. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/ui/colab.py +0 -0
  104. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/ui/colab_chainlit.py +0 -0
  105. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/ui/components/aicoder.py +0 -0
  106. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/ui/context.py +0 -0
  107. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/ui/database_config.py +0 -0
  108. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/ui/db.py +0 -0
  109. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/ui/realtime.py +0 -0
  110. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/ui/realtimeclient/__init__.py +0 -0
  111. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/ui/realtimeclient/tools.py +0 -0
  112. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/ui/sql_alchemy.py +0 -0
  113. {praisonai-2.3.78 → praisonai-2.3.80}/praisonai/upload_vision.py +0 -0
  114. {praisonai-2.3.78 → praisonai-2.3.80}/pyproject.toml +0 -0
  115. {praisonai-2.3.78 → praisonai-2.3.80}/setup.cfg +0 -0
  116. {praisonai-2.3.78 → praisonai-2.3.80}/tests/test.py +0 -0
  117. {praisonai-2.3.78 → praisonai-2.3.80}/tests/test_agents_playbook.py +0 -0
  118. {praisonai-2.3.78 → praisonai-2.3.80}/tests/test_approval_system.py +0 -0
  119. {praisonai-2.3.78 → praisonai-2.3.80}/tests/test_basic.py +0 -0
  120. {praisonai-2.3.78 → praisonai-2.3.80}/tests/test_cli_features.py +0 -0
  121. {praisonai-2.3.78 → praisonai-2.3.80}/tests/test_custom_tools_fix.py +0 -0
  122. {praisonai-2.3.78 → praisonai-2.3.80}/tests/test_double_api_fix.py +0 -0
  123. {praisonai-2.3.78 → praisonai-2.3.80}/tests/test_n8n_integration.py +0 -0
  124. {praisonai-2.3.78 → praisonai-2.3.80}/tests/test_phase2_refactor.py +0 -0
  125. {praisonai-2.3.78 → praisonai-2.3.80}/tests/test_runner.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PraisonAI
3
- Version: 2.3.78
3
+ Version: 2.3.80
4
4
  Summary: PraisonAI is an AI Agents Framework with Self Reflection. PraisonAI application combines PraisonAI Agents, AutoGen, and CrewAI into a low-code solution for building and managing multi-agent LLM systems, focusing on simplicity, customisation, and efficient human-agent collaboration.
5
5
  Author: Mervin Praison
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PraisonAI
3
- Version: 2.3.78
3
+ Version: 2.3.80
4
4
  Summary: PraisonAI is an AI Agents Framework with Self Reflection. PraisonAI application combines PraisonAI Agents, AutoGen, and CrewAI into a low-code solution for building and managing multi-agent LLM systems, focusing on simplicity, customisation, and efficient human-agent collaboration.
5
5
  Author: Mervin Praison
6
6
  License: MIT
@@ -47,6 +47,7 @@ praisonai/cli/features/mcp.py
47
47
  praisonai/cli/features/message_queue.py
48
48
  praisonai/cli/features/metrics.py
49
49
  praisonai/cli/features/n8n.py
50
+ praisonai/cli/features/profile.py
50
51
  praisonai/cli/features/profiling.py
51
52
  praisonai/cli/features/repo_map.py
52
53
  praisonai/cli/features/router.py
@@ -177,12 +177,13 @@ def save_compare_result(result: CompareResult, path: str) -> bool:
177
177
  return False
178
178
 
179
179
 
180
- def format_comparison_table(result: CompareResult) -> str:
180
+ def format_comparison_table(result: CompareResult, show_responses: bool = True) -> str:
181
181
  """
182
- Format comparison result as a table string.
182
+ Format comparison result as a table string with optional response display.
183
183
 
184
184
  Args:
185
185
  result: CompareResult to format
186
+ show_responses: Whether to show actual responses from each mode
186
187
 
187
188
  Returns:
188
189
  Formatted table string
@@ -190,10 +191,12 @@ def format_comparison_table(result: CompareResult) -> str:
190
191
  try:
191
192
  from rich.console import Console
192
193
  from rich.table import Table
194
+ from rich.panel import Panel
193
195
  from io import StringIO
194
196
 
195
197
  console = Console(file=StringIO(), force_terminal=True)
196
198
 
199
+ # Main comparison table
197
200
  table = Table(title=f"Comparison: {result.query[:50]}...")
198
201
  table.add_column("Mode", style="cyan")
199
202
  table.add_column("Time (ms)", style="green")
@@ -224,6 +227,26 @@ def format_comparison_table(result: CompareResult) -> str:
224
227
  )
225
228
 
226
229
  console.print(table)
230
+
231
+ # Show responses if enabled
232
+ if show_responses:
233
+ console.print("\n[bold cyan]📝 Responses:[/bold cyan]")
234
+ for c in result.comparisons:
235
+ if c.error is None:
236
+ # Truncate long responses
237
+ response_preview = c.output[:500] + "..." if len(c.output) > 500 else c.output
238
+ console.print(Panel(
239
+ response_preview,
240
+ title=f"[bold]{c.mode}[/bold] ({c.execution_time_ms:.0f}ms)",
241
+ border_style="cyan"
242
+ ))
243
+ else:
244
+ console.print(Panel(
245
+ f"[red]Error: {c.error}[/red]",
246
+ title=f"[bold]{c.mode}[/bold]",
247
+ border_style="red"
248
+ ))
249
+
227
250
  return console.file.getvalue()
228
251
  except ImportError:
229
252
  lines = [f"Comparison: {result.query}"]
@@ -231,6 +254,8 @@ def format_comparison_table(result: CompareResult) -> str:
231
254
  for c in result.comparisons:
232
255
  status = "OK" if c.error is None else f"ERROR: {c.error}"
233
256
  lines.append(f"{c.mode}: {c.execution_time_ms:.1f}ms - {status}")
257
+ if show_responses and c.error is None:
258
+ lines.append(f" Response: {c.output[:200]}...")
234
259
  return "\n".join(lines)
235
260
 
236
261
 
@@ -347,13 +347,36 @@ class EvalHandler:
347
347
  return batch_result
348
348
 
349
349
 
350
- def handle_eval_command(args) -> None:
350
+ def handle_eval_command(args) -> int:
351
351
  """
352
352
  Handle the eval CLI command.
353
353
 
354
354
  Args:
355
- args: Parsed command line arguments
355
+ args: Command line arguments (list or parsed namespace)
356
+
357
+ Returns:
358
+ Exit code
356
359
  """
360
+ import argparse
361
+
362
+ # If args is a list, parse it first
363
+ if isinstance(args, list):
364
+ parser = argparse.ArgumentParser(prog="praisonai eval")
365
+ subparsers = parser.add_subparsers(dest='eval_type')
366
+ add_eval_parser_subcommands(subparsers)
367
+
368
+ try:
369
+ args = parser.parse_args(args)
370
+ except SystemExit:
371
+ return 1
372
+
373
+ if not args.eval_type:
374
+ parser.print_help()
375
+ print("\n[bold]Examples:[/bold]")
376
+ print(" praisonai eval accuracy --prompt \"What is 2+2?\" --expected \"4\"")
377
+ print(" praisonai eval performance --agent agents.yaml --input \"Hello\"")
378
+ return 0
379
+
357
380
  handler = EvalHandler(verbose=getattr(args, 'verbose', False))
358
381
 
359
382
  eval_type = getattr(args, 'eval_type', 'accuracy')
@@ -420,12 +443,40 @@ def handle_eval_command(args) -> None:
420
443
  )
421
444
  else:
422
445
  print(f"Unknown evaluation type: {eval_type}")
423
- return
446
+ return 1
424
447
 
425
448
  if 'error' in result:
426
449
  print(f"Error: {result['error']}")
450
+ return 1
427
451
  elif not getattr(args, 'quiet', False):
428
452
  print(json.dumps(result, indent=2))
453
+
454
+ return 0
455
+
456
+
457
+ def add_eval_parser_subcommands(subparsers) -> None:
458
+ """Add eval subcommand parsers to an existing subparsers object."""
459
+ accuracy_parser = subparsers.add_parser('accuracy', help='Run accuracy evaluation')
460
+ accuracy_parser.add_argument('--agent', '-a', help='Agent config file (optional if --prompt used)')
461
+ accuracy_parser.add_argument('--prompt', '-p', type=str, help='Direct prompt (alternative to --agent)')
462
+ accuracy_parser.add_argument('--llm', help='LLM model for agent (when using --prompt)')
463
+ accuracy_parser.add_argument('--input', '-i', help='Input text (defaults to --prompt if not provided)')
464
+ accuracy_parser.add_argument('--expected', '-e', required=True, help='Expected output')
465
+ accuracy_parser.add_argument('--iterations', '-n', type=int, default=1, help='Number of iterations')
466
+ accuracy_parser.add_argument('--model', '-m', help='Judge model')
467
+ accuracy_parser.add_argument('--output', '-o', help='Output file')
468
+ accuracy_parser.add_argument('--verbose', '-v', action='store_true', help='Verbose output')
469
+ accuracy_parser.add_argument('--quiet', '-q', action='store_true', help='Suppress JSON output')
470
+
471
+ perf_parser = subparsers.add_parser('performance', help='Run performance evaluation')
472
+ perf_parser.add_argument('--agent', '-a', default='agents.yaml', help='Agent config file')
473
+ perf_parser.add_argument('--input', '-i', default='Hello', help='Input text')
474
+ perf_parser.add_argument('--iterations', '-n', type=int, default=10, help='Number of iterations')
475
+ perf_parser.add_argument('--warmup', '-w', type=int, default=2, help='Warmup runs')
476
+ perf_parser.add_argument('--memory', action='store_true', default=True, help='Track memory')
477
+ perf_parser.add_argument('--output', '-o', help='Output file')
478
+ perf_parser.add_argument('--verbose', '-v', action='store_true', help='Verbose output')
479
+ perf_parser.add_argument('--quiet', '-q', action='store_true', help='Suppress JSON output')
429
480
 
430
481
 
431
482
  def add_eval_parser(subparsers) -> None:
@@ -70,17 +70,46 @@ class FastContextHandler(FlagHandler):
70
70
 
71
71
  return True, ""
72
72
 
73
+ def _count_files_quick(self, path: str, max_count: int = 1000) -> int:
74
+ """Quickly count files in directory, stopping at max_count."""
75
+ count = 0
76
+ ignore_dirs = {'.git', '__pycache__', 'node_modules', '.venv', 'venv', '.cache', 'dist', 'build'}
77
+
78
+ for root, dirs, files in os.walk(path):
79
+ dirs[:] = [d for d in dirs if d not in ignore_dirs and not d.startswith('.')]
80
+ count += len(files)
81
+ if count >= max_count:
82
+ return count
83
+ return count
84
+
73
85
  def _get_folder_tree(self, path: str, max_depth: int = 2) -> str:
74
- """Get folder tree structure for context."""
75
- tree_lines = []
76
- path = os.path.abspath(path)
86
+ """
87
+ Get a compact folder tree structure for context.
77
88
 
78
- ignore_dirs = {'.git', '__pycache__', 'node_modules', '.venv', 'venv', '.tox', 'dist', 'build', '.egg-info'}
79
- ignore_exts = {'.pyc', '.pyo', '.so', '.o', '.a', '.dylib'}
89
+ Optimized for large directories:
90
+ - Limits depth to max_depth (default 2)
91
+ - Shows max 10 files per directory
92
+ - Truncates total output to 50 lines
93
+ - Skips common non-code directories
94
+ """
95
+ # Quick check for very large directories
96
+ file_count = self._count_files_quick(path, 500)
97
+ if file_count >= 500:
98
+ # For large directories, reduce depth and show summary
99
+ max_depth = 1
100
+ logger.debug(f"Large directory detected ({file_count}+ files), reducing tree depth")
101
+
102
+ tree_lines = []
103
+ ignore_dirs = {'.git', '__pycache__', 'node_modules', '.venv', 'venv', '.cache', 'dist', 'build', 'coverage', '.pytest_cache', '.mypy_cache', 'eggs', '*.egg-info'}
104
+ ignore_exts = {'.pyc', '.pyo', '.so', '.o', '.a', '.dylib', '.class', '.jar'}
105
+ total_files = 0
106
+ max_total_files = 100 # Stop after seeing this many files
80
107
 
81
108
  def walk_tree(current_path, prefix="", depth=0):
82
- if depth > max_depth:
109
+ nonlocal total_files
110
+ if depth > max_depth or total_files >= max_total_files:
83
111
  return
112
+
84
113
  try:
85
114
  entries = sorted(os.listdir(current_path))
86
115
  except PermissionError:
@@ -98,14 +127,21 @@ class FastContextHandler(FlagHandler):
98
127
  if ext not in ignore_exts and not entry.startswith('.'):
99
128
  files.append(entry)
100
129
 
101
- for i, d in enumerate(dirs):
102
- is_last = (i == len(dirs) - 1) and not files
130
+ # Limit directories shown at each level
131
+ dirs_to_show = dirs[:15]
132
+ for i, d in enumerate(dirs_to_show):
133
+ is_last = (i == len(dirs_to_show) - 1) and not files
103
134
  tree_lines.append(f"{prefix}{'└── ' if is_last else '├── '}{d}/")
104
135
  walk_tree(os.path.join(current_path, d),
105
136
  prefix + (' ' if is_last else '│ '), depth + 1)
137
+ if len(dirs) > 15:
138
+ tree_lines.append(f"{prefix} ... and {len(dirs) - 15} more directories")
106
139
 
107
- for i, f in enumerate(files[:10]): # Limit files shown
108
- is_last = i == len(files[:10]) - 1
140
+ # Limit files shown
141
+ files_to_show = files[:10]
142
+ total_files += len(files)
143
+ for i, f in enumerate(files_to_show):
144
+ is_last = i == len(files_to_show) - 1
109
145
  tree_lines.append(f"{prefix}{'└── ' if is_last else '├── '}{f}")
110
146
  if len(files) > 10:
111
147
  tree_lines.append(f"{prefix} ... and {len(files) - 10} more files")
@@ -72,11 +72,11 @@ class HandoffHandler(FlagHandler):
72
72
  self.print_status(msg, "error")
73
73
  return []
74
74
 
75
- from praisonaiagents import Agent, Handoff
75
+ from praisonaiagents import Agent, handoff
76
76
 
77
77
  agents = []
78
78
 
79
- # Create agents for each role
79
+ # First pass: Create all agents without handoffs
80
80
  for i, name in enumerate(agent_names):
81
81
  agent_config = {
82
82
  "name": name.title().replace("_", " "),
@@ -91,26 +91,45 @@ class HandoffHandler(FlagHandler):
91
91
  agent = Agent(**agent_config)
92
92
  agents.append(agent)
93
93
 
94
- # Set up handoffs between agents
95
- for i, agent in enumerate(agents[:-1]):
96
- next_agent = agents[i + 1]
97
- handoff = Handoff(
98
- agent=next_agent,
99
- tool_name_override=f"handoff_to_{agent_names[i + 1]}",
100
- tool_description_override=f"Hand off task to {agent_names[i + 1]} for further processing"
101
- )
94
+ # Second pass: Set up handoffs by recreating agents with handoff config
95
+ final_agents = []
96
+ for i, name in enumerate(agent_names):
97
+ agent_config = {
98
+ "name": name.title().replace("_", " "),
99
+ "role": name.title().replace("_", " "),
100
+ "goal": f"Complete tasks as {name}",
101
+ "backstory": f"Expert {name} with specialized skills"
102
+ }
103
+
104
+ if llm:
105
+ agent_config["llm"] = llm
106
+
107
+ # Add handoff to next agent if not the last agent
108
+ if i < len(agent_names) - 1:
109
+ next_agent = agents[i + 1]
110
+ next_agent_name = agent_names[i + 1]
111
+
112
+ # Pass handoffs via constructor - this properly registers them as tools
113
+ agent_config["handoffs"] = [
114
+ handoff(
115
+ agent=next_agent,
116
+ tool_name_override=f"handoff_to_{next_agent_name}",
117
+ tool_description_override=f"Hand off the task to {next_agent_name}. Call this after completing your part."
118
+ )
119
+ ]
120
+
121
+ # Add instructions to use handoff
122
+ agent_config["instructions"] = f"After completing your task, you MUST use the handoff_to_{next_agent_name} tool to pass your work to the next agent."
102
123
 
103
- # Add handoff to agent
104
- if not hasattr(agent, 'handoffs') or agent.handoffs is None:
105
- agent.handoffs = []
106
- agent.handoffs.append(handoff)
124
+ final_agent = Agent(**agent_config)
125
+ final_agents.append(final_agent)
107
126
 
108
- self.print_status(f"🤝 Created {len(agents)} agents with handoff chain", "success")
127
+ self.print_status(f"🤝 Created {len(final_agents)} agents with handoff chain", "success")
109
128
  for i, name in enumerate(agent_names):
110
129
  arrow = " → " if i < len(agent_names) - 1 else ""
111
130
  self.print_status(f" {name}{arrow}", "info")
112
131
 
113
- return agents
132
+ return final_agents
114
133
 
115
134
  def apply_to_agent_config(self, config: Dict[str, Any], flag_value: Any) -> Dict[str, Any]:
116
135
  """