PraisonAI 3.0.0__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 (393) hide show
  1. praisonai/__init__.py +54 -0
  2. praisonai/__main__.py +15 -0
  3. praisonai/acp/__init__.py +54 -0
  4. praisonai/acp/config.py +159 -0
  5. praisonai/acp/server.py +587 -0
  6. praisonai/acp/session.py +219 -0
  7. praisonai/adapters/__init__.py +50 -0
  8. praisonai/adapters/readers.py +395 -0
  9. praisonai/adapters/rerankers.py +315 -0
  10. praisonai/adapters/retrievers.py +394 -0
  11. praisonai/adapters/vector_stores.py +409 -0
  12. praisonai/agent_scheduler.py +337 -0
  13. praisonai/agents_generator.py +903 -0
  14. praisonai/api/call.py +292 -0
  15. praisonai/auto.py +1197 -0
  16. praisonai/capabilities/__init__.py +275 -0
  17. praisonai/capabilities/a2a.py +140 -0
  18. praisonai/capabilities/assistants.py +283 -0
  19. praisonai/capabilities/audio.py +320 -0
  20. praisonai/capabilities/batches.py +469 -0
  21. praisonai/capabilities/completions.py +336 -0
  22. praisonai/capabilities/container_files.py +155 -0
  23. praisonai/capabilities/containers.py +93 -0
  24. praisonai/capabilities/embeddings.py +158 -0
  25. praisonai/capabilities/files.py +467 -0
  26. praisonai/capabilities/fine_tuning.py +293 -0
  27. praisonai/capabilities/guardrails.py +182 -0
  28. praisonai/capabilities/images.py +330 -0
  29. praisonai/capabilities/mcp.py +190 -0
  30. praisonai/capabilities/messages.py +270 -0
  31. praisonai/capabilities/moderations.py +154 -0
  32. praisonai/capabilities/ocr.py +217 -0
  33. praisonai/capabilities/passthrough.py +204 -0
  34. praisonai/capabilities/rag.py +207 -0
  35. praisonai/capabilities/realtime.py +160 -0
  36. praisonai/capabilities/rerank.py +165 -0
  37. praisonai/capabilities/responses.py +266 -0
  38. praisonai/capabilities/search.py +109 -0
  39. praisonai/capabilities/skills.py +133 -0
  40. praisonai/capabilities/vector_store_files.py +334 -0
  41. praisonai/capabilities/vector_stores.py +304 -0
  42. praisonai/capabilities/videos.py +141 -0
  43. praisonai/chainlit_ui.py +304 -0
  44. praisonai/chat/__init__.py +106 -0
  45. praisonai/chat/app.py +125 -0
  46. praisonai/cli/__init__.py +26 -0
  47. praisonai/cli/app.py +213 -0
  48. praisonai/cli/commands/__init__.py +75 -0
  49. praisonai/cli/commands/acp.py +70 -0
  50. praisonai/cli/commands/completion.py +333 -0
  51. praisonai/cli/commands/config.py +166 -0
  52. praisonai/cli/commands/debug.py +142 -0
  53. praisonai/cli/commands/diag.py +55 -0
  54. praisonai/cli/commands/doctor.py +166 -0
  55. praisonai/cli/commands/environment.py +179 -0
  56. praisonai/cli/commands/lsp.py +112 -0
  57. praisonai/cli/commands/mcp.py +210 -0
  58. praisonai/cli/commands/profile.py +457 -0
  59. praisonai/cli/commands/run.py +228 -0
  60. praisonai/cli/commands/schedule.py +150 -0
  61. praisonai/cli/commands/serve.py +97 -0
  62. praisonai/cli/commands/session.py +212 -0
  63. praisonai/cli/commands/traces.py +145 -0
  64. praisonai/cli/commands/version.py +101 -0
  65. praisonai/cli/configuration/__init__.py +18 -0
  66. praisonai/cli/configuration/loader.py +353 -0
  67. praisonai/cli/configuration/paths.py +114 -0
  68. praisonai/cli/configuration/schema.py +164 -0
  69. praisonai/cli/features/__init__.py +268 -0
  70. praisonai/cli/features/acp.py +236 -0
  71. praisonai/cli/features/action_orchestrator.py +546 -0
  72. praisonai/cli/features/agent_scheduler.py +773 -0
  73. praisonai/cli/features/agent_tools.py +474 -0
  74. praisonai/cli/features/agents.py +375 -0
  75. praisonai/cli/features/at_mentions.py +471 -0
  76. praisonai/cli/features/auto_memory.py +182 -0
  77. praisonai/cli/features/autonomy_mode.py +490 -0
  78. praisonai/cli/features/background.py +356 -0
  79. praisonai/cli/features/base.py +168 -0
  80. praisonai/cli/features/capabilities.py +1326 -0
  81. praisonai/cli/features/checkpoints.py +338 -0
  82. praisonai/cli/features/code_intelligence.py +652 -0
  83. praisonai/cli/features/compaction.py +294 -0
  84. praisonai/cli/features/compare.py +534 -0
  85. praisonai/cli/features/cost_tracker.py +514 -0
  86. praisonai/cli/features/debug.py +810 -0
  87. praisonai/cli/features/deploy.py +517 -0
  88. praisonai/cli/features/diag.py +289 -0
  89. praisonai/cli/features/doctor/__init__.py +63 -0
  90. praisonai/cli/features/doctor/checks/__init__.py +24 -0
  91. praisonai/cli/features/doctor/checks/acp_checks.py +240 -0
  92. praisonai/cli/features/doctor/checks/config_checks.py +366 -0
  93. praisonai/cli/features/doctor/checks/db_checks.py +366 -0
  94. praisonai/cli/features/doctor/checks/env_checks.py +543 -0
  95. praisonai/cli/features/doctor/checks/lsp_checks.py +199 -0
  96. praisonai/cli/features/doctor/checks/mcp_checks.py +349 -0
  97. praisonai/cli/features/doctor/checks/memory_checks.py +268 -0
  98. praisonai/cli/features/doctor/checks/network_checks.py +251 -0
  99. praisonai/cli/features/doctor/checks/obs_checks.py +328 -0
  100. praisonai/cli/features/doctor/checks/performance_checks.py +235 -0
  101. praisonai/cli/features/doctor/checks/permissions_checks.py +259 -0
  102. praisonai/cli/features/doctor/checks/selftest_checks.py +322 -0
  103. praisonai/cli/features/doctor/checks/serve_checks.py +426 -0
  104. praisonai/cli/features/doctor/checks/skills_checks.py +231 -0
  105. praisonai/cli/features/doctor/checks/tools_checks.py +371 -0
  106. praisonai/cli/features/doctor/engine.py +266 -0
  107. praisonai/cli/features/doctor/formatters.py +310 -0
  108. praisonai/cli/features/doctor/handler.py +397 -0
  109. praisonai/cli/features/doctor/models.py +264 -0
  110. praisonai/cli/features/doctor/registry.py +239 -0
  111. praisonai/cli/features/endpoints.py +1019 -0
  112. praisonai/cli/features/eval.py +560 -0
  113. praisonai/cli/features/external_agents.py +231 -0
  114. praisonai/cli/features/fast_context.py +410 -0
  115. praisonai/cli/features/flow_display.py +566 -0
  116. praisonai/cli/features/git_integration.py +651 -0
  117. praisonai/cli/features/guardrail.py +171 -0
  118. praisonai/cli/features/handoff.py +185 -0
  119. praisonai/cli/features/hooks.py +583 -0
  120. praisonai/cli/features/image.py +384 -0
  121. praisonai/cli/features/interactive_runtime.py +585 -0
  122. praisonai/cli/features/interactive_tools.py +380 -0
  123. praisonai/cli/features/interactive_tui.py +603 -0
  124. praisonai/cli/features/jobs.py +632 -0
  125. praisonai/cli/features/knowledge.py +531 -0
  126. praisonai/cli/features/lite.py +244 -0
  127. praisonai/cli/features/lsp_cli.py +225 -0
  128. praisonai/cli/features/mcp.py +169 -0
  129. praisonai/cli/features/message_queue.py +587 -0
  130. praisonai/cli/features/metrics.py +211 -0
  131. praisonai/cli/features/n8n.py +673 -0
  132. praisonai/cli/features/observability.py +293 -0
  133. praisonai/cli/features/ollama.py +361 -0
  134. praisonai/cli/features/output_style.py +273 -0
  135. praisonai/cli/features/package.py +631 -0
  136. praisonai/cli/features/performance.py +308 -0
  137. praisonai/cli/features/persistence.py +636 -0
  138. praisonai/cli/features/profile.py +226 -0
  139. praisonai/cli/features/profiler/__init__.py +81 -0
  140. praisonai/cli/features/profiler/core.py +558 -0
  141. praisonai/cli/features/profiler/optimizations.py +652 -0
  142. praisonai/cli/features/profiler/suite.py +386 -0
  143. praisonai/cli/features/profiling.py +350 -0
  144. praisonai/cli/features/queue/__init__.py +73 -0
  145. praisonai/cli/features/queue/manager.py +395 -0
  146. praisonai/cli/features/queue/models.py +286 -0
  147. praisonai/cli/features/queue/persistence.py +564 -0
  148. praisonai/cli/features/queue/scheduler.py +484 -0
  149. praisonai/cli/features/queue/worker.py +372 -0
  150. praisonai/cli/features/recipe.py +1723 -0
  151. praisonai/cli/features/recipes.py +449 -0
  152. praisonai/cli/features/registry.py +229 -0
  153. praisonai/cli/features/repo_map.py +860 -0
  154. praisonai/cli/features/router.py +466 -0
  155. praisonai/cli/features/sandbox_executor.py +515 -0
  156. praisonai/cli/features/serve.py +829 -0
  157. praisonai/cli/features/session.py +222 -0
  158. praisonai/cli/features/skills.py +856 -0
  159. praisonai/cli/features/slash_commands.py +650 -0
  160. praisonai/cli/features/telemetry.py +179 -0
  161. praisonai/cli/features/templates.py +1384 -0
  162. praisonai/cli/features/thinking.py +305 -0
  163. praisonai/cli/features/todo.py +334 -0
  164. praisonai/cli/features/tools.py +680 -0
  165. praisonai/cli/features/tui/__init__.py +83 -0
  166. praisonai/cli/features/tui/app.py +580 -0
  167. praisonai/cli/features/tui/cli.py +566 -0
  168. praisonai/cli/features/tui/debug.py +511 -0
  169. praisonai/cli/features/tui/events.py +99 -0
  170. praisonai/cli/features/tui/mock_provider.py +328 -0
  171. praisonai/cli/features/tui/orchestrator.py +652 -0
  172. praisonai/cli/features/tui/screens/__init__.py +50 -0
  173. praisonai/cli/features/tui/screens/main.py +245 -0
  174. praisonai/cli/features/tui/screens/queue.py +174 -0
  175. praisonai/cli/features/tui/screens/session.py +124 -0
  176. praisonai/cli/features/tui/screens/settings.py +148 -0
  177. praisonai/cli/features/tui/widgets/__init__.py +56 -0
  178. praisonai/cli/features/tui/widgets/chat.py +261 -0
  179. praisonai/cli/features/tui/widgets/composer.py +224 -0
  180. praisonai/cli/features/tui/widgets/queue_panel.py +200 -0
  181. praisonai/cli/features/tui/widgets/status.py +167 -0
  182. praisonai/cli/features/tui/widgets/tool_panel.py +248 -0
  183. praisonai/cli/features/workflow.py +720 -0
  184. praisonai/cli/legacy.py +236 -0
  185. praisonai/cli/main.py +5559 -0
  186. praisonai/cli/schedule_cli.py +54 -0
  187. praisonai/cli/state/__init__.py +31 -0
  188. praisonai/cli/state/identifiers.py +161 -0
  189. praisonai/cli/state/sessions.py +313 -0
  190. praisonai/code/__init__.py +93 -0
  191. praisonai/code/agent_tools.py +344 -0
  192. praisonai/code/diff/__init__.py +21 -0
  193. praisonai/code/diff/diff_strategy.py +432 -0
  194. praisonai/code/tools/__init__.py +27 -0
  195. praisonai/code/tools/apply_diff.py +221 -0
  196. praisonai/code/tools/execute_command.py +275 -0
  197. praisonai/code/tools/list_files.py +274 -0
  198. praisonai/code/tools/read_file.py +206 -0
  199. praisonai/code/tools/search_replace.py +248 -0
  200. praisonai/code/tools/write_file.py +217 -0
  201. praisonai/code/utils/__init__.py +46 -0
  202. praisonai/code/utils/file_utils.py +307 -0
  203. praisonai/code/utils/ignore_utils.py +308 -0
  204. praisonai/code/utils/text_utils.py +276 -0
  205. praisonai/db/__init__.py +64 -0
  206. praisonai/db/adapter.py +531 -0
  207. praisonai/deploy/__init__.py +62 -0
  208. praisonai/deploy/api.py +231 -0
  209. praisonai/deploy/docker.py +454 -0
  210. praisonai/deploy/doctor.py +367 -0
  211. praisonai/deploy/main.py +327 -0
  212. praisonai/deploy/models.py +179 -0
  213. praisonai/deploy/providers/__init__.py +33 -0
  214. praisonai/deploy/providers/aws.py +331 -0
  215. praisonai/deploy/providers/azure.py +358 -0
  216. praisonai/deploy/providers/base.py +101 -0
  217. praisonai/deploy/providers/gcp.py +314 -0
  218. praisonai/deploy/schema.py +208 -0
  219. praisonai/deploy.py +185 -0
  220. praisonai/endpoints/__init__.py +53 -0
  221. praisonai/endpoints/a2u_server.py +410 -0
  222. praisonai/endpoints/discovery.py +165 -0
  223. praisonai/endpoints/providers/__init__.py +28 -0
  224. praisonai/endpoints/providers/a2a.py +253 -0
  225. praisonai/endpoints/providers/a2u.py +208 -0
  226. praisonai/endpoints/providers/agents_api.py +171 -0
  227. praisonai/endpoints/providers/base.py +231 -0
  228. praisonai/endpoints/providers/mcp.py +263 -0
  229. praisonai/endpoints/providers/recipe.py +206 -0
  230. praisonai/endpoints/providers/tools_mcp.py +150 -0
  231. praisonai/endpoints/registry.py +131 -0
  232. praisonai/endpoints/server.py +161 -0
  233. praisonai/inbuilt_tools/__init__.py +24 -0
  234. praisonai/inbuilt_tools/autogen_tools.py +117 -0
  235. praisonai/inc/__init__.py +2 -0
  236. praisonai/inc/config.py +96 -0
  237. praisonai/inc/models.py +155 -0
  238. praisonai/integrations/__init__.py +56 -0
  239. praisonai/integrations/base.py +303 -0
  240. praisonai/integrations/claude_code.py +270 -0
  241. praisonai/integrations/codex_cli.py +255 -0
  242. praisonai/integrations/cursor_cli.py +195 -0
  243. praisonai/integrations/gemini_cli.py +222 -0
  244. praisonai/jobs/__init__.py +67 -0
  245. praisonai/jobs/executor.py +425 -0
  246. praisonai/jobs/models.py +230 -0
  247. praisonai/jobs/router.py +314 -0
  248. praisonai/jobs/server.py +186 -0
  249. praisonai/jobs/store.py +203 -0
  250. praisonai/llm/__init__.py +66 -0
  251. praisonai/llm/registry.py +382 -0
  252. praisonai/mcp_server/__init__.py +152 -0
  253. praisonai/mcp_server/adapters/__init__.py +74 -0
  254. praisonai/mcp_server/adapters/agents.py +128 -0
  255. praisonai/mcp_server/adapters/capabilities.py +168 -0
  256. praisonai/mcp_server/adapters/cli_tools.py +568 -0
  257. praisonai/mcp_server/adapters/extended_capabilities.py +462 -0
  258. praisonai/mcp_server/adapters/knowledge.py +93 -0
  259. praisonai/mcp_server/adapters/memory.py +104 -0
  260. praisonai/mcp_server/adapters/prompts.py +306 -0
  261. praisonai/mcp_server/adapters/resources.py +124 -0
  262. praisonai/mcp_server/adapters/tools_bridge.py +280 -0
  263. praisonai/mcp_server/auth/__init__.py +48 -0
  264. praisonai/mcp_server/auth/api_key.py +291 -0
  265. praisonai/mcp_server/auth/oauth.py +460 -0
  266. praisonai/mcp_server/auth/oidc.py +289 -0
  267. praisonai/mcp_server/auth/scopes.py +260 -0
  268. praisonai/mcp_server/cli.py +852 -0
  269. praisonai/mcp_server/elicitation.py +445 -0
  270. praisonai/mcp_server/icons.py +302 -0
  271. praisonai/mcp_server/recipe_adapter.py +573 -0
  272. praisonai/mcp_server/recipe_cli.py +824 -0
  273. praisonai/mcp_server/registry.py +703 -0
  274. praisonai/mcp_server/sampling.py +422 -0
  275. praisonai/mcp_server/server.py +490 -0
  276. praisonai/mcp_server/tasks.py +443 -0
  277. praisonai/mcp_server/transports/__init__.py +18 -0
  278. praisonai/mcp_server/transports/http_stream.py +376 -0
  279. praisonai/mcp_server/transports/stdio.py +132 -0
  280. praisonai/persistence/__init__.py +84 -0
  281. praisonai/persistence/config.py +238 -0
  282. praisonai/persistence/conversation/__init__.py +25 -0
  283. praisonai/persistence/conversation/async_mysql.py +427 -0
  284. praisonai/persistence/conversation/async_postgres.py +410 -0
  285. praisonai/persistence/conversation/async_sqlite.py +371 -0
  286. praisonai/persistence/conversation/base.py +151 -0
  287. praisonai/persistence/conversation/json_store.py +250 -0
  288. praisonai/persistence/conversation/mysql.py +387 -0
  289. praisonai/persistence/conversation/postgres.py +401 -0
  290. praisonai/persistence/conversation/singlestore.py +240 -0
  291. praisonai/persistence/conversation/sqlite.py +341 -0
  292. praisonai/persistence/conversation/supabase.py +203 -0
  293. praisonai/persistence/conversation/surrealdb.py +287 -0
  294. praisonai/persistence/factory.py +301 -0
  295. praisonai/persistence/hooks/__init__.py +18 -0
  296. praisonai/persistence/hooks/agent_hooks.py +297 -0
  297. praisonai/persistence/knowledge/__init__.py +26 -0
  298. praisonai/persistence/knowledge/base.py +144 -0
  299. praisonai/persistence/knowledge/cassandra.py +232 -0
  300. praisonai/persistence/knowledge/chroma.py +295 -0
  301. praisonai/persistence/knowledge/clickhouse.py +242 -0
  302. praisonai/persistence/knowledge/cosmosdb_vector.py +438 -0
  303. praisonai/persistence/knowledge/couchbase.py +286 -0
  304. praisonai/persistence/knowledge/lancedb.py +216 -0
  305. praisonai/persistence/knowledge/langchain_adapter.py +291 -0
  306. praisonai/persistence/knowledge/lightrag_adapter.py +212 -0
  307. praisonai/persistence/knowledge/llamaindex_adapter.py +256 -0
  308. praisonai/persistence/knowledge/milvus.py +277 -0
  309. praisonai/persistence/knowledge/mongodb_vector.py +306 -0
  310. praisonai/persistence/knowledge/pgvector.py +335 -0
  311. praisonai/persistence/knowledge/pinecone.py +253 -0
  312. praisonai/persistence/knowledge/qdrant.py +301 -0
  313. praisonai/persistence/knowledge/redis_vector.py +291 -0
  314. praisonai/persistence/knowledge/singlestore_vector.py +299 -0
  315. praisonai/persistence/knowledge/surrealdb_vector.py +309 -0
  316. praisonai/persistence/knowledge/upstash_vector.py +266 -0
  317. praisonai/persistence/knowledge/weaviate.py +223 -0
  318. praisonai/persistence/migrations/__init__.py +10 -0
  319. praisonai/persistence/migrations/manager.py +251 -0
  320. praisonai/persistence/orchestrator.py +406 -0
  321. praisonai/persistence/state/__init__.py +21 -0
  322. praisonai/persistence/state/async_mongodb.py +200 -0
  323. praisonai/persistence/state/base.py +107 -0
  324. praisonai/persistence/state/dynamodb.py +226 -0
  325. praisonai/persistence/state/firestore.py +175 -0
  326. praisonai/persistence/state/gcs.py +155 -0
  327. praisonai/persistence/state/memory.py +245 -0
  328. praisonai/persistence/state/mongodb.py +158 -0
  329. praisonai/persistence/state/redis.py +190 -0
  330. praisonai/persistence/state/upstash.py +144 -0
  331. praisonai/persistence/tests/__init__.py +3 -0
  332. praisonai/persistence/tests/test_all_backends.py +633 -0
  333. praisonai/profiler.py +1214 -0
  334. praisonai/recipe/__init__.py +134 -0
  335. praisonai/recipe/bridge.py +278 -0
  336. praisonai/recipe/core.py +893 -0
  337. praisonai/recipe/exceptions.py +54 -0
  338. praisonai/recipe/history.py +402 -0
  339. praisonai/recipe/models.py +266 -0
  340. praisonai/recipe/operations.py +440 -0
  341. praisonai/recipe/policy.py +422 -0
  342. praisonai/recipe/registry.py +849 -0
  343. praisonai/recipe/runtime.py +214 -0
  344. praisonai/recipe/security.py +711 -0
  345. praisonai/recipe/serve.py +859 -0
  346. praisonai/recipe/server.py +613 -0
  347. praisonai/scheduler/__init__.py +45 -0
  348. praisonai/scheduler/agent_scheduler.py +552 -0
  349. praisonai/scheduler/base.py +124 -0
  350. praisonai/scheduler/daemon_manager.py +225 -0
  351. praisonai/scheduler/state_manager.py +155 -0
  352. praisonai/scheduler/yaml_loader.py +193 -0
  353. praisonai/scheduler.py +194 -0
  354. praisonai/setup/__init__.py +1 -0
  355. praisonai/setup/build.py +21 -0
  356. praisonai/setup/post_install.py +23 -0
  357. praisonai/setup/setup_conda_env.py +25 -0
  358. praisonai/setup.py +16 -0
  359. praisonai/templates/__init__.py +116 -0
  360. praisonai/templates/cache.py +364 -0
  361. praisonai/templates/dependency_checker.py +358 -0
  362. praisonai/templates/discovery.py +391 -0
  363. praisonai/templates/loader.py +564 -0
  364. praisonai/templates/registry.py +511 -0
  365. praisonai/templates/resolver.py +206 -0
  366. praisonai/templates/security.py +327 -0
  367. praisonai/templates/tool_override.py +498 -0
  368. praisonai/templates/tools_doctor.py +256 -0
  369. praisonai/test.py +105 -0
  370. praisonai/train.py +562 -0
  371. praisonai/train_vision.py +306 -0
  372. praisonai/ui/agents.py +824 -0
  373. praisonai/ui/callbacks.py +57 -0
  374. praisonai/ui/chainlit_compat.py +246 -0
  375. praisonai/ui/chat.py +532 -0
  376. praisonai/ui/code.py +717 -0
  377. praisonai/ui/colab.py +474 -0
  378. praisonai/ui/colab_chainlit.py +81 -0
  379. praisonai/ui/components/aicoder.py +284 -0
  380. praisonai/ui/context.py +283 -0
  381. praisonai/ui/database_config.py +56 -0
  382. praisonai/ui/db.py +294 -0
  383. praisonai/ui/realtime.py +488 -0
  384. praisonai/ui/realtimeclient/__init__.py +756 -0
  385. praisonai/ui/realtimeclient/tools.py +242 -0
  386. praisonai/ui/sql_alchemy.py +710 -0
  387. praisonai/upload_vision.py +140 -0
  388. praisonai/version.py +1 -0
  389. praisonai-3.0.0.dist-info/METADATA +3493 -0
  390. praisonai-3.0.0.dist-info/RECORD +393 -0
  391. praisonai-3.0.0.dist-info/WHEEL +5 -0
  392. praisonai-3.0.0.dist-info/entry_points.txt +4 -0
  393. praisonai-3.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,585 @@
