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,56 @@
1
+ """
2
+ TUI Widgets for PraisonAI.
3
+
4
+ Reusable UI components for the TUI application.
5
+ """
6
+
7
+ from typing import TYPE_CHECKING
8
+
9
+ if TYPE_CHECKING:
10
+ from .chat import ChatWidget
11
+ from .composer import ComposerWidget
12
+ from .status import StatusWidget
13
+ from .queue_panel import QueuePanelWidget
14
+ from .tool_panel import ToolPanelWidget
15
+
16
+ _lazy_cache = {}
17
+
18
+
19
+ def __getattr__(name: str):
20
+ """Lazy load widgets."""
21
+ global _lazy_cache
22
+
23
+ if name in _lazy_cache:
24
+ return _lazy_cache[name]
25
+
26
+ if name == "ChatWidget":
27
+ from .chat import ChatWidget
28
+ _lazy_cache[name] = ChatWidget
29
+ return ChatWidget
30
+ elif name == "ComposerWidget":
31
+ from .composer import ComposerWidget
32
+ _lazy_cache[name] = ComposerWidget
33
+ return ComposerWidget
34
+ elif name == "StatusWidget":
35
+ from .status import StatusWidget
36
+ _lazy_cache[name] = StatusWidget
37
+ return StatusWidget
38
+ elif name == "QueuePanelWidget":
39
+ from .queue_panel import QueuePanelWidget
40
+ _lazy_cache[name] = QueuePanelWidget
41
+ return QueuePanelWidget
42
+ elif name == "ToolPanelWidget":
43
+ from .tool_panel import ToolPanelWidget
44
+ _lazy_cache[name] = ToolPanelWidget
45
+ return ToolPanelWidget
46
+
47
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
48
+
49
+
50
+ __all__ = [
51
+ "ChatWidget",
52
+ "ComposerWidget",
53
+ "StatusWidget",
54
+ "QueuePanelWidget",
55
+ "ToolPanelWidget",
56
+ ]
@@ -0,0 +1,261 @@
1
+ """
2
+ Chat Widget for PraisonAI TUI.
3
+
4
+ Displays chat history with streaming support.
5
+ """
6
+
7
+ from typing import List, Optional
8
+ from dataclasses import dataclass, field
9
+ import time
10
+
11
+ try:
12
+ from textual.widget import Widget
13
+ from textual.widgets import Static, RichLog
14
+ from textual.containers import ScrollableContainer, Vertical
15
+ from textual.reactive import reactive
16
+ from textual.message import Message
17
+ from rich.markdown import Markdown
18
+ from rich.panel import Panel
19
+ from rich.text import Text
20
+ TEXTUAL_AVAILABLE = True
21
+ except ImportError:
22
+ TEXTUAL_AVAILABLE = False
23
+ Widget = object
24
+ Message = object
25
+
26
+
27
+ @dataclass
28
+ class ChatMessage:
29
+ """A single chat message."""
30
+ role: str # "user", "assistant", "system", "tool"
31
+ content: str
32
+ timestamp: float = field(default_factory=time.time)
33
+ run_id: Optional[str] = None
34
+ agent_name: Optional[str] = None
35
+ is_streaming: bool = False
36
+
37
+ @property
38
+ def display_role(self) -> str:
39
+ """Get display name for role."""
40
+ if self.role == "user":
41
+ return "You"
42
+ elif self.role == "assistant":
43
+ return self.agent_name or "Assistant"
44
+ elif self.role == "system":
45
+ return "System"
46
+ elif self.role == "tool":
47
+ return "Tool"
48
+ return self.role.title()
49
+
50
+
51
+ if TEXTUAL_AVAILABLE:
52
+ class ChatWidget(ScrollableContainer):
53
+ """
54
+ Widget for displaying chat history.
55
+
56
+ Features:
57
+ - Streaming message updates
58
+ - Markdown rendering
59
+ - Auto-scroll to bottom
60
+ - Message history management
61
+ """
62
+
63
+ DEFAULT_CSS = """
64
+ ChatWidget {
65
+ height: 100%;
66
+ border: solid $primary;
67
+ background: $surface;
68
+ padding: 0 1;
69
+ }
70
+
71
+ ChatWidget .message-user {
72
+ background: $primary-darken-2;
73
+ margin: 1 0;
74
+ padding: 1;
75
+ }
76
+
77
+ ChatWidget .message-assistant {
78
+ background: $surface-darken-1;
79
+ margin: 1 0;
80
+ padding: 1;
81
+ }
82
+
83
+ ChatWidget .message-system {
84
+ background: $warning-darken-3;
85
+ margin: 1 0;
86
+ padding: 1;
87
+ color: $text-muted;
88
+ }
89
+
90
+ ChatWidget .message-streaming {
91
+ border: dashed $accent;
92
+ }
93
+ """
94
+
95
+ class MessageAdded(Message):
96
+ """Event when a message is added."""
97
+ def __init__(self, message: ChatMessage):
98
+ self.message = message
99
+ super().__init__()
100
+
101
+ class StreamingUpdate(Message):
102
+ """Event when streaming content updates."""
103
+ def __init__(self, run_id: str, content: str):
104
+ self.run_id = run_id
105
+ self.content = content
106
+ super().__init__()
107
+
108
+ def __init__(
109
+ self,
110
+ max_messages: int = 1000,
111
+ name: Optional[str] = None,
112
+ id: Optional[str] = None,
113
+ classes: Optional[str] = None,
114
+ ):
115
+ super().__init__(name=name, id=id, classes=classes)
116
+ self._messages: List[ChatMessage] = []
117
+ self._max_messages = max_messages
118
+ self._streaming_widgets: dict = {}
119
+
120
+ def compose(self):
121
+ """Compose the widget."""
122
+ yield Vertical(id="chat-messages")
123
+
124
+ async def add_message(self, message: ChatMessage) -> None:
125
+ """Add a message to the chat."""
126
+ self._messages.append(message)
127
+
128
+ # Trim old messages if needed
129
+ if len(self._messages) > self._max_messages:
130
+ self._messages = self._messages[-self._max_messages:]
131
+
132
+ # Create message widget
133
+ await self._render_message(message)
134
+
135
+ # Auto-scroll to bottom
136
+ self.scroll_end(animate=False)
137
+
138
+ self.post_message(self.MessageAdded(message))
139
+
140
+ async def _render_message(self, message: ChatMessage) -> None:
141
+ """Render a single message."""
142
+ container = self.query_one("#chat-messages", Vertical)
143
+
144
+ # Create role label
145
+ role_style = {
146
+ "user": "bold cyan",
147
+ "assistant": "bold green",
148
+ "system": "bold yellow",
149
+ "tool": "bold magenta",
150
+ }.get(message.role, "bold")
151
+
152
+ role_text = Text(f"{message.display_role}:", style=role_style)
153
+
154
+ # Create content
155
+ try:
156
+ content = Markdown(message.content)
157
+ except Exception:
158
+ content = Text(message.content)
159
+
160
+ # Create panel
161
+ css_class = f"message-{message.role}"
162
+ if message.is_streaming:
163
+ css_class += " message-streaming"
164
+
165
+ widget_id = f"msg-{message.run_id or id(message)}"
166
+
167
+ panel = Static(
168
+ Panel(content, title=str(role_text), border_style=role_style),
169
+ id=widget_id,
170
+ classes=css_class,
171
+ )
172
+
173
+ await container.mount(panel)
174
+
175
+ if message.is_streaming:
176
+ self._streaming_widgets[message.run_id] = widget_id
177
+
178
+ async def update_streaming(self, run_id: str, content: str) -> None:
179
+ """Update a streaming message."""
180
+ if run_id not in self._streaming_widgets:
181
+ return
182
+
183
+ widget_id = self._streaming_widgets[run_id]
184
+
185
+ try:
186
+ widget = self.query_one(f"#{widget_id}", Static)
187
+
188
+ # Find the message
189
+ for msg in self._messages:
190
+ if msg.run_id == run_id:
191
+ msg.content = content
192
+ break
193
+
194
+ # Update content
195
+ try:
196
+ rendered = Markdown(content + " ▌")
197
+ except Exception:
198
+ rendered = Text(content + " ▌")
199
+
200
+ widget.update(Panel(rendered, title="Assistant", border_style="bold green"))
201
+
202
+ # Auto-scroll
203
+ self.scroll_end(animate=False)
204
+
205
+ except Exception:
206
+ pass
207
+
208
+ async def complete_streaming(self, run_id: str, final_content: str) -> None:
209
+ """Complete a streaming message."""
210
+ if run_id not in self._streaming_widgets:
211
+ return
212
+
213
+ widget_id = self._streaming_widgets.pop(run_id)
214
+
215
+ try:
216
+ widget = self.query_one(f"#{widget_id}", Static)
217
+
218
+ # Update message
219
+ for msg in self._messages:
220
+ if msg.run_id == run_id:
221
+ msg.content = final_content
222
+ msg.is_streaming = False
223
+ break
224
+
225
+ # Update content without cursor
226
+ try:
227
+ rendered = Markdown(final_content)
228
+ except Exception:
229
+ rendered = Text(final_content)
230
+
231
+ widget.update(Panel(rendered, title="Assistant", border_style="bold green"))
232
+ widget.remove_class("message-streaming")
233
+
234
+ except Exception:
235
+ pass
236
+
237
+ async def clear(self) -> None:
238
+ """Clear all messages."""
239
+ self._messages.clear()
240
+ self._streaming_widgets.clear()
241
+
242
+ container = self.query_one("#chat-messages", Vertical)
243
+ await container.remove_children()
244
+
245
+ @property
246
+ def messages(self) -> List[ChatMessage]:
247
+ """Get all messages."""
248
+ return self._messages.copy()
249
+
250
+ @property
251
+ def message_count(self) -> int:
252
+ """Get message count."""
253
+ return len(self._messages)
254
+
255
+ else:
256
+ class ChatWidget:
257
+ """Placeholder when Textual is not available."""
258
+ def __init__(self, *args, **kwargs):
259
+ raise ImportError(
260
+ "Textual is required for TUI. Install with: pip install praisonai[tui]"
261
+ )
@@ -0,0 +1,224 @@
1
+ """
2
+ Composer Widget for PraisonAI TUI.
3
+
4
+ Input area for composing messages with slash command support.
5
+ """
6
+
7
+ from typing import Callable, List, Optional
8
+ import asyncio
9
+
10
+ try:
11
+ from textual.widget import Widget
12
+ from textual.widgets import TextArea, Input
13
+ from textual.containers import Horizontal, Vertical
14
+ from textual.message import Message
15
+ from textual.binding import Binding
16
+ from textual import events
17
+ from rich.text import Text
18
+ TEXTUAL_AVAILABLE = True
19
+ except ImportError:
20
+ TEXTUAL_AVAILABLE = False
21
+ Widget = object
22
+ Message = object
23
+
24
+
25
+ if TEXTUAL_AVAILABLE:
26
+ class ComposerWidget(Vertical):
27
+ """
28
+ Input composer for chat messages.
29
+
30
+ Features:
31
+ - Multi-line input
32
+ - Slash command detection
33
+ - @ mention support
34
+ - History navigation
35
+ - Send on Enter or Ctrl+Enter
36
+ """
37
+
38
+ BINDINGS = [
39
+ Binding("ctrl+enter", "submit", "Send", show=True),
40
+ Binding("escape", "cancel", "Cancel", show=False),
41
+ Binding("up", "history_prev", "Previous", show=False),
42
+ Binding("down", "history_next", "Next", show=False),
43
+ ]
44
+
45
+ DEFAULT_CSS = """
46
+ ComposerWidget {
47
+ height: auto;
48
+ max-height: 10;
49
+ border: solid $primary;
50
+ background: $surface;
51
+ padding: 0 1;
52
+ }
53
+
54
+ ComposerWidget TextArea {
55
+ height: auto;
56
+ min-height: 3;
57
+ max-height: 8;
58
+ }
59
+
60
+ ComposerWidget .hint {
61
+ color: $text-muted;
62
+ text-style: italic;
63
+ }
64
+ """
65
+
66
+ class Submitted(Message):
67
+ """Event when message is submitted."""
68
+ def __init__(self, content: str, is_command: bool = False):
69
+ self.content = content
70
+ self.is_command = is_command
71
+ super().__init__()
72
+
73
+ class CommandDetected(Message):
74
+ """Event when a slash command is detected."""
75
+ def __init__(self, command: str, args: str):
76
+ self.command = command
77
+ self.args = args
78
+ super().__init__()
79
+
80
+ def __init__(
81
+ self,
82
+ placeholder: str = "Type your message... (Ctrl+Enter to send, /help for commands)",
83
+ multiline: bool = True,
84
+ name: Optional[str] = None,
85
+ id: Optional[str] = None,
86
+ classes: Optional[str] = None,
87
+ ):
88
+ super().__init__(name=name, id=id, classes=classes)
89
+ self._placeholder = placeholder
90
+ self._multiline = multiline
91
+ self._history: List[str] = []
92
+ self._history_index = -1
93
+ self._current_input = ""
94
+ self._is_processing = False
95
+
96
+ def compose(self):
97
+ """Compose the widget."""
98
+ yield TextArea(
99
+ "",
100
+ id="composer-input",
101
+ language=None,
102
+ )
103
+
104
+ def on_mount(self) -> None:
105
+ """Handle mount event."""
106
+ text_area = self.query_one("#composer-input", TextArea)
107
+ text_area.focus()
108
+
109
+ async def on_text_area_changed(self, event: TextArea.Changed) -> None:
110
+ """Handle text changes."""
111
+ content = event.text_area.text
112
+
113
+ # Detect slash commands
114
+ if content.startswith("/"):
115
+ parts = content[1:].split(maxsplit=1)
116
+ if parts:
117
+ command = parts[0]
118
+ args = parts[1] if len(parts) > 1 else ""
119
+ self.post_message(self.CommandDetected(command, args))
120
+
121
+ def on_key(self, event: events.Key) -> None:
122
+ """Handle key events."""
123
+ # Submit on Enter (single line mode) or Ctrl+Enter
124
+ if event.key == "enter" and not self._multiline:
125
+ event.prevent_default()
126
+ self.action_submit()
127
+ elif event.key == "ctrl+enter":
128
+ event.prevent_default()
129
+ self.action_submit()
130
+
131
+ def action_submit(self) -> None:
132
+ """Submit the current input."""
133
+ if self._is_processing:
134
+ return
135
+
136
+ text_area = self.query_one("#composer-input", TextArea)
137
+ content = text_area.text.strip()
138
+
139
+ if not content:
140
+ return
141
+
142
+ # Add to history
143
+ if content and (not self._history or self._history[-1] != content):
144
+ self._history.append(content)
145
+ self._history_index = -1
146
+
147
+ # Check if it's a command
148
+ is_command = content.startswith("/")
149
+
150
+ # Clear input
151
+ text_area.clear()
152
+
153
+ # Emit event
154
+ self.post_message(self.Submitted(content, is_command))
155
+
156
+ def action_cancel(self) -> None:
157
+ """Cancel current input."""
158
+ text_area = self.query_one("#composer-input", TextArea)
159
+ text_area.clear()
160
+ self._history_index = -1
161
+
162
+ def action_history_prev(self) -> None:
163
+ """Navigate to previous history item."""
164
+ if not self._history:
165
+ return
166
+
167
+ text_area = self.query_one("#composer-input", TextArea)
168
+
169
+ if self._history_index == -1:
170
+ self._current_input = text_area.text
171
+ self._history_index = len(self._history) - 1
172
+ elif self._history_index > 0:
173
+ self._history_index -= 1
174
+
175
+ text_area.clear()
176
+ text_area.insert(self._history[self._history_index])
177
+
178
+ def action_history_next(self) -> None:
179
+ """Navigate to next history item."""
180
+ if self._history_index == -1:
181
+ return
182
+
183
+ text_area = self.query_one("#composer-input", TextArea)
184
+
185
+ if self._history_index < len(self._history) - 1:
186
+ self._history_index += 1
187
+ text_area.clear()
188
+ text_area.insert(self._history[self._history_index])
189
+ else:
190
+ self._history_index = -1
191
+ text_area.clear()
192
+ text_area.insert(self._current_input)
193
+
194
+ def set_processing(self, processing: bool) -> None:
195
+ """Set processing state."""
196
+ self._is_processing = processing
197
+ text_area = self.query_one("#composer-input", TextArea)
198
+ text_area.disabled = processing
199
+
200
+ @property
201
+ def text(self) -> str:
202
+ """Get current text."""
203
+ text_area = self.query_one("#composer-input", TextArea)
204
+ return text_area.text
205
+
206
+ @text.setter
207
+ def text(self, value: str) -> None:
208
+ """Set current text."""
209
+ text_area = self.query_one("#composer-input", TextArea)
210
+ text_area.clear()
211
+ text_area.insert(value)
212
+
213
+ def focus_input(self) -> None:
214
+ """Focus the input area."""
215
+ text_area = self.query_one("#composer-input", TextArea)
216
+ text_area.focus()
217
+
218
+ else:
219
+ class ComposerWidget:
220
+ """Placeholder when Textual is not available."""
221
+ def __init__(self, *args, **kwargs):
222
+ raise ImportError(
223
+ "Textual is required for TUI. Install with: pip install praisonai[tui]"
224
+ )