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,422 @@
1
+ """
2
+ Packaging checks for the Doctor CLI module.
3
+
4
+ Validates PraisonAI packaging and entry point configurations to diagnose
5
+ Windows daemon invocation issues (python -m praisonai vs praisonai console script).
6
+ """
7
+
8
+ import os
9
+ import subprocess
10
+ import sys
11
+ import platform
12
+ import shutil
13
+
14
+ from ..models import (
15
+ CheckCategory,
16
+ CheckResult,
17
+ CheckStatus,
18
+ CheckSeverity,
19
+ DoctorConfig,
20
+ )
21
+ from ..registry import register_check
22
+
23
+
24
+ @register_check(
25
+ id="praisonai_package_structure",
26
+ title="PraisonAI Package Structure",
27
+ description="Check if praisonai package has correct structure for both -m and script execution",
28
+ category=CheckCategory.PACKAGING,
29
+ severity=CheckSeverity.HIGH,
30
+ )
31
+ def check_praisonai_package_structure(config: DoctorConfig) -> CheckResult:
32
+ """Check PraisonAI package structure and entry points."""
33
+ issues = []
34
+ metadata = {}
35
+
36
+ try:
37
+ import praisonai
38
+ package_path = praisonai.__file__
39
+ if package_path:
40
+ package_dir = os.path.dirname(package_path)
41
+ metadata["package_path"] = package_path
42
+ metadata["package_dir"] = package_dir
43
+
44
+ # Check for __main__.py
45
+ main_py_path = os.path.join(package_dir, "__main__.py")
46
+ if os.path.exists(main_py_path):
47
+ metadata["has_main_py"] = True
48
+ metadata["main_py_path"] = main_py_path
49
+ else:
50
+ issues.append("Missing __main__.py - python -m praisonai will not work")
51
+ metadata["has_main_py"] = False
52
+ else:
53
+ issues.append("praisonai.__file__ is None - namespace package detected")
54
+
55
+ except ImportError as e:
56
+ issues.append(f"Cannot import praisonai: {e}")
57
+ metadata["import_error"] = str(e)
58
+
59
+ # Check console script availability
60
+ praisonai_script = shutil.which("praisonai")
61
+ if praisonai_script:
62
+ metadata["console_script_path"] = praisonai_script
63
+ metadata["has_console_script"] = True
64
+ else:
65
+ issues.append("praisonai console script not found in PATH")
66
+ metadata["has_console_script"] = False
67
+
68
+ if not issues:
69
+ return CheckResult(
70
+ id="praisonai_package_structure",
71
+ title="PraisonAI Package Structure",
72
+ category=CheckCategory.PACKAGING,
73
+ status=CheckStatus.PASS,
74
+ message="Package structure is correct for both -m and script execution",
75
+ metadata=metadata,
76
+ )
77
+ else:
78
+ return CheckResult(
79
+ id="praisonai_package_structure",
80
+ title="PraisonAI Package Structure",
81
+ category=CheckCategory.PACKAGING,
82
+ status=CheckStatus.FAIL,
83
+ message=f"Package structure issues: {'; '.join(issues)}",
84
+ metadata=metadata,
85
+ )
86
+
87
+
88
+ @register_check(
89
+ id="python_module_execution",
90
+ title="Python Module Execution",
91
+ description="Test if python -m praisonai works correctly",
92
+ category=CheckCategory.PACKAGING,
93
+ severity=CheckSeverity.HIGH,
94
+ )
95
+ def check_python_module_execution(config: DoctorConfig) -> CheckResult:
96
+ """Test python -m praisonai execution."""
97
+ metadata = {"test_command": f"{sys.executable} -m praisonai --version"}
98
+
99
+ try:
100
+ result = subprocess.run(
101
+ [sys.executable, "-m", "praisonai", "--version"],
102
+ capture_output=True,
103
+ text=True,
104
+ timeout=config.timeout,
105
+ )
106
+
107
+ metadata["return_code"] = result.returncode
108
+ metadata["stdout"] = result.stdout.strip()
109
+ metadata["stderr"] = result.stderr.strip()
110
+
111
+ if result.returncode == 0:
112
+ return CheckResult(
113
+ id="python_module_execution",
114
+ title="Python Module Execution",
115
+ category=CheckCategory.PACKAGING,
116
+ status=CheckStatus.PASS,
117
+ message=f"python -m praisonai works: {result.stdout.strip() or result.stderr.strip()}",
118
+ metadata=metadata,
119
+ )
120
+ else:
121
+ return CheckResult(
122
+ id="python_module_execution",
123
+ title="Python Module Execution",
124
+ category=CheckCategory.PACKAGING,
125
+ status=CheckStatus.FAIL,
126
+ message=f"python -m praisonai failed with exit code {result.returncode}",
127
+ metadata=metadata,
128
+ )
129
+
130
+ except subprocess.TimeoutExpired:
131
+ return CheckResult(
132
+ id="python_module_execution",
133
+ title="Python Module Execution",
134
+ category=CheckCategory.PACKAGING,
135
+ status=CheckStatus.FAIL,
136
+ message=f"python -m praisonai timed out (>{config.timeout}s)",
137
+ metadata=metadata,
138
+ )
139
+ except Exception as e:
140
+ metadata["exception"] = str(e)
141
+ return CheckResult(
142
+ id="python_module_execution",
143
+ title="Python Module Execution",
144
+ category=CheckCategory.PACKAGING,
145
+ status=CheckStatus.FAIL,
146
+ message=f"python -m praisonai execution failed: {e}",
147
+ metadata=metadata,
148
+ )
149
+
150
+
151
+ @register_check(
152
+ id="console_script_execution",
153
+ title="Console Script Execution",
154
+ description="Test if praisonai console script works correctly",
155
+ category=CheckCategory.PACKAGING,
156
+ severity=CheckSeverity.HIGH,
157
+ )
158
+ def check_console_script_execution(config: DoctorConfig) -> CheckResult:
159
+ """Test praisonai console script execution."""
160
+ praisonai_script = shutil.which("praisonai")
161
+
162
+ if not praisonai_script:
163
+ return CheckResult(
164
+ id="console_script_execution",
165
+ title="Console Script Execution",
166
+ category=CheckCategory.PACKAGING,
167
+ status=CheckStatus.FAIL,
168
+ message="praisonai console script not found in PATH",
169
+ metadata={"script_path": None},
170
+ )
171
+
172
+ metadata = {
173
+ "script_path": praisonai_script,
174
+ "test_command": f"{praisonai_script} --version"
175
+ }
176
+
177
+ try:
178
+ result = subprocess.run(
179
+ [praisonai_script, "--version"],
180
+ capture_output=True,
181
+ text=True,
182
+ timeout=config.timeout,
183
+ )
184
+
185
+ metadata["return_code"] = result.returncode
186
+ metadata["stdout"] = result.stdout.strip()
187
+ metadata["stderr"] = result.stderr.strip()
188
+
189
+ if result.returncode == 0:
190
+ return CheckResult(
191
+ id="console_script_execution",
192
+ title="Console Script Execution",
193
+ category=CheckCategory.PACKAGING,
194
+ status=CheckStatus.PASS,
195
+ message=f"praisonai console script works: {result.stdout.strip() or result.stderr.strip()}",
196
+ metadata=metadata,
197
+ )
198
+ else:
199
+ return CheckResult(
200
+ id="console_script_execution",
201
+ title="Console Script Execution",
202
+ category=CheckCategory.PACKAGING,
203
+ status=CheckStatus.FAIL,
204
+ message=f"praisonai console script failed with exit code {result.returncode}",
205
+ metadata=metadata,
206
+ )
207
+
208
+ except subprocess.TimeoutExpired:
209
+ return CheckResult(
210
+ id="console_script_execution",
211
+ title="Console Script Execution",
212
+ category=CheckCategory.PACKAGING,
213
+ status=CheckStatus.FAIL,
214
+ message=f"praisonai console script timed out (>{config.timeout}s)",
215
+ metadata=metadata,
216
+ )
217
+ except Exception as e:
218
+ metadata["exception"] = str(e)
219
+ return CheckResult(
220
+ id="console_script_execution",
221
+ title="Console Script Execution",
222
+ category=CheckCategory.PACKAGING,
223
+ status=CheckStatus.FAIL,
224
+ message=f"praisonai console script execution failed: {e}",
225
+ metadata=metadata,
226
+ )
227
+
228
+
229
+ @register_check(
230
+ id="windows_daemon_context",
231
+ title="Windows Daemon Context",
232
+ description="Check Windows-specific daemon execution context",
233
+ category=CheckCategory.PACKAGING,
234
+ severity=CheckSeverity.MEDIUM,
235
+ )
236
+ def check_windows_daemon_context(config: DoctorConfig) -> CheckResult:
237
+ """Check Windows daemon execution context and provide recommendations."""
238
+ metadata = {
239
+ "platform": platform.system(),
240
+ "is_windows": platform.system().lower() == "windows"
241
+ }
242
+
243
+ if not metadata["is_windows"]:
244
+ return CheckResult(
245
+ id="windows_daemon_context",
246
+ title="Windows Daemon Context",
247
+ category=CheckCategory.PACKAGING,
248
+ status=CheckStatus.SKIP,
249
+ message="Not Windows - check skipped",
250
+ metadata=metadata,
251
+ )
252
+
253
+ recommendations = []
254
+ warnings = []
255
+
256
+ # Check if in virtual environment
257
+ in_venv = hasattr(sys, 'real_prefix') or (hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix)
258
+ metadata["in_venv"] = in_venv
259
+ metadata["sys_prefix"] = sys.prefix
260
+ metadata["sys_executable"] = sys.executable
261
+
262
+ if in_venv:
263
+ venv_scripts_dir = os.path.join(sys.prefix, "Scripts")
264
+ metadata["venv_scripts_dir"] = venv_scripts_dir
265
+
266
+ # Check if Scripts dir exists and has praisonai.exe
267
+ if os.path.exists(venv_scripts_dir):
268
+ praisonai_exe = os.path.join(venv_scripts_dir, "praisonai.exe")
269
+ metadata["venv_praisonai_exe_exists"] = os.path.exists(praisonai_exe)
270
+
271
+ if os.path.exists(praisonai_exe):
272
+ recommendations.append(f"For daemons, use absolute path: {praisonai_exe}")
273
+ else:
274
+ warnings.append("praisonai.exe not found in venv Scripts directory")
275
+ else:
276
+ warnings.append("Virtual environment Scripts directory not found")
277
+ else:
278
+ warnings.append("Not in virtual environment - daemon may use global Python")
279
+
280
+ # Check Python executable type
281
+ if "WindowsApps" in sys.executable:
282
+ warnings.append("Using Microsoft Store Python - may cause daemon issues")
283
+ recommendations.append("Consider using python.org Python or conda for daemons")
284
+
285
+ # Check encoding environment
286
+ pythonioencoding = os.environ.get("PYTHONIOENCODING")
287
+ metadata["pythonioencoding"] = pythonioencoding
288
+
289
+ if not pythonioencoding:
290
+ recommendations.append("Set PYTHONIOENCODING=utf-8 for daemon encoding safety")
291
+
292
+ # Generate canonical daemon command
293
+ praisonai_script = shutil.which("praisonai")
294
+ if praisonai_script:
295
+ canonical_command = f'"{praisonai_script}"'
296
+ else:
297
+ canonical_command = f'"{sys.executable}" -m praisonai'
298
+
299
+ metadata["canonical_daemon_command"] = canonical_command
300
+ recommendations.append(f"Recommended daemon command: {canonical_command}")
301
+
302
+ if warnings:
303
+ status = CheckStatus.WARN
304
+ message = f"Windows daemon context has warnings: {'; '.join(warnings[:2])}"
305
+ else:
306
+ status = CheckStatus.PASS
307
+ message = "Windows daemon context is good"
308
+
309
+ if recommendations:
310
+ metadata["recommendations"] = recommendations
311
+
312
+ return CheckResult(
313
+ id="windows_daemon_context",
314
+ title="Windows Daemon Context",
315
+ category=CheckCategory.PACKAGING,
316
+ status=status,
317
+ message=message,
318
+ metadata=metadata,
319
+ )
320
+
321
+
322
+ @register_check(
323
+ id="packaging_metadata",
324
+ title="Packaging Metadata",
325
+ description="Check PraisonAI package installation metadata",
326
+ category=CheckCategory.PACKAGING,
327
+ severity=CheckSeverity.LOW,
328
+ )
329
+ def check_packaging_metadata(config: DoctorConfig) -> CheckResult:
330
+ """Check PraisonAI package installation metadata."""
331
+ metadata = {}
332
+ issues = []
333
+
334
+ try:
335
+ import praisonai
336
+ metadata["praisonai_file"] = getattr(praisonai, "__file__", None)
337
+ metadata["praisonai_version"] = getattr(praisonai, "__version__", "unknown")
338
+
339
+ # Check if installed via pip (using modern importlib.metadata)
340
+ try:
341
+ try:
342
+ from importlib.metadata import distribution, PackageNotFoundError
343
+ except ImportError:
344
+ # Python < 3.8 fallback
345
+ from importlib_metadata import distribution, PackageNotFoundError
346
+
347
+ try:
348
+ dist = distribution("praisonai")
349
+ metadata["pip_version"] = dist.version
350
+ metadata["pip_location"] = str(dist.locate_file(""))
351
+ metadata["pip_installed"] = True
352
+ except PackageNotFoundError:
353
+ metadata["pip_installed"] = False
354
+ issues.append("Package not found in pip registry (editable install?)")
355
+ except ImportError:
356
+ metadata["importlib_metadata_available"] = False
357
+
358
+ # Check for editable install markers
359
+ if praisonai.__file__:
360
+ package_dir = os.path.dirname(praisonai.__file__)
361
+ # Look for .egg-link or similar editable install markers
362
+ site_packages = []
363
+ try:
364
+ import site
365
+ site_packages.extend(site.getsitepackages())
366
+ if hasattr(site, 'getusersitepackages'):
367
+ site_packages.append(site.getusersitepackages())
368
+ except Exception as e:
369
+ metadata["site_packages_probe_error"] = str(e)
370
+
371
+ is_editable = False
372
+ for sp in site_packages:
373
+ if sp and os.path.exists(sp):
374
+ # Check for both case variations of egg-link files
375
+ for egg_link_name in ("praisonai.egg-link", "PraisonAI.egg-link"):
376
+ egg_link = os.path.join(sp, egg_link_name)
377
+ if os.path.exists(egg_link):
378
+ is_editable = True
379
+ metadata["editable_install"] = True
380
+ metadata["egg_link_path"] = egg_link
381
+ break
382
+ if is_editable:
383
+ break
384
+
385
+ # Check for modern editable install markers (PEP 660)
386
+ try:
387
+ for file in os.listdir(sp):
388
+ if file.startswith("__editable__") and "praisonai" in file.lower() and file.endswith(".pth"):
389
+ is_editable = True
390
+ metadata["editable_install"] = True
391
+ metadata["editable_pth_path"] = os.path.join(sp, file)
392
+ break
393
+ except OSError:
394
+ pass
395
+ if is_editable:
396
+ break
397
+
398
+ if not is_editable:
399
+ metadata["editable_install"] = False
400
+
401
+ except ImportError as e:
402
+ issues.append(f"Cannot import praisonai: {e}")
403
+ metadata["import_error"] = str(e)
404
+
405
+ if not issues:
406
+ return CheckResult(
407
+ id="packaging_metadata",
408
+ title="Packaging Metadata",
409
+ category=CheckCategory.PACKAGING,
410
+ status=CheckStatus.PASS,
411
+ message="Package metadata is accessible",
412
+ metadata=metadata,
413
+ )
414
+ else:
415
+ return CheckResult(
416
+ id="packaging_metadata",
417
+ title="Packaging Metadata",
418
+ category=CheckCategory.PACKAGING,
419
+ status=CheckStatus.WARN,
420
+ message=f"Package metadata issues: {'; '.join(issues)}",
421
+ metadata=metadata,
422
+ )
@@ -0,0 +1,235 @@
1
+ """
2
+ Performance checks for the Doctor CLI module.
3
+
4
+ Measures import times and identifies slow imports.
5
+ """
6
+
7
+ import sys
8
+ import time
9
+
10
+ from ..models import (
11
+ CheckCategory,
12
+ CheckResult,
13
+ CheckStatus,
14
+ CheckSeverity,
15
+ DoctorConfig,
16
+ )
17
+ from ..registry import register_check
18
+
19
+
20
+ def _measure_import_time(module_name: str) -> tuple:
21
+ """Measure import time for a module."""
22
+ # Remove from cache if present
23
+ modules_before = set(sys.modules.keys())
24
+
25
+ start = time.perf_counter()
26
+ try:
27
+ __import__(module_name)
28
+ elapsed = (time.perf_counter() - start) * 1000 # ms
29
+ return True, elapsed, None
30
+ except ImportError as e:
31
+ elapsed = (time.perf_counter() - start) * 1000
32
+ return False, elapsed, str(e)
33
+ finally:
34
+ # Clean up imported modules (optional, for accurate re-measurement)
35
+ pass
36
+
37
+
38
+ @register_check(
39
+ id="performance_praisonai_import",
40
+ title="PraisonAI Import Time",
41
+ description="Measure praisonai package import time",
42
+ category=CheckCategory.PERFORMANCE,
43
+ severity=CheckSeverity.INFO,
44
+ )
45
+ def check_performance_praisonai_import(config: DoctorConfig) -> CheckResult:
46
+ """Measure praisonai package import time."""
47
+ budget_ms = config.budget_ms or 2000 # 2 second default budget
48
+
49
+ # praisonai is already imported, measure a fresh import of a submodule
50
+ start = time.perf_counter()
51
+ try:
52
+ # Import version module (lightweight)
53
+ from praisonai import version
54
+ elapsed = (time.perf_counter() - start) * 1000
55
+
56
+ if elapsed > budget_ms:
57
+ return CheckResult(
58
+ id="performance_praisonai_import",
59
+ title="PraisonAI Import Time",
60
+ category=CheckCategory.PERFORMANCE,
61
+ status=CheckStatus.WARN,
62
+ message=f"Import time {elapsed:.0f}ms exceeds budget {budget_ms}ms",
63
+ remediation="Check for slow dependencies or disk I/O issues",
64
+ )
65
+ else:
66
+ return CheckResult(
67
+ id="performance_praisonai_import",
68
+ title="PraisonAI Import Time",
69
+ category=CheckCategory.PERFORMANCE,
70
+ status=CheckStatus.PASS,
71
+ message=f"Import time: {elapsed:.0f}ms (budget: {budget_ms}ms)",
72
+ metadata={"import_time_ms": elapsed, "budget_ms": budget_ms},
73
+ )
74
+ except ImportError as e:
75
+ return CheckResult(
76
+ id="performance_praisonai_import",
77
+ title="PraisonAI Import Time",
78
+ category=CheckCategory.PERFORMANCE,
79
+ status=CheckStatus.FAIL,
80
+ message=f"Import failed: {e}",
81
+ )
82
+
83
+
84
+ @register_check(
85
+ id="performance_praisonaiagents_import",
86
+ title="PraisonAI Agents Import Time",
87
+ description="Measure praisonaiagents package import time",
88
+ category=CheckCategory.PERFORMANCE,
89
+ severity=CheckSeverity.INFO,
90
+ )
91
+ def check_performance_praisonaiagents_import(config: DoctorConfig) -> CheckResult:
92
+ """Measure praisonaiagents package import time."""
93
+ budget_ms = config.budget_ms or 3000 # 3 second default budget
94
+
95
+ start = time.perf_counter()
96
+ try:
97
+ import praisonaiagents
98
+ elapsed = (time.perf_counter() - start) * 1000
99
+
100
+ if elapsed > budget_ms:
101
+ return CheckResult(
102
+ id="performance_praisonaiagents_import",
103
+ title="PraisonAI Agents Import Time",
104
+ category=CheckCategory.PERFORMANCE,
105
+ status=CheckStatus.WARN,
106
+ message=f"Import time {elapsed:.0f}ms exceeds budget {budget_ms}ms",
107
+ remediation="Check for slow dependencies",
108
+ )
109
+ else:
110
+ return CheckResult(
111
+ id="performance_praisonaiagents_import",
112
+ title="PraisonAI Agents Import Time",
113
+ category=CheckCategory.PERFORMANCE,
114
+ status=CheckStatus.PASS,
115
+ message=f"Import time: {elapsed:.0f}ms (budget: {budget_ms}ms)",
116
+ metadata={"import_time_ms": elapsed, "budget_ms": budget_ms},
117
+ )
118
+ except ImportError as e:
119
+ return CheckResult(
120
+ id="performance_praisonaiagents_import",
121
+ title="PraisonAI Agents Import Time",
122
+ category=CheckCategory.PERFORMANCE,
123
+ status=CheckStatus.FAIL,
124
+ message=f"Import failed: {e}",
125
+ )
126
+
127
+
128
+ @register_check(
129
+ id="performance_slow_imports",
130
+ title="Slow Import Detection",
131
+ description="Detect slow-loading optional dependencies",
132
+ category=CheckCategory.PERFORMANCE,
133
+ severity=CheckSeverity.INFO,
134
+ requires_deep=True,
135
+ )
136
+ def check_performance_slow_imports(config: DoctorConfig) -> CheckResult:
137
+ """Detect slow-loading optional dependencies."""
138
+ modules_to_check = [
139
+ "litellm",
140
+ "chromadb",
141
+ "langchain",
142
+ "torch",
143
+ "transformers",
144
+ "pandas",
145
+ "numpy",
146
+ ]
147
+
148
+ top_n = config.top_n or 5
149
+ budget_ms = config.budget_ms or 1000
150
+
151
+ import_times = []
152
+
153
+ for module in modules_to_check:
154
+ if module in sys.modules:
155
+ # Already imported, skip
156
+ continue
157
+
158
+ success, elapsed, error = _measure_import_time(module)
159
+ if success:
160
+ import_times.append((module, elapsed))
161
+
162
+ # Sort by time, descending
163
+ import_times.sort(key=lambda x: x[1], reverse=True)
164
+
165
+ slow_imports = [(m, t) for m, t in import_times if t > budget_ms]
166
+ top_imports = import_times[:top_n]
167
+
168
+ if slow_imports:
169
+ slow_str = ", ".join(f"{m}({t:.0f}ms)" for m, t in slow_imports[:3])
170
+ return CheckResult(
171
+ id="performance_slow_imports",
172
+ title="Slow Import Detection",
173
+ category=CheckCategory.PERFORMANCE,
174
+ status=CheckStatus.WARN,
175
+ message=f"{len(slow_imports)} slow import(s) detected",
176
+ details=f"Slow: {slow_str}",
177
+ metadata={"slow_imports": slow_imports, "top_imports": top_imports},
178
+ )
179
+ elif top_imports:
180
+ top_str = ", ".join(f"{m}({t:.0f}ms)" for m, t in top_imports[:3])
181
+ return CheckResult(
182
+ id="performance_slow_imports",
183
+ title="Slow Import Detection",
184
+ category=CheckCategory.PERFORMANCE,
185
+ status=CheckStatus.PASS,
186
+ message=f"No slow imports (top: {top_str})",
187
+ metadata={"top_imports": top_imports},
188
+ )
189
+ else:
190
+ return CheckResult(
191
+ id="performance_slow_imports",
192
+ title="Slow Import Detection",
193
+ category=CheckCategory.PERFORMANCE,
194
+ status=CheckStatus.SKIP,
195
+ message="No optional modules to measure",
196
+ )
197
+
198
+
199
+ @register_check(
200
+ id="performance_loaded_modules",
201
+ title="Loaded Modules Count",
202
+ description="Count currently loaded Python modules",
203
+ category=CheckCategory.PERFORMANCE,
204
+ severity=CheckSeverity.INFO,
205
+ )
206
+ def check_performance_loaded_modules(config: DoctorConfig) -> CheckResult:
207
+ """Count currently loaded Python modules."""
208
+ module_count = len(sys.modules)
209
+
210
+ # Categorize modules
211
+ stdlib_count = 0
212
+ third_party_count = 0
213
+ praison_count = 0
214
+
215
+ for name in sys.modules:
216
+ if name.startswith("praisonai"):
217
+ praison_count += 1
218
+ elif "site-packages" in str(getattr(sys.modules[name], "__file__", "")):
219
+ third_party_count += 1
220
+ else:
221
+ stdlib_count += 1
222
+
223
+ return CheckResult(
224
+ id="performance_loaded_modules",
225
+ title="Loaded Modules Count",
226
+ category=CheckCategory.PERFORMANCE,
227
+ status=CheckStatus.PASS,
228
+ message=f"{module_count} modules loaded (praison: {praison_count})",
229
+ metadata={
230
+ "total": module_count,
231
+ "praison": praison_count,
232
+ "third_party": third_party_count,
233
+ "stdlib": stdlib_count,
234
+ },
235
+ )