1
+ """
2
+ Interactive Runtime for PraisonAI.
3
+
4
+ Provides the core runtime that powers both TUI interactive mode and debug non-interactive mode.
5
+ Manages LSP and ACP subsystem lifecycle with graceful degradation.
6
+ """
7
+
8
+ import asyncio
9
+ import logging
10
+ import time
11
+ from dataclasses import dataclass, field
12
+ from enum import Enum
13
+ from pathlib import Path
14
+ from typing import Any, Dict, List, Optional
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ class SubsystemStatus(Enum):
20
+ """Status of a subsystem."""
21
+ NOT_STARTED = "not_started"
22
+ STARTING = "starting"
23
+ READY = "ready"
24
+ FAILED = "failed"
25
+ STOPPED = "stopped"
26
+
27
+
28
+ @dataclass
29
+ class SubsystemState:
30
+ """State of a subsystem."""
31
+ status: SubsystemStatus = SubsystemStatus.NOT_STARTED
32
+ error: Optional[str] = None
33
+ start_time: Optional[float] = None
34
+ ready_time: Optional[float] = None
35
+
36
+
37
+ @dataclass
38
+ class RuntimeConfig:
39
+ """Configuration for InteractiveRuntime."""
40
+ workspace: str = "."
41
+ lsp_enabled: bool = True
42
+ acp_enabled: bool = True
43
+ approval_mode: str = "manual" # manual, auto, scoped
44
+ trace_enabled: bool = False
45
+ trace_file: Optional[str] = None
46
+ json_output: bool = False
47
+ timeout: float = 60.0
48
+ model: Optional[str] = None
49
+ verbose: bool = False
50
+
51
+
52
+ @dataclass
53
+ class TraceEntry:
54
+ """A single trace entry."""
55
+ timestamp: float
56
+ category: str # lsp, acp, tool, llm, file
57
+ action: str
58
+ params: Dict[str, Any] = field(default_factory=dict)
59
+ result: Optional[Any] = None
60
+ duration_ms: Optional[float] = None
61
+ error: Optional[str] = None
62
+
63
+
64
+ @dataclass
65
+ class RuntimeTrace:
66
+ """Trace of a runtime session."""
67
+ version: str = "1.0"
68
+ start_time: float = field(default_factory=time.time)
69
+ end_time: Optional[float] = None
70
+ config: Optional[RuntimeConfig] = None
71
+ entries: List[TraceEntry] = field(default_factory=list)
72
+
73
+ def add_entry(self, category: str, action: str, params: Dict[str, Any] = None,
74
+ result: Any = None, duration_ms: float = None, error: str = None):
75
+ """Add a trace entry."""
76
+ self.entries.append(TraceEntry(
77
+ timestamp=time.time(),
78
+ category=category,
79
+ action=action,
80
+ params=params or {},
81
+ result=result,
82
+ duration_ms=duration_ms,
83
+ error=error
84
+ ))
85
+
86
+ def to_dict(self) -> Dict[str, Any]:
87
+ """Convert trace to dictionary."""
88
+ return {
89
+ "version": self.version,
90
+ "start_time": self.start_time,
91
+ "end_time": self.end_time,
92
+ "config": {
93
+ "workspace": self.config.workspace if self.config else None,
94
+ "lsp_enabled": self.config.lsp_enabled if self.config else None,
95
+ "acp_enabled": self.config.acp_enabled if self.config else None,
96
+ "approval_mode": self.config.approval_mode if self.config else None,
97
+ } if self.config else None,
98
+ "entries": [
99
+ {
100
+ "timestamp": e.timestamp,
101
+ "category": e.category,
102
+ "action": e.action,
103
+ "params": e.params,
104
+ "result": e.result,
105
+ "duration_ms": e.duration_ms,
106
+ "error": e.error
107
+ }
108
+ for e in self.entries
109
+ ]
110
+ }
111
+
112
+
113
+ class InteractiveRuntime:
114
+ """
115
+ Core runtime for interactive coding assistant.
116
+
117
+ Manages:
118
+ - LSP subsystem for code intelligence
119
+ - ACP subsystem for action orchestration
120
+ - Trace collection for debugging
121
+ - Graceful degradation when subsystems fail
122
+ """
123
+
124
+ def __init__(self, config: RuntimeConfig = None):
125
+ """Initialize the runtime."""
126
+ self.config = config or RuntimeConfig()
127
+ self._lsp_state = SubsystemState()
128
+ self._acp_state = SubsystemState()
129
+ self._lsp_client = None
130
+ self._acp_session = None
131
+ self._trace = RuntimeTrace(config=self.config) if self.config.trace_enabled else None
132
+ self._started = False
133
+ self._read_only = False # Enforced when ACP fails
134
+
135
+ @property
136
+ def lsp_ready(self) -> bool:
137
+ """Check if LSP is ready."""
138
+ return self._lsp_state.status == SubsystemStatus.READY
139
+
140
+ @property
141
+ def acp_ready(self) -> bool:
142
+ """Check if ACP is ready."""
143
+ return self._acp_state.status == SubsystemStatus.READY
144
+
145
+ @property
146
+ def read_only(self) -> bool:
147
+ """Check if runtime is in read-only mode."""
148
+ return self._read_only or not self.acp_ready
149
+
150
+ def get_status(self) -> Dict[str, Any]:
151
+ """Get runtime status."""
152
+ return {
153
+ "started": self._started,
154
+ "workspace": self.config.workspace,
155
+ "lsp": {
156
+ "enabled": self.config.lsp_enabled,
157
+ "status": self._lsp_state.status.value,
158
+ "ready": self.lsp_ready,
159
+ "error": self._lsp_state.error
160
+ },
161
+ "acp": {
162
+ "enabled": self.config.acp_enabled,
163
+ "status": self._acp_state.status.value,
164
+ "ready": self.acp_ready,
165
+ "error": self._acp_state.error
166
+ },
167
+ "read_only": self.read_only,
168
+ "approval_mode": self.config.approval_mode
169
+ }
170
+
171
+ async def start(self) -> Dict[str, Any]:
172
+ """
173
+ Start the runtime and all enabled subsystems.
174
+
175
+ Returns status dict with subsystem states.
176
+ """
177
+ if self._started:
178
+ return self.get_status()
179
+
180
+ workspace = Path(self.config.workspace).resolve()
181
+ if not workspace.exists():
182
+ workspace.mkdir(parents=True, exist_ok=True)
183
+
184
+ self.config.workspace = str(workspace)
185
+
186
+ # Start subsystems in parallel
187
+ tasks = []
188
+ if self.config.lsp_enabled:
189
+ tasks.append(self._start_lsp())
190
+ if self.config.acp_enabled:
191
+ tasks.append(self._start_acp())
192
+
193
+ if tasks:
194
+ await asyncio.gather(*tasks, return_exceptions=True)
195
+
196
+ # Enforce read-only if ACP failed
197
+ if self.config.acp_enabled and not self.acp_ready:
198
+ self._read_only = True
199
+ logger.warning("ACP unavailable - runtime is READ-ONLY")
200
+
201
+ self._started = True
202
+
203
+ if self._trace:
204
+ self._trace.add_entry(
205
+ category="runtime",
206
+ action="start",
207
+ params={"workspace": self.config.workspace},
208
+ result=self.get_status()
209
+ )
210
+
211
+ return self.get_status()
212
+
213
+ async def _start_lsp(self):
214
+ """Start LSP subsystem."""
215
+ self._lsp_state.status = SubsystemStatus.STARTING
216
+ self._lsp_state.start_time = time.time()
217
+
218
+ try:
219
+ # Lazy import LSP client
220
+ from praisonaiagents.lsp import LSPClient
221
+
222
+ # Detect language from workspace
223
+ language = self._detect_workspace_language()
224
+
225
+ self._lsp_client = LSPClient(
226
+ language=language,
227
+ root_uri=f"file://{self.config.workspace}"
228
+ )
229
+ await self._lsp_client.start()
230
+
231
+ # Wait for initialization
232
+ if self._lsp_client.is_running:
233
+ self._lsp_state.status = SubsystemStatus.READY
234
+ self._lsp_state.ready_time = time.time()
235
+ logger.info(f"LSP ready for {language}")
236
+ else:
237
+ raise RuntimeError("LSP client failed to start")
238
+
239
+ except ImportError as e:
240
+ self._lsp_state.status = SubsystemStatus.FAILED
241
+ self._lsp_state.error = f"LSP module not available: {e}"
242
+ logger.warning(self._lsp_state.error)
243
+ except Exception as e:
244
+ self._lsp_state.status = SubsystemStatus.FAILED
245
+ self._lsp_state.error = str(e)
246
+ logger.warning(f"LSP failed to start: {e}")
247
+
248
+ async def _start_acp(self):
249
+ """Start ACP subsystem (in-process session)."""
250
+ self._acp_state.status = SubsystemStatus.STARTING
251
+ self._acp_state.start_time = time.time()
252
+
253
+ try:
254
+ # Lazy import ACP
255
+ from praisonai.acp.session import ACPSession
256
+ from pathlib import Path
257
+
258
+ # Create a session for the workspace
259
+ self._acp_session = ACPSession.create(
260
+ workspace=Path(self.config.workspace)
261
+ )
262
+ self._acp_session.mode = self.config.approval_mode
263
+
264
+ self._acp_state.status = SubsystemStatus.READY
265
+ self._acp_state.ready_time = time.time()
266
+ logger.info("ACP session ready")
267
+
268
+ except ImportError as e:
269
+ self._acp_state.status = SubsystemStatus.FAILED
270
+ self._acp_state.error = f"ACP module not available: {e}"
271
+ logger.warning(self._acp_state.error)
272
+ except Exception as e:
273
+ self._acp_state.status = SubsystemStatus.FAILED
274
+ self._acp_state.error = str(e)
275
+ logger.warning(f"ACP failed to start: {e}")
276
+
277
+ async def stop(self):
278
+ """Stop the runtime and all subsystems."""
279
+ if self._lsp_client:
280
+ try:
281
+ await self._lsp_client.stop()
282
+ except Exception as e:
283
+ logger.warning(f"Error stopping LSP: {e}")
284
+ self._lsp_state.status = SubsystemStatus.STOPPED
285
+
286
+ if self._acp_session:
287
+ # ACPSession is a dataclass, no close method needed
288
+ self._acp_session = None
289
+ self._acp_state.status = SubsystemStatus.STOPPED
290
+
291
+ if self._trace:
292
+ self._trace.end_time = time.time()
293
+ self._trace.add_entry(
294
+ category="runtime",
295
+ action="stop",
296
+ result=self.get_status()
297
+ )
298
+
299
+ self._started = False
300
+
301
+ def _detect_workspace_language(self) -> str:
302
+ """Detect primary language in workspace."""
303
+ workspace = Path(self.config.workspace)
304
+
305
+ # Count files by extension
306
+ extensions = {}
307
+ for ext in [".py", ".js", ".ts", ".tsx", ".jsx", ".go", ".rs", ".java"]:
308
+ count = len(list(workspace.rglob(f"*{ext}")))
309
+ if count > 0:
310
+ extensions[ext] = count
311
+
312
+ if not extensions:
313
+ return "python" # Default
314
+
315
+ # Map extensions to languages
316
+ ext_to_lang = {
317
+ ".py": "python",
318
+ ".js": "javascript",
319
+ ".ts": "typescript",
320
+ ".tsx": "typescript",
321
+ ".jsx": "javascript",
322
+ ".go": "go",
323
+ ".rs": "rust",
324
+ ".java": "java"
325
+ }
326
+
327
+ # Return language with most files
328
+ top_ext = max(extensions, key=extensions.get)
329
+ return ext_to_lang.get(top_ext, "python")
330
+
331
+ # LSP Operations
332
+ async def lsp_get_symbols(self, file_path: str) -> List[Dict[str, Any]]:
333
+ """Get symbols in a file via LSP."""
334
+ if not self.lsp_ready:
335
+ return []
336
+
337
+ start = time.time()
338
+ try:
339
+ # Use workspace symbols or document symbols
340
+ result = await self._lsp_client.get_document_symbols(file_path)
341
+ duration = (time.time() - start) * 1000
342
+
343
+ if self._trace:
344
+ self._trace.add_entry(
345
+ category="lsp",
346
+ action="get_symbols",
347
+ params={"file": file_path},
348
+ result={"count": len(result) if result else 0},
349
+ duration_ms=duration
350
+ )
351
+
352
+ return result or []
353
+ except Exception as e:
354
+ if self._trace:
355
+ self._trace.add_entry(
356
+ category="lsp",
357
+ action="get_symbols",
358
+ params={"file": file_path},
359
+ error=str(e)
360
+ )
361
+ return []
362
+
363
+ async def lsp_get_definition(self, file_path: str, line: int, col: int) -> List[Dict[str, Any]]:
364
+ """Get definition location via LSP."""
365
+ if not self.lsp_ready:
366
+ return []
367
+
368
+ start = time.time()
369
+ try:
370
+ result = await self._lsp_client.get_definition(file_path, line, col)
371
+ duration = (time.time() - start) * 1000
372
+
373
+ if self._trace:
374
+ self._trace.add_entry(
375
+ category="lsp",
376
+ action="get_definition",
377
+ params={"file": file_path, "line": line, "col": col},
378
+ result={"count": len(result) if result else 0},
379
+ duration_ms=duration
380
+ )
381
+
382
+ return [loc.__dict__ if hasattr(loc, '__dict__') else loc for loc in (result or [])]
383
+ except Exception as e:
384
+ if self._trace:
385
+ self._trace.add_entry(
386
+ category="lsp",
387
+ action="get_definition",
388
+ params={"file": file_path, "line": line, "col": col},
389
+ error=str(e)
390
+ )
391
+ return []
392
+
393
+ async def lsp_get_references(self, file_path: str, line: int, col: int) -> List[Dict[str, Any]]:
394
+ """Get references via LSP."""
395
+ if not self.lsp_ready:
396
+ return []
397
+
398
+ start = time.time()
399
+ try:
400
+ result = await self._lsp_client.get_references(file_path, line, col)
401
+ duration = (time.time() - start) * 1000
402
+
403
+ if self._trace:
404
+ self._trace.add_entry(
405
+ category="lsp",
406
+ action="get_references",
407
+ params={"file": file_path, "line": line, "col": col},
408
+ result={"count": len(result) if result else 0},
409
+ duration_ms=duration
410
+ )
411
+
412
+ return [loc.__dict__ if hasattr(loc, '__dict__') else loc for loc in (result or [])]
413
+ except Exception as e:
414
+ if self._trace:
415
+ self._trace.add_entry(
416
+ category="lsp",
417
+ action="get_references",
418
+ params={"file": file_path, "line": line, "col": col},
419
+ error=str(e)
420
+ )
421
+ return []
422
+
423
+ async def lsp_get_diagnostics(self, file_path: str = None) -> List[Dict[str, Any]]:
424
+ """Get diagnostics via LSP."""
425
+ if not self.lsp_ready:
426
+ return []
427
+
428
+ start = time.time()
429
+ try:
430
+ if file_path:
431
+ result = await self._lsp_client.get_diagnostics(file_path)
432
+ else:
433
+ result = self._lsp_client._diagnostics # All cached diagnostics
434
+ duration = (time.time() - start) * 1000
435
+
436
+ if self._trace:
437
+ self._trace.add_entry(
438
+ category="lsp",
439
+ action="get_diagnostics",
440
+ params={"file": file_path},
441
+ result={"count": len(result) if result else 0},
442
+ duration_ms=duration
443
+ )
444
+
445
+ return [d.__dict__ if hasattr(d, '__dict__') else d for d in (result or [])]
446
+ except Exception as e:
447
+ if self._trace:
448
+ self._trace.add_entry(
449
+ category="lsp",
450
+ action="get_diagnostics",
451
+ params={"file": file_path},
452
+ error=str(e)
453
+ )
454
+ return []
455
+
456
+ # ACP Operations
457
+ async def acp_create_plan(self, prompt: str) -> Dict[str, Any]:
458
+ """Create an action plan via ACP session tracking."""
459
+ if not self.acp_ready:
460
+ return {"error": "ACP not available", "read_only": True}
461
+
462
+ start = time.time()
463
+ try:
464
+ # ACP session tracks the plan but doesn't create it
465
+ # Return empty steps - ActionOrchestrator handles actual planning
466
+ result = {"steps": [], "prompt": prompt}
467
+ duration = (time.time() - start) * 1000
468
+
469
+ if self._trace:
470
+ self._trace.add_entry(
471
+ category="acp",
472
+ action="create_plan",
473
+ params={"prompt": prompt[:100]},
474
+ result=result,
475
+ duration_ms=duration
476
+ )
477
+
478
+ return result
479
+ except Exception as e:
480
+ if self._trace:
481
+ self._trace.add_entry(
482
+ category="acp",
483
+ action="create_plan",
484
+ params={"prompt": prompt[:100]},
485
+ error=str(e)
486
+ )
487
+ return {"error": str(e)}
488
+
489
+ async def acp_apply_plan(self, plan: Dict[str, Any], auto_approve: bool = False) -> Dict[str, Any]:
490
+ """Apply an action plan via ACP session tracking."""
491
+ if self.read_only:
492
+ return {"error": "Runtime is read-only", "applied": False}
493
+
494
+ start = time.time()
495
+ try:
496
+ # Track the action in session
497
+ if self._acp_session:
498
+ self._acp_session.add_tool_call(
499
+ tool_call_id=f"plan_{int(time.time())}",
500
+ title="apply_plan",
501
+ status="applied" if auto_approve else "pending"
502
+ )
503
+
504
+ result = {"applied": True, "auto_approve": auto_approve}
505
+ duration = (time.time() - start) * 1000
506
+
507
+ if self._trace:
508
+ self._trace.add_entry(
509
+ category="acp",
510
+ action="apply_plan",
511
+ params={"plan_id": plan.get("id"), "auto_approve": auto_approve},
512
+ result=result,
513
+ duration_ms=duration
514
+ )
515
+
516
+ return result
517
+ except Exception as e:
518
+ if self._trace:
519
+ self._trace.add_entry(
520
+ category="acp",
521
+ action="apply_plan",
522
+ params={"plan_id": plan.get("id")},
523
+ error=str(e)
524
+ )
525
+ return {"error": str(e), "applied": False}
526
+
527
+ def get_trace(self) -> Optional[RuntimeTrace]:
528
+ """Get the runtime trace."""
529
+ return self._trace
530
+
531
+ def save_trace(self, path: str = None):
532
+ """Save trace to file."""
533
+ if not self._trace:
534
+ return
535
+
536
+ import json
537
+ path = path or self.config.trace_file or "praisonai_trace.json"
538
+ with open(path, "w") as f:
539
+ json.dump(self._trace.to_dict(), f, indent=2, default=str)
540
+ logger.info(f"Trace saved to {path}")
541
+
542
+
543
+ def create_runtime(
544
+ workspace: str = ".",
545
+ lsp: bool = True,
546
+ acp: bool = True,
547
+ approval: str = "manual",
548
+ trace: bool = False,
549
+ trace_file: str = None,
550
+ json_output: bool = False,
551
+ timeout: float = 60.0,
552
+ model: str = None,
553
+ verbose: bool = False
554
+ ) -> InteractiveRuntime:
555
+ """
556
+ Factory function to create an InteractiveRuntime.
557
+
558
+ Args:
559
+ workspace: Workspace root directory
560
+ lsp: Enable LSP subsystem
561
+ acp: Enable ACP subsystem
562
+ approval: Approval mode (manual, auto, scoped)
563
+ trace: Enable trace collection
564
+ trace_file: Path to save trace
565
+ json_output: Enable JSON output mode
566
+ timeout: Operation timeout
567
+ model: LLM model to use
568
+ verbose: Enable verbose output
569
+
570
+ Returns:
571
+ Configured InteractiveRuntime instance
572
+ """
573
+ config = RuntimeConfig(
574
+ workspace=workspace,
575
+ lsp_enabled=lsp,
576
+ acp_enabled=acp,
577
+ approval_mode=approval,
578
+ trace_enabled=trace,
579
+ trace_file=trace_file,
580
+ json_output=json_output,
581
+ timeout=timeout,
582
+ model=model,
583
+ verbose=verbose
584
+ )
585
+ return InteractiveRuntime(config)