praisonai-code 0.0.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.
- praisonai_code/__init__.py +17 -0
- praisonai_code/cli/__init__.py +12 -0
- praisonai_code/cli/_forward_shim.py +10 -0
- praisonai_code/cli/_paths.py +88 -0
- praisonai_code/cli/_warnings.py +58 -0
- praisonai_code/cli/app.py +757 -0
- praisonai_code/cli/approval_backend.py +272 -0
- praisonai_code/cli/branding.py +94 -0
- praisonai_code/cli/commands/__init__.py +114 -0
- praisonai_code/cli/commands/acp.py +80 -0
- praisonai_code/cli/commands/agent.py +116 -0
- praisonai_code/cli/commands/agents.py +80 -0
- praisonai_code/cli/commands/app.py +139 -0
- praisonai_code/cli/commands/attach.py +95 -0
- praisonai_code/cli/commands/audit.py +102 -0
- praisonai_code/cli/commands/auth.py +508 -0
- praisonai_code/cli/commands/batch.py +848 -0
- praisonai_code/cli/commands/benchmark.py +286 -0
- praisonai_code/cli/commands/browser.py +299 -0
- praisonai_code/cli/commands/call.py +45 -0
- praisonai_code/cli/commands/chat.py +332 -0
- praisonai_code/cli/commands/checkpoint.py +170 -0
- praisonai_code/cli/commands/code.py +276 -0
- praisonai_code/cli/commands/command.py +114 -0
- praisonai_code/cli/commands/commit.py +47 -0
- praisonai_code/cli/commands/completion.py +333 -0
- praisonai_code/cli/commands/config.py +681 -0
- praisonai_code/cli/commands/context.py +414 -0
- praisonai_code/cli/commands/daemon.py +203 -0
- praisonai_code/cli/commands/debug.py +142 -0
- praisonai_code/cli/commands/deploy.py +71 -0
- praisonai_code/cli/commands/diag.py +55 -0
- praisonai_code/cli/commands/docs.py +1575 -0
- praisonai_code/cli/commands/doctor.py +332 -0
- praisonai_code/cli/commands/endpoints.py +51 -0
- praisonai_code/cli/commands/environment.py +179 -0
- praisonai_code/cli/commands/eval.py +131 -0
- praisonai_code/cli/commands/examples.py +953 -0
- praisonai_code/cli/commands/flow.py +436 -0
- praisonai_code/cli/commands/github.py +752 -0
- praisonai_code/cli/commands/hooks.py +74 -0
- praisonai_code/cli/commands/init.py +174 -0
- praisonai_code/cli/commands/knowledge.py +440 -0
- praisonai_code/cli/commands/langextract.py +120 -0
- praisonai_code/cli/commands/langfuse.py +984 -0
- praisonai_code/cli/commands/loop.py +211 -0
- praisonai_code/cli/commands/lsp.py +112 -0
- praisonai_code/cli/commands/managed.py +659 -0
- praisonai_code/cli/commands/mcp.py +763 -0
- praisonai_code/cli/commands/memory.py +298 -0
- praisonai_code/cli/commands/models.py +264 -0
- praisonai_code/cli/commands/n8n.py +326 -0
- praisonai_code/cli/commands/obs.py +19 -0
- praisonai_code/cli/commands/package.py +76 -0
- praisonai_code/cli/commands/paths.py +106 -0
- praisonai_code/cli/commands/permissions.py +272 -0
- praisonai_code/cli/commands/plugins.py +609 -0
- praisonai_code/cli/commands/port.py +530 -0
- praisonai_code/cli/commands/profile.py +466 -0
- praisonai_code/cli/commands/publish.py +193 -0
- praisonai_code/cli/commands/rag.py +913 -0
- praisonai_code/cli/commands/realtime.py +52 -0
- praisonai_code/cli/commands/recipe.py +684 -0
- praisonai_code/cli/commands/registry.py +59 -0
- praisonai_code/cli/commands/replay.py +830 -0
- praisonai_code/cli/commands/research.py +49 -0
- praisonai_code/cli/commands/retrieval.py +377 -0
- praisonai_code/cli/commands/rules.py +71 -0
- praisonai_code/cli/commands/run.py +1573 -0
- praisonai_code/cli/commands/sandbox.py +371 -0
- praisonai_code/cli/commands/schedule.py +529 -0
- praisonai_code/cli/commands/serve.py +690 -0
- praisonai_code/cli/commands/session.py +450 -0
- praisonai_code/cli/commands/setup.py +174 -0
- praisonai_code/cli/commands/skills.py +545 -0
- praisonai_code/cli/commands/standardise.py +711 -0
- praisonai_code/cli/commands/templates.py +54 -0
- praisonai_code/cli/commands/test.py +558 -0
- praisonai_code/cli/commands/todo.py +74 -0
- praisonai_code/cli/commands/tools.py +205 -0
- praisonai_code/cli/commands/traces.py +145 -0
- praisonai_code/cli/commands/tracker.py +852 -0
- praisonai_code/cli/commands/train.py +613 -0
- praisonai_code/cli/commands/ui.py +172 -0
- praisonai_code/cli/commands/up.py +354 -0
- praisonai_code/cli/commands/validate.py +291 -0
- praisonai_code/cli/commands/version.py +101 -0
- praisonai_code/cli/commands/workflow.py +97 -0
- praisonai_code/cli/config_loader.py +437 -0
- praisonai_code/cli/configuration/__init__.py +27 -0
- praisonai_code/cli/configuration/config.schema.json +57 -0
- praisonai_code/cli/configuration/credentials.py +446 -0
- praisonai_code/cli/configuration/loader.py +364 -0
- praisonai_code/cli/configuration/model_resolver.py +161 -0
- praisonai_code/cli/configuration/oauth.py +389 -0
- praisonai_code/cli/configuration/paths.py +224 -0
- praisonai_code/cli/configuration/resolver.py +687 -0
- praisonai_code/cli/configuration/schema.py +317 -0
- praisonai_code/cli/execution/__init__.py +99 -0
- praisonai_code/cli/execution/core.py +208 -0
- praisonai_code/cli/execution/profiler.py +898 -0
- praisonai_code/cli/execution/request.py +85 -0
- praisonai_code/cli/execution/result.py +74 -0
- praisonai_code/cli/fallback_schema.py +416 -0
- praisonai_code/cli/features/__init__.py +278 -0
- praisonai_code/cli/features/_endpoint_registry.py +64 -0
- praisonai_code/cli/features/_search_registry.py +43 -0
- praisonai_code/cli/features/acp.py +236 -0
- praisonai_code/cli/features/action_orchestrator.py +576 -0
- praisonai_code/cli/features/agent_scheduler.py +773 -0
- praisonai_code/cli/features/agent_tools.py +603 -0
- praisonai_code/cli/features/agents.py +397 -0
- praisonai_code/cli/features/at_mentions.py +471 -0
- praisonai_code/cli/features/audit_cli.py +270 -0
- praisonai_code/cli/features/auto_memory.py +182 -0
- praisonai_code/cli/features/auto_mode.py +552 -0
- praisonai_code/cli/features/autonomy_mode.py +546 -0
- praisonai_code/cli/features/background.py +356 -0
- praisonai_code/cli/features/base.py +168 -0
- praisonai_code/cli/features/benchmark.py +1462 -0
- praisonai_code/cli/features/capabilities.py +1326 -0
- praisonai_code/cli/features/checkpoints.py +345 -0
- praisonai_code/cli/features/cli_profiler.py +335 -0
- praisonai_code/cli/features/code_intelligence.py +666 -0
- praisonai_code/cli/features/compaction.py +294 -0
- praisonai_code/cli/features/compare.py +534 -0
- praisonai_code/cli/features/config_hierarchy.py +366 -0
- praisonai_code/cli/features/context_manager.py +597 -0
- praisonai_code/cli/features/cost_tracker.py +514 -0
- praisonai_code/cli/features/csv_test_runner.py +736 -0
- praisonai_code/cli/features/custom_definitions.py +790 -0
- praisonai_code/cli/features/debug.py +810 -0
- praisonai_code/cli/features/deploy.py +605 -0
- praisonai_code/cli/features/diag.py +289 -0
- praisonai_code/cli/features/display_jsonl.py +173 -0
- praisonai_code/cli/features/doctor/__init__.py +63 -0
- praisonai_code/cli/features/doctor/checks/__init__.py +29 -0
- praisonai_code/cli/features/doctor/checks/acp_checks.py +220 -0
- praisonai_code/cli/features/doctor/checks/bot_checks.py +340 -0
- praisonai_code/cli/features/doctor/checks/config_checks.py +373 -0
- praisonai_code/cli/features/doctor/checks/db_checks.py +366 -0
- praisonai_code/cli/features/doctor/checks/env_checks.py +637 -0
- praisonai_code/cli/features/doctor/checks/gateway_checks.py +387 -0
- praisonai_code/cli/features/doctor/checks/lsp_checks.py +231 -0
- praisonai_code/cli/features/doctor/checks/mcp_checks.py +367 -0
- praisonai_code/cli/features/doctor/checks/memory_checks.py +268 -0
- praisonai_code/cli/features/doctor/checks/network_checks.py +251 -0
- praisonai_code/cli/features/doctor/checks/obs_checks.py +328 -0
- praisonai_code/cli/features/doctor/checks/packaging_checks.py +422 -0
- praisonai_code/cli/features/doctor/checks/performance_checks.py +235 -0
- praisonai_code/cli/features/doctor/checks/permissions_checks.py +259 -0
- praisonai_code/cli/features/doctor/checks/runtime_checks.py +650 -0
- praisonai_code/cli/features/doctor/checks/runtime_migration_checks.py +220 -0
- praisonai_code/cli/features/doctor/checks/selftest_checks.py +322 -0
- praisonai_code/cli/features/doctor/checks/serve_checks.py +426 -0
- praisonai_code/cli/features/doctor/checks/skills_checks.py +327 -0
- praisonai_code/cli/features/doctor/checks/tools_checks.py +371 -0
- praisonai_code/cli/features/doctor/engine.py +266 -0
- praisonai_code/cli/features/doctor/formatters.py +377 -0
- praisonai_code/cli/features/doctor/handler.py +564 -0
- praisonai_code/cli/features/doctor/models.py +276 -0
- praisonai_code/cli/features/doctor/registry.py +239 -0
- praisonai_code/cli/features/endpoints.py +1016 -0
- praisonai_code/cli/features/eval.py +559 -0
- praisonai_code/cli/features/examples.py +707 -0
- praisonai_code/cli/features/external_agents.py +231 -0
- praisonai_code/cli/features/fast_context.py +410 -0
- praisonai_code/cli/features/file_history.py +320 -0
- praisonai_code/cli/features/flow_display.py +566 -0
- praisonai_code/cli/features/git_attribution.py +159 -0
- praisonai_code/cli/features/git_integration.py +651 -0
- praisonai_code/cli/features/guardrail.py +171 -0
- praisonai_code/cli/features/handoff.py +252 -0
- praisonai_code/cli/features/hooks.py +583 -0
- praisonai_code/cli/features/hybrid_workflow.py +391 -0
- praisonai_code/cli/features/image.py +384 -0
- praisonai_code/cli/features/interactive_core_headless.py +450 -0
- praisonai_code/cli/features/interactive_runtime.py +600 -0
- praisonai_code/cli/features/interactive_test_harness.py +537 -0
- praisonai_code/cli/features/interactive_tools.py +428 -0
- praisonai_code/cli/features/interactive_tui.py +603 -0
- praisonai_code/cli/features/job_workflow.py +906 -0
- praisonai_code/cli/features/jobs.py +632 -0
- praisonai_code/cli/features/knowledge.py +531 -0
- praisonai_code/cli/features/knowledge_cli.py +438 -0
- praisonai_code/cli/features/lite.py +244 -0
- praisonai_code/cli/features/logs.py +200 -0
- praisonai_code/cli/features/lsp_cli.py +225 -0
- praisonai_code/cli/features/lsp_diagnostics.py +185 -0
- praisonai_code/cli/features/mcp.py +344 -0
- praisonai_code/cli/features/message_queue.py +587 -0
- praisonai_code/cli/features/metrics.py +210 -0
- praisonai_code/cli/features/migrate.py +1329 -0
- praisonai_code/cli/features/migration_flow.py +463 -0
- praisonai_code/cli/features/migration_spec.py +276 -0
- praisonai_code/cli/features/n8n.py +703 -0
- praisonai_code/cli/features/observability.py +293 -0
- praisonai_code/cli/features/ollama.py +361 -0
- praisonai_code/cli/features/output_modes.py +155 -0
- praisonai_code/cli/features/output_style.py +273 -0
- praisonai_code/cli/features/package.py +631 -0
- praisonai_code/cli/features/performance.py +308 -0
- praisonai_code/cli/features/persistence.py +636 -0
- praisonai_code/cli/features/profiler/__init__.py +81 -0
- praisonai_code/cli/features/profiler/core.py +558 -0
- praisonai_code/cli/features/profiler/optimizations.py +652 -0
- praisonai_code/cli/features/profiler/suite.py +386 -0
- praisonai_code/cli/features/queue/__init__.py +73 -0
- praisonai_code/cli/features/queue/manager.py +435 -0
- praisonai_code/cli/features/queue/models.py +289 -0
- praisonai_code/cli/features/queue/persistence.py +564 -0
- praisonai_code/cli/features/queue/scheduler.py +529 -0
- praisonai_code/cli/features/queue/worker.py +400 -0
- praisonai_code/cli/features/recipe.py +2187 -0
- praisonai_code/cli/features/recipe_creator.py +996 -0
- praisonai_code/cli/features/recipe_optimizer.py +1364 -0
- praisonai_code/cli/features/recipe_prompts.py +226 -0
- praisonai_code/cli/features/registry.py +229 -0
- praisonai_code/cli/features/repo_map.py +860 -0
- praisonai_code/cli/features/router.py +466 -0
- praisonai_code/cli/features/safe_shell.py +427 -0
- praisonai_code/cli/features/sandbox_cli.py +283 -0
- praisonai_code/cli/features/sandbox_executor.py +536 -0
- praisonai_code/cli/features/sdk_knowledge.py +500 -0
- praisonai_code/cli/features/session.py +222 -0
- praisonai_code/cli/features/session_checkpoints.py +208 -0
- praisonai_code/cli/features/setup/__init__.py +9 -0
- praisonai_code/cli/features/setup/handler.py +355 -0
- praisonai_code/cli/features/setup/templates.py +62 -0
- praisonai_code/cli/features/skills.py +940 -0
- praisonai_code/cli/features/slash_commands.py +692 -0
- praisonai_code/cli/features/telemetry.py +179 -0
- praisonai_code/cli/features/templates.py +1390 -0
- praisonai_code/cli/features/thinking.py +343 -0
- praisonai_code/cli/features/todo.py +334 -0
- praisonai_code/cli/features/tools.py +680 -0
- praisonai_code/cli/features/tui/__init__.py +83 -0
- praisonai_code/cli/features/tui/app.py +871 -0
- praisonai_code/cli/features/tui/cli.py +580 -0
- praisonai_code/cli/features/tui/config.py +150 -0
- praisonai_code/cli/features/tui/debug.py +526 -0
- praisonai_code/cli/features/tui/events.py +99 -0
- praisonai_code/cli/features/tui/mock_provider.py +328 -0
- praisonai_code/cli/features/tui/orchestrator.py +652 -0
- praisonai_code/cli/features/tui/screens/__init__.py +50 -0
- praisonai_code/cli/features/tui/screens/help.py +157 -0
- praisonai_code/cli/features/tui/screens/main.py +568 -0
- praisonai_code/cli/features/tui/screens/queue.py +174 -0
- praisonai_code/cli/features/tui/screens/session.py +124 -0
- praisonai_code/cli/features/tui/screens/settings.py +148 -0
- praisonai_code/cli/features/tui/session_store.py +198 -0
- praisonai_code/cli/features/tui/widgets/__init__.py +56 -0
- praisonai_code/cli/features/tui/widgets/chat.py +263 -0
- praisonai_code/cli/features/tui/widgets/command_popup.py +258 -0
- praisonai_code/cli/features/tui/widgets/composer.py +292 -0
- praisonai_code/cli/features/tui/widgets/file_popup.py +207 -0
- praisonai_code/cli/features/tui/widgets/queue_panel.py +223 -0
- praisonai_code/cli/features/tui/widgets/status.py +181 -0
- praisonai_code/cli/features/tui/widgets/tool_panel.py +307 -0
- praisonai_code/cli/features/wizard.py +289 -0
- praisonai_code/cli/features/workflow.py +802 -0
- praisonai_code/cli/features/yaml_utils.py +321 -0
- praisonai_code/cli/interactive/__init__.py +48 -0
- praisonai_code/cli/interactive/async_tui.py +1218 -0
- praisonai_code/cli/interactive/config.py +139 -0
- praisonai_code/cli/interactive/core.py +618 -0
- praisonai_code/cli/interactive/events.py +131 -0
- praisonai_code/cli/interactive/frontends/__init__.py +31 -0
- praisonai_code/cli/interactive/frontends/rich_frontend.py +462 -0
- praisonai_code/cli/interactive/frontends/textual_frontend.py +157 -0
- praisonai_code/cli/interactive/praison_io.py +502 -0
- praisonai_code/cli/interactive/repl.py +297 -0
- praisonai_code/cli/interactive/split_tui.py +456 -0
- praisonai_code/cli/interactive/tui_app.py +457 -0
- praisonai_code/cli/langfuse_client.py +360 -0
- praisonai_code/cli/main.py +7421 -0
- praisonai_code/cli/output/__init__.py +25 -0
- praisonai_code/cli/output/console.py +456 -0
- praisonai_code/cli/output/event_bridge.py +191 -0
- praisonai_code/cli/schedule_cli.py +54 -0
- praisonai_code/cli/schema_provider.py +23 -0
- praisonai_code/cli/session/__init__.py +16 -0
- praisonai_code/cli/session/resume.py +148 -0
- praisonai_code/cli/session/unified.py +548 -0
- praisonai_code/cli/state/__init__.py +31 -0
- praisonai_code/cli/state/identifiers.py +161 -0
- praisonai_code/cli/state/project_sessions.py +383 -0
- praisonai_code/cli/state/sessions.py +390 -0
- praisonai_code/cli/ui/__init__.py +160 -0
- praisonai_code/cli/ui/config.py +46 -0
- praisonai_code/cli/ui/events.py +61 -0
- praisonai_code/cli/ui/mg_backend.py +342 -0
- praisonai_code/cli/ui/plain.py +133 -0
- praisonai_code/cli/ui/rich_backend.py +162 -0
- praisonai_code/cli/unified_schema.py +655 -0
- praisonai_code/cli/utils/env_utils.py +126 -0
- praisonai_code/cli/utils/project.py +131 -0
- praisonai_code/cli_backends/__init__.py +73 -0
- praisonai_code/cli_backends/claude.py +373 -0
- praisonai_code/cli_backends/registry.py +113 -0
- praisonai_code/runtime/__init__.py +36 -0
- praisonai_code/runtime/__main__.py +81 -0
- praisonai_code/runtime/client.py +131 -0
- praisonai_code/runtime/descriptor.py +209 -0
- praisonai_code/runtime/server.py +356 -0
- praisonai_code-0.0.1.dist-info/METADATA +80 -0
- praisonai_code-0.0.1.dist-info/RECORD +309 -0
- praisonai_code-0.0.1.dist-info/WHEEL +5 -0
- praisonai_code-0.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,852 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tracker command for autonomous agent execution with step-by-step tracking.
|
|
3
|
+
|
|
4
|
+
Uses the centralized autonomy system with:
|
|
5
|
+
- All default autonomy tools (search, file, shell, code, etc.)
|
|
6
|
+
- Auto-approval enabled by default (full_auto mode)
|
|
7
|
+
- Real-time step tracking and status output
|
|
8
|
+
- Summary table at completion
|
|
9
|
+
- Gap analysis for debugging
|
|
10
|
+
|
|
11
|
+
The tracker integrates with the Agent's autonomy system to provide
|
|
12
|
+
comprehensive tracking of autonomous agent execution.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from typing import Optional, List
|
|
16
|
+
from dataclasses import dataclass, field
|
|
17
|
+
from datetime import datetime
|
|
18
|
+
import os
|
|
19
|
+
import time
|
|
20
|
+
|
|
21
|
+
import typer
|
|
22
|
+
from rich.console import Console
|
|
23
|
+
from rich.table import Table
|
|
24
|
+
from rich.panel import Panel
|
|
25
|
+
|
|
26
|
+
console = Console()
|
|
27
|
+
|
|
28
|
+
app = typer.Typer(
|
|
29
|
+
name="tracker",
|
|
30
|
+
help="Run agents in autonomous mode with step-by-step tracking",
|
|
31
|
+
no_args_is_help=False,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass
|
|
36
|
+
class TrackedStep:
|
|
37
|
+
"""A single tracked step in the autonomous execution."""
|
|
38
|
+
step_number: int
|
|
39
|
+
timestamp: str
|
|
40
|
+
action_type: str # chat, tool_call, completion, error
|
|
41
|
+
action_name: str # tool name or "thinking"
|
|
42
|
+
input_summary: str
|
|
43
|
+
output_summary: str
|
|
44
|
+
duration_seconds: float
|
|
45
|
+
success: bool
|
|
46
|
+
error: Optional[str] = None
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@dataclass
|
|
50
|
+
class TrackerResult:
|
|
51
|
+
"""Result of a tracked autonomous execution."""
|
|
52
|
+
task: str
|
|
53
|
+
success: bool
|
|
54
|
+
completion_reason: str
|
|
55
|
+
total_steps: int
|
|
56
|
+
total_duration: float
|
|
57
|
+
steps: List[TrackedStep] = field(default_factory=list)
|
|
58
|
+
tools_used: List[str] = field(default_factory=list)
|
|
59
|
+
gaps_identified: List[str] = field(default_factory=list)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
# ============================================================================
|
|
63
|
+
# CENTRALIZED AUTONOMY DEFAULT TOOLS
|
|
64
|
+
# These are the default tools available in autonomy mode (no API keys required)
|
|
65
|
+
# ============================================================================
|
|
66
|
+
AUTONOMY_DEFAULT_TOOLS = [
|
|
67
|
+
# Web Search & Crawl
|
|
68
|
+
"search_web", # Unified web search (DuckDuckGo fallback)
|
|
69
|
+
"internet_search", # DuckDuckGo search
|
|
70
|
+
"web_crawl", # Web page crawling
|
|
71
|
+
"scrape_page", # Page scraping
|
|
72
|
+
"extract_text", # Text extraction
|
|
73
|
+
"extract_links", # Link extraction
|
|
74
|
+
|
|
75
|
+
# File Operations
|
|
76
|
+
"read_file", # Read file contents
|
|
77
|
+
"write_file", # Write to files
|
|
78
|
+
"list_files", # List directory contents
|
|
79
|
+
"copy_file", # Copy files
|
|
80
|
+
"move_file", # Move files
|
|
81
|
+
"delete_file", # Delete files
|
|
82
|
+
"get_file_info", # Get file metadata
|
|
83
|
+
|
|
84
|
+
# Shell & System
|
|
85
|
+
"execute_command", # Execute shell commands
|
|
86
|
+
"list_processes", # List running processes
|
|
87
|
+
"get_system_info", # Get system information
|
|
88
|
+
|
|
89
|
+
# Python Code Execution
|
|
90
|
+
"execute_code", # Execute Python code
|
|
91
|
+
"analyze_code", # Analyze code structure
|
|
92
|
+
"format_code", # Format Python code
|
|
93
|
+
"lint_code", # Lint Python code
|
|
94
|
+
|
|
95
|
+
# ACP Tools (Agent-Centric Protocol)
|
|
96
|
+
"acp_create_file", # Create files (with approval tracking)
|
|
97
|
+
"acp_edit_file", # Edit files (with approval tracking)
|
|
98
|
+
"acp_delete_file", # Delete files (with approval tracking)
|
|
99
|
+
"acp_execute_command", # Execute commands (with approval tracking)
|
|
100
|
+
|
|
101
|
+
# LSP Tools (Language Server Protocol)
|
|
102
|
+
"lsp_list_symbols", # List symbols in a file
|
|
103
|
+
"lsp_find_definition", # Go to definition
|
|
104
|
+
"lsp_find_references", # Find all references
|
|
105
|
+
"lsp_get_diagnostics", # Get code diagnostics/errors
|
|
106
|
+
|
|
107
|
+
# Scheduling
|
|
108
|
+
"schedule_add", # Add scheduled task
|
|
109
|
+
"schedule_list", # List scheduled tasks
|
|
110
|
+
"schedule_remove", # Remove scheduled task
|
|
111
|
+
]
|
|
112
|
+
|
|
113
|
+
# Extended tools (may require API keys)
|
|
114
|
+
EXTENDED_TOOLS = [
|
|
115
|
+
"tavily_search", # Tavily search (requires TAVILY_API_KEY)
|
|
116
|
+
"tavily_extract", # Tavily extract
|
|
117
|
+
"exa_search", # Exa search (requires EXA_API_KEY)
|
|
118
|
+
"exa_answer", # Exa answer
|
|
119
|
+
"crawl4ai", # Advanced crawling (requires playwright)
|
|
120
|
+
"ydc_search", # You.com search (requires YDC_API_KEY)
|
|
121
|
+
]
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _get_tools(tool_names: List[str]) -> List:
|
|
125
|
+
"""Resolve tool names to actual tool functions."""
|
|
126
|
+
tools = []
|
|
127
|
+
resolved_names = set()
|
|
128
|
+
try:
|
|
129
|
+
from praisonaiagents import tools as tool_module
|
|
130
|
+
for name in tool_names:
|
|
131
|
+
try:
|
|
132
|
+
tool_func = getattr(tool_module, name)
|
|
133
|
+
# Check if it's a module (e.g., web_crawl returns module)
|
|
134
|
+
# In that case, get the function with the same name from the module
|
|
135
|
+
if hasattr(tool_func, '__file__') and hasattr(tool_func, name):
|
|
136
|
+
tool_func = getattr(tool_func, name)
|
|
137
|
+
tools.append(tool_func)
|
|
138
|
+
resolved_names.add(name)
|
|
139
|
+
except AttributeError:
|
|
140
|
+
pass # Try interactive tools below
|
|
141
|
+
except ImportError:
|
|
142
|
+
console.print("[red]Error: praisonaiagents not installed[/red]")
|
|
143
|
+
|
|
144
|
+
# Resolve ACP/LSP tools via interactive_tools (they need runtime initialization)
|
|
145
|
+
unresolved = [n for n in tool_names if n not in resolved_names]
|
|
146
|
+
if any(n.startswith(("acp_", "lsp_")) for n in unresolved):
|
|
147
|
+
try:
|
|
148
|
+
from praisonai.cli.features.interactive_tools import get_interactive_tools, ToolConfig
|
|
149
|
+
config = ToolConfig.from_env()
|
|
150
|
+
config.workspace = os.getcwd()
|
|
151
|
+
interactive = get_interactive_tools(config=config)
|
|
152
|
+
interactive_map = {t.__name__: t for t in interactive}
|
|
153
|
+
for name in unresolved:
|
|
154
|
+
if name in interactive_map:
|
|
155
|
+
tools.append(interactive_map[name])
|
|
156
|
+
resolved_names.add(name)
|
|
157
|
+
except ImportError:
|
|
158
|
+
pass # Interactive tools not available
|
|
159
|
+
except Exception as e:
|
|
160
|
+
console.print(f"[dim]Note: ACP/LSP tools not loaded: {e}[/dim]")
|
|
161
|
+
|
|
162
|
+
return tools
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def _summarize_text(text: str, max_len: int = 50) -> str:
|
|
166
|
+
"""Summarize text to max length."""
|
|
167
|
+
if not text:
|
|
168
|
+
return ""
|
|
169
|
+
text = str(text).replace("\n", " ").strip()
|
|
170
|
+
if len(text) > max_len:
|
|
171
|
+
return text[:max_len-3] + "..."
|
|
172
|
+
return text
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def _run_tracked_task(
|
|
176
|
+
task: str,
|
|
177
|
+
tools: List,
|
|
178
|
+
max_iterations: int,
|
|
179
|
+
model: Optional[str],
|
|
180
|
+
verbose: bool,
|
|
181
|
+
step_callback: Optional[callable] = None,
|
|
182
|
+
auto_approve: bool = True,
|
|
183
|
+
) -> TrackerResult:
|
|
184
|
+
"""Run a task with step tracking using centralized autonomy system.
|
|
185
|
+
|
|
186
|
+
Args:
|
|
187
|
+
task: The task to execute
|
|
188
|
+
tools: List of tool functions to use
|
|
189
|
+
max_iterations: Maximum autonomous iterations
|
|
190
|
+
model: LLM model to use (optional)
|
|
191
|
+
verbose: Show verbose output
|
|
192
|
+
step_callback: Callback for each step (optional)
|
|
193
|
+
auto_approve: Auto-approve all tool calls (default: True)
|
|
194
|
+
"""
|
|
195
|
+
from praisonaiagents import Agent
|
|
196
|
+
|
|
197
|
+
# ========================================================================
|
|
198
|
+
# AUTO-APPROVE: Set env var so @require_approval decorators on tool
|
|
199
|
+
# functions (execute_code, execute_command, write_file, etc.) auto-approve.
|
|
200
|
+
# The decorator checks PRAISONAI_AUTO_APPROVE env var independently of
|
|
201
|
+
# the Agent's approval= parameter.
|
|
202
|
+
# ========================================================================
|
|
203
|
+
prev_auto_approve = os.environ.get("PRAISONAI_AUTO_APPROVE")
|
|
204
|
+
if auto_approve:
|
|
205
|
+
os.environ["PRAISONAI_AUTO_APPROVE"] = "true"
|
|
206
|
+
|
|
207
|
+
steps: List[TrackedStep] = []
|
|
208
|
+
tools_used: set = set()
|
|
209
|
+
start_time = time.time()
|
|
210
|
+
step_number = 0
|
|
211
|
+
|
|
212
|
+
# ========================================================================
|
|
213
|
+
# CENTRALIZED AUTONOMY CONFIGURATION
|
|
214
|
+
# Uses full_auto level with auto-approval for autonomous tracking
|
|
215
|
+
# ========================================================================
|
|
216
|
+
agent_kwargs = {
|
|
217
|
+
"name": "tracker_agent",
|
|
218
|
+
"instructions": """You are an autonomous agent with access to various tools.
|
|
219
|
+
You MUST use tools to complete tasks - do NOT rely on your training knowledge alone.
|
|
220
|
+
|
|
221
|
+
TOOL USAGE RULES:
|
|
222
|
+
- For research/search tasks: use search_web or internet_search
|
|
223
|
+
- For file operations: use read_file, write_file, list_files
|
|
224
|
+
- For safe file creation/editing: use acp_create_file, acp_edit_file
|
|
225
|
+
- For code tasks: use execute_code, analyze_code
|
|
226
|
+
- For shell commands: use execute_command or acp_execute_command
|
|
227
|
+
- For web scraping: use web_crawl, scrape_page, extract_text
|
|
228
|
+
- For code intelligence: use lsp_list_symbols, lsp_find_definition, lsp_find_references, lsp_get_diagnostics
|
|
229
|
+
|
|
230
|
+
When you have fully completed the task, say 'Task completed' or 'Done'.""",
|
|
231
|
+
"tools": tools,
|
|
232
|
+
# Centralized autonomy configuration
|
|
233
|
+
"autonomy": {
|
|
234
|
+
"enabled": True,
|
|
235
|
+
"level": "full_auto", # Full autonomous mode
|
|
236
|
+
"max_iterations": max_iterations,
|
|
237
|
+
"doom_loop_threshold": 5,
|
|
238
|
+
"auto_escalate": True,
|
|
239
|
+
},
|
|
240
|
+
# Auto-approve all tools for tracker (agent-centric approval)
|
|
241
|
+
"approval": auto_approve,
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if model:
|
|
245
|
+
agent_kwargs["llm"] = model
|
|
246
|
+
|
|
247
|
+
if verbose:
|
|
248
|
+
pass # Use default full output
|
|
249
|
+
else:
|
|
250
|
+
agent_kwargs["output"] = "status" # Real-time status like Agent(output="status")
|
|
251
|
+
|
|
252
|
+
agent = Agent(**agent_kwargs)
|
|
253
|
+
|
|
254
|
+
# Wrap execute_tool to track tool calls
|
|
255
|
+
original_execute_tool = agent.execute_tool
|
|
256
|
+
|
|
257
|
+
def tracked_execute_tool(tool_name, *args, **kwargs):
|
|
258
|
+
nonlocal step_number
|
|
259
|
+
step_number += 1
|
|
260
|
+
step_start = time.time()
|
|
261
|
+
tools_used.add(tool_name)
|
|
262
|
+
|
|
263
|
+
try:
|
|
264
|
+
result = original_execute_tool(tool_name, *args, **kwargs)
|
|
265
|
+
duration = time.time() - step_start
|
|
266
|
+
|
|
267
|
+
step = TrackedStep(
|
|
268
|
+
step_number=step_number,
|
|
269
|
+
timestamp=datetime.now().isoformat(),
|
|
270
|
+
action_type="tool_call",
|
|
271
|
+
action_name=tool_name,
|
|
272
|
+
input_summary=_summarize_text(str(args) + str(kwargs)),
|
|
273
|
+
output_summary=_summarize_text(str(result)),
|
|
274
|
+
duration_seconds=duration,
|
|
275
|
+
success=True,
|
|
276
|
+
)
|
|
277
|
+
steps.append(step)
|
|
278
|
+
if step_callback:
|
|
279
|
+
step_callback(step)
|
|
280
|
+
return result
|
|
281
|
+
except Exception as e:
|
|
282
|
+
duration = time.time() - step_start
|
|
283
|
+
step = TrackedStep(
|
|
284
|
+
step_number=step_number,
|
|
285
|
+
timestamp=datetime.now().isoformat(),
|
|
286
|
+
action_type="tool_error",
|
|
287
|
+
action_name=tool_name,
|
|
288
|
+
input_summary=_summarize_text(str(args) + str(kwargs)),
|
|
289
|
+
output_summary="",
|
|
290
|
+
duration_seconds=duration,
|
|
291
|
+
success=False,
|
|
292
|
+
error=str(e),
|
|
293
|
+
)
|
|
294
|
+
steps.append(step)
|
|
295
|
+
if step_callback:
|
|
296
|
+
step_callback(step)
|
|
297
|
+
raise
|
|
298
|
+
|
|
299
|
+
agent.execute_tool = tracked_execute_tool
|
|
300
|
+
|
|
301
|
+
# Track chat calls
|
|
302
|
+
original_chat = agent.chat
|
|
303
|
+
|
|
304
|
+
def tracked_chat(prompt, *args, **kwargs):
|
|
305
|
+
nonlocal step_number
|
|
306
|
+
step_number += 1
|
|
307
|
+
step_start = time.time()
|
|
308
|
+
|
|
309
|
+
try:
|
|
310
|
+
result = original_chat(prompt, *args, **kwargs)
|
|
311
|
+
duration = time.time() - step_start
|
|
312
|
+
|
|
313
|
+
# Record step
|
|
314
|
+
step = TrackedStep(
|
|
315
|
+
step_number=step_number,
|
|
316
|
+
timestamp=datetime.now().isoformat(),
|
|
317
|
+
action_type="chat",
|
|
318
|
+
action_name="thinking",
|
|
319
|
+
input_summary=_summarize_text(prompt),
|
|
320
|
+
output_summary=_summarize_text(str(result)),
|
|
321
|
+
duration_seconds=duration,
|
|
322
|
+
success=True,
|
|
323
|
+
)
|
|
324
|
+
steps.append(step)
|
|
325
|
+
|
|
326
|
+
if step_callback:
|
|
327
|
+
step_callback(step)
|
|
328
|
+
|
|
329
|
+
return result
|
|
330
|
+
except Exception as e:
|
|
331
|
+
duration = time.time() - step_start
|
|
332
|
+
step = TrackedStep(
|
|
333
|
+
step_number=step_number,
|
|
334
|
+
timestamp=datetime.now().isoformat(),
|
|
335
|
+
action_type="error",
|
|
336
|
+
action_name="chat_error",
|
|
337
|
+
input_summary=_summarize_text(prompt),
|
|
338
|
+
output_summary="",
|
|
339
|
+
duration_seconds=duration,
|
|
340
|
+
success=False,
|
|
341
|
+
error=str(e),
|
|
342
|
+
)
|
|
343
|
+
steps.append(step)
|
|
344
|
+
if step_callback:
|
|
345
|
+
step_callback(step)
|
|
346
|
+
raise
|
|
347
|
+
|
|
348
|
+
agent.chat = tracked_chat
|
|
349
|
+
|
|
350
|
+
# Run autonomous loop
|
|
351
|
+
try:
|
|
352
|
+
result = agent.run_autonomous(
|
|
353
|
+
prompt=task,
|
|
354
|
+
max_iterations=max_iterations,
|
|
355
|
+
)
|
|
356
|
+
|
|
357
|
+
total_duration = time.time() - start_time
|
|
358
|
+
|
|
359
|
+
# Extract tool usage from AutonomyResult.actions
|
|
360
|
+
if hasattr(result, 'actions') and result.actions:
|
|
361
|
+
for action in result.actions:
|
|
362
|
+
if isinstance(action, dict):
|
|
363
|
+
action_type = action.get('type', 'unknown')
|
|
364
|
+
action_name = action.get('name', action.get('tool', 'unknown'))
|
|
365
|
+
if action_type == 'tool_call' or 'tool' in action:
|
|
366
|
+
tools_used.add(action_name)
|
|
367
|
+
|
|
368
|
+
# Analyze gaps
|
|
369
|
+
gaps = []
|
|
370
|
+
if result.iterations >= max_iterations:
|
|
371
|
+
gaps.append("Hit max iterations - task may be incomplete")
|
|
372
|
+
if result.completion_reason == "doom_loop":
|
|
373
|
+
gaps.append("Doom loop detected - agent repeated same actions")
|
|
374
|
+
if result.completion_reason == "needs_help":
|
|
375
|
+
gaps.append("Agent requested human help")
|
|
376
|
+
if not tools_used and len(tools) > 0:
|
|
377
|
+
gaps.append("No tools were used despite being available")
|
|
378
|
+
|
|
379
|
+
return TrackerResult(
|
|
380
|
+
task=task,
|
|
381
|
+
success=result.success,
|
|
382
|
+
completion_reason=result.completion_reason,
|
|
383
|
+
total_steps=len(steps) if steps else result.iterations,
|
|
384
|
+
total_duration=total_duration,
|
|
385
|
+
steps=steps,
|
|
386
|
+
tools_used=list(tools_used),
|
|
387
|
+
gaps_identified=gaps,
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
except Exception as e:
|
|
391
|
+
total_duration = time.time() - start_time
|
|
392
|
+
return TrackerResult(
|
|
393
|
+
task=task,
|
|
394
|
+
success=False,
|
|
395
|
+
completion_reason="error",
|
|
396
|
+
total_steps=len(steps),
|
|
397
|
+
total_duration=total_duration,
|
|
398
|
+
steps=steps,
|
|
399
|
+
tools_used=list(tools_used),
|
|
400
|
+
gaps_identified=[f"Error: {str(e)}"],
|
|
401
|
+
)
|
|
402
|
+
finally:
|
|
403
|
+
# Stop the LSP/ACP runtime if it was started
|
|
404
|
+
try:
|
|
405
|
+
from praisonai.cli.features.interactive_tools import cleanup_runtime
|
|
406
|
+
cleanup_runtime()
|
|
407
|
+
except Exception:
|
|
408
|
+
pass
|
|
409
|
+
|
|
410
|
+
# Restore original PRAISONAI_AUTO_APPROVE env var
|
|
411
|
+
if prev_auto_approve is None:
|
|
412
|
+
os.environ.pop("PRAISONAI_AUTO_APPROVE", None)
|
|
413
|
+
else:
|
|
414
|
+
os.environ["PRAISONAI_AUTO_APPROVE"] = prev_auto_approve
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
def _print_step_table(steps: List[TrackedStep]) -> None:
|
|
418
|
+
"""Print a table of all steps."""
|
|
419
|
+
table = Table(title="📊 Execution Steps", show_header=True, header_style="bold cyan")
|
|
420
|
+
table.add_column("#", style="dim", width=4)
|
|
421
|
+
table.add_column("Type", width=10)
|
|
422
|
+
table.add_column("Action", width=15)
|
|
423
|
+
table.add_column("Input", width=30)
|
|
424
|
+
table.add_column("Output", width=30)
|
|
425
|
+
table.add_column("Time", width=8)
|
|
426
|
+
table.add_column("Status", width=8)
|
|
427
|
+
|
|
428
|
+
for step in steps:
|
|
429
|
+
status = "✅" if step.success else "❌"
|
|
430
|
+
table.add_row(
|
|
431
|
+
str(step.step_number),
|
|
432
|
+
step.action_type,
|
|
433
|
+
step.action_name,
|
|
434
|
+
step.input_summary,
|
|
435
|
+
step.output_summary,
|
|
436
|
+
f"{step.duration_seconds:.2f}s",
|
|
437
|
+
status,
|
|
438
|
+
)
|
|
439
|
+
|
|
440
|
+
console.print(table)
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
def _print_summary(result: TrackerResult) -> None:
|
|
444
|
+
"""Print execution summary."""
|
|
445
|
+
status_icon = "✅" if result.success else "❌"
|
|
446
|
+
|
|
447
|
+
console.print(Panel(
|
|
448
|
+
f"""
|
|
449
|
+
[bold]{status_icon} Task: {_summarize_text(result.task, 60)}[/bold]
|
|
450
|
+
|
|
451
|
+
[cyan]Completion Reason:[/cyan] {result.completion_reason}
|
|
452
|
+
[cyan]Total Steps:[/cyan] {result.total_steps}
|
|
453
|
+
[cyan]Total Duration:[/cyan] {result.total_duration:.2f}s
|
|
454
|
+
[cyan]Tools Used:[/cyan] {', '.join(result.tools_used) if result.tools_used else 'None'}
|
|
455
|
+
""",
|
|
456
|
+
title="📋 Execution Summary",
|
|
457
|
+
border_style="green" if result.success else "red",
|
|
458
|
+
))
|
|
459
|
+
|
|
460
|
+
if result.gaps_identified:
|
|
461
|
+
console.print("\n[bold yellow]⚠️ Gaps Identified:[/bold yellow]")
|
|
462
|
+
for gap in result.gaps_identified:
|
|
463
|
+
console.print(f" • {gap}")
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
@app.callback()
|
|
467
|
+
def tracker_main(ctx: typer.Context):
|
|
468
|
+
"""Autonomous agent tracking with step-by-step analysis.
|
|
469
|
+
|
|
470
|
+
Run tasks with full execution tracing and optional quality judging.
|
|
471
|
+
|
|
472
|
+
Commands:
|
|
473
|
+
|
|
474
|
+
praisonai tracker run "Search for Python best practices"
|
|
475
|
+
|
|
476
|
+
praisonai tracker judge "What is 2+2?" --expected "4"
|
|
477
|
+
|
|
478
|
+
praisonai tracker tools
|
|
479
|
+
|
|
480
|
+
praisonai tracker batch tasks.json
|
|
481
|
+
"""
|
|
482
|
+
pass
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
@app.command(name="run")
|
|
486
|
+
def tracker_run(
|
|
487
|
+
task: str = typer.Argument(..., help="Task for the agent to complete"),
|
|
488
|
+
max_iterations: int = typer.Option(20, "--max-iterations", "-n", help="Maximum iterations (default: 20)"),
|
|
489
|
+
model: Optional[str] = typer.Option(None, "--model", "-m", help="LLM model to use"),
|
|
490
|
+
tools: Optional[str] = typer.Option(None, "--tools", "-t", help="Comma-separated tool names to use"),
|
|
491
|
+
extended: bool = typer.Option(False, "--extended", "-e", help="Include extended tools (may require API keys)"),
|
|
492
|
+
verbose: bool = typer.Option(False, "--verbose", "-v", help="Show verbose output"),
|
|
493
|
+
live: bool = typer.Option(True, "--live/--no-live", help="Show live step updates (default: on)"),
|
|
494
|
+
):
|
|
495
|
+
"""Run an agent with step-by-step tracking.
|
|
496
|
+
|
|
497
|
+
Examples:
|
|
498
|
+
|
|
499
|
+
praisonai tracker run "Search for Python best practices and summarize"
|
|
500
|
+
|
|
501
|
+
praisonai tracker run "Read config.yaml and explain its structure" -v
|
|
502
|
+
|
|
503
|
+
praisonai tracker run "Find trending AI news" --tools search_web,web_crawl
|
|
504
|
+
"""
|
|
505
|
+
_run_and_display(task, max_iterations, model, tools, extended, verbose, live)
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
def _run_and_display(
|
|
509
|
+
task: str,
|
|
510
|
+
max_iterations: int = 20,
|
|
511
|
+
model: Optional[str] = None,
|
|
512
|
+
tools: Optional[str] = None,
|
|
513
|
+
extended: bool = False,
|
|
514
|
+
verbose: bool = False,
|
|
515
|
+
live: bool = True,
|
|
516
|
+
) -> TrackerResult:
|
|
517
|
+
"""Shared logic: run a tracked task and display results."""
|
|
518
|
+
# Resolve tools
|
|
519
|
+
tool_names = AUTONOMY_DEFAULT_TOOLS.copy()
|
|
520
|
+
if extended:
|
|
521
|
+
tool_names.extend(EXTENDED_TOOLS)
|
|
522
|
+
if tools:
|
|
523
|
+
tool_names = [t.strip() for t in tools.split(",")]
|
|
524
|
+
|
|
525
|
+
resolved_tools = _get_tools(tool_names)
|
|
526
|
+
|
|
527
|
+
console.print(f"\n[bold cyan]🔍 Agent Tracker[/bold cyan]")
|
|
528
|
+
console.print(f"[dim]Task: {_summarize_text(task, 70)}[/dim]")
|
|
529
|
+
console.print(f"[dim]Tools: {len(resolved_tools)} loaded ({', '.join(tool_names[:5])}{'...' if len(tool_names) > 5 else ''})[/dim]")
|
|
530
|
+
console.print(f"[dim]Max iterations: {max_iterations}[/dim]\n")
|
|
531
|
+
|
|
532
|
+
# Step callback for live updates
|
|
533
|
+
def step_callback(step: TrackedStep):
|
|
534
|
+
if live:
|
|
535
|
+
status = "✅" if step.success else "❌"
|
|
536
|
+
console.print(f" [{step.step_number}] {status} {step.action_type}: {step.action_name} ({step.duration_seconds:.2f}s)")
|
|
537
|
+
|
|
538
|
+
# Run tracked task
|
|
539
|
+
with console.status("[bold green]Running autonomous agent...[/bold green]") if not live else console:
|
|
540
|
+
result = _run_tracked_task(
|
|
541
|
+
task=task,
|
|
542
|
+
tools=resolved_tools,
|
|
543
|
+
max_iterations=max_iterations,
|
|
544
|
+
model=model,
|
|
545
|
+
verbose=verbose,
|
|
546
|
+
step_callback=step_callback if live else None,
|
|
547
|
+
)
|
|
548
|
+
|
|
549
|
+
# Print results
|
|
550
|
+
console.print("\n")
|
|
551
|
+
_print_step_table(result.steps)
|
|
552
|
+
console.print("\n")
|
|
553
|
+
_print_summary(result)
|
|
554
|
+
|
|
555
|
+
return result
|
|
556
|
+
|
|
557
|
+
|
|
558
|
+
@app.command(name="batch")
|
|
559
|
+
def tracker_batch(
|
|
560
|
+
tasks_file: str = typer.Argument(..., help="JSON file with list of tasks"),
|
|
561
|
+
max_iterations: int = typer.Option(20, "--max-iterations", "-n", help="Maximum iterations per task"),
|
|
562
|
+
model: Optional[str] = typer.Option(None, "--model", "-m", help="LLM model to use"),
|
|
563
|
+
output: Optional[str] = typer.Option(None, "--output", "-o", help="Output JSON file for results"),
|
|
564
|
+
):
|
|
565
|
+
"""Run multiple tasks and generate a comparison table.
|
|
566
|
+
|
|
567
|
+
The tasks file should be a JSON array of task strings:
|
|
568
|
+
|
|
569
|
+
["Task 1", "Task 2", "Task 3"]
|
|
570
|
+
|
|
571
|
+
Example:
|
|
572
|
+
|
|
573
|
+
praisonai tracker batch tasks.json -o results.json
|
|
574
|
+
"""
|
|
575
|
+
import json
|
|
576
|
+
|
|
577
|
+
try:
|
|
578
|
+
with open(tasks_file) as f:
|
|
579
|
+
tasks = json.load(f)
|
|
580
|
+
except Exception as e:
|
|
581
|
+
console.print(f"[red]Error reading tasks file: {e}[/red]")
|
|
582
|
+
raise typer.Exit(1)
|
|
583
|
+
|
|
584
|
+
if not isinstance(tasks, list):
|
|
585
|
+
console.print("[red]Tasks file must contain a JSON array of strings[/red]")
|
|
586
|
+
raise typer.Exit(1)
|
|
587
|
+
|
|
588
|
+
console.print(f"\n[bold cyan]🔍 Batch Agent Tracker[/bold cyan]")
|
|
589
|
+
console.print(f"[dim]Running {len(tasks)} tasks...[/dim]\n")
|
|
590
|
+
|
|
591
|
+
resolved_tools = _get_tools(AUTONOMY_DEFAULT_TOOLS)
|
|
592
|
+
results: List[TrackerResult] = []
|
|
593
|
+
|
|
594
|
+
for i, task in enumerate(tasks, 1):
|
|
595
|
+
console.print(f"\n[bold]Task {i}/{len(tasks)}:[/bold] {_summarize_text(task, 50)}")
|
|
596
|
+
|
|
597
|
+
result = _run_tracked_task(
|
|
598
|
+
task=task,
|
|
599
|
+
tools=resolved_tools,
|
|
600
|
+
max_iterations=max_iterations,
|
|
601
|
+
model=model,
|
|
602
|
+
verbose=False,
|
|
603
|
+
step_callback=None,
|
|
604
|
+
)
|
|
605
|
+
results.append(result)
|
|
606
|
+
|
|
607
|
+
status = "✅" if result.success else "❌"
|
|
608
|
+
console.print(f" {status} {result.completion_reason} ({result.total_steps} steps, {result.total_duration:.2f}s)")
|
|
609
|
+
|
|
610
|
+
# Print summary table
|
|
611
|
+
console.print("\n")
|
|
612
|
+
table = Table(title="📊 Batch Results Summary", show_header=True, header_style="bold cyan")
|
|
613
|
+
table.add_column("#", style="dim", width=4)
|
|
614
|
+
table.add_column("Task", width=40)
|
|
615
|
+
table.add_column("Status", width=8)
|
|
616
|
+
table.add_column("Reason", width=15)
|
|
617
|
+
table.add_column("Steps", width=6)
|
|
618
|
+
table.add_column("Duration", width=10)
|
|
619
|
+
table.add_column("Gaps", width=20)
|
|
620
|
+
|
|
621
|
+
for i, result in enumerate(results, 1):
|
|
622
|
+
status = "✅" if result.success else "❌"
|
|
623
|
+
gaps = ", ".join(result.gaps_identified[:2]) if result.gaps_identified else "-"
|
|
624
|
+
table.add_row(
|
|
625
|
+
str(i),
|
|
626
|
+
_summarize_text(result.task, 40),
|
|
627
|
+
status,
|
|
628
|
+
result.completion_reason,
|
|
629
|
+
str(result.total_steps),
|
|
630
|
+
f"{result.total_duration:.2f}s",
|
|
631
|
+
_summarize_text(gaps, 20),
|
|
632
|
+
)
|
|
633
|
+
|
|
634
|
+
console.print(table)
|
|
635
|
+
|
|
636
|
+
# Calculate stats
|
|
637
|
+
success_count = sum(1 for r in results if r.success)
|
|
638
|
+
total_steps = sum(r.total_steps for r in results)
|
|
639
|
+
total_duration = sum(r.total_duration for r in results)
|
|
640
|
+
|
|
641
|
+
console.print(f"\n[bold]Summary:[/bold]")
|
|
642
|
+
console.print(f" Success rate: {success_count}/{len(results)} ({100*success_count/len(results):.1f}%)")
|
|
643
|
+
console.print(f" Total steps: {total_steps}")
|
|
644
|
+
console.print(f" Total duration: {total_duration:.2f}s")
|
|
645
|
+
|
|
646
|
+
# Save results if output specified
|
|
647
|
+
if output:
|
|
648
|
+
output_data = {
|
|
649
|
+
"tasks": len(results),
|
|
650
|
+
"success_rate": success_count / len(results),
|
|
651
|
+
"total_steps": total_steps,
|
|
652
|
+
"total_duration": total_duration,
|
|
653
|
+
"results": [
|
|
654
|
+
{
|
|
655
|
+
"task": r.task,
|
|
656
|
+
"success": r.success,
|
|
657
|
+
"completion_reason": r.completion_reason,
|
|
658
|
+
"steps": r.total_steps,
|
|
659
|
+
"duration": r.total_duration,
|
|
660
|
+
"gaps": r.gaps_identified,
|
|
661
|
+
}
|
|
662
|
+
for r in results
|
|
663
|
+
],
|
|
664
|
+
}
|
|
665
|
+
with open(output, "w") as f:
|
|
666
|
+
json.dump(output_data, f, indent=2)
|
|
667
|
+
console.print(f"\n[green]Results saved to {output}[/green]")
|
|
668
|
+
|
|
669
|
+
|
|
670
|
+
@app.command(name="tools")
|
|
671
|
+
def tracker_tools():
|
|
672
|
+
"""List all available tools for the tracker."""
|
|
673
|
+
console.print("\n[bold cyan]🛠️ Available Tools[/bold cyan]\n")
|
|
674
|
+
|
|
675
|
+
console.print("[bold]Default Tools (no API keys required):[/bold]")
|
|
676
|
+
for tool in AUTONOMY_DEFAULT_TOOLS:
|
|
677
|
+
console.print(f" • {tool}")
|
|
678
|
+
|
|
679
|
+
console.print("\n[bold]Extended Tools (may require API keys):[/bold]")
|
|
680
|
+
for tool in EXTENDED_TOOLS:
|
|
681
|
+
console.print(f" • {tool}")
|
|
682
|
+
|
|
683
|
+
console.print("\n[dim]Use --tools to specify custom tools, e.g.:[/dim]")
|
|
684
|
+
console.print("[dim] praisonai tracker 'task' --tools search_web,read_file[/dim]")
|
|
685
|
+
|
|
686
|
+
|
|
687
|
+
# ============================================================================
|
|
688
|
+
# JUDGE FEATURE
|
|
689
|
+
# ============================================================================
|
|
690
|
+
|
|
691
|
+
DEFAULT_JUDGE_CRITERIA = """Evaluate this autonomous agent execution trace:
|
|
692
|
+
1. Task Completion: Did the agent fully complete the assigned task?
|
|
693
|
+
2. Tool Selection: Were appropriate tools chosen for each step?
|
|
694
|
+
3. Efficiency: Was the task completed with minimal unnecessary steps?
|
|
695
|
+
4. Error Handling: Were errors handled gracefully without crashing?
|
|
696
|
+
5. Output Quality: Is the final output accurate and useful?"""
|
|
697
|
+
|
|
698
|
+
|
|
699
|
+
def _format_trace_for_judge(result: TrackerResult) -> str:
|
|
700
|
+
"""Convert TrackerResult into a structured string for LLM evaluation."""
|
|
701
|
+
lines = []
|
|
702
|
+
lines.append(f"Task: \"{result.task}\"")
|
|
703
|
+
status = "success" if result.success else "failed"
|
|
704
|
+
lines.append(f"Completion: {status} (reason: {result.completion_reason})")
|
|
705
|
+
lines.append(f"Duration: {result.total_duration:.1f}s | Steps: {result.total_steps} | Tools Used: {', '.join(result.tools_used) if result.tools_used else 'None'}")
|
|
706
|
+
lines.append("")
|
|
707
|
+
|
|
708
|
+
for step in result.steps:
|
|
709
|
+
icon = "✅" if step.success else "❌"
|
|
710
|
+
lines.append(f"Step {step.step_number}: [{step.action_type}] {step.action_name} ({step.duration_seconds:.1f}s) {icon}")
|
|
711
|
+
lines.append(f" Input: {step.input_summary}")
|
|
712
|
+
lines.append(f" Output: {step.output_summary}")
|
|
713
|
+
if step.error:
|
|
714
|
+
lines.append(f" Error: {step.error}")
|
|
715
|
+
lines.append("")
|
|
716
|
+
|
|
717
|
+
if result.gaps_identified:
|
|
718
|
+
lines.append(f"Gaps: {'; '.join(result.gaps_identified)}")
|
|
719
|
+
else:
|
|
720
|
+
lines.append("Gaps: None")
|
|
721
|
+
|
|
722
|
+
return "\n".join(lines)
|
|
723
|
+
|
|
724
|
+
|
|
725
|
+
def _print_judge_verdict(judge_result, threshold: float) -> None:
|
|
726
|
+
"""Print the judge verdict with rich formatting."""
|
|
727
|
+
score = getattr(judge_result, 'score', 0) or 0
|
|
728
|
+
passed = score >= threshold
|
|
729
|
+
reasoning = getattr(judge_result, 'reasoning', '') or ''
|
|
730
|
+
suggestions = getattr(judge_result, 'suggestions', []) or []
|
|
731
|
+
|
|
732
|
+
# Score bar
|
|
733
|
+
bar_len = 20
|
|
734
|
+
filled = int(score / 10 * bar_len)
|
|
735
|
+
bar = "█" * filled + "░" * (bar_len - filled)
|
|
736
|
+
|
|
737
|
+
color = "green" if passed else ("yellow" if score >= 5 else "red")
|
|
738
|
+
icon = "✅" if passed else "❌"
|
|
739
|
+
|
|
740
|
+
console.print(Panel(
|
|
741
|
+
f"""
|
|
742
|
+
[bold]{icon} Score: [{color}]{score:.1f}/10[/{color}][/bold] [{color}]{bar}[/{color}]
|
|
743
|
+
[bold]Threshold:[/bold] {threshold} | [bold]Verdict:[/bold] [{'green' if passed else 'red'}]{'PASS' if passed else 'FAIL'}[/{'green' if passed else 'red'}]
|
|
744
|
+
|
|
745
|
+
[bold]Reasoning:[/bold]
|
|
746
|
+
{reasoning}
|
|
747
|
+
""",
|
|
748
|
+
title="⚖️ Judge Verdict",
|
|
749
|
+
border_style=color,
|
|
750
|
+
))
|
|
751
|
+
|
|
752
|
+
if suggestions:
|
|
753
|
+
console.print("[bold yellow]💡 Suggestions:[/bold yellow]")
|
|
754
|
+
for s in suggestions:
|
|
755
|
+
console.print(f" • {s}")
|
|
756
|
+
|
|
757
|
+
|
|
758
|
+
@app.command(name="judge")
|
|
759
|
+
def tracker_judge(
|
|
760
|
+
task: str = typer.Argument(..., help="Task to execute and judge"),
|
|
761
|
+
criteria: Optional[str] = typer.Option(None, "--criteria", "-c", help="Custom evaluation criteria"),
|
|
762
|
+
expected: Optional[str] = typer.Option(None, "--expected", "-e", help="Expected output for accuracy evaluation"),
|
|
763
|
+
threshold: float = typer.Option(7.0, "--threshold", help="Pass/fail score threshold (1-10)"),
|
|
764
|
+
max_iterations: int = typer.Option(20, "--max-iterations", "-n", help="Maximum iterations (default: 20)"),
|
|
765
|
+
model: Optional[str] = typer.Option(None, "--model", "-m", help="LLM model to use"),
|
|
766
|
+
judge_model: Optional[str] = typer.Option(None, "--judge-model", help="LLM model for judge (default: same as agent)"),
|
|
767
|
+
tools: Optional[str] = typer.Option(None, "--tools", "-t", help="Comma-separated tool names to use"),
|
|
768
|
+
extended: bool = typer.Option(False, "--extended", help="Include extended tools (may require API keys)"),
|
|
769
|
+
verbose: bool = typer.Option(False, "--verbose", "-v", help="Show verbose output"),
|
|
770
|
+
):
|
|
771
|
+
"""Run a task and judge the execution quality.
|
|
772
|
+
|
|
773
|
+
Executes the task with step tracking, then evaluates the execution
|
|
774
|
+
trace using an LLM judge. Reports a score (1-10), pass/fail verdict,
|
|
775
|
+
reasoning, and improvement suggestions.
|
|
776
|
+
|
|
777
|
+
Examples:
|
|
778
|
+
|
|
779
|
+
praisonai tracker judge "Calculate fibonacci(10) using execute_code"
|
|
780
|
+
|
|
781
|
+
praisonai tracker judge "Search for AI news" --criteria "Must use search_web"
|
|
782
|
+
|
|
783
|
+
praisonai tracker judge "What is 2+2?" --expected "4" --threshold 8.0
|
|
784
|
+
"""
|
|
785
|
+
# Resolve tools
|
|
786
|
+
tool_names = AUTONOMY_DEFAULT_TOOLS.copy()
|
|
787
|
+
if extended:
|
|
788
|
+
tool_names.extend(EXTENDED_TOOLS)
|
|
789
|
+
if tools:
|
|
790
|
+
tool_names = [t.strip() for t in tools.split(",")]
|
|
791
|
+
|
|
792
|
+
resolved_tools = _get_tools(tool_names)
|
|
793
|
+
|
|
794
|
+
console.print(f"\n[bold cyan]⚖️ Agent Tracker + Judge[/bold cyan]")
|
|
795
|
+
console.print(f"[dim]Task: {_summarize_text(task, 70)}[/dim]")
|
|
796
|
+
console.print(f"[dim]Tools: {len(resolved_tools)} loaded | Threshold: {threshold}[/dim]\n")
|
|
797
|
+
|
|
798
|
+
# Step 1: Run the task
|
|
799
|
+
console.print("[bold]Phase 1: Executing task...[/bold]\n")
|
|
800
|
+
|
|
801
|
+
def step_callback(step: TrackedStep):
|
|
802
|
+
status = "✅" if step.success else "❌"
|
|
803
|
+
console.print(f" [{step.step_number}] {status} {step.action_type}: {step.action_name} ({step.duration_seconds:.2f}s)")
|
|
804
|
+
|
|
805
|
+
result = _run_tracked_task(
|
|
806
|
+
task=task,
|
|
807
|
+
tools=resolved_tools,
|
|
808
|
+
max_iterations=max_iterations,
|
|
809
|
+
model=model,
|
|
810
|
+
verbose=verbose,
|
|
811
|
+
step_callback=step_callback,
|
|
812
|
+
)
|
|
813
|
+
|
|
814
|
+
# Print step table + summary
|
|
815
|
+
console.print("\n")
|
|
816
|
+
_print_step_table(result.steps)
|
|
817
|
+
console.print("\n")
|
|
818
|
+
_print_summary(result)
|
|
819
|
+
|
|
820
|
+
# Step 2: Judge the execution
|
|
821
|
+
console.print("\n[bold]Phase 2: Judging execution...[/bold]\n")
|
|
822
|
+
|
|
823
|
+
try:
|
|
824
|
+
from praisonaiagents.eval import Judge
|
|
825
|
+
|
|
826
|
+
trace_text = _format_trace_for_judge(result)
|
|
827
|
+
|
|
828
|
+
judge_kwargs = {}
|
|
829
|
+
if judge_model:
|
|
830
|
+
judge_kwargs["model"] = judge_model
|
|
831
|
+
elif model:
|
|
832
|
+
judge_kwargs["model"] = model
|
|
833
|
+
|
|
834
|
+
judge = Judge(threshold=threshold, **judge_kwargs)
|
|
835
|
+
|
|
836
|
+
eval_criteria = criteria or DEFAULT_JUDGE_CRITERIA
|
|
837
|
+
|
|
838
|
+
judge_result = judge.run(
|
|
839
|
+
output=trace_text,
|
|
840
|
+
criteria=eval_criteria,
|
|
841
|
+
expected=expected,
|
|
842
|
+
input=task,
|
|
843
|
+
)
|
|
844
|
+
|
|
845
|
+
_print_judge_verdict(judge_result, threshold)
|
|
846
|
+
|
|
847
|
+
except ImportError:
|
|
848
|
+
console.print("[red]Error: praisonaiagents.eval not available[/red]")
|
|
849
|
+
console.print("[dim]Install with: pip install praisonaiagents[/dim]")
|
|
850
|
+
except Exception as e:
|
|
851
|
+
console.print(f"[red]Judge error: {e}[/red]")
|
|
852
|
+
|