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.
Files changed (309) hide show
  1. praisonai_code/__init__.py +17 -0
  2. praisonai_code/cli/__init__.py +12 -0
  3. praisonai_code/cli/_forward_shim.py +10 -0
  4. praisonai_code/cli/_paths.py +88 -0
  5. praisonai_code/cli/_warnings.py +58 -0
  6. praisonai_code/cli/app.py +757 -0
  7. praisonai_code/cli/approval_backend.py +272 -0
  8. praisonai_code/cli/branding.py +94 -0
  9. praisonai_code/cli/commands/__init__.py +114 -0
  10. praisonai_code/cli/commands/acp.py +80 -0
  11. praisonai_code/cli/commands/agent.py +116 -0
  12. praisonai_code/cli/commands/agents.py +80 -0
  13. praisonai_code/cli/commands/app.py +139 -0
  14. praisonai_code/cli/commands/attach.py +95 -0
  15. praisonai_code/cli/commands/audit.py +102 -0
  16. praisonai_code/cli/commands/auth.py +508 -0
  17. praisonai_code/cli/commands/batch.py +848 -0
  18. praisonai_code/cli/commands/benchmark.py +286 -0
  19. praisonai_code/cli/commands/browser.py +299 -0
  20. praisonai_code/cli/commands/call.py +45 -0
  21. praisonai_code/cli/commands/chat.py +332 -0
  22. praisonai_code/cli/commands/checkpoint.py +170 -0
  23. praisonai_code/cli/commands/code.py +276 -0
  24. praisonai_code/cli/commands/command.py +114 -0
  25. praisonai_code/cli/commands/commit.py +47 -0
  26. praisonai_code/cli/commands/completion.py +333 -0
  27. praisonai_code/cli/commands/config.py +681 -0
  28. praisonai_code/cli/commands/context.py +414 -0
  29. praisonai_code/cli/commands/daemon.py +203 -0
  30. praisonai_code/cli/commands/debug.py +142 -0
  31. praisonai_code/cli/commands/deploy.py +71 -0
  32. praisonai_code/cli/commands/diag.py +55 -0
  33. praisonai_code/cli/commands/docs.py +1575 -0
  34. praisonai_code/cli/commands/doctor.py +332 -0
  35. praisonai_code/cli/commands/endpoints.py +51 -0
  36. praisonai_code/cli/commands/environment.py +179 -0
  37. praisonai_code/cli/commands/eval.py +131 -0
  38. praisonai_code/cli/commands/examples.py +953 -0
  39. praisonai_code/cli/commands/flow.py +436 -0
  40. praisonai_code/cli/commands/github.py +752 -0
  41. praisonai_code/cli/commands/hooks.py +74 -0
  42. praisonai_code/cli/commands/init.py +174 -0
  43. praisonai_code/cli/commands/knowledge.py +440 -0
  44. praisonai_code/cli/commands/langextract.py +120 -0
  45. praisonai_code/cli/commands/langfuse.py +984 -0
  46. praisonai_code/cli/commands/loop.py +211 -0
  47. praisonai_code/cli/commands/lsp.py +112 -0
  48. praisonai_code/cli/commands/managed.py +659 -0
  49. praisonai_code/cli/commands/mcp.py +763 -0
  50. praisonai_code/cli/commands/memory.py +298 -0
  51. praisonai_code/cli/commands/models.py +264 -0
  52. praisonai_code/cli/commands/n8n.py +326 -0
  53. praisonai_code/cli/commands/obs.py +19 -0
  54. praisonai_code/cli/commands/package.py +76 -0
  55. praisonai_code/cli/commands/paths.py +106 -0
  56. praisonai_code/cli/commands/permissions.py +272 -0
  57. praisonai_code/cli/commands/plugins.py +609 -0
  58. praisonai_code/cli/commands/port.py +530 -0
  59. praisonai_code/cli/commands/profile.py +466 -0
  60. praisonai_code/cli/commands/publish.py +193 -0
  61. praisonai_code/cli/commands/rag.py +913 -0
  62. praisonai_code/cli/commands/realtime.py +52 -0
  63. praisonai_code/cli/commands/recipe.py +684 -0
  64. praisonai_code/cli/commands/registry.py +59 -0
  65. praisonai_code/cli/commands/replay.py +830 -0
  66. praisonai_code/cli/commands/research.py +49 -0
  67. praisonai_code/cli/commands/retrieval.py +377 -0
  68. praisonai_code/cli/commands/rules.py +71 -0
  69. praisonai_code/cli/commands/run.py +1573 -0
  70. praisonai_code/cli/commands/sandbox.py +371 -0
  71. praisonai_code/cli/commands/schedule.py +529 -0
  72. praisonai_code/cli/commands/serve.py +690 -0
  73. praisonai_code/cli/commands/session.py +450 -0
  74. praisonai_code/cli/commands/setup.py +174 -0
  75. praisonai_code/cli/commands/skills.py +545 -0
  76. praisonai_code/cli/commands/standardise.py +711 -0
  77. praisonai_code/cli/commands/templates.py +54 -0
  78. praisonai_code/cli/commands/test.py +558 -0
  79. praisonai_code/cli/commands/todo.py +74 -0
  80. praisonai_code/cli/commands/tools.py +205 -0
  81. praisonai_code/cli/commands/traces.py +145 -0
  82. praisonai_code/cli/commands/tracker.py +852 -0
  83. praisonai_code/cli/commands/train.py +613 -0
  84. praisonai_code/cli/commands/ui.py +172 -0
  85. praisonai_code/cli/commands/up.py +354 -0
  86. praisonai_code/cli/commands/validate.py +291 -0
  87. praisonai_code/cli/commands/version.py +101 -0
  88. praisonai_code/cli/commands/workflow.py +97 -0
  89. praisonai_code/cli/config_loader.py +437 -0
  90. praisonai_code/cli/configuration/__init__.py +27 -0
  91. praisonai_code/cli/configuration/config.schema.json +57 -0
  92. praisonai_code/cli/configuration/credentials.py +446 -0
  93. praisonai_code/cli/configuration/loader.py +364 -0
  94. praisonai_code/cli/configuration/model_resolver.py +161 -0
  95. praisonai_code/cli/configuration/oauth.py +389 -0
  96. praisonai_code/cli/configuration/paths.py +224 -0
  97. praisonai_code/cli/configuration/resolver.py +687 -0
  98. praisonai_code/cli/configuration/schema.py +317 -0
  99. praisonai_code/cli/execution/__init__.py +99 -0
  100. praisonai_code/cli/execution/core.py +208 -0
  101. praisonai_code/cli/execution/profiler.py +898 -0
  102. praisonai_code/cli/execution/request.py +85 -0
  103. praisonai_code/cli/execution/result.py +74 -0
  104. praisonai_code/cli/fallback_schema.py +416 -0
  105. praisonai_code/cli/features/__init__.py +278 -0
  106. praisonai_code/cli/features/_endpoint_registry.py +64 -0
  107. praisonai_code/cli/features/_search_registry.py +43 -0
  108. praisonai_code/cli/features/acp.py +236 -0
  109. praisonai_code/cli/features/action_orchestrator.py +576 -0
  110. praisonai_code/cli/features/agent_scheduler.py +773 -0
  111. praisonai_code/cli/features/agent_tools.py +603 -0
  112. praisonai_code/cli/features/agents.py +397 -0
  113. praisonai_code/cli/features/at_mentions.py +471 -0
  114. praisonai_code/cli/features/audit_cli.py +270 -0
  115. praisonai_code/cli/features/auto_memory.py +182 -0
  116. praisonai_code/cli/features/auto_mode.py +552 -0
  117. praisonai_code/cli/features/autonomy_mode.py +546 -0
  118. praisonai_code/cli/features/background.py +356 -0
  119. praisonai_code/cli/features/base.py +168 -0
  120. praisonai_code/cli/features/benchmark.py +1462 -0
  121. praisonai_code/cli/features/capabilities.py +1326 -0
  122. praisonai_code/cli/features/checkpoints.py +345 -0
  123. praisonai_code/cli/features/cli_profiler.py +335 -0
  124. praisonai_code/cli/features/code_intelligence.py +666 -0
  125. praisonai_code/cli/features/compaction.py +294 -0
  126. praisonai_code/cli/features/compare.py +534 -0
  127. praisonai_code/cli/features/config_hierarchy.py +366 -0
  128. praisonai_code/cli/features/context_manager.py +597 -0
  129. praisonai_code/cli/features/cost_tracker.py +514 -0
  130. praisonai_code/cli/features/csv_test_runner.py +736 -0
  131. praisonai_code/cli/features/custom_definitions.py +790 -0
  132. praisonai_code/cli/features/debug.py +810 -0
  133. praisonai_code/cli/features/deploy.py +605 -0
  134. praisonai_code/cli/features/diag.py +289 -0
  135. praisonai_code/cli/features/display_jsonl.py +173 -0
  136. praisonai_code/cli/features/doctor/__init__.py +63 -0
  137. praisonai_code/cli/features/doctor/checks/__init__.py +29 -0
  138. praisonai_code/cli/features/doctor/checks/acp_checks.py +220 -0
  139. praisonai_code/cli/features/doctor/checks/bot_checks.py +340 -0
  140. praisonai_code/cli/features/doctor/checks/config_checks.py +373 -0
  141. praisonai_code/cli/features/doctor/checks/db_checks.py +366 -0
  142. praisonai_code/cli/features/doctor/checks/env_checks.py +637 -0
  143. praisonai_code/cli/features/doctor/checks/gateway_checks.py +387 -0
  144. praisonai_code/cli/features/doctor/checks/lsp_checks.py +231 -0
  145. praisonai_code/cli/features/doctor/checks/mcp_checks.py +367 -0
  146. praisonai_code/cli/features/doctor/checks/memory_checks.py +268 -0
  147. praisonai_code/cli/features/doctor/checks/network_checks.py +251 -0
  148. praisonai_code/cli/features/doctor/checks/obs_checks.py +328 -0
  149. praisonai_code/cli/features/doctor/checks/packaging_checks.py +422 -0
  150. praisonai_code/cli/features/doctor/checks/performance_checks.py +235 -0
  151. praisonai_code/cli/features/doctor/checks/permissions_checks.py +259 -0
  152. praisonai_code/cli/features/doctor/checks/runtime_checks.py +650 -0
  153. praisonai_code/cli/features/doctor/checks/runtime_migration_checks.py +220 -0
  154. praisonai_code/cli/features/doctor/checks/selftest_checks.py +322 -0
  155. praisonai_code/cli/features/doctor/checks/serve_checks.py +426 -0
  156. praisonai_code/cli/features/doctor/checks/skills_checks.py +327 -0
  157. praisonai_code/cli/features/doctor/checks/tools_checks.py +371 -0
  158. praisonai_code/cli/features/doctor/engine.py +266 -0
  159. praisonai_code/cli/features/doctor/formatters.py +377 -0
  160. praisonai_code/cli/features/doctor/handler.py +564 -0
  161. praisonai_code/cli/features/doctor/models.py +276 -0
  162. praisonai_code/cli/features/doctor/registry.py +239 -0
  163. praisonai_code/cli/features/endpoints.py +1016 -0
  164. praisonai_code/cli/features/eval.py +559 -0
  165. praisonai_code/cli/features/examples.py +707 -0
  166. praisonai_code/cli/features/external_agents.py +231 -0
  167. praisonai_code/cli/features/fast_context.py +410 -0
  168. praisonai_code/cli/features/file_history.py +320 -0
  169. praisonai_code/cli/features/flow_display.py +566 -0
  170. praisonai_code/cli/features/git_attribution.py +159 -0
  171. praisonai_code/cli/features/git_integration.py +651 -0
  172. praisonai_code/cli/features/guardrail.py +171 -0
  173. praisonai_code/cli/features/handoff.py +252 -0
  174. praisonai_code/cli/features/hooks.py +583 -0
  175. praisonai_code/cli/features/hybrid_workflow.py +391 -0
  176. praisonai_code/cli/features/image.py +384 -0
  177. praisonai_code/cli/features/interactive_core_headless.py +450 -0
  178. praisonai_code/cli/features/interactive_runtime.py +600 -0
  179. praisonai_code/cli/features/interactive_test_harness.py +537 -0
  180. praisonai_code/cli/features/interactive_tools.py +428 -0
  181. praisonai_code/cli/features/interactive_tui.py +603 -0
  182. praisonai_code/cli/features/job_workflow.py +906 -0
  183. praisonai_code/cli/features/jobs.py +632 -0
  184. praisonai_code/cli/features/knowledge.py +531 -0
  185. praisonai_code/cli/features/knowledge_cli.py +438 -0
  186. praisonai_code/cli/features/lite.py +244 -0
  187. praisonai_code/cli/features/logs.py +200 -0
  188. praisonai_code/cli/features/lsp_cli.py +225 -0
  189. praisonai_code/cli/features/lsp_diagnostics.py +185 -0
  190. praisonai_code/cli/features/mcp.py +344 -0
  191. praisonai_code/cli/features/message_queue.py +587 -0
  192. praisonai_code/cli/features/metrics.py +210 -0
  193. praisonai_code/cli/features/migrate.py +1329 -0
  194. praisonai_code/cli/features/migration_flow.py +463 -0
  195. praisonai_code/cli/features/migration_spec.py +276 -0
  196. praisonai_code/cli/features/n8n.py +703 -0
  197. praisonai_code/cli/features/observability.py +293 -0
  198. praisonai_code/cli/features/ollama.py +361 -0
  199. praisonai_code/cli/features/output_modes.py +155 -0
  200. praisonai_code/cli/features/output_style.py +273 -0
  201. praisonai_code/cli/features/package.py +631 -0
  202. praisonai_code/cli/features/performance.py +308 -0
  203. praisonai_code/cli/features/persistence.py +636 -0
  204. praisonai_code/cli/features/profiler/__init__.py +81 -0
  205. praisonai_code/cli/features/profiler/core.py +558 -0
  206. praisonai_code/cli/features/profiler/optimizations.py +652 -0
  207. praisonai_code/cli/features/profiler/suite.py +386 -0
  208. praisonai_code/cli/features/queue/__init__.py +73 -0
  209. praisonai_code/cli/features/queue/manager.py +435 -0
  210. praisonai_code/cli/features/queue/models.py +289 -0
  211. praisonai_code/cli/features/queue/persistence.py +564 -0
  212. praisonai_code/cli/features/queue/scheduler.py +529 -0
  213. praisonai_code/cli/features/queue/worker.py +400 -0
  214. praisonai_code/cli/features/recipe.py +2187 -0
  215. praisonai_code/cli/features/recipe_creator.py +996 -0
  216. praisonai_code/cli/features/recipe_optimizer.py +1364 -0
  217. praisonai_code/cli/features/recipe_prompts.py +226 -0
  218. praisonai_code/cli/features/registry.py +229 -0
  219. praisonai_code/cli/features/repo_map.py +860 -0
  220. praisonai_code/cli/features/router.py +466 -0
  221. praisonai_code/cli/features/safe_shell.py +427 -0
  222. praisonai_code/cli/features/sandbox_cli.py +283 -0
  223. praisonai_code/cli/features/sandbox_executor.py +536 -0
  224. praisonai_code/cli/features/sdk_knowledge.py +500 -0
  225. praisonai_code/cli/features/session.py +222 -0
  226. praisonai_code/cli/features/session_checkpoints.py +208 -0
  227. praisonai_code/cli/features/setup/__init__.py +9 -0
  228. praisonai_code/cli/features/setup/handler.py +355 -0
  229. praisonai_code/cli/features/setup/templates.py +62 -0
  230. praisonai_code/cli/features/skills.py +940 -0
  231. praisonai_code/cli/features/slash_commands.py +692 -0
  232. praisonai_code/cli/features/telemetry.py +179 -0
  233. praisonai_code/cli/features/templates.py +1390 -0
  234. praisonai_code/cli/features/thinking.py +343 -0
  235. praisonai_code/cli/features/todo.py +334 -0
  236. praisonai_code/cli/features/tools.py +680 -0
  237. praisonai_code/cli/features/tui/__init__.py +83 -0
  238. praisonai_code/cli/features/tui/app.py +871 -0
  239. praisonai_code/cli/features/tui/cli.py +580 -0
  240. praisonai_code/cli/features/tui/config.py +150 -0
  241. praisonai_code/cli/features/tui/debug.py +526 -0
  242. praisonai_code/cli/features/tui/events.py +99 -0
  243. praisonai_code/cli/features/tui/mock_provider.py +328 -0
  244. praisonai_code/cli/features/tui/orchestrator.py +652 -0
  245. praisonai_code/cli/features/tui/screens/__init__.py +50 -0
  246. praisonai_code/cli/features/tui/screens/help.py +157 -0
  247. praisonai_code/cli/features/tui/screens/main.py +568 -0
  248. praisonai_code/cli/features/tui/screens/queue.py +174 -0
  249. praisonai_code/cli/features/tui/screens/session.py +124 -0
  250. praisonai_code/cli/features/tui/screens/settings.py +148 -0
  251. praisonai_code/cli/features/tui/session_store.py +198 -0
  252. praisonai_code/cli/features/tui/widgets/__init__.py +56 -0
  253. praisonai_code/cli/features/tui/widgets/chat.py +263 -0
  254. praisonai_code/cli/features/tui/widgets/command_popup.py +258 -0
  255. praisonai_code/cli/features/tui/widgets/composer.py +292 -0
  256. praisonai_code/cli/features/tui/widgets/file_popup.py +207 -0
  257. praisonai_code/cli/features/tui/widgets/queue_panel.py +223 -0
  258. praisonai_code/cli/features/tui/widgets/status.py +181 -0
  259. praisonai_code/cli/features/tui/widgets/tool_panel.py +307 -0
  260. praisonai_code/cli/features/wizard.py +289 -0
  261. praisonai_code/cli/features/workflow.py +802 -0
  262. praisonai_code/cli/features/yaml_utils.py +321 -0
  263. praisonai_code/cli/interactive/__init__.py +48 -0
  264. praisonai_code/cli/interactive/async_tui.py +1218 -0
  265. praisonai_code/cli/interactive/config.py +139 -0
  266. praisonai_code/cli/interactive/core.py +618 -0
  267. praisonai_code/cli/interactive/events.py +131 -0
  268. praisonai_code/cli/interactive/frontends/__init__.py +31 -0
  269. praisonai_code/cli/interactive/frontends/rich_frontend.py +462 -0
  270. praisonai_code/cli/interactive/frontends/textual_frontend.py +157 -0
  271. praisonai_code/cli/interactive/praison_io.py +502 -0
  272. praisonai_code/cli/interactive/repl.py +297 -0
  273. praisonai_code/cli/interactive/split_tui.py +456 -0
  274. praisonai_code/cli/interactive/tui_app.py +457 -0
  275. praisonai_code/cli/langfuse_client.py +360 -0
  276. praisonai_code/cli/main.py +7421 -0
  277. praisonai_code/cli/output/__init__.py +25 -0
  278. praisonai_code/cli/output/console.py +456 -0
  279. praisonai_code/cli/output/event_bridge.py +191 -0
  280. praisonai_code/cli/schedule_cli.py +54 -0
  281. praisonai_code/cli/schema_provider.py +23 -0
  282. praisonai_code/cli/session/__init__.py +16 -0
  283. praisonai_code/cli/session/resume.py +148 -0
  284. praisonai_code/cli/session/unified.py +548 -0
  285. praisonai_code/cli/state/__init__.py +31 -0
  286. praisonai_code/cli/state/identifiers.py +161 -0
  287. praisonai_code/cli/state/project_sessions.py +383 -0
  288. praisonai_code/cli/state/sessions.py +390 -0
  289. praisonai_code/cli/ui/__init__.py +160 -0
  290. praisonai_code/cli/ui/config.py +46 -0
  291. praisonai_code/cli/ui/events.py +61 -0
  292. praisonai_code/cli/ui/mg_backend.py +342 -0
  293. praisonai_code/cli/ui/plain.py +133 -0
  294. praisonai_code/cli/ui/rich_backend.py +162 -0
  295. praisonai_code/cli/unified_schema.py +655 -0
  296. praisonai_code/cli/utils/env_utils.py +126 -0
  297. praisonai_code/cli/utils/project.py +131 -0
  298. praisonai_code/cli_backends/__init__.py +73 -0
  299. praisonai_code/cli_backends/claude.py +373 -0
  300. praisonai_code/cli_backends/registry.py +113 -0
  301. praisonai_code/runtime/__init__.py +36 -0
  302. praisonai_code/runtime/__main__.py +81 -0
  303. praisonai_code/runtime/client.py +131 -0
  304. praisonai_code/runtime/descriptor.py +209 -0
  305. praisonai_code/runtime/server.py +356 -0
  306. praisonai_code-0.0.1.dist-info/METADATA +80 -0
  307. praisonai_code-0.0.1.dist-info/RECORD +309 -0
  308. praisonai_code-0.0.1.dist-info/WHEEL +5 -0
  309. praisonai_code-0.0.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,466 @@
