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,637 @@
1
+ """
2
+ Environment checks for the Doctor CLI module.
3
+
4
+ Validates Python version, packages, API keys, and environment variables.
5
+ """
6
+
7
+ import os
8
+ import platform
9
+ import shutil
10
+ import sys
11
+ from typing import List, Optional
12
+
13
+ from ..models import (
14
+ CheckCategory,
15
+ CheckResult,
16
+ CheckStatus,
17
+ CheckSeverity,
18
+ DoctorConfig,
19
+ )
20
+ from ..registry import register_check
21
+
22
+
23
+ @register_check(
24
+ id="python_version",
25
+ title="Python Version",
26
+ description="Check Python version is 3.9+",
27
+ category=CheckCategory.ENVIRONMENT,
28
+ severity=CheckSeverity.CRITICAL,
29
+ )
30
+ def check_python_version(config: DoctorConfig) -> CheckResult:
31
+ """Check Python version is 3.9+."""
32
+ version = sys.version_info
33
+ version_str = f"{version.major}.{version.minor}.{version.micro}"
34
+
35
+ if version.major >= 3 and version.minor >= 9:
36
+ return CheckResult(
37
+ id="python_version",
38
+ title="Python Version",
39
+ category=CheckCategory.ENVIRONMENT,
40
+ status=CheckStatus.PASS,
41
+ message=f"Python {version_str} (>= 3.9 required)",
42
+ metadata={"version": version_str, "executable": sys.executable},
43
+ )
44
+ else:
45
+ return CheckResult(
46
+ id="python_version",
47
+ title="Python Version",
48
+ category=CheckCategory.ENVIRONMENT,
49
+ status=CheckStatus.FAIL,
50
+ message=f"Python {version_str} found, but 3.9+ required",
51
+ remediation="Upgrade Python to version 3.9 or higher",
52
+ severity=CheckSeverity.CRITICAL,
53
+ )
54
+
55
+
56
+ @register_check(
57
+ id="praisonai_package",
58
+ title="PraisonAI Package",
59
+ description="Check praisonai package is installed",
60
+ category=CheckCategory.ENVIRONMENT,
61
+ severity=CheckSeverity.CRITICAL,
62
+ )
63
+ def check_praisonai_package(config: DoctorConfig) -> CheckResult:
64
+ """Check praisonai package is installed."""
65
+ try:
66
+ from praisonai.version import __version__
67
+ return CheckResult(
68
+ id="praisonai_package",
69
+ title="PraisonAI Package",
70
+ category=CheckCategory.ENVIRONMENT,
71
+ status=CheckStatus.PASS,
72
+ message=f"praisonai {__version__} installed",
73
+ metadata={"version": __version__},
74
+ )
75
+ except ImportError as e:
76
+ return CheckResult(
77
+ id="praisonai_package",
78
+ title="PraisonAI Package",
79
+ category=CheckCategory.ENVIRONMENT,
80
+ status=CheckStatus.FAIL,
81
+ message="praisonai package not found",
82
+ details=str(e),
83
+ remediation="Install with: pip install praisonai",
84
+ severity=CheckSeverity.CRITICAL,
85
+ )
86
+
87
+
88
+ @register_check(
89
+ id="praisonaiagents_package",
90
+ title="PraisonAI Agents Package",
91
+ description="Check praisonaiagents package is installed",
92
+ category=CheckCategory.ENVIRONMENT,
93
+ severity=CheckSeverity.CRITICAL,
94
+ )
95
+ def check_praisonaiagents_package(config: DoctorConfig) -> CheckResult:
96
+ """Check praisonaiagents package is installed."""
97
+ try:
98
+ import praisonaiagents
99
+ # praisonaiagents does not expose __version__ at module level yet, so
100
+ # prefer importlib.metadata (which reads the installed dist-info) and
101
+ # fall back to the attribute for forward-compat.
102
+ version = getattr(praisonaiagents, "__version__", None)
103
+ if not version:
104
+ try:
105
+ from importlib.metadata import version as _pkg_version
106
+ version = _pkg_version("praisonaiagents")
107
+ except Exception:
108
+ version = "unknown"
109
+ return CheckResult(
110
+ id="praisonaiagents_package",
111
+ title="PraisonAI Agents Package",
112
+ category=CheckCategory.ENVIRONMENT,
113
+ status=CheckStatus.PASS,
114
+ message=f"praisonaiagents {version} installed",
115
+ metadata={"version": version},
116
+ )
117
+ except ImportError as e:
118
+ return CheckResult(
119
+ id="praisonaiagents_package",
120
+ title="PraisonAI Agents Package",
121
+ category=CheckCategory.ENVIRONMENT,
122
+ status=CheckStatus.FAIL,
123
+ message="praisonaiagents package not found",
124
+ details=str(e),
125
+ remediation="Install with: pip install praisonaiagents",
126
+ severity=CheckSeverity.CRITICAL,
127
+ )
128
+
129
+
130
+ @register_check(
131
+ id="openai_api_key",
132
+ title="OpenAI API Key",
133
+ description="Check OPENAI_API_KEY is configured",
134
+ category=CheckCategory.ENVIRONMENT,
135
+ severity=CheckSeverity.HIGH,
136
+ )
137
+ def check_openai_api_key(config: DoctorConfig) -> CheckResult:
138
+ """Check OpenAI API key is configured."""
139
+ key = os.environ.get("OPENAI_API_KEY", "")
140
+
141
+ if key:
142
+ # Mask the key for display
143
+ if config.show_keys and len(key) > 12:
144
+ masked = f"{key[:4]}...{key[-4:]}"
145
+ else:
146
+ masked = "***configured***"
147
+
148
+ # Basic validation
149
+ if key.startswith("sk-") or key.startswith("sk-proj-"):
150
+ return CheckResult(
151
+ id="openai_api_key",
152
+ title="OpenAI API Key",
153
+ category=CheckCategory.ENVIRONMENT,
154
+ status=CheckStatus.PASS,
155
+ message=f"OPENAI_API_KEY configured ({masked})",
156
+ metadata={"masked_key": masked},
157
+ )
158
+ else:
159
+ return CheckResult(
160
+ id="openai_api_key",
161
+ title="OpenAI API Key",
162
+ category=CheckCategory.ENVIRONMENT,
163
+ status=CheckStatus.WARN,
164
+ message=f"OPENAI_API_KEY set but format unexpected ({masked})",
165
+ remediation="Verify the API key format is correct",
166
+ )
167
+ else:
168
+ # Check for alternative providers
169
+ alternatives = []
170
+ if os.environ.get("ANTHROPIC_API_KEY"):
171
+ alternatives.append("ANTHROPIC_API_KEY")
172
+ if os.environ.get("GOOGLE_API_KEY") or os.environ.get("GEMINI_API_KEY"):
173
+ alternatives.append("GOOGLE/GEMINI_API_KEY")
174
+ if os.environ.get("OLLAMA_HOST") or shutil.which("ollama"):
175
+ alternatives.append("Ollama")
176
+
177
+ if alternatives:
178
+ return CheckResult(
179
+ id="openai_api_key",
180
+ title="OpenAI API Key",
181
+ category=CheckCategory.ENVIRONMENT,
182
+ status=CheckStatus.WARN,
183
+ message=f"OPENAI_API_KEY not set, but alternatives found: {', '.join(alternatives)}",
184
+ details="OpenAI is the default provider. Other providers require explicit configuration.",
185
+ )
186
+ else:
187
+ return CheckResult(
188
+ id="openai_api_key",
189
+ title="OpenAI API Key",
190
+ category=CheckCategory.ENVIRONMENT,
191
+ status=CheckStatus.FAIL,
192
+ message="OPENAI_API_KEY not configured and no alternative providers found",
193
+ remediation="Run 'praisonai setup' to configure API keys, or set OPENAI_API_KEY environment variable",
194
+ severity=CheckSeverity.HIGH,
195
+ )
196
+
197
+
198
+ @register_check(
199
+ id="anthropic_api_key",
200
+ title="Anthropic API Key",
201
+ description="Check ANTHROPIC_API_KEY is configured",
202
+ category=CheckCategory.ENVIRONMENT,
203
+ severity=CheckSeverity.LOW,
204
+ )
205
+ def check_anthropic_api_key(config: DoctorConfig) -> CheckResult:
206
+ """Check Anthropic API key is configured."""
207
+ key = os.environ.get("ANTHROPIC_API_KEY", "")
208
+
209
+ if key:
210
+ if config.show_keys and len(key) > 12:
211
+ masked = f"{key[:4]}...{key[-4:]}"
212
+ else:
213
+ masked = "***configured***"
214
+
215
+ return CheckResult(
216
+ id="anthropic_api_key",
217
+ title="Anthropic API Key",
218
+ category=CheckCategory.ENVIRONMENT,
219
+ status=CheckStatus.PASS,
220
+ message=f"ANTHROPIC_API_KEY configured ({masked})",
221
+ )
222
+ else:
223
+ return CheckResult(
224
+ id="anthropic_api_key",
225
+ title="Anthropic API Key",
226
+ category=CheckCategory.ENVIRONMENT,
227
+ status=CheckStatus.SKIP,
228
+ message="ANTHROPIC_API_KEY not set (optional)",
229
+ )
230
+
231
+
232
+ @register_check(
233
+ id="google_api_key",
234
+ title="Google/Gemini API Key",
235
+ description="Check GOOGLE_API_KEY or GEMINI_API_KEY is configured",
236
+ category=CheckCategory.ENVIRONMENT,
237
+ severity=CheckSeverity.LOW,
238
+ )
239
+ def check_google_api_key(config: DoctorConfig) -> CheckResult:
240
+ """Check Google/Gemini API key is configured."""
241
+ key = os.environ.get("GOOGLE_API_KEY") or os.environ.get("GEMINI_API_KEY", "")
242
+
243
+ if key:
244
+ if config.show_keys and len(key) > 12:
245
+ masked = f"{key[:4]}...{key[-4:]}"
246
+ else:
247
+ masked = "***configured***"
248
+
249
+ return CheckResult(
250
+ id="google_api_key",
251
+ title="Google/Gemini API Key",
252
+ category=CheckCategory.ENVIRONMENT,
253
+ status=CheckStatus.PASS,
254
+ message=f"Google/Gemini API key configured ({masked})",
255
+ )
256
+ else:
257
+ return CheckResult(
258
+ id="google_api_key",
259
+ title="Google/Gemini API Key",
260
+ category=CheckCategory.ENVIRONMENT,
261
+ status=CheckStatus.SKIP,
262
+ message="GOOGLE_API_KEY/GEMINI_API_KEY not set (optional)",
263
+ )
264
+
265
+
266
+ @register_check(
267
+ id="os_info",
268
+ title="Operating System",
269
+ description="Check operating system information",
270
+ category=CheckCategory.ENVIRONMENT,
271
+ severity=CheckSeverity.INFO,
272
+ )
273
+ def check_os_info(config: DoctorConfig) -> CheckResult:
274
+ """Check operating system information."""
275
+ os_name = platform.system()
276
+ os_version = platform.release()
277
+ arch = platform.machine()
278
+
279
+ return CheckResult(
280
+ id="os_info",
281
+ title="Operating System",
282
+ category=CheckCategory.ENVIRONMENT,
283
+ status=CheckStatus.PASS,
284
+ message=f"{os_name} {os_version} ({arch})",
285
+ metadata={
286
+ "os_name": os_name,
287
+ "os_version": os_version,
288
+ "architecture": arch,
289
+ },
290
+ )
291
+
292
+
293
+ @register_check(
294
+ id="virtual_env",
295
+ title="Virtual Environment",
296
+ description="Check if running in a virtual environment",
297
+ category=CheckCategory.ENVIRONMENT,
298
+ severity=CheckSeverity.INFO,
299
+ )
300
+ def check_virtual_env(config: DoctorConfig) -> CheckResult:
301
+ """Check if running in a virtual environment."""
302
+ venv = os.environ.get("VIRTUAL_ENV")
303
+ conda_env = os.environ.get("CONDA_DEFAULT_ENV")
304
+
305
+ if venv:
306
+ return CheckResult(
307
+ id="virtual_env",
308
+ title="Virtual Environment",
309
+ category=CheckCategory.ENVIRONMENT,
310
+ status=CheckStatus.PASS,
311
+ message=f"Running in venv: {os.path.basename(venv)}",
312
+ metadata={"venv_path": venv},
313
+ )
314
+ elif conda_env:
315
+ return CheckResult(
316
+ id="virtual_env",
317
+ title="Virtual Environment",
318
+ category=CheckCategory.ENVIRONMENT,
319
+ status=CheckStatus.PASS,
320
+ message=f"Running in conda env: {conda_env}",
321
+ metadata={"conda_env": conda_env},
322
+ )
323
+ else:
324
+ return CheckResult(
325
+ id="virtual_env",
326
+ title="Virtual Environment",
327
+ category=CheckCategory.ENVIRONMENT,
328
+ status=CheckStatus.WARN,
329
+ message="Not running in a virtual environment",
330
+ remediation="Consider using a virtual environment for isolation",
331
+ )
332
+
333
+
334
+ @register_check(
335
+ id="git_available",
336
+ title="Git",
337
+ description="Check if git is available",
338
+ category=CheckCategory.ENVIRONMENT,
339
+ severity=CheckSeverity.LOW,
340
+ )
341
+ def check_git_available(config: DoctorConfig) -> CheckResult:
342
+ """Check if git is available."""
343
+ git_path = shutil.which("git")
344
+
345
+ if git_path:
346
+ import subprocess
347
+ try:
348
+ result = subprocess.run(
349
+ ["git", "--version"],
350
+ capture_output=True,
351
+ text=True,
352
+ timeout=5,
353
+ )
354
+ version = result.stdout.strip() if result.returncode == 0 else "unknown"
355
+ return CheckResult(
356
+ id="git_available",
357
+ title="Git",
358
+ category=CheckCategory.ENVIRONMENT,
359
+ status=CheckStatus.PASS,
360
+ message=f"Git available: {version}",
361
+ metadata={"path": git_path},
362
+ )
363
+ except Exception:
364
+ return CheckResult(
365
+ id="git_available",
366
+ title="Git",
367
+ category=CheckCategory.ENVIRONMENT,
368
+ status=CheckStatus.PASS,
369
+ message=f"Git available at {git_path}",
370
+ )
371
+ else:
372
+ return CheckResult(
373
+ id="git_available",
374
+ title="Git",
375
+ category=CheckCategory.ENVIRONMENT,
376
+ status=CheckStatus.WARN,
377
+ message="Git not found in PATH",
378
+ remediation="Install git for version control features",
379
+ )
380
+
381
+
382
+ @register_check(
383
+ id="docker_available",
384
+ title="Docker",
385
+ description="Check if Docker is available",
386
+ category=CheckCategory.ENVIRONMENT,
387
+ severity=CheckSeverity.LOW,
388
+ )
389
+ def check_docker_available(config: DoctorConfig) -> CheckResult:
390
+ """Check if Docker is available."""
391
+ docker_path = shutil.which("docker")
392
+
393
+ if docker_path:
394
+ import subprocess
395
+ try:
396
+ result = subprocess.run(
397
+ ["docker", "--version"],
398
+ capture_output=True,
399
+ text=True,
400
+ timeout=5,
401
+ )
402
+ if result.returncode == 0:
403
+ version = result.stdout.strip()
404
+ return CheckResult(
405
+ id="docker_available",
406
+ title="Docker",
407
+ category=CheckCategory.ENVIRONMENT,
408
+ status=CheckStatus.PASS,
409
+ message=f"Docker available: {version}",
410
+ metadata={"path": docker_path},
411
+ )
412
+ except Exception:
413
+ pass
414
+
415
+ return CheckResult(
416
+ id="docker_available",
417
+ title="Docker",
418
+ category=CheckCategory.ENVIRONMENT,
419
+ status=CheckStatus.WARN,
420
+ message="Docker found but not responding",
421
+ remediation="Ensure Docker daemon is running",
422
+ )
423
+ else:
424
+ return CheckResult(
425
+ id="docker_available",
426
+ title="Docker",
427
+ category=CheckCategory.ENVIRONMENT,
428
+ status=CheckStatus.SKIP,
429
+ message="Docker not found (optional)",
430
+ )
431
+
432
+
433
+ @register_check(
434
+ id="npx_available",
435
+ title="npx (Node.js)",
436
+ description="Check if npx is available for MCP servers",
437
+ category=CheckCategory.ENVIRONMENT,
438
+ severity=CheckSeverity.LOW,
439
+ )
440
+ def check_npx_available(config: DoctorConfig) -> CheckResult:
441
+ """Check if npx is available for MCP servers."""
442
+ npx_path = shutil.which("npx")
443
+
444
+ if npx_path:
445
+ return CheckResult(
446
+ id="npx_available",
447
+ title="npx (Node.js)",
448
+ category=CheckCategory.ENVIRONMENT,
449
+ status=CheckStatus.PASS,
450
+ message=f"npx available at {npx_path}",
451
+ metadata={"path": npx_path},
452
+ )
453
+ else:
454
+ return CheckResult(
455
+ id="npx_available",
456
+ title="npx (Node.js)",
457
+ category=CheckCategory.ENVIRONMENT,
458
+ status=CheckStatus.WARN,
459
+ message="npx not found (required for some MCP servers)",
460
+ remediation="Install Node.js to use MCP servers that require npx",
461
+ )
462
+
463
+
464
+ @register_check(
465
+ id="optional_deps",
466
+ title="Optional Dependencies",
467
+ description="Check availability of optional dependencies",
468
+ category=CheckCategory.ENVIRONMENT,
469
+ severity=CheckSeverity.INFO,
470
+ requires_deep=True,
471
+ )
472
+ def check_optional_deps(config: DoctorConfig) -> CheckResult:
473
+ """Check availability of optional dependencies."""
474
+ optional_packages = [
475
+ ("chromadb", "Knowledge/RAG features"),
476
+ ("mem0ai", "Memory features"),
477
+ ("litellm", "Multi-provider LLM support"),
478
+ ("praisonaiui", "aiui (Chat/Dashboard UI)"),
479
+ ("gradio", "Gradio UI"),
480
+ ("crawl4ai", "Web crawling"),
481
+ ("tavily", "Tavily search"),
482
+ ("duckduckgo_search", "DuckDuckGo search"),
483
+ ]
484
+
485
+ available = []
486
+ missing = []
487
+
488
+ for package, description in optional_packages:
489
+ try:
490
+ __import__(package)
491
+ available.append(package)
492
+ except ImportError:
493
+ missing.append(f"{package} ({description})")
494
+
495
+ if missing:
496
+ return CheckResult(
497
+ id="optional_deps",
498
+ title="Optional Dependencies",
499
+ category=CheckCategory.ENVIRONMENT,
500
+ status=CheckStatus.PASS,
501
+ message=f"{len(available)} optional packages available, {len(missing)} not installed",
502
+ details=f"Missing: {', '.join(missing[:5])}{'...' if len(missing) > 5 else ''}",
503
+ metadata={"available": available, "missing": missing},
504
+ )
505
+ else:
506
+ return CheckResult(
507
+ id="optional_deps",
508
+ title="Optional Dependencies",
509
+ category=CheckCategory.ENVIRONMENT,
510
+ status=CheckStatus.PASS,
511
+ message=f"All {len(available)} optional packages available",
512
+ metadata={"available": available},
513
+ )
514
+
515
+
516
+ @register_check(
517
+ id="stale_packages",
518
+ title="Stale Package Artifacts",
519
+ description="Check for stale praisonaiagents artifacts in site-packages",
520
+ category=CheckCategory.ENVIRONMENT,
521
+ severity=CheckSeverity.CRITICAL,
522
+ )
523
+ def check_stale_packages(config: DoctorConfig) -> CheckResult:
524
+ """
525
+ Check for stale praisonaiagents artifacts in site-packages.
526
+
527
+ This detects namespace package shadowing issues where a stale directory
528
+ in site-packages (without __init__.py) shadows the real package.
529
+ """
530
+ import site
531
+
532
+ stale_dirs = []
533
+
534
+ # Check all site-packages directories
535
+ try:
536
+ site_packages = site.getsitepackages()
537
+ except AttributeError:
538
+ # Some environments don't have getsitepackages
539
+ site_packages = []
540
+
541
+ # Also check user site-packages
542
+ user_site = site.getusersitepackages() if hasattr(site, 'getusersitepackages') else None
543
+ if user_site:
544
+ site_packages = list(site_packages) + [user_site]
545
+
546
+ for sp in site_packages:
547
+ praisonai_dir = os.path.join(sp, 'praisonaiagents')
548
+ if os.path.isdir(praisonai_dir):
549
+ init_path = os.path.join(praisonai_dir, '__init__.py')
550
+ if not os.path.exists(init_path):
551
+ # This is a stale namespace package directory
552
+ stale_dirs.append(praisonai_dir)
553
+
554
+ if stale_dirs:
555
+ return CheckResult(
556
+ id="stale_packages",
557
+ title="Stale Package Artifacts",
558
+ category=CheckCategory.ENVIRONMENT,
559
+ status=CheckStatus.FAIL,
560
+ message=f"Found {len(stale_dirs)} stale praisonaiagents directory(ies)",
561
+ details=f"Stale directories: {', '.join(stale_dirs)}",
562
+ remediation=(
563
+ "Remove stale directories to fix import errors:\n"
564
+ f" rm -rf {stale_dirs[0]}\n"
565
+ "Then reinstall: pip install praisonaiagents"
566
+ ),
567
+ severity=CheckSeverity.CRITICAL,
568
+ metadata={"stale_dirs": stale_dirs},
569
+ )
570
+
571
+ # Also verify the package loads correctly (not as namespace)
572
+ try:
573
+ import praisonaiagents
574
+ if praisonaiagents.__file__ is None:
575
+ return CheckResult(
576
+ id="stale_packages",
577
+ title="Stale Package Artifacts",
578
+ category=CheckCategory.ENVIRONMENT,
579
+ status=CheckStatus.FAIL,
580
+ message="praisonaiagents loaded as namespace package",
581
+ details="__file__ is None, indicating namespace package shadowing",
582
+ remediation=(
583
+ "Remove stale praisonaiagents directory from site-packages:\n"
584
+ " rm -rf $(python -c \"import site; print(site.getsitepackages()[0])\")/praisonaiagents/\n"
585
+ "Then reinstall: pip install praisonaiagents"
586
+ ),
587
+ severity=CheckSeverity.CRITICAL,
588
+ )
589
+ except ImportError:
590
+ pass # Package not installed - handled by praisonaiagents_package check
591
+
592
+ return CheckResult(
593
+ id="stale_packages",
594
+ title="Stale Package Artifacts",
595
+ category=CheckCategory.ENVIRONMENT,
596
+ status=CheckStatus.PASS,
597
+ message="No stale package artifacts found",
598
+ )
599
+
600
+
601
+ @register_check(
602
+ id="model_env_vars",
603
+ title="Model Configuration",
604
+ description="Check model-related environment variables",
605
+ category=CheckCategory.ENVIRONMENT,
606
+ severity=CheckSeverity.INFO,
607
+ )
608
+ def check_model_env_vars(config: DoctorConfig) -> CheckResult:
609
+ """Check model-related environment variables."""
610
+ model_vars = {
611
+ "MODEL_NAME": os.environ.get("MODEL_NAME"),
612
+ "OPENAI_MODEL_NAME": os.environ.get("OPENAI_MODEL_NAME"),
613
+ "OPENAI_BASE_URL": os.environ.get("OPENAI_BASE_URL"),
614
+ "OPENAI_API_BASE": os.environ.get("OPENAI_API_BASE"),
615
+ }
616
+
617
+ configured = {k: v for k, v in model_vars.items() if v}
618
+
619
+ if configured:
620
+ details = ", ".join(f"{k}={v}" for k, v in configured.items())
621
+ return CheckResult(
622
+ id="model_env_vars",
623
+ title="Model Configuration",
624
+ category=CheckCategory.ENVIRONMENT,
625
+ status=CheckStatus.PASS,
626
+ message=f"Model config: {details}",
627
+ metadata=configured,
628
+ )
629
+ else:
630
+ return CheckResult(
631
+ id="model_env_vars",
632
+ title="Model Configuration",
633
+ category=CheckCategory.ENVIRONMENT,
634
+ status=CheckStatus.PASS,
635
+ message="Using default model configuration (gpt-4o-mini)",
636
+ metadata={"default_model": "gpt-4o-mini"},
637
+ )