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,345 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Checkpoints CLI Feature for PraisonAI.
|
|
3
|
+
|
|
4
|
+
Provides CLI commands for managing file-level checkpoints.
|
|
5
|
+
|
|
6
|
+
Commands:
|
|
7
|
+
- praisonai checkpoint save <message> # Save a checkpoint
|
|
8
|
+
- praisonai checkpoint restore <id> # Restore to a checkpoint
|
|
9
|
+
- praisonai checkpoint list # List all checkpoints
|
|
10
|
+
- praisonai checkpoint diff [from] [to] # Show diff between checkpoints
|
|
11
|
+
- praisonai checkpoint delete # Delete all checkpoints
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import os
|
|
15
|
+
import asyncio
|
|
16
|
+
from typing import Optional, List
|
|
17
|
+
from dataclasses import dataclass
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class CheckpointsHandler:
|
|
21
|
+
"""
|
|
22
|
+
Handler for checkpoints CLI commands.
|
|
23
|
+
|
|
24
|
+
Provides functionality to:
|
|
25
|
+
- Save checkpoints before file modifications
|
|
26
|
+
- Restore workspace to previous checkpoints
|
|
27
|
+
- View diffs between checkpoints
|
|
28
|
+
- List and manage checkpoints
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def __init__(self, workspace_dir: Optional[str] = None, verbose: bool = False,
|
|
32
|
+
storage_dir: Optional[str] = None):
|
|
33
|
+
self.workspace_dir = workspace_dir or os.getcwd()
|
|
34
|
+
self.verbose = verbose
|
|
35
|
+
self.storage_dir = storage_dir
|
|
36
|
+
self._service = None
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
def feature_name(self) -> str:
|
|
40
|
+
return "checkpoints"
|
|
41
|
+
|
|
42
|
+
async def _get_service(self):
|
|
43
|
+
"""Lazy load the checkpoint service."""
|
|
44
|
+
if self._service is None:
|
|
45
|
+
from praisonaiagents.checkpoints import CheckpointService
|
|
46
|
+
kwargs = {"workspace_dir": self.workspace_dir}
|
|
47
|
+
if self.storage_dir:
|
|
48
|
+
kwargs["storage_dir"] = self.storage_dir
|
|
49
|
+
self._service = CheckpointService(**kwargs)
|
|
50
|
+
await self._service.initialize()
|
|
51
|
+
return self._service
|
|
52
|
+
|
|
53
|
+
async def save(self, message: str, allow_empty: bool = False, quiet: bool = False) -> bool:
|
|
54
|
+
"""
|
|
55
|
+
Save a checkpoint.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
message: Checkpoint message
|
|
59
|
+
allow_empty: Allow checkpoint even if no changes
|
|
60
|
+
quiet: Suppress success/error console output (for auto-checkpointing
|
|
61
|
+
in machine-readable run modes)
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
True if successful
|
|
65
|
+
"""
|
|
66
|
+
service = await self._get_service()
|
|
67
|
+
result = await service.save(message, allow_empty=allow_empty)
|
|
68
|
+
|
|
69
|
+
if result.success:
|
|
70
|
+
if not quiet:
|
|
71
|
+
self._print_success(f"Checkpoint saved: {result.checkpoint.short_id}")
|
|
72
|
+
self._print_info(f"Message: {result.checkpoint.message}")
|
|
73
|
+
return True
|
|
74
|
+
else:
|
|
75
|
+
if not quiet:
|
|
76
|
+
self._print_error(f"Failed to save checkpoint: {result.error}")
|
|
77
|
+
return False
|
|
78
|
+
|
|
79
|
+
async def restore(self, checkpoint_id: str) -> bool:
|
|
80
|
+
"""
|
|
81
|
+
Restore to a checkpoint.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
checkpoint_id: Checkpoint ID to restore
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
True if successful
|
|
88
|
+
"""
|
|
89
|
+
service = await self._get_service()
|
|
90
|
+
result = await service.restore(checkpoint_id)
|
|
91
|
+
|
|
92
|
+
if result.success:
|
|
93
|
+
self._print_success(f"Restored to checkpoint: {result.checkpoint.short_id}")
|
|
94
|
+
return True
|
|
95
|
+
else:
|
|
96
|
+
self._print_error(f"Failed to restore: {result.error}")
|
|
97
|
+
return False
|
|
98
|
+
|
|
99
|
+
async def list_checkpoints(self, limit: int = 20) -> List[dict]:
|
|
100
|
+
"""
|
|
101
|
+
List all checkpoints.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
limit: Maximum number to show
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
List of checkpoint dictionaries
|
|
108
|
+
"""
|
|
109
|
+
service = await self._get_service()
|
|
110
|
+
checkpoints = await service.list_checkpoints(limit=limit)
|
|
111
|
+
|
|
112
|
+
if not checkpoints:
|
|
113
|
+
self._print_info("No checkpoints found")
|
|
114
|
+
return []
|
|
115
|
+
|
|
116
|
+
self._print_checkpoints_table(checkpoints)
|
|
117
|
+
return [cp.to_dict() for cp in checkpoints]
|
|
118
|
+
|
|
119
|
+
def _print_checkpoints_table(self, checkpoints):
|
|
120
|
+
"""Print checkpoints in table format."""
|
|
121
|
+
try:
|
|
122
|
+
from rich.console import Console
|
|
123
|
+
from rich.table import Table
|
|
124
|
+
|
|
125
|
+
console = Console()
|
|
126
|
+
table = Table(title="Checkpoints")
|
|
127
|
+
table.add_column("ID", style="cyan")
|
|
128
|
+
table.add_column("Message", style="green")
|
|
129
|
+
table.add_column("Timestamp", style="yellow")
|
|
130
|
+
|
|
131
|
+
for cp in checkpoints:
|
|
132
|
+
table.add_row(
|
|
133
|
+
cp.short_id,
|
|
134
|
+
cp.message[:50] + "..." if len(cp.message) > 50 else cp.message,
|
|
135
|
+
cp.timestamp.strftime("%Y-%m-%d %H:%M:%S")
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
console.print(table)
|
|
139
|
+
except ImportError:
|
|
140
|
+
print("Checkpoints:")
|
|
141
|
+
for cp in checkpoints:
|
|
142
|
+
print(f" {cp.short_id} - {cp.message} ({cp.timestamp})")
|
|
143
|
+
|
|
144
|
+
async def diff(
|
|
145
|
+
self,
|
|
146
|
+
from_id: Optional[str] = None,
|
|
147
|
+
to_id: Optional[str] = None
|
|
148
|
+
) -> dict:
|
|
149
|
+
"""
|
|
150
|
+
Show diff between checkpoints.
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
from_id: Starting checkpoint (default: previous)
|
|
154
|
+
to_id: Ending checkpoint (default: current)
|
|
155
|
+
|
|
156
|
+
Returns:
|
|
157
|
+
Diff dictionary
|
|
158
|
+
"""
|
|
159
|
+
service = await self._get_service()
|
|
160
|
+
diff = await service.diff(from_id, to_id)
|
|
161
|
+
|
|
162
|
+
self._print_diff(diff)
|
|
163
|
+
return diff.to_dict()
|
|
164
|
+
|
|
165
|
+
def _print_diff(self, diff):
|
|
166
|
+
"""Print diff in a readable format."""
|
|
167
|
+
try:
|
|
168
|
+
from rich.console import Console
|
|
169
|
+
from rich.table import Table
|
|
170
|
+
|
|
171
|
+
console = Console()
|
|
172
|
+
|
|
173
|
+
if not diff.files:
|
|
174
|
+
console.print("[yellow]No changes[/yellow]")
|
|
175
|
+
return
|
|
176
|
+
|
|
177
|
+
console.print(f"\n[bold]Changes from {diff.from_checkpoint} to {diff.to_checkpoint or 'working directory'}[/bold]\n")
|
|
178
|
+
|
|
179
|
+
table = Table()
|
|
180
|
+
table.add_column("Status", style="cyan")
|
|
181
|
+
table.add_column("File", style="white")
|
|
182
|
+
table.add_column("+", style="green")
|
|
183
|
+
table.add_column("-", style="red")
|
|
184
|
+
|
|
185
|
+
for f in diff.files:
|
|
186
|
+
status_color = {
|
|
187
|
+
"added": "green",
|
|
188
|
+
"modified": "yellow",
|
|
189
|
+
"deleted": "red"
|
|
190
|
+
}.get(f.status, "white")
|
|
191
|
+
|
|
192
|
+
table.add_row(
|
|
193
|
+
f"[{status_color}]{f.status}[/{status_color}]",
|
|
194
|
+
f.path,
|
|
195
|
+
str(f.additions),
|
|
196
|
+
str(f.deletions)
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
console.print(table)
|
|
200
|
+
console.print(f"\n[green]+{diff.total_additions}[/green] [red]-{diff.total_deletions}[/red]")
|
|
201
|
+
except ImportError:
|
|
202
|
+
print(f"Changes from {diff.from_checkpoint} to {diff.to_checkpoint or 'working directory'}:")
|
|
203
|
+
for f in diff.files:
|
|
204
|
+
print(f" {f.status}: {f.path} (+{f.additions}/-{f.deletions})")
|
|
205
|
+
|
|
206
|
+
async def delete_all(self) -> bool:
|
|
207
|
+
"""
|
|
208
|
+
Delete all checkpoints.
|
|
209
|
+
|
|
210
|
+
Returns:
|
|
211
|
+
True if successful
|
|
212
|
+
"""
|
|
213
|
+
service = await self._get_service()
|
|
214
|
+
result = await service.delete_all()
|
|
215
|
+
|
|
216
|
+
if result:
|
|
217
|
+
self._print_success("All checkpoints deleted")
|
|
218
|
+
return True
|
|
219
|
+
else:
|
|
220
|
+
self._print_error("Failed to delete checkpoints")
|
|
221
|
+
return False
|
|
222
|
+
|
|
223
|
+
def _print_success(self, message: str):
|
|
224
|
+
"""Print success message."""
|
|
225
|
+
try:
|
|
226
|
+
from rich.console import Console
|
|
227
|
+
Console().print(f"[green]✓[/green] {message}")
|
|
228
|
+
except ImportError:
|
|
229
|
+
print(f"✓ {message}")
|
|
230
|
+
|
|
231
|
+
def _print_error(self, message: str):
|
|
232
|
+
"""Print error message."""
|
|
233
|
+
try:
|
|
234
|
+
from rich.console import Console
|
|
235
|
+
Console().print(f"[red]✗[/red] {message}")
|
|
236
|
+
except ImportError:
|
|
237
|
+
print(f"✗ {message}")
|
|
238
|
+
|
|
239
|
+
def _print_info(self, message: str):
|
|
240
|
+
"""Print info message."""
|
|
241
|
+
try:
|
|
242
|
+
from rich.console import Console
|
|
243
|
+
Console().print(f"[dim]{message}[/dim]")
|
|
244
|
+
except ImportError:
|
|
245
|
+
print(message)
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def handle_checkpoint_command(args: List[str], workspace_dir: Optional[str] = None, verbose: bool = False):
|
|
249
|
+
"""
|
|
250
|
+
Handle checkpoint CLI commands.
|
|
251
|
+
|
|
252
|
+
Usage:
|
|
253
|
+
praisonai checkpoint save <message> # Save a checkpoint
|
|
254
|
+
praisonai checkpoint restore <id> # Restore to a checkpoint
|
|
255
|
+
praisonai checkpoint list [--limit N] # List checkpoints
|
|
256
|
+
praisonai checkpoint diff [from] [to] # Show diff
|
|
257
|
+
praisonai checkpoint delete # Delete all checkpoints
|
|
258
|
+
"""
|
|
259
|
+
handler = CheckpointsHandler(workspace_dir=workspace_dir, verbose=verbose)
|
|
260
|
+
|
|
261
|
+
if not args:
|
|
262
|
+
print("Usage: praisonai checkpoint <command> [options]")
|
|
263
|
+
print("\nCommands:")
|
|
264
|
+
print(" save <message> Save a checkpoint with message")
|
|
265
|
+
print(" restore <id> Restore to a checkpoint")
|
|
266
|
+
print(" list [--limit N] List checkpoints (default: 20)")
|
|
267
|
+
print(" diff [from] [to] Show diff between checkpoints")
|
|
268
|
+
print(" delete Delete all checkpoints")
|
|
269
|
+
return
|
|
270
|
+
|
|
271
|
+
command = args[0]
|
|
272
|
+
|
|
273
|
+
if command == "save":
|
|
274
|
+
if len(args) < 2:
|
|
275
|
+
print("Usage: praisonai checkpoint save <message>")
|
|
276
|
+
return
|
|
277
|
+
|
|
278
|
+
message = " ".join(args[1:])
|
|
279
|
+
allow_empty = "--allow-empty" in args
|
|
280
|
+
if allow_empty:
|
|
281
|
+
message = message.replace("--allow-empty", "").strip()
|
|
282
|
+
|
|
283
|
+
asyncio.run(handler.save(message, allow_empty=allow_empty))
|
|
284
|
+
|
|
285
|
+
elif command == "restore":
|
|
286
|
+
if len(args) < 2:
|
|
287
|
+
print("Usage: praisonai checkpoint restore <checkpoint_id>")
|
|
288
|
+
return
|
|
289
|
+
|
|
290
|
+
asyncio.run(handler.restore(args[1]))
|
|
291
|
+
|
|
292
|
+
elif command == "list":
|
|
293
|
+
limit = 20
|
|
294
|
+
if "--limit" in args:
|
|
295
|
+
idx = args.index("--limit")
|
|
296
|
+
if idx + 1 < len(args):
|
|
297
|
+
try:
|
|
298
|
+
limit = int(args[idx + 1])
|
|
299
|
+
except ValueError:
|
|
300
|
+
pass
|
|
301
|
+
|
|
302
|
+
asyncio.run(handler.list_checkpoints(limit=limit))
|
|
303
|
+
|
|
304
|
+
elif command == "diff":
|
|
305
|
+
from_id = args[1] if len(args) > 1 else None
|
|
306
|
+
to_id = args[2] if len(args) > 2 else None
|
|
307
|
+
|
|
308
|
+
asyncio.run(handler.diff(from_id, to_id))
|
|
309
|
+
|
|
310
|
+
elif command == "delete":
|
|
311
|
+
# Confirm deletion
|
|
312
|
+
try:
|
|
313
|
+
from rich.console import Console
|
|
314
|
+
from rich.prompt import Confirm
|
|
315
|
+
|
|
316
|
+
console = Console()
|
|
317
|
+
if Confirm.ask("[yellow]Delete all checkpoints?[/yellow]"):
|
|
318
|
+
asyncio.run(handler.delete_all())
|
|
319
|
+
else:
|
|
320
|
+
console.print("[dim]Cancelled[/dim]")
|
|
321
|
+
except ImportError:
|
|
322
|
+
response = input("Delete all checkpoints? [y/N] ")
|
|
323
|
+
if response.lower() == 'y':
|
|
324
|
+
asyncio.run(handler.delete_all())
|
|
325
|
+
else:
|
|
326
|
+
print("Cancelled")
|
|
327
|
+
|
|
328
|
+
elif command == "help" or command == "--help":
|
|
329
|
+
print("Checkpoint CLI Commands:")
|
|
330
|
+
print("\n praisonai checkpoint save <message>")
|
|
331
|
+
print(" Save a checkpoint with the given message")
|
|
332
|
+
print(" Options: --allow-empty Allow checkpoint with no changes")
|
|
333
|
+
print("\n praisonai checkpoint restore <id>")
|
|
334
|
+
print(" Restore workspace to a specific checkpoint")
|
|
335
|
+
print("\n praisonai checkpoint list [--limit N]")
|
|
336
|
+
print(" List all checkpoints (default limit: 20)")
|
|
337
|
+
print("\n praisonai checkpoint diff [from_id] [to_id]")
|
|
338
|
+
print(" Show diff between checkpoints")
|
|
339
|
+
print(" If no IDs given, shows diff from last checkpoint to current")
|
|
340
|
+
print("\n praisonai checkpoint delete")
|
|
341
|
+
print(" Delete all checkpoints for this workspace")
|
|
342
|
+
|
|
343
|
+
else:
|
|
344
|
+
print(f"Unknown command: {command}")
|
|
345
|
+
print("Use 'praisonai checkpoint help' for available commands")
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CLI Profiler - Unified profiling for terminal-native CLI commands.
|
|
3
|
+
|
|
4
|
+
Provides --profile and --profile-deep flags for eligible commands:
|
|
5
|
+
- praisonai "prompt"
|
|
6
|
+
- praisonai chat
|
|
7
|
+
- praisonai chat "prompt"
|
|
8
|
+
- praisonai code
|
|
9
|
+
- praisonai code "prompt"
|
|
10
|
+
- praisonai run agents.yaml
|
|
11
|
+
|
|
12
|
+
NOT supported for:
|
|
13
|
+
- praisonai ui ... (browser-based)
|
|
14
|
+
- praisonai tui (Textual TUI)
|
|
15
|
+
- praisonai call (long-running server)
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import time
|
|
19
|
+
from dataclasses import dataclass
|
|
20
|
+
from typing import Any, Dict, Optional
|
|
21
|
+
|
|
22
|
+
import typer
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# Commands that support profiling
|
|
26
|
+
PROFILING_ELIGIBLE_COMMANDS = frozenset({
|
|
27
|
+
"chat",
|
|
28
|
+
"code",
|
|
29
|
+
"run",
|
|
30
|
+
# Direct prompt is handled separately in main.py
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
# Commands that explicitly reject profiling
|
|
34
|
+
PROFILING_INELIGIBLE_COMMANDS = frozenset({
|
|
35
|
+
"ui",
|
|
36
|
+
"tui",
|
|
37
|
+
"call",
|
|
38
|
+
"realtime",
|
|
39
|
+
"serve",
|
|
40
|
+
"schedule",
|
|
41
|
+
"acp",
|
|
42
|
+
"mcp",
|
|
43
|
+
"lsp",
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass
|
|
48
|
+
class CLIProfileConfig:
|
|
49
|
+
"""Configuration for CLI profiling."""
|
|
50
|
+
enabled: bool = False
|
|
51
|
+
deep: bool = False
|
|
52
|
+
output_format: str = "text" # text or json
|
|
53
|
+
save_path: Optional[str] = None
|
|
54
|
+
|
|
55
|
+
@classmethod
|
|
56
|
+
def from_flags(cls, profile: bool = False, profile_deep: bool = False) -> 'CLIProfileConfig':
|
|
57
|
+
"""Create config from CLI flags."""
|
|
58
|
+
return cls(
|
|
59
|
+
enabled=profile or profile_deep,
|
|
60
|
+
deep=profile_deep,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@dataclass
|
|
65
|
+
class CLIProfileResult:
|
|
66
|
+
"""Result from CLI profiling."""
|
|
67
|
+
import_time_ms: float = 0.0
|
|
68
|
+
agent_init_time_ms: float = 0.0
|
|
69
|
+
execution_time_ms: float = 0.0
|
|
70
|
+
total_time_ms: float = 0.0
|
|
71
|
+
response: str = ""
|
|
72
|
+
|
|
73
|
+
# Deep profiling data (only if deep=True)
|
|
74
|
+
function_stats: Optional[list] = None
|
|
75
|
+
call_graph: Optional[dict] = None
|
|
76
|
+
|
|
77
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
78
|
+
"""Convert to dictionary."""
|
|
79
|
+
result = {
|
|
80
|
+
"import_time_ms": self.import_time_ms,
|
|
81
|
+
"agent_init_time_ms": self.agent_init_time_ms,
|
|
82
|
+
"execution_time_ms": self.execution_time_ms,
|
|
83
|
+
"total_time_ms": self.total_time_ms,
|
|
84
|
+
}
|
|
85
|
+
if self.function_stats:
|
|
86
|
+
result["function_stats"] = self.function_stats
|
|
87
|
+
if self.call_graph:
|
|
88
|
+
result["call_graph"] = self.call_graph
|
|
89
|
+
return result
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def print_profile_report(result: CLIProfileResult, config: CLIProfileConfig):
|
|
93
|
+
"""Print profiling report to console."""
|
|
94
|
+
try:
|
|
95
|
+
from rich.console import Console
|
|
96
|
+
from rich.table import Table
|
|
97
|
+
from rich.panel import Panel
|
|
98
|
+
console = Console()
|
|
99
|
+
except ImportError:
|
|
100
|
+
# Fallback to simple output
|
|
101
|
+
print("\n─── Profiling ───")
|
|
102
|
+
print(f"Import: {result.import_time_ms:.1f}ms")
|
|
103
|
+
print(f"Agent setup: {result.agent_init_time_ms:.1f}ms")
|
|
104
|
+
print(f"Execution: {result.execution_time_ms:.1f}ms")
|
|
105
|
+
print(f"Total: {result.total_time_ms:.1f}ms")
|
|
106
|
+
return
|
|
107
|
+
|
|
108
|
+
# Create timing table
|
|
109
|
+
table = Table(show_header=False, box=None, padding=(0, 1))
|
|
110
|
+
table.add_column("Phase", style="cyan")
|
|
111
|
+
table.add_column("Time", style="green", justify="right")
|
|
112
|
+
|
|
113
|
+
table.add_row("Import", f"{result.import_time_ms:.1f}ms")
|
|
114
|
+
table.add_row("Agent setup", f"{result.agent_init_time_ms:.1f}ms")
|
|
115
|
+
table.add_row("Execution", f"{result.execution_time_ms:.1f}ms")
|
|
116
|
+
table.add_row("─" * 12, "─" * 10)
|
|
117
|
+
table.add_row("Total", f"{result.total_time_ms:.1f}ms")
|
|
118
|
+
|
|
119
|
+
console.print("\n")
|
|
120
|
+
console.print(Panel(table, title="[bold]Profiling[/bold]", border_style="dim"))
|
|
121
|
+
|
|
122
|
+
# Deep profiling output
|
|
123
|
+
if config.deep and result.function_stats:
|
|
124
|
+
console.print("\n[bold yellow]⚠️ Deep profiling enabled - overhead included in measurements[/bold yellow]")
|
|
125
|
+
|
|
126
|
+
func_table = Table(title="Top Functions by Cumulative Time")
|
|
127
|
+
func_table.add_column("Function", style="cyan")
|
|
128
|
+
func_table.add_column("Calls", justify="right")
|
|
129
|
+
func_table.add_column("Cumul (ms)", justify="right", style="green")
|
|
130
|
+
|
|
131
|
+
for func in result.function_stats[:15]:
|
|
132
|
+
name = func.get("name", "")[:40]
|
|
133
|
+
calls = func.get("calls", 0)
|
|
134
|
+
cumtime = func.get("cumulative_time_ms", 0)
|
|
135
|
+
func_table.add_row(name, str(calls), f"{cumtime:.2f}")
|
|
136
|
+
|
|
137
|
+
console.print(func_table)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def reject_profiling_for_command(command_name: str):
|
|
141
|
+
"""Raise error if profiling is attempted on ineligible command."""
|
|
142
|
+
if command_name in PROFILING_INELIGIBLE_COMMANDS:
|
|
143
|
+
raise typer.BadParameter(
|
|
144
|
+
f"Profiling is not supported for '{command_name}' command.\n"
|
|
145
|
+
f"Profiling is only available for terminal-native execution commands:\n"
|
|
146
|
+
f" praisonai \"prompt\" --profile\n"
|
|
147
|
+
f" praisonai chat \"prompt\" --profile\n"
|
|
148
|
+
f" praisonai code \"prompt\" --profile\n"
|
|
149
|
+
f" praisonai run agents.yaml --profile"
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class CLIProfiler:
|
|
154
|
+
"""
|
|
155
|
+
CLI Profiler for wrapping command execution.
|
|
156
|
+
|
|
157
|
+
Usage:
|
|
158
|
+
profiler = CLIProfiler(config)
|
|
159
|
+
with profiler.profile():
|
|
160
|
+
# execution code
|
|
161
|
+
profiler.print_report()
|
|
162
|
+
"""
|
|
163
|
+
|
|
164
|
+
def __init__(self, config: CLIProfileConfig):
|
|
165
|
+
self.config = config
|
|
166
|
+
self._result = CLIProfileResult()
|
|
167
|
+
self._start_time: float = 0.0
|
|
168
|
+
self._cprofile = None
|
|
169
|
+
|
|
170
|
+
# Phase timestamps
|
|
171
|
+
self._import_start: float = 0.0
|
|
172
|
+
self._import_end: float = 0.0
|
|
173
|
+
self._init_start: float = 0.0
|
|
174
|
+
self._init_end: float = 0.0
|
|
175
|
+
self._exec_start: float = 0.0
|
|
176
|
+
self._exec_end: float = 0.0
|
|
177
|
+
|
|
178
|
+
def start(self):
|
|
179
|
+
"""Start profiling."""
|
|
180
|
+
if not self.config.enabled:
|
|
181
|
+
return
|
|
182
|
+
|
|
183
|
+
self._start_time = time.perf_counter()
|
|
184
|
+
|
|
185
|
+
if self.config.deep:
|
|
186
|
+
import cProfile
|
|
187
|
+
self._cprofile = cProfile.Profile()
|
|
188
|
+
self._cprofile.enable()
|
|
189
|
+
|
|
190
|
+
def stop(self):
|
|
191
|
+
"""Stop profiling and compute results."""
|
|
192
|
+
if not self.config.enabled:
|
|
193
|
+
return
|
|
194
|
+
|
|
195
|
+
end_time = time.perf_counter()
|
|
196
|
+
|
|
197
|
+
if self._cprofile:
|
|
198
|
+
self._cprofile.disable()
|
|
199
|
+
self._extract_cprofile_stats()
|
|
200
|
+
|
|
201
|
+
self._result.total_time_ms = (end_time - self._start_time) * 1000
|
|
202
|
+
|
|
203
|
+
def mark_import_start(self):
|
|
204
|
+
"""Mark start of import phase."""
|
|
205
|
+
if self.config.enabled:
|
|
206
|
+
self._import_start = time.perf_counter()
|
|
207
|
+
|
|
208
|
+
def mark_import_end(self):
|
|
209
|
+
"""Mark end of import phase."""
|
|
210
|
+
if self.config.enabled:
|
|
211
|
+
self._import_end = time.perf_counter()
|
|
212
|
+
self._result.import_time_ms = (self._import_end - self._import_start) * 1000
|
|
213
|
+
|
|
214
|
+
def mark_init_start(self):
|
|
215
|
+
"""Mark start of agent initialization."""
|
|
216
|
+
if self.config.enabled:
|
|
217
|
+
self._init_start = time.perf_counter()
|
|
218
|
+
|
|
219
|
+
def mark_init_end(self):
|
|
220
|
+
"""Mark end of agent initialization."""
|
|
221
|
+
if self.config.enabled:
|
|
222
|
+
self._init_end = time.perf_counter()
|
|
223
|
+
self._result.agent_init_time_ms = (self._init_end - self._init_start) * 1000
|
|
224
|
+
|
|
225
|
+
def mark_exec_start(self):
|
|
226
|
+
"""Mark start of execution."""
|
|
227
|
+
if self.config.enabled:
|
|
228
|
+
self._exec_start = time.perf_counter()
|
|
229
|
+
|
|
230
|
+
def mark_exec_end(self):
|
|
231
|
+
"""Mark end of execution."""
|
|
232
|
+
if self.config.enabled:
|
|
233
|
+
self._exec_end = time.perf_counter()
|
|
234
|
+
self._result.execution_time_ms = (self._exec_end - self._exec_start) * 1000
|
|
235
|
+
|
|
236
|
+
def set_response(self, response: str):
|
|
237
|
+
"""Set the response for the profile result."""
|
|
238
|
+
self._result.response = response
|
|
239
|
+
|
|
240
|
+
def _extract_cprofile_stats(self):
|
|
241
|
+
"""Extract statistics from cProfile."""
|
|
242
|
+
if not self._cprofile:
|
|
243
|
+
return
|
|
244
|
+
|
|
245
|
+
import pstats
|
|
246
|
+
|
|
247
|
+
stats = pstats.Stats(self._cprofile)
|
|
248
|
+
stats.sort_stats("cumulative")
|
|
249
|
+
|
|
250
|
+
function_stats = []
|
|
251
|
+
for (filename, line, name), (cc, nc, tt, ct, callers) in stats.stats.items():
|
|
252
|
+
if len(function_stats) >= 100:
|
|
253
|
+
break
|
|
254
|
+
function_stats.append({
|
|
255
|
+
"name": name,
|
|
256
|
+
"file": filename,
|
|
257
|
+
"line": line,
|
|
258
|
+
"calls": nc,
|
|
259
|
+
"total_time_ms": tt * 1000,
|
|
260
|
+
"cumulative_time_ms": ct * 1000,
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
function_stats.sort(key=lambda x: x["cumulative_time_ms"], reverse=True)
|
|
264
|
+
self._result.function_stats = function_stats[:30]
|
|
265
|
+
|
|
266
|
+
def get_result(self) -> CLIProfileResult:
|
|
267
|
+
"""Get the profiling result."""
|
|
268
|
+
return self._result
|
|
269
|
+
|
|
270
|
+
def print_report(self):
|
|
271
|
+
"""Print the profiling report."""
|
|
272
|
+
if self.config.enabled:
|
|
273
|
+
print_profile_report(self._result, self.config)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def run_with_profiling(
|
|
277
|
+
prompt: str,
|
|
278
|
+
config: CLIProfileConfig,
|
|
279
|
+
model: Optional[str] = None,
|
|
280
|
+
verbose: bool = False,
|
|
281
|
+
tools: Optional[str] = None,
|
|
282
|
+
) -> tuple:
|
|
283
|
+
"""
|
|
284
|
+
Run a prompt with profiling enabled.
|
|
285
|
+
|
|
286
|
+
Returns:
|
|
287
|
+
Tuple of (response, CLIProfileResult)
|
|
288
|
+
"""
|
|
289
|
+
profiler = CLIProfiler(config)
|
|
290
|
+
profiler.start()
|
|
291
|
+
|
|
292
|
+
# Import phase
|
|
293
|
+
profiler.mark_import_start()
|
|
294
|
+
try:
|
|
295
|
+
from praisonaiagents import Agent
|
|
296
|
+
except ImportError:
|
|
297
|
+
raise ImportError("praisonaiagents not installed. Install with: pip install praisonaiagents")
|
|
298
|
+
profiler.mark_import_end()
|
|
299
|
+
|
|
300
|
+
# Agent initialization phase
|
|
301
|
+
profiler.mark_init_start()
|
|
302
|
+
agent_config = {
|
|
303
|
+
"name": "ProfiledAgent",
|
|
304
|
+
"role": "Assistant",
|
|
305
|
+
"goal": "Complete the task",
|
|
306
|
+
"verbose": verbose,
|
|
307
|
+
}
|
|
308
|
+
if model:
|
|
309
|
+
agent_config["llm"] = model
|
|
310
|
+
|
|
311
|
+
agent = Agent(**agent_config)
|
|
312
|
+
profiler.mark_init_end()
|
|
313
|
+
|
|
314
|
+
# Execution phase
|
|
315
|
+
profiler.mark_exec_start()
|
|
316
|
+
response = agent.start(prompt)
|
|
317
|
+
profiler.mark_exec_end()
|
|
318
|
+
|
|
319
|
+
profiler.stop()
|
|
320
|
+
profiler.set_response(str(response) if response else "")
|
|
321
|
+
|
|
322
|
+
return response, profiler.get_result()
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
# Export all
|
|
326
|
+
__all__ = [
|
|
327
|
+
"CLIProfileConfig",
|
|
328
|
+
"CLIProfileResult",
|
|
329
|
+
"CLIProfiler",
|
|
330
|
+
"PROFILING_ELIGIBLE_COMMANDS",
|
|
331
|
+
"PROFILING_INELIGIBLE_COMMANDS",
|
|
332
|
+
"print_profile_report",
|
|
333
|
+
"reject_profiling_for_command",
|
|
334
|
+
"run_with_profiling",
|
|
335
|
+
]
|