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,83 @@
1
+ """
2
+ TUI (Terminal User Interface) for PraisonAI.
3
+
4
+ Provides an app-like interactive experience with:
5
+ - Event-loop driven async UI
6
+ - Multi-pane layout (chat, tools, queue, status)
7
+ - Streaming output
8
+ - Queue management
9
+ - Session persistence
10
+ - Headless simulation mode for CI/testing
11
+
12
+ Requires the [tui] extra: pip install praisonai[tui]
13
+ """
14
+
15
+ from typing import TYPE_CHECKING
16
+
17
+ if TYPE_CHECKING:
18
+ from .app import TUIApp
19
+ from .events import TUIEvent, TUIEventType
20
+ from .orchestrator import TuiOrchestrator, UIStateModel, SimulationRunner
21
+ from .mock_provider import MockProvider, MockProviderConfig
22
+
23
+ _lazy_cache = {}
24
+
25
+
26
+ def __getattr__(name: str):
27
+ """Lazy load TUI components."""
28
+ global _lazy_cache
29
+
30
+ if name in _lazy_cache:
31
+ return _lazy_cache[name]
32
+
33
+ if name == "TUIApp":
34
+ from .app import TUIApp
35
+ _lazy_cache[name] = TUIApp
36
+ return TUIApp
37
+ elif name == "TUIEvent":
38
+ from .events import TUIEvent
39
+ _lazy_cache[name] = TUIEvent
40
+ return TUIEvent
41
+ elif name == "TUIEventType":
42
+ from .events import TUIEventType
43
+ _lazy_cache[name] = TUIEventType
44
+ return TUIEventType
45
+ elif name == "run_tui":
46
+ from .app import run_tui
47
+ _lazy_cache[name] = run_tui
48
+ return run_tui
49
+ elif name == "TuiOrchestrator":
50
+ from .orchestrator import TuiOrchestrator
51
+ _lazy_cache[name] = TuiOrchestrator
52
+ return TuiOrchestrator
53
+ elif name == "UIStateModel":
54
+ from .orchestrator import UIStateModel
55
+ _lazy_cache[name] = UIStateModel
56
+ return UIStateModel
57
+ elif name == "SimulationRunner":
58
+ from .orchestrator import SimulationRunner
59
+ _lazy_cache[name] = SimulationRunner
60
+ return SimulationRunner
61
+ elif name == "MockProvider":
62
+ from .mock_provider import MockProvider
63
+ _lazy_cache[name] = MockProvider
64
+ return MockProvider
65
+ elif name == "MockProviderConfig":
66
+ from .mock_provider import MockProviderConfig
67
+ _lazy_cache[name] = MockProviderConfig
68
+ return MockProviderConfig
69
+
70
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
71
+
72
+
73
+ __all__ = [
74
+ "TUIApp",
75
+ "TUIEvent",
76
+ "TUIEventType",
77
+ "run_tui",
78
+ "TuiOrchestrator",
79
+ "UIStateModel",
80
+ "SimulationRunner",
81
+ "MockProvider",
82
+ "MockProviderConfig",
83
+ ]
@@ -0,0 +1,580 @@
1
+ """
2
+ Main TUI Application for PraisonAI.
3
+
4
+ Event-loop driven async TUI with multi-pane layout.
5
+ """
6
+
7
+ import asyncio
8
+ import logging
9
+ import os
10
+ import uuid
11
+ from typing import Any, Dict, List, Optional
12
+
13
+ try:
14
+ from textual.app import App, ComposeResult
15
+ from textual.binding import Binding
16
+ from textual.screen import Screen
17
+ from textual import events
18
+ TEXTUAL_AVAILABLE = True
19
+ except ImportError:
20
+ TEXTUAL_AVAILABLE = False
21
+ App = object
22
+
23
+ logger = logging.getLogger(__name__)
24
+
25
+
26
+ if TEXTUAL_AVAILABLE:
27
+ from .screens.main import MainScreen
28
+ from .screens.queue import QueueScreen
29
+ from .screens.settings import SettingsScreen
30
+ from .screens.session import SessionScreen
31
+ from .widgets.chat import ChatMessage
32
+ from .widgets.status import StatusInfo
33
+ from .widgets.queue_panel import QueueItem
34
+ from ..queue import QueueManager, QueueConfig, QueuedRun, RunPriority
35
+
36
+ class TUIApp(App):
37
+ """
38
+ PraisonAI Terminal User Interface Application.
39
+
40
+ Features:
41
+ - Event-loop driven async UI
42
+ - Multi-pane layout (chat, tools, queue, status)
43
+ - Streaming output
44
+ - Queue management
45
+ - Session persistence
46
+ """
47
+
48
+ TITLE = "PraisonAI"
49
+ SUB_TITLE = "AI Agent Terminal"
50
+
51
+ CSS = """
52
+ Screen {
53
+ background: $surface;
54
+ }
55
+ """
56
+
57
+ BINDINGS = [
58
+ Binding("ctrl+q", "quit", "Quit", show=True, priority=True),
59
+ Binding("ctrl+c", "cancel", "Cancel", show=True),
60
+ ]
61
+
62
+ SCREENS = {
63
+ "main": MainScreen,
64
+ "queue": QueueScreen,
65
+ "settings": SettingsScreen,
66
+ "sessions": SessionScreen,
67
+ }
68
+
69
+ def __init__(
70
+ self,
71
+ workspace: Optional[str] = None,
72
+ session_id: Optional[str] = None,
73
+ model: Optional[str] = None,
74
+ agent_config: Optional[Dict[str, Any]] = None,
75
+ queue_config: Optional[QueueConfig] = None,
76
+ enable_acp: bool = True,
77
+ enable_lsp: bool = True,
78
+ ):
79
+ super().__init__()
80
+
81
+ self.workspace = workspace or os.getcwd()
82
+ self.session_id = session_id or str(uuid.uuid4())[:8]
83
+ self.model = model or os.environ.get("OPENAI_MODEL", "gpt-4o-mini")
84
+ self.agent_config = agent_config or {}
85
+ self.enable_acp = enable_acp
86
+ self.enable_lsp = enable_lsp
87
+
88
+ # Load default interactive tools (ACP + LSP) if not provided
89
+ if "tools" not in self.agent_config or not self.agent_config["tools"]:
90
+ self.agent_config["tools"] = self._load_default_tools()
91
+
92
+ # Queue manager
93
+ self.queue_config = queue_config or QueueConfig()
94
+ self.queue_manager: Optional[QueueManager] = None
95
+
96
+ # State
97
+ self._current_run_id: Optional[str] = None
98
+ self._streaming_content: str = ""
99
+ self._total_tokens: int = 0
100
+ self._total_cost: float = 0.0
101
+
102
+ # Slash command handlers
103
+ self._command_handlers: Dict[str, callable] = {}
104
+ self._setup_commands()
105
+
106
+ def _load_default_tools(self) -> list:
107
+ """
108
+ Load default interactive tools (ACP + LSP) for TUI.
109
+
110
+ Uses the canonical interactive_tools provider.
111
+ """
112
+ try:
113
+ from ..interactive_tools import get_interactive_tools, ToolConfig
114
+
115
+ # Build disable list based on flags
116
+ disable = []
117
+ if not self.enable_acp:
118
+ disable.append('acp')
119
+ if not self.enable_lsp:
120
+ disable.append('lsp')
121
+
122
+ config = ToolConfig(
123
+ workspace=self.workspace,
124
+ enable_acp=self.enable_acp,
125
+ enable_lsp=self.enable_lsp,
126
+ )
127
+
128
+ tools = get_interactive_tools(
129
+ config=config,
130
+ disable=disable if disable else None,
131
+ )
132
+
133
+ logger.info(f"TUI loaded {len(tools)} default tools (ACP: {self.enable_acp}, LSP: {self.enable_lsp})")
134
+ return tools
135
+
136
+ except ImportError as e:
137
+ logger.warning(f"Could not load interactive tools: {e}")
138
+ return []
139
+ except Exception as e:
140
+ logger.error(f"Error loading default tools: {e}")
141
+ return []
142
+
143
+ def _setup_commands(self) -> None:
144
+ """Setup slash command handlers."""
145
+ self._command_handlers = {
146
+ "help": self._cmd_help,
147
+ "clear": self._cmd_clear,
148
+ "model": self._cmd_model,
149
+ "queue": self._cmd_queue,
150
+ "cancel": self._cmd_cancel,
151
+ "retry": self._cmd_retry,
152
+ "settings": self._cmd_settings,
153
+ "sessions": self._cmd_sessions,
154
+ "cost": self._cmd_cost,
155
+ "exit": self._cmd_exit,
156
+ "quit": self._cmd_exit,
157
+ }
158
+
159
+ async def on_mount(self) -> None:
160
+ """Handle app mount."""
161
+ # Initialize queue manager
162
+ self.queue_manager = QueueManager(
163
+ config=self.queue_config,
164
+ on_output=self._handle_output,
165
+ on_complete=self._handle_complete,
166
+ on_error=self._handle_error,
167
+ )
168
+
169
+ await self.queue_manager.start(recover=True)
170
+ self.queue_manager.set_session(self.session_id)
171
+
172
+ # Push main screen
173
+ await self.push_screen("main")
174
+
175
+ # Update status
176
+ self._update_status()
177
+
178
+ async def on_unmount(self) -> None:
179
+ """Handle app unmount."""
180
+ if self.queue_manager:
181
+ await self.queue_manager.stop()
182
+
183
+ def compose(self) -> ComposeResult:
184
+ """Compose the app."""
185
+ yield from ()
186
+
187
+ # Event handlers
188
+
189
+ async def on_main_screen_message_submitted(
190
+ self, event: MainScreen.MessageSubmitted
191
+ ) -> None:
192
+ """Handle message submission from main screen."""
193
+ content = event.content
194
+
195
+ # Add user message to chat
196
+ main_screen = self.screen
197
+ if isinstance(main_screen, MainScreen):
198
+ await main_screen.add_user_message(content)
199
+ main_screen.set_processing(True)
200
+
201
+ # Submit to queue
202
+ try:
203
+ run_id = await self.queue_manager.submit(
204
+ input_content=content,
205
+ agent_name=self.agent_config.get("name", "Assistant"),
206
+ config={
207
+ "agent_config": {
208
+ "name": self.agent_config.get("name", "Assistant"),
209
+ "instructions": self.agent_config.get(
210
+ "instructions",
211
+ "You are a helpful AI assistant."
212
+ ),
213
+ "model": self.model,
214
+ "tools": self.agent_config.get("tools", []),
215
+ "verbose": False,
216
+ }
217
+ }
218
+ )
219
+
220
+ self._current_run_id = run_id
221
+ self._streaming_content = ""
222
+
223
+ # Add streaming placeholder
224
+ if isinstance(main_screen, MainScreen):
225
+ await main_screen.add_assistant_message(
226
+ content="",
227
+ run_id=run_id,
228
+ agent_name=self.agent_config.get("name", "Assistant"),
229
+ streaming=True,
230
+ )
231
+
232
+ except Exception as e:
233
+ logger.error(f"Failed to submit: {e}")
234
+ if isinstance(main_screen, MainScreen):
235
+ main_screen.set_processing(False)
236
+ await main_screen.add_assistant_message(
237
+ content=f"Error: {e}",
238
+ agent_name="System",
239
+ )
240
+
241
+ self._update_status()
242
+
243
+ async def on_main_screen_command_executed(
244
+ self, event: MainScreen.CommandExecuted
245
+ ) -> None:
246
+ """Handle command execution."""
247
+ command = event.command.lower()
248
+ args = event.args
249
+
250
+ handler = self._command_handlers.get(command)
251
+ if handler:
252
+ await handler(args)
253
+ else:
254
+ # Unknown command
255
+ main_screen = self.screen
256
+ if isinstance(main_screen, MainScreen):
257
+ await main_screen.add_assistant_message(
258
+ content=f"Unknown command: /{command}\nType /help for available commands.",
259
+ agent_name="System",
260
+ )
261
+
262
+ async def on_main_screen_cancel_requested(
263
+ self, event: MainScreen.CancelRequested
264
+ ) -> None:
265
+ """Handle cancel request."""
266
+ if self._current_run_id:
267
+ await self.queue_manager.cancel(self._current_run_id)
268
+ self._current_run_id = None
269
+
270
+ main_screen = self.screen
271
+ if isinstance(main_screen, MainScreen):
272
+ main_screen.set_processing(False)
273
+ await main_screen.add_assistant_message(
274
+ content="Operation cancelled.",
275
+ agent_name="System",
276
+ )
277
+
278
+ self._update_status()
279
+
280
+ async def on_queue_screen_run_cancelled(
281
+ self, event: QueueScreen.RunCancelled
282
+ ) -> None:
283
+ """Handle run cancellation from queue screen."""
284
+ await self.queue_manager.cancel(event.run_id)
285
+ self._update_queue_screen()
286
+
287
+ async def on_queue_screen_run_retried(
288
+ self, event: QueueScreen.RunRetried
289
+ ) -> None:
290
+ """Handle run retry from queue screen."""
291
+ await self.queue_manager.retry(event.run_id)
292
+ self._update_queue_screen()
293
+
294
+ async def on_queue_screen_queue_cleared(
295
+ self, event: QueueScreen.QueueCleared
296
+ ) -> None:
297
+ """Handle queue clear."""
298
+ await self.queue_manager.clear_queue()
299
+ self._update_queue_screen()
300
+
301
+ async def on_settings_screen_settings_saved(
302
+ self, event: SettingsScreen.SettingsSaved
303
+ ) -> None:
304
+ """Handle settings save."""
305
+ settings = event.settings
306
+
307
+ if "model" in settings:
308
+ self.model = settings["model"]
309
+
310
+ if "max_concurrent" in settings:
311
+ self.queue_config.max_concurrent_global = settings["max_concurrent"]
312
+
313
+ if "autosave_interval" in settings:
314
+ self.queue_config.autosave_interval_seconds = settings["autosave_interval"]
315
+
316
+ self._update_status()
317
+
318
+ async def on_session_screen_session_selected(
319
+ self, event: SessionScreen.SessionSelected
320
+ ) -> None:
321
+ """Handle session selection."""
322
+ self.session_id = event.session_id
323
+ self.queue_manager.set_session(self.session_id)
324
+ self._update_status()
325
+
326
+ # Queue callbacks
327
+
328
+ async def _handle_output(self, run_id: str, chunk: str) -> None:
329
+ """Handle streaming output."""
330
+ if run_id != self._current_run_id:
331
+ return
332
+
333
+ self._streaming_content += chunk
334
+
335
+ main_screen = self.screen
336
+ if isinstance(main_screen, MainScreen):
337
+ await main_screen.update_streaming(run_id, self._streaming_content)
338
+
339
+ async def _handle_complete(self, run_id: str, run: QueuedRun) -> None:
340
+ """Handle run completion."""
341
+ main_screen = self.screen
342
+ if isinstance(main_screen, MainScreen):
343
+ await main_screen.complete_streaming(
344
+ run_id,
345
+ run.output_content or self._streaming_content
346
+ )
347
+ main_screen.set_processing(False)
348
+
349
+ # Update metrics
350
+ if run.metrics:
351
+ self._total_tokens += run.metrics.get("tokens", 0)
352
+ self._total_cost += run.metrics.get("cost", 0.0)
353
+
354
+ if run_id == self._current_run_id:
355
+ self._current_run_id = None
356
+ self._streaming_content = ""
357
+
358
+ self._update_status()
359
+
360
+ async def _handle_error(self, run_id: str, error: Exception) -> None:
361
+ """Handle run error."""
362
+ main_screen = self.screen
363
+ if isinstance(main_screen, MainScreen):
364
+ await main_screen.complete_streaming(run_id, f"Error: {error}")
365
+ main_screen.set_processing(False)
366
+
367
+ if run_id == self._current_run_id:
368
+ self._current_run_id = None
369
+ self._streaming_content = ""
370
+
371
+ self._update_status()
372
+
373
+ # Command handlers
374
+
375
+ async def _cmd_help(self, args: str) -> None:
376
+ """Show help."""
377
+ help_text = """
378
+ **Available Commands:**
379
+
380
+ - `/help` - Show this help
381
+ - `/clear` - Clear chat history
382
+ - `/model [name]` - Show or set model
383
+ - `/queue` - Show queue screen
384
+ - `/cancel [run_id]` - Cancel a run
385
+ - `/retry [run_id]` - Retry a failed run
386
+ - `/settings` - Show settings
387
+ - `/sessions` - Browse sessions
388
+ - `/cost` - Show cost summary
389
+ - `/exit` or `/quit` - Exit TUI
390
+
391
+ **Keyboard Shortcuts:**
392
+
393
+ - `Ctrl+Enter` - Send message
394
+ - `Ctrl+C` - Cancel current operation
395
+ - `Ctrl+Q` - Quit
396
+ - `Ctrl+L` - Clear screen
397
+ - `F1` - Help
398
+ - `F2` - Toggle queue panel
399
+ - `F3` - Settings
400
+ - `F5` - Clear chat
401
+ - `Tab` - Cycle focus
402
+ - `Esc` - Cancel/close
403
+ """
404
+ main_screen = self.screen
405
+ if isinstance(main_screen, MainScreen):
406
+ await main_screen.add_assistant_message(
407
+ content=help_text,
408
+ agent_name="Help",
409
+ )
410
+
411
+ async def _cmd_clear(self, args: str) -> None:
412
+ """Clear chat."""
413
+ main_screen = self.screen
414
+ if isinstance(main_screen, MainScreen):
415
+ await main_screen.query_one("#chat-widget").clear()
416
+
417
+ async def _cmd_model(self, args: str) -> None:
418
+ """Show or set model."""
419
+ main_screen = self.screen
420
+ if args:
421
+ self.model = args.strip()
422
+ msg = f"Model set to: {self.model}"
423
+ else:
424
+ msg = f"Current model: {self.model}"
425
+
426
+ if isinstance(main_screen, MainScreen):
427
+ await main_screen.add_assistant_message(content=msg, agent_name="System")
428
+
429
+ self._update_status()
430
+
431
+ async def _cmd_queue(self, args: str) -> None:
432
+ """Show queue screen."""
433
+ await self.push_screen("queue")
434
+ self._update_queue_screen()
435
+
436
+ async def _cmd_cancel(self, args: str) -> None:
437
+ """Cancel a run."""
438
+ run_id = args.strip() if args else self._current_run_id
439
+ if run_id:
440
+ await self.queue_manager.cancel(run_id)
441
+ main_screen = self.screen
442
+ if isinstance(main_screen, MainScreen):
443
+ await main_screen.add_assistant_message(
444
+ content=f"Cancelled run: {run_id}",
445
+ agent_name="System",
446
+ )
447
+ self._update_status()
448
+
449
+ async def _cmd_retry(self, args: str) -> None:
450
+ """Retry a failed run."""
451
+ run_id = args.strip()
452
+ if run_id:
453
+ new_id = await self.queue_manager.retry(run_id)
454
+ main_screen = self.screen
455
+ if isinstance(main_screen, MainScreen):
456
+ if new_id:
457
+ await main_screen.add_assistant_message(
458
+ content=f"Retrying run {run_id} as {new_id}",
459
+ agent_name="System",
460
+ )
461
+ else:
462
+ await main_screen.add_assistant_message(
463
+ content=f"Cannot retry run: {run_id}",
464
+ agent_name="System",
465
+ )
466
+ self._update_status()
467
+
468
+ async def _cmd_settings(self, args: str) -> None:
469
+ """Show settings."""
470
+ await self.push_screen(SettingsScreen(current_settings={
471
+ "model": self.model,
472
+ "max_concurrent": self.queue_config.max_concurrent_global,
473
+ "autosave_interval": int(self.queue_config.autosave_interval_seconds),
474
+ }))
475
+
476
+ async def _cmd_sessions(self, args: str) -> None:
477
+ """Show sessions."""
478
+ await self.push_screen("sessions")
479
+
480
+ async def _cmd_cost(self, args: str) -> None:
481
+ """Show cost summary."""
482
+ msg = f"""
483
+ **Cost Summary:**
484
+
485
+ - Total Tokens: {self._total_tokens:,}
486
+ - Total Cost: ${self._total_cost:.4f}
487
+ - Session: {self.session_id}
488
+ """
489
+ main_screen = self.screen
490
+ if isinstance(main_screen, MainScreen):
491
+ await main_screen.add_assistant_message(content=msg, agent_name="System")
492
+
493
+ async def _cmd_exit(self, args: str) -> None:
494
+ """Exit TUI."""
495
+ self.exit()
496
+
497
+ # Helper methods
498
+
499
+ def _update_status(self) -> None:
500
+ """Update status bar."""
501
+ main_screen = self.screen
502
+ if isinstance(main_screen, MainScreen):
503
+ main_screen.update_status(StatusInfo(
504
+ session_id=self.session_id,
505
+ model=self.model,
506
+ total_tokens=self._total_tokens,
507
+ total_cost=self._total_cost,
508
+ queued_count=self.queue_manager.queued_count if self.queue_manager else 0,
509
+ running_count=self.queue_manager.running_count if self.queue_manager else 0,
510
+ is_processing=self._current_run_id is not None,
511
+ ))
512
+
513
+ def _update_queue_screen(self) -> None:
514
+ """Update queue screen if visible."""
515
+ if isinstance(self.screen, QueueScreen):
516
+ runs = self.queue_manager.list_runs(limit=100)
517
+ self.screen.update_runs([
518
+ {
519
+ "run_id": r.run_id,
520
+ "agent_name": r.agent_name,
521
+ "input": r.input_content,
522
+ "state": r.state.value,
523
+ "priority": r.priority.name.lower(),
524
+ "wait_time": f"{r.wait_seconds:.1f}s" if r.wait_seconds else "-",
525
+ "duration": f"{r.duration_seconds:.1f}s" if r.duration_seconds else "-",
526
+ }
527
+ for r in runs
528
+ ])
529
+
530
+ def action_quit(self) -> None:
531
+ """Quit the application."""
532
+ self.exit()
533
+
534
+ def action_cancel(self) -> None:
535
+ """Cancel current operation."""
536
+ if self._current_run_id and self.queue_manager:
537
+ asyncio.create_task(self.queue_manager.cancel(self._current_run_id))
538
+
539
+ else:
540
+ class TUIApp:
541
+ """Placeholder when Textual is not available."""
542
+ def __init__(self, *args, **kwargs):
543
+ raise ImportError(
544
+ "Textual is required for TUI. Install with: pip install praisonai[tui]"
545
+ )
546
+
547
+
548
+ def run_tui(
549
+ workspace: Optional[str] = None,
550
+ session_id: Optional[str] = None,
551
+ model: Optional[str] = None,
552
+ agent_config: Optional[Dict[str, Any]] = None,
553
+ enable_acp: bool = True,
554
+ enable_lsp: bool = True,
555
+ ) -> None:
556
+ """
557
+ Run the PraisonAI TUI.
558
+
559
+ Args:
560
+ workspace: Working directory.
561
+ session_id: Session ID to resume.
562
+ model: Default model.
563
+ agent_config: Agent configuration.
564
+ enable_acp: Enable ACP tools (default: True).
565
+ enable_lsp: Enable LSP tools (default: True).
566
+ """
567
+ if not TEXTUAL_AVAILABLE:
568
+ raise ImportError(
569
+ "Textual is required for TUI. Install with: pip install praisonai[tui]"
570
+ )
571
+
572
+ app = TUIApp(
573
+ workspace=workspace,
574
+ session_id=session_id,
575
+ model=model,
576
+ agent_config=agent_config,
577
+ enable_acp=enable_acp,
578
+ enable_lsp=enable_lsp,
579
+ )
580
+ app.run()