1
+ """
2
+ Profile command group for PraisonAI CLI.
3
+
4
+ Provides detailed cProfile-based profiling for query execution.
5
+ """
6
+
7
+ import sys
8
+ from typing import Optional
9
+
10
+ import typer
11
+
12
+ app = typer.Typer(help="Performance profiling and diagnostics")
13
+
14
+
15
+ @app.command("query")
16
+ def profile_query(
17
+ prompt: str = typer.Argument(..., help="Prompt to profile"),
18
+ model: Optional[str] = typer.Option(None, "--model", "-m", help="Model to use"),
19
+ stream: bool = typer.Option(False, "--stream/--no-stream", help="Use streaming mode"),
20
+ deep: bool = typer.Option(False, "--deep", help="Enable deep call tracing (higher overhead)"),
21
+ limit: int = typer.Option(30, "--limit", "-n", help="Top N functions to show"),
22
+ sort: str = typer.Option("cumulative", "--sort", "-s", help="Sort by: cumulative or tottime"),
23
+ show_callers: bool = typer.Option(False, "--show-callers", help="Show caller functions"),
24
+ show_callees: bool = typer.Option(False, "--show-callees", help="Show callee functions"),
25
+ save: Optional[str] = typer.Option(None, "--save", help="Save artifacts to path (creates .prof, .txt, .json)"),
26
+ output_format: str = typer.Option("text", "--format", "-f", help="Output format: text or json"),
27
+ ):
28
+ """
29
+ Profile a query execution with detailed timing breakdown.
30
+
31
+ Uses the unified profiling architecture for consistent results across
32
+ CLI direct invocation (--profile) and this command.
33
+
34
+ Shows per-function timing, call graphs, and latency metrics.
35
+
36
+ Examples:
37
+ praisonai profile query "What is 2+2?"
38
+ praisonai profile query "Hello" --limit 20
39
+ praisonai profile query "Test" --deep --show-callers --show-callees
40
+ praisonai profile query "Test" --save ./profile_results
41
+ """
42
+ # Use unified profiler for consistent results
43
+ try:
44
+ from praisonai.cli.execution import ExecutionRequest, Profiler, ProfilerConfig as UnifiedProfilerConfig
45
+ except ImportError as e:
46
+ typer.echo(f"Error: Unified profiler not available: {e}", err=True)
47
+ raise typer.Exit(1)
48
+
49
+ # Warn about deep tracing overhead
50
+ if deep:
51
+ typer.echo("⚠️ Deep call tracing enabled - this adds significant overhead", err=True)
52
+
53
+ # Create unified execution request
54
+ request = ExecutionRequest(
55
+ prompt=prompt,
56
+ agent_name="ProfiledAgent",
57
+ model=model,
58
+ stream=stream,
59
+ )
60
+
61
+ # Create unified profiler config
62
+ config = UnifiedProfilerConfig(
63
+ layer=2 if deep else 1,
64
+ limit=limit,
65
+ sort_by=sort,
66
+ show_callers=show_callers,
67
+ show_callees=show_callees,
68
+ output_format=output_format,
69
+ save_path=save,
70
+ )
71
+
72
+ # Run profiler
73
+ typer.echo("🔬 Starting profiled execution...", err=True)
74
+
75
+ try:
76
+ profiler = Profiler(config)
77
+ result, report = profiler.profile_sync(request, invocation_method="profile_command")
78
+ except ImportError as e:
79
+ typer.echo(f"Error: {e}", err=True)
80
+ typer.echo("Install praisonaiagents: pip install praisonaiagents", err=True)
81
+ raise typer.Exit(1)
82
+ except Exception as e:
83
+ typer.echo(f"Error during profiling: {e}", err=True)
84
+ raise typer.Exit(1)
85
+
86
+ # Output results
87
+ if output_format == "json":
88
+ typer.echo(report.to_json())
89
+ else:
90
+ typer.echo(report.to_text())
91
+
92
+ # Save artifacts if requested
93
+ if save:
94
+ try:
95
+ import os
96
+ os.makedirs(save, exist_ok=True)
97
+
98
+ # Save JSON report
99
+ json_path = os.path.join(save, "profile_report.json")
100
+ with open(json_path, 'w') as f:
101
+ f.write(report.to_json())
102
+
103
+ # Save text report
104
+ txt_path = os.path.join(save, "profile_report.txt")
105
+ with open(txt_path, 'w') as f:
106
+ f.write(report.to_text())
107
+
108
+ typer.echo("\n✅ Artifacts saved:", err=True)
109
+ typer.echo(f" {json_path}", err=True)
110
+ typer.echo(f" {txt_path}", err=True)
111
+ except Exception as e:
112
+ typer.echo(f"Warning: Failed to save artifacts: {e}", err=True)
113
+
114
+
115
+ @app.command("imports")
116
+ def profile_imports():
117
+ """
118
+ Profile module import times.
119
+
120
+ Shows which modules take longest to import, useful for optimizing startup time.
121
+ """
122
+ import subprocess
123
+ import re
124
+
125
+ typer.echo("🔬 Profiling import times...", err=True)
126
+
127
+ try:
128
+ result = subprocess.run(
129
+ [sys.executable, "-X", "importtime", "-c", "import praisonaiagents"],
130
+ capture_output=True,
131
+ text=True,
132
+ timeout=60,
133
+ )
134
+
135
+ # Parse import times
136
+ import_times = []
137
+ for line in result.stderr.split('\n'):
138
+ if 'import time:' in line:
139
+ match = re.search(r'import time:\s+(\d+)\s+\|\s+(\d+)\s+\|\s+(.+)', line)
140
+ if match:
141
+ self_time = int(match.group(1))
142
+ cumulative = int(match.group(2))
143
+ module = match.group(3).strip()
144
+ import_times.append((module, self_time, cumulative))
145
+
146
+ # Sort by cumulative time
147
+ import_times.sort(key=lambda x: x[2], reverse=True)
148
+
149
+ # Display
150
+ typer.echo("\n" + "=" * 70)
151
+ typer.echo("Import Time Analysis")
152
+ typer.echo("=" * 70)
153
+ typer.echo(f"{'Module':<45} {'Self (μs)':>10} {'Cumul (μs)':>12}")
154
+ typer.echo("-" * 70)
155
+
156
+ for module, self_time, cumulative in import_times[:30]:
157
+ module_name = module
158
+ if len(module_name) > 43:
159
+ module_name = "..." + module_name[-40:]
160
+ typer.echo(f"{module_name:<45} {self_time:>10} {cumulative:>12}")
161
+
162
+ typer.echo("-" * 70)
163
+
164
+ if import_times:
165
+ total_time = max(t[2] for t in import_times)
166
+ typer.echo(f"Total import time: {total_time / 1000:.2f} ms")
167
+
168
+ except subprocess.TimeoutExpired:
169
+ typer.echo("Error: Import profiling timed out", err=True)
170
+ raise typer.Exit(1)
171
+ except Exception as e:
172
+ typer.echo(f"Error: {e}", err=True)
173
+ raise typer.Exit(1)
174
+
175
+
176
+ @app.command("startup")
177
+ def profile_startup():
178
+ """
179
+ Profile CLI startup time.
180
+
181
+ Measures time from CLI invocation to ready state.
182
+ """
183
+ import time
184
+ import subprocess
185
+
186
+ typer.echo("🔬 Profiling startup time...", err=True)
187
+
188
+ # Measure cold start (new process)
189
+ start = time.perf_counter()
190
+ subprocess.run(
191
+ [sys.executable, "-c", "import praisonai; import praisonai.cli"],
192
+ capture_output=True,
193
+ text=True,
194
+ )
195
+ cold_start = (time.perf_counter() - start) * 1000
196
+
197
+ # Measure warm start (imports cached)
198
+ start = time.perf_counter()
199
+ subprocess.run(
200
+ [sys.executable, "-c", "import praisonai; import praisonai.cli"],
201
+ capture_output=True,
202
+ text=True,
203
+ )
204
+ warm_start = (time.perf_counter() - start) * 1000
205
+
206
+ typer.echo("\n" + "=" * 50)
207
+ typer.echo("Startup Time Analysis")
208
+ typer.echo("=" * 50)
209
+ typer.echo(f"Cold Start: {cold_start:>10.2f} ms")
210
+ typer.echo(f"Warm Start: {warm_start:>10.2f} ms")
211
+ typer.echo("=" * 50)
212
+
213
+ if cold_start > 1000:
214
+ typer.echo("\n⚠️ Cold start > 1s - consider lazy imports", err=True)
215
+
216
+
217
+ @app.command("suite")
218
+ def profile_suite(
219
+ output_dir: Optional[str] = typer.Option("/tmp/praisonai_profile_suite", "--output", "-o", help="Output directory for results"),
220
+ iterations: int = typer.Option(3, "--iterations", "-n", help="Iterations per scenario"),
221
+ quick: bool = typer.Option(False, "--quick", help="Quick mode (fewer iterations)"),
222
+ ):
223
+ """
224
+ Run a comprehensive profiling suite.
225
+
226
+ Runs multiple scenarios (streaming/non-streaming, simple/complex prompts)
227
+ and produces aggregated statistics and reports.
228
+
229
+ Examples:
230
+ praisonai profile suite
231
+ praisonai profile suite --output ./my_profile_results
232
+ praisonai profile suite --quick
233
+ """
234
+ try:
235
+ from praisonai.cli.features.profiler import run_profile_suite, ScenarioConfig
236
+ except ImportError as e:
237
+ typer.echo(f"Error: Profiler module not available: {e}", err=True)
238
+ raise typer.Exit(1)
239
+
240
+ # Define scenarios
241
+ iters = 1 if quick else iterations
242
+ scenarios = [
243
+ ScenarioConfig(name="simple_non_stream", prompt="hi", stream=False, iterations=iters, warmup=0 if quick else 1),
244
+ ScenarioConfig(name="simple_stream", prompt="hi", stream=True, iterations=iters, warmup=0 if quick else 1),
245
+ ]
246
+
247
+ if not quick:
248
+ scenarios.extend([
249
+ ScenarioConfig(name="medium_non_stream", prompt="Explain Python in 2 sentences", stream=False, iterations=max(1, iters-1)),
250
+ ScenarioConfig(name="medium_stream", prompt="Explain Python in 2 sentences", stream=True, iterations=max(1, iters-1)),
251
+ ])
252
+
253
+ typer.echo("🔬 Running Profile Suite...", err=True)
254
+ typer.echo(f" Output: {output_dir}", err=True)
255
+ typer.echo(f" Scenarios: {len(scenarios)}", err=True)
256
+ typer.echo(f" Iterations: {iterations}", err=True)
257
+
258
+ try:
259
+ result = run_profile_suite(
260
+ output_dir=output_dir,
261
+ scenarios=scenarios,
262
+ verbose=True,
263
+ )
264
+
265
+ # Print summary
266
+ typer.echo("\n" + "=" * 60)
267
+ typer.echo("Profile Suite Summary")
268
+ typer.echo("=" * 60)
269
+ typer.echo(f"Startup Cold: {result.startup_cold_ms:.2f}ms")
270
+ typer.echo(f"Startup Warm: {result.startup_warm_ms:.2f}ms")
271
+
272
+ if result.import_analysis:
273
+ typer.echo(f"\nTop Import: {result.import_analysis[0]['module']}")
274
+ typer.echo(f" Time: {result.import_analysis[0]['cumulative_ms']:.2f}ms")
275
+
276
+ typer.echo("\nScenario Results:")
277
+ for scenario in result.scenarios:
278
+ stats = scenario.get_stats(scenario.total_times)
279
+ typer.echo(f" {scenario.name}: {stats['mean']:.2f}ms (±{stats['stdev']:.2f}ms)")
280
+
281
+ typer.echo(f"\n✅ Full results saved to: {output_dir}")
282
+
283
+ except Exception as e:
284
+ typer.echo(f"Error running suite: {e}", err=True)
285
+ raise typer.Exit(1)
286
+
287
+
288
+ @app.command("snapshot")
289
+ def profile_snapshot(
290
+ name: str = typer.Argument("current", help="Name for the snapshot"),
291
+ baseline: bool = typer.Option(False, "--baseline", "-b", help="Save as baseline for comparison"),
292
+ compare: bool = typer.Option(False, "--compare", "-c", help="Compare against baseline"),
293
+ output_format: str = typer.Option("text", "--format", "-f", help="Output format: text or json"),
294
+ ):
295
+ """
296
+ Create or compare performance snapshots.
297
+
298
+ Snapshots record timing data for baseline comparison.
299
+ Use --baseline to save a reference point, then --compare to check for regressions.
300
+
301
+ Examples:
302
+ praisonai profile snapshot --baseline
303
+ praisonai profile snapshot current --compare
304
+ praisonai profile snapshot v2.0 --format json
305
+ """
306
+ try:
307
+ from praisonai.cli.features.profiler import (
308
+ run_profile_suite,
309
+ ScenarioConfig,
310
+ PerfSnapshotManager,
311
+ create_snapshot_from_suite,
312
+ format_comparison_report,
313
+ )
314
+ except ImportError as e:
315
+ typer.echo(f"Error: Profiler module not available: {e}", err=True)
316
+ raise typer.Exit(1)
317
+
318
+ manager = PerfSnapshotManager()
319
+
320
+ if compare:
321
+ # Load baseline and compare
322
+ baseline_snap = manager.load_baseline()
323
+ if not baseline_snap:
324
+ typer.echo("❌ No baseline found. Create one with: praisonai profile snapshot --baseline", err=True)
325
+ raise typer.Exit(1)
326
+
327
+ typer.echo("🔬 Running current profile for comparison...", err=True)
328
+
329
+ # Run quick suite
330
+ scenarios = [
331
+ ScenarioConfig(name="simple", prompt="hi", stream=False, iterations=2, warmup=1),
332
+ ]
333
+ result = run_profile_suite(
334
+ output_dir="/tmp/praisonai_snapshot_compare",
335
+ scenarios=scenarios,
336
+ output="minimal",
337
+ )
338
+
339
+ current_snap = create_snapshot_from_suite(result, name)
340
+ comparison = manager.compare(current_snap)
341
+
342
+ if output_format == "json":
343
+ import json
344
+ typer.echo(json.dumps(comparison.to_dict(), indent=2))
345
+ else:
346
+ typer.echo(format_comparison_report(comparison))
347
+
348
+ if comparison.is_regression():
349
+ raise typer.Exit(1) # Exit with error on regression
350
+ else:
351
+ # Create new snapshot
352
+ typer.echo(f"🔬 Creating performance snapshot '{name}'...", err=True)
353
+
354
+ scenarios = [
355
+ ScenarioConfig(name="simple", prompt="hi", stream=False, iterations=3, warmup=1),
356
+ ScenarioConfig(name="stream", prompt="hi", stream=True, iterations=2, warmup=1),
357
+ ]
358
+ result = run_profile_suite(
359
+ output_dir="/tmp/praisonai_snapshot",
360
+ scenarios=scenarios,
361
+ verbose=True,
362
+ )
363
+
364
+ snapshot = create_snapshot_from_suite(result, name)
365
+
366
+ if baseline:
367
+ path = manager.save_baseline(snapshot)
368
+ typer.echo(f"\n✅ Baseline saved to: {path}")
369
+ else:
370
+ path = manager.save(snapshot)
371
+ typer.echo(f"\n✅ Snapshot saved to: {path}")
372
+
373
+ if output_format == "json":
374
+ import json
375
+ typer.echo(json.dumps(snapshot.to_dict(), indent=2))
376
+ else:
377
+ typer.echo("\nSnapshot Summary:")
378
+ typer.echo(f" Startup Cold: {snapshot.startup_cold_ms:.2f}ms")
379
+ typer.echo(f" Import Time: {snapshot.import_time_ms:.2f}ms")
380
+ typer.echo(f" Query Time: {snapshot.query_time_ms:.2f}ms")
381
+
382
+
383
+ @app.command("optimize")
384
+ def profile_optimize(
385
+ prewarm: bool = typer.Option(False, "--prewarm", help="Enable provider pre-warming"),
386
+ lite: bool = typer.Option(False, "--lite", help="Enable lite mode (skip type validation)"),
387
+ show_config: bool = typer.Option(False, "--show", help="Show current optimization config"),
388
+ ):
389
+ """
390
+ Configure performance optimizations.
391
+
392
+ All optimizations are opt-in and safe by default.
393
+
394
+ Examples:
395
+ praisonai profile optimize --show
396
+ praisonai profile optimize --prewarm
397
+ praisonai profile optimize --lite
398
+ """
399
+ try:
400
+ from praisonai.cli.features.profiler import (
401
+ PrewarmManager,
402
+ get_lite_mode_config,
403
+ get_provider_cache,
404
+ )
405
+ except ImportError as e:
406
+ typer.echo(f"Error: Profiler module not available: {e}", err=True)
407
+ raise typer.Exit(1)
408
+
409
+ if show_config:
410
+ typer.echo("=" * 50)
411
+ typer.echo("Performance Optimization Status")
412
+ typer.echo("=" * 50)
413
+
414
+ lite_config = get_lite_mode_config()
415
+ typer.echo(f"\nLite Mode: {'Enabled' if lite_config.enabled else 'Disabled'}")
416
+ if lite_config.enabled:
417
+ typer.echo(f" Skip Type Validation: {lite_config.skip_type_validation}")
418
+ typer.echo(f" Skip Model Validation: {lite_config.skip_model_validation}")
419
+ typer.echo(f" Minimal Imports: {lite_config.minimal_imports}")
420
+
421
+ typer.echo(f"\nPre-warming: {'Enabled' if PrewarmManager.is_enabled() else 'Disabled'}")
422
+
423
+ cache = get_provider_cache()
424
+ stats = cache.get_stats()
425
+ typer.echo("\nProvider Cache:")
426
+ typer.echo(f" Hits: {stats['hits']}")
427
+ typer.echo(f" Misses: {stats['misses']}")
428
+ typer.echo(f" Size: {stats['size']}")
429
+
430
+ typer.echo("\n" + "=" * 50)
431
+ typer.echo("\nTo enable optimizations, set environment variables:")
432
+ typer.echo(" PRAISONAI_LITE_MODE=1")
433
+ typer.echo(" PRAISONAI_SKIP_TYPE_VALIDATION=1")
434
+ typer.echo(" PRAISONAI_MINIMAL_IMPORTS=1")
435
+ return
436
+
437
+ if prewarm:
438
+ typer.echo("🔥 Enabling provider pre-warming...")
439
+ PrewarmManager.enable()
440
+
441
+ import os
442
+ if os.environ.get('OPENAI_API_KEY'):
443
+ typer.echo(" Pre-warming OpenAI...")
444
+ PrewarmManager.prewarm_provider('openai')
445
+ if os.environ.get('ANTHROPIC_API_KEY'):
446
+ typer.echo(" Pre-warming Anthropic...")
447
+ PrewarmManager.prewarm_provider('anthropic')
448
+
449
+ typer.echo("✅ Pre-warming initiated (runs in background)")
450
+
451
+ if lite:
452
+ typer.echo("⚡ Lite mode configuration:")
453
+ typer.echo(" Set these environment variables to enable:")
454
+ typer.echo(" export PRAISONAI_LITE_MODE=1")
455
+ typer.echo(" export PRAISONAI_SKIP_TYPE_VALIDATION=1")
456
+ typer.echo(" export PRAISONAI_MINIMAL_IMPORTS=1")
457
+
458
+ if not prewarm and not lite and not show_config:
459
+ typer.echo("Use --show to see current config, or --prewarm/--lite to enable optimizations")
460
+
461
+
462
+ @app.callback(invoke_without_command=True)
463
+ def profile_callback(ctx: typer.Context):
464
+ """Show profile help if no subcommand."""
465
+ if ctx.invoked_subcommand is None:
466
+ typer.echo(ctx.get_help())
@@ -0,0 +1,193 @@
1
+ """
2
+ Publish command group for PraisonAI CLI.
3
+
4
+ Provides package publishing with automatic version bumping.
5
+ """
6
+
7
+ import os
8
+ import re
9
+ import shutil
10
+ import subprocess
11
+ import sys
12
+ from pathlib import Path
13
+
14
+ import typer
15
+ from rich import print as rprint
16
+
17
+ app = typer.Typer(help="Package publishing")
18
+
19
+
20
+ def _find_pyproject() -> Path:
21
+ """Find pyproject.toml in current directory."""
22
+ p = Path.cwd() / "pyproject.toml"
23
+ if not p.exists():
24
+ rprint("[red]Error: No pyproject.toml found in current directory[/red]")
25
+ raise typer.Exit(1)
26
+ return p
27
+
28
+
29
+ def _read_version(pyproject: Path) -> str:
30
+ """Read version from pyproject.toml."""
31
+ content = pyproject.read_text()
32
+ match = re.search(r'^version\s*=\s*"([^"]+)"', content, re.MULTILINE)
33
+ if not match:
34
+ rprint("[red]Error: Could not find version in pyproject.toml[/red]")
35
+ raise typer.Exit(1)
36
+ return match.group(1)
37
+
38
+
39
+ def _bump_version(version: str, major: bool = False, minor: bool = False) -> str:
40
+ """Bump version string.
41
+
42
+ Default bumps patch (last number): 1.5.21 -> 1.5.22
43
+ --minor: 1.5.21 -> 1.6.0
44
+ --major: 1.5.21 -> 2.0.0
45
+ """
46
+ parts = version.split(".")
47
+ if len(parts) != 3:
48
+ rprint(f"[red]Error: Version '{version}' is not in X.Y.Z format[/red]")
49
+ raise typer.Exit(1)
50
+
51
+ maj, min_, patch = int(parts[0]), int(parts[1]), int(parts[2])
52
+
53
+ if major:
54
+ return f"{maj + 1}.0.0"
55
+ elif minor:
56
+ return f"{maj}.{min_ + 1}.0"
57
+ else:
58
+ return f"{maj}.{min_}.{patch + 1}"
59
+
60
+
61
+ def _write_version(pyproject: Path, old_version: str, new_version: str):
62
+ """Write new version to pyproject.toml."""
63
+ content = pyproject.read_text()
64
+ content = content.replace(f'version = "{old_version}"', f'version = "{new_version}"', 1)
65
+ pyproject.write_text(content)
66
+
67
+
68
+ def _run_step(cmd: list[str], label: str, dry_run: bool = False) -> bool:
69
+ """Run a build step, printing status."""
70
+ cmd_str = " ".join(cmd)
71
+ if dry_run:
72
+ rprint(f" [dim]→ {cmd_str}[/dim]")
73
+ return True
74
+
75
+ rprint(f"[cyan]→ {cmd_str}[/cyan]")
76
+ result = subprocess.run(cmd, capture_output=True, text=True)
77
+ if result.returncode != 0:
78
+ rprint(f"[red]✗ {label} failed[/red]")
79
+ if result.stderr:
80
+ rprint(f"[red]{result.stderr.strip()}[/red]")
81
+ if result.stdout:
82
+ rprint(f"[dim]{result.stdout.strip()}[/dim]")
83
+ return False
84
+
85
+ if result.stdout:
86
+ # Show concise output
87
+ lines = result.stdout.strip().split("\n")
88
+ for line in lines[-5:]: # last 5 lines
89
+ rprint(f" [dim]{line}[/dim]")
90
+ rprint(f"[green]✓ {label}[/green]")
91
+ return True
92
+
93
+
94
+ @app.callback(invoke_without_command=True)
95
+ def publish_main(ctx: typer.Context):
96
+ """
97
+ Publish packages to registries.
98
+
99
+ Use a subcommand like 'pypi' to specify the target.
100
+
101
+ Examples:
102
+ praisonai publish pypi
103
+ praisonai publish pypi --minor
104
+ praisonai publish pypi --dry-run
105
+ """
106
+ if ctx.invoked_subcommand is None:
107
+ rprint("[yellow]Usage: praisonai publish pypi [OPTIONS][/yellow]")
108
+ rprint("Run [bold]praisonai publish pypi --help[/bold] for options.")
109
+ raise typer.Exit(0)
110
+
111
+
112
+ @app.command("pypi")
113
+ def publish_pypi(
114
+ major: bool = typer.Option(False, "--major", help="Bump major version (X.0.0)"),
115
+ minor: bool = typer.Option(False, "--minor", help="Bump minor version (X.Y.0)"),
116
+ no_bump: bool = typer.Option(False, "--no-bump", help="Skip version increment"),
117
+ dry_run: bool = typer.Option(False, "--dry-run", "-n", help="Show what would happen without executing"),
118
+ token_var: str = typer.Option("PYPI_TOKEN", "--token-var", help="Environment variable name for PyPI token"),
119
+ ):
120
+ """
121
+ Publish package to PyPI.
122
+
123
+ Auto-increments the patch version (last number) by default,
124
+ then runs: rm -rf dist && uv lock && uv build && uv publish
125
+
126
+ Examples:
127
+ praisonai publish pypi # bump patch: 1.5.21 -> 1.5.22
128
+ praisonai publish pypi --minor # bump minor: 1.5.21 -> 1.6.0
129
+ praisonai publish pypi --major # bump major: 1.5.21 -> 2.0.0
130
+ praisonai publish pypi --no-bump # publish without version change
131
+ praisonai publish pypi --dry-run # preview without executing
132
+ praisonai publish pypi --token-var UV_PUBLISH_TOKEN
133
+ """
134
+ # 1. Find pyproject.toml
135
+ pyproject = _find_pyproject()
136
+ old_version = _read_version(pyproject)
137
+
138
+ # 2. Bump version
139
+ if no_bump:
140
+ new_version = old_version
141
+ rprint(f"[yellow]Skipping version bump — publishing {old_version}[/yellow]")
142
+ else:
143
+ if major and minor:
144
+ rprint("[red]Error: Cannot use --major and --minor together[/red]")
145
+ raise typer.Exit(1)
146
+ new_version = _bump_version(old_version, major=major, minor=minor)
147
+ bump_type = "major" if major else ("minor" if minor else "patch")
148
+ rprint(f"[cyan]Version bump ({bump_type}): {old_version} → {new_version}[/cyan]")
149
+
150
+ # 3. Get PyPI token
151
+ token = os.environ.get(token_var)
152
+ if not token:
153
+ rprint(f"[red]Error: {token_var} environment variable not set[/red]")
154
+ rprint(f"[dim]Set it with: export {token_var}=pypi-...[/dim]")
155
+ raise typer.Exit(1)
156
+ rprint(f"[green]✓ PyPI token found ({token_var})[/green]")
157
+
158
+ # 4. Dry run summary
159
+ if dry_run:
160
+ rprint("\n[yellow]Dry run — no changes will be made:[/yellow]")
161
+ if not no_bump:
162
+ rprint(f" [dim]→ Update pyproject.toml: {old_version} → {new_version}[/dim]")
163
+ _run_step(["rm", "-rf", "dist"], "clean", dry_run=True)
164
+ _run_step(["uv", "lock"], "lock", dry_run=True)
165
+ _run_step(["uv", "build"], "build", dry_run=True)
166
+ _run_step(["uv", "publish", "--token", f"${token_var}"], "publish", dry_run=True)
167
+ rprint("[green]Dry run complete.[/green]")
168
+ return
169
+
170
+ # 5. Write new version
171
+ if not no_bump:
172
+ _write_version(pyproject, old_version, new_version)
173
+ rprint(f"[green]✓ Updated pyproject.toml[/green]")
174
+
175
+ # 6. Clean dist
176
+ dist_dir = Path.cwd() / "dist"
177
+ if dist_dir.exists():
178
+ shutil.rmtree(dist_dir)
179
+ rprint("[green]✓ Cleaned dist/[/green]")
180
+
181
+ # 7. uv lock
182
+ if not _run_step(["uv", "lock"], "uv lock"):
183
+ raise typer.Exit(1)
184
+
185
+ # 8. uv build
186
+ if not _run_step(["uv", "build"], "uv build"):
187
+ raise typer.Exit(1)
188
+
189
+ # 9. uv publish
190
+ if not _run_step(["uv", "publish", "--token", token], "uv publish"):
191
+ raise typer.Exit(1)
192
+
193
+ rprint(f"\n[bold green]✓ Published {new_version} to PyPI[/bold green]")