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,395 @@
1
+ """
2
+ Queue Manager for PraisonAI.
3
+
4
+ High-level interface that coordinates scheduler, workers, and persistence.
5
+ """
6
+
7
+ import asyncio
8
+ import logging
9
+ import os
10
+ import time
11
+ from typing import Any, Callable, Coroutine, Dict, List, Optional
12
+
13
+ from .models import QueuedRun, RunState, RunPriority, QueueConfig, QueueStats, QueueEvent
14
+ from .scheduler import QueueScheduler, QueueFullError
15
+ from .worker import WorkerPool
16
+ from .persistence import QueuePersistence
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+
21
+ class QueueManager:
22
+ """
23
+ High-level queue manager that coordinates all queue components.
24
+
25
+ Provides a unified interface for:
26
+ - Submitting and managing runs
27
+ - Starting/stopping workers
28
+ - Persistence and crash recovery
29
+ - Event handling
30
+ """
31
+
32
+ def __init__(
33
+ self,
34
+ config: Optional[QueueConfig] = None,
35
+ on_output: Optional[Callable[[str, str], Coroutine[Any, Any, None]]] = None,
36
+ on_complete: Optional[Callable[[str, QueuedRun], Coroutine[Any, Any, None]]] = None,
37
+ on_error: Optional[Callable[[str, Exception], Coroutine[Any, Any, None]]] = None,
38
+ on_event: Optional[Callable[[QueueEvent], Coroutine[Any, Any, None]]] = None,
39
+ ):
40
+ """
41
+ Initialize queue manager.
42
+
43
+ Args:
44
+ config: Queue configuration.
45
+ on_output: Callback for streaming output.
46
+ on_complete: Callback when run completes.
47
+ on_error: Callback when run fails.
48
+ on_event: Callback for queue events.
49
+ """
50
+ self.config = config or QueueConfig()
51
+
52
+ # Initialize components
53
+ self.scheduler = QueueScheduler(self.config)
54
+ self.persistence = QueuePersistence(self.config.db_path) if self.config.enable_persistence else None
55
+
56
+ # Store callbacks
57
+ self._on_output = on_output
58
+ self._on_complete = on_complete
59
+ self._on_error = on_error
60
+ self._on_event = on_event
61
+
62
+ # Worker pool (created on start)
63
+ self.workers: Optional[WorkerPool] = None
64
+
65
+ # Autosave task
66
+ self._autosave_task: Optional[asyncio.Task] = None
67
+
68
+ # Running state
69
+ self._running = False
70
+
71
+ # Session tracking
72
+ self._current_session_id: Optional[str] = None
73
+
74
+ async def start(self, recover: bool = True) -> None:
75
+ """
76
+ Start the queue manager.
77
+
78
+ Args:
79
+ recover: If True, recover pending runs from persistence.
80
+ """
81
+ if self._running:
82
+ return
83
+
84
+ logger.info("Starting queue manager")
85
+
86
+ # Initialize persistence
87
+ if self.persistence:
88
+ self.persistence.initialize()
89
+
90
+ if recover:
91
+ await self._recover_runs()
92
+
93
+ # Create and start worker pool
94
+ self.workers = WorkerPool(
95
+ scheduler=self.scheduler,
96
+ on_output=self._on_output,
97
+ on_complete=self._wrap_complete_callback(),
98
+ on_error=self._wrap_error_callback(),
99
+ on_event=self._on_event,
100
+ max_workers=self.config.max_concurrent_global,
101
+ poll_interval=self.config.worker_poll_interval,
102
+ stream_buffer_size=self.config.stream_buffer_size,
103
+ )
104
+
105
+ await self.workers.start()
106
+
107
+ # Start autosave
108
+ if self.persistence and self.config.autosave_interval_seconds > 0:
109
+ self._autosave_task = asyncio.create_task(
110
+ self._autosave_loop(),
111
+ name="queue-autosave"
112
+ )
113
+
114
+ self._running = True
115
+ logger.info("Queue manager started")
116
+
117
+ async def stop(self, timeout: float = 10.0) -> None:
118
+ """
119
+ Stop the queue manager gracefully.
120
+
121
+ Args:
122
+ timeout: Seconds to wait for workers to finish.
123
+ """
124
+ if not self._running:
125
+ return
126
+
127
+ logger.info("Stopping queue manager")
128
+
129
+ # Stop autosave
130
+ if self._autosave_task:
131
+ self._autosave_task.cancel()
132
+ try:
133
+ await self._autosave_task
134
+ except asyncio.CancelledError:
135
+ pass
136
+ self._autosave_task = None
137
+
138
+ # Stop workers
139
+ if self.workers:
140
+ await self.workers.stop(timeout=timeout)
141
+ self.workers = None
142
+
143
+ # Final save
144
+ if self.persistence:
145
+ await self._save_all_runs()
146
+ self.persistence.close()
147
+
148
+ self._running = False
149
+ logger.info("Queue manager stopped")
150
+
151
+ async def submit(
152
+ self,
153
+ input_content: str,
154
+ agent_name: str = "Assistant",
155
+ priority: RunPriority = RunPriority.NORMAL,
156
+ session_id: Optional[str] = None,
157
+ workspace: Optional[str] = None,
158
+ config: Optional[Dict[str, Any]] = None,
159
+ run_id: Optional[str] = None,
160
+ ) -> str:
161
+ """
162
+ Submit a new run to the queue.
163
+
164
+ Args:
165
+ input_content: The input/prompt for the agent.
166
+ agent_name: Name of the agent to use.
167
+ priority: Run priority.
168
+ session_id: Session ID for grouping.
169
+ workspace: Workspace path.
170
+ config: Additional configuration.
171
+ run_id: Optional specific run ID.
172
+
173
+ Returns:
174
+ The run ID.
175
+ """
176
+ run = QueuedRun(
177
+ agent_name=agent_name,
178
+ input_content=input_content,
179
+ priority=priority,
180
+ session_id=session_id or self._current_session_id,
181
+ workspace=workspace or os.getcwd(),
182
+ config=config or {},
183
+ max_retries=self.config.default_max_retries,
184
+ )
185
+
186
+ if run_id:
187
+ run.run_id = run_id
188
+
189
+ # Submit to scheduler
190
+ result_id = await self.scheduler.submit(run)
191
+
192
+ # Persist
193
+ if self.persistence:
194
+ self.persistence.save_run(run)
195
+
196
+ return result_id
197
+
198
+ async def cancel(self, run_id: str) -> bool:
199
+ """Cancel a run."""
200
+ result = await self.scheduler.cancel(run_id)
201
+
202
+ if result and self.persistence:
203
+ run = self.scheduler.get_run(run_id)
204
+ if run:
205
+ self.persistence.save_run(run)
206
+
207
+ return result
208
+
209
+ async def retry(self, run_id: str) -> Optional[str]:
210
+ """Retry a failed run."""
211
+ new_id = await self.scheduler.retry(run_id)
212
+
213
+ if new_id and self.persistence:
214
+ run = self.scheduler.get_run(new_id)
215
+ if run:
216
+ self.persistence.save_run(run)
217
+
218
+ return new_id
219
+
220
+ async def pause(self, run_id: str) -> bool:
221
+ """Pause a running run."""
222
+ return await self.scheduler.pause(run_id)
223
+
224
+ async def resume(self, run_id: str) -> bool:
225
+ """Resume a paused run."""
226
+ return await self.scheduler.resume(run_id)
227
+
228
+ def get_run(self, run_id: str) -> Optional[QueuedRun]:
229
+ """Get a run by ID."""
230
+ return self.scheduler.get_run(run_id)
231
+
232
+ def get_queued(self) -> List[QueuedRun]:
233
+ """Get all queued runs."""
234
+ return self.scheduler.get_queued()
235
+
236
+ def get_running(self) -> List[QueuedRun]:
237
+ """Get all running runs."""
238
+ return self.scheduler.get_running()
239
+
240
+ def list_runs(
241
+ self,
242
+ state: Optional[RunState] = None,
243
+ session_id: Optional[str] = None,
244
+ limit: int = 100,
245
+ ) -> List[QueuedRun]:
246
+ """
247
+ List runs with optional filters.
248
+
249
+ For active runs, uses scheduler. For historical, uses persistence.
250
+ """
251
+ if state and state.is_terminal() and self.persistence:
252
+ return self.persistence.list_runs(
253
+ state=state,
254
+ session_id=session_id,
255
+ limit=limit,
256
+ )
257
+
258
+ # Get from scheduler
259
+ runs = self.scheduler.get_all()
260
+
261
+ if state:
262
+ runs = [r for r in runs if r.state == state]
263
+ if session_id:
264
+ runs = [r for r in runs if r.session_id == session_id]
265
+
266
+ return runs[:limit]
267
+
268
+ async def clear_queue(self) -> int:
269
+ """Clear all queued runs."""
270
+ count = await self.scheduler.clear_queue()
271
+ return count
272
+
273
+ def get_stats(self) -> QueueStats:
274
+ """Get queue statistics."""
275
+ if self.persistence:
276
+ return self.persistence.get_stats(self._current_session_id)
277
+
278
+ # Calculate from scheduler
279
+ queued = self.scheduler.get_queued()
280
+ running = self.scheduler.get_running()
281
+
282
+ return QueueStats(
283
+ queued_count=len(queued),
284
+ running_count=len(running),
285
+ )
286
+
287
+ # Session management
288
+
289
+ def set_session(self, session_id: str) -> None:
290
+ """Set the current session ID."""
291
+ self._current_session_id = session_id
292
+
293
+ def save_session_state(self, state: Dict[str, Any]) -> None:
294
+ """Save session state."""
295
+ if self.persistence and self._current_session_id:
296
+ self.persistence.save_session(
297
+ self._current_session_id,
298
+ state=state,
299
+ )
300
+
301
+ def load_session_state(self, session_id: str) -> Optional[Dict[str, Any]]:
302
+ """Load session state."""
303
+ if self.persistence:
304
+ return self.persistence.load_session(session_id)
305
+ return None
306
+
307
+ def list_sessions(self, limit: int = 50) -> List[Dict[str, Any]]:
308
+ """List recent sessions."""
309
+ if self.persistence:
310
+ return self.persistence.list_sessions(limit)
311
+ return []
312
+
313
+ # Internal methods
314
+
315
+ async def _recover_runs(self) -> None:
316
+ """Recover pending runs from persistence."""
317
+ if not self.persistence:
318
+ return
319
+
320
+ # Mark interrupted runs as failed
321
+ interrupted = self.persistence.mark_interrupted_as_failed()
322
+ if interrupted:
323
+ logger.warning(f"Marked {interrupted} interrupted runs as failed")
324
+
325
+ # Load pending runs
326
+ pending = self.persistence.load_pending_runs()
327
+ if pending:
328
+ logger.info(f"Recovering {len(pending)} pending runs")
329
+ self.scheduler.load_runs(pending)
330
+
331
+ for run in pending:
332
+ self.persistence.mark_recovered(run.run_id)
333
+
334
+ async def _save_all_runs(self) -> None:
335
+ """Save all active runs to persistence."""
336
+ if not self.persistence:
337
+ return
338
+
339
+ for run in self.scheduler.get_all():
340
+ self.persistence.save_run(run)
341
+
342
+ async def _autosave_loop(self) -> None:
343
+ """Periodically save runs to persistence."""
344
+ while True:
345
+ try:
346
+ await asyncio.sleep(self.config.autosave_interval_seconds)
347
+ await self._save_all_runs()
348
+ logger.debug("Autosaved queue state")
349
+ except asyncio.CancelledError:
350
+ break
351
+ except Exception as e:
352
+ logger.error(f"Autosave error: {e}")
353
+
354
+ def _wrap_complete_callback(self):
355
+ """Wrap complete callback to include persistence."""
356
+ async def wrapper(run_id: str, run: QueuedRun):
357
+ # Persist
358
+ if self.persistence:
359
+ self.persistence.save_run(run)
360
+
361
+ # Call user callback
362
+ if self._on_complete:
363
+ await self._on_complete(run_id, run)
364
+
365
+ return wrapper
366
+
367
+ def _wrap_error_callback(self):
368
+ """Wrap error callback to include persistence."""
369
+ async def wrapper(run_id: str, error: Exception):
370
+ # Persist
371
+ if self.persistence:
372
+ run = self.scheduler.get_run(run_id)
373
+ if run:
374
+ self.persistence.save_run(run)
375
+
376
+ # Call user callback
377
+ if self._on_error:
378
+ await self._on_error(run_id, error)
379
+
380
+ return wrapper
381
+
382
+ @property
383
+ def is_running(self) -> bool:
384
+ """Whether the queue manager is running."""
385
+ return self._running
386
+
387
+ @property
388
+ def queued_count(self) -> int:
389
+ """Number of queued runs."""
390
+ return self.scheduler.queued_count
391
+
392
+ @property
393
+ def running_count(self) -> int:
394
+ """Number of running runs."""
395
+ return self.scheduler.running_count
@@ -0,0 +1,286 @@
1
+ """
2
+ Data models for the PraisonAI Queue System.
3
+
4
+ Defines QueuedRun, RunState, RunPriority, and QueueConfig.
5
+ """
6
+
7
+ from dataclasses import dataclass, field
8
+ from enum import Enum, IntEnum
9
+ from typing import Any, Dict, List, Optional
10
+ import time
11
+ import uuid
12
+
13
+
14
+ class RunState(str, Enum):
15
+ """State of a queued run."""
16
+ QUEUED = "queued"
17
+ RUNNING = "running"
18
+ PAUSED = "paused"
19
+ SUCCEEDED = "succeeded"
20
+ FAILED = "failed"
21
+ CANCELLED = "cancelled"
22
+
23
+ def is_terminal(self) -> bool:
24
+ """Check if this is a terminal state."""
25
+ return self in (RunState.SUCCEEDED, RunState.FAILED, RunState.CANCELLED)
26
+
27
+ def is_active(self) -> bool:
28
+ """Check if this is an active (non-terminal) state."""
29
+ return not self.is_terminal()
30
+
31
+
32
+ class RunPriority(IntEnum):
33
+ """Priority levels for queued runs. Higher value = higher priority."""
34
+ LOW = 0
35
+ NORMAL = 1
36
+ HIGH = 2
37
+ URGENT = 3
38
+
39
+ @classmethod
40
+ def from_string(cls, s: str) -> "RunPriority":
41
+ """Parse priority from string."""
42
+ mapping = {
43
+ "low": cls.LOW,
44
+ "normal": cls.NORMAL,
45
+ "high": cls.HIGH,
46
+ "urgent": cls.URGENT,
47
+ }
48
+ return mapping.get(s.lower(), cls.NORMAL)
49
+
50
+
51
+ @dataclass
52
+ class QueuedRun:
53
+ """A single queued agent run."""
54
+
55
+ # Identity
56
+ run_id: str = field(default_factory=lambda: str(uuid.uuid4())[:8])
57
+ agent_name: str = ""
58
+ input_content: str = ""
59
+
60
+ # State
61
+ state: RunState = RunState.QUEUED
62
+ priority: RunPriority = RunPriority.NORMAL
63
+
64
+ # Attribution / tracing
65
+ session_id: Optional[str] = None
66
+ trace_id: Optional[str] = None
67
+ workspace: Optional[str] = None
68
+ user_id: Optional[str] = None
69
+
70
+ # Timing
71
+ created_at: float = field(default_factory=time.time)
72
+ started_at: Optional[float] = None
73
+ ended_at: Optional[float] = None
74
+
75
+ # Results
76
+ output_content: Optional[str] = None
77
+ error: Optional[str] = None
78
+ metrics: Dict[str, Any] = field(default_factory=dict)
79
+
80
+ # Retry tracking
81
+ retry_count: int = 0
82
+ max_retries: int = 3
83
+ parent_run_id: Optional[str] = None # Link to original run if this is a retry
84
+
85
+ # Configuration
86
+ config: Dict[str, Any] = field(default_factory=dict)
87
+
88
+ # Streaming state
89
+ output_chunks: List[str] = field(default_factory=list)
90
+
91
+ def __post_init__(self):
92
+ """Ensure proper types after initialization."""
93
+ if isinstance(self.state, str):
94
+ self.state = RunState(self.state)
95
+ if isinstance(self.priority, int) and not isinstance(self.priority, RunPriority):
96
+ self.priority = RunPriority(self.priority)
97
+
98
+ @property
99
+ def duration_seconds(self) -> Optional[float]:
100
+ """Get run duration in seconds."""
101
+ if self.started_at is None:
102
+ return None
103
+ end = self.ended_at or time.time()
104
+ return end - self.started_at
105
+
106
+ @property
107
+ def wait_seconds(self) -> float:
108
+ """Get time spent waiting in queue."""
109
+ start = self.started_at or time.time()
110
+ return start - self.created_at
111
+
112
+ def can_retry(self) -> bool:
113
+ """Check if this run can be retried."""
114
+ return (
115
+ self.state == RunState.FAILED and
116
+ self.retry_count < self.max_retries
117
+ )
118
+
119
+ def to_dict(self) -> Dict[str, Any]:
120
+ """Convert to dictionary for persistence."""
121
+ return {
122
+ "run_id": self.run_id,
123
+ "agent_name": self.agent_name,
124
+ "input_content": self.input_content,
125
+ "state": self.state.value,
126
+ "priority": int(self.priority),
127
+ "session_id": self.session_id,
128
+ "trace_id": self.trace_id,
129
+ "workspace": self.workspace,
130
+ "user_id": self.user_id,
131
+ "created_at": self.created_at,
132
+ "started_at": self.started_at,
133
+ "ended_at": self.ended_at,
134
+ "output_content": self.output_content,
135
+ "error": self.error,
136
+ "metrics": self.metrics,
137
+ "retry_count": self.retry_count,
138
+ "max_retries": self.max_retries,
139
+ "parent_run_id": self.parent_run_id,
140
+ "config": self.config,
141
+ }
142
+
143
+ @classmethod
144
+ def from_dict(cls, data: Dict[str, Any]) -> "QueuedRun":
145
+ """Create from dictionary."""
146
+ # Handle state conversion
147
+ state = data.get("state", "queued")
148
+ if isinstance(state, str):
149
+ state = RunState(state)
150
+
151
+ # Handle priority conversion
152
+ priority = data.get("priority", 1)
153
+ if isinstance(priority, int):
154
+ priority = RunPriority(priority)
155
+
156
+ return cls(
157
+ run_id=data.get("run_id", str(uuid.uuid4())[:8]),
158
+ agent_name=data.get("agent_name", ""),
159
+ input_content=data.get("input_content", ""),
160
+ state=state,
161
+ priority=priority,
162
+ session_id=data.get("session_id"),
163
+ trace_id=data.get("trace_id"),
164
+ workspace=data.get("workspace"),
165
+ user_id=data.get("user_id"),
166
+ created_at=data.get("created_at", time.time()),
167
+ started_at=data.get("started_at"),
168
+ ended_at=data.get("ended_at"),
169
+ output_content=data.get("output_content"),
170
+ error=data.get("error"),
171
+ metrics=data.get("metrics", {}),
172
+ retry_count=data.get("retry_count", 0),
173
+ max_retries=data.get("max_retries", 3),
174
+ parent_run_id=data.get("parent_run_id"),
175
+ config=data.get("config", {}),
176
+ )
177
+
178
+
179
+ @dataclass
180
+ class QueueConfig:
181
+ """Configuration for the queue system."""
182
+
183
+ # Concurrency limits
184
+ max_concurrent_global: int = 4
185
+ max_concurrent_per_agent: int = 2
186
+ max_concurrent_per_workspace: int = 4
187
+
188
+ # Queue limits
189
+ max_queue_size: int = 100
190
+
191
+ # Defaults
192
+ default_priority: RunPriority = RunPriority.NORMAL
193
+ default_max_retries: int = 3
194
+
195
+ # Persistence
196
+ enable_persistence: bool = True
197
+ db_path: str = ".praison/queue.db"
198
+
199
+ # Autosave
200
+ autosave_interval_seconds: float = 30.0
201
+
202
+ # Backpressure
203
+ stream_buffer_size: int = 1000
204
+ drop_strategy: str = "oldest" # "oldest" or "newest"
205
+
206
+ # Timeouts
207
+ run_timeout_seconds: Optional[float] = None # None = no timeout
208
+ worker_poll_interval: float = 0.1
209
+
210
+ def to_dict(self) -> Dict[str, Any]:
211
+ """Convert to dictionary."""
212
+ return {
213
+ "max_concurrent_global": self.max_concurrent_global,
214
+ "max_concurrent_per_agent": self.max_concurrent_per_agent,
215
+ "max_concurrent_per_workspace": self.max_concurrent_per_workspace,
216
+ "max_queue_size": self.max_queue_size,
217
+ "default_priority": int(self.default_priority),
218
+ "default_max_retries": self.default_max_retries,
219
+ "enable_persistence": self.enable_persistence,
220
+ "db_path": self.db_path,
221
+ "autosave_interval_seconds": self.autosave_interval_seconds,
222
+ "stream_buffer_size": self.stream_buffer_size,
223
+ "drop_strategy": self.drop_strategy,
224
+ "run_timeout_seconds": self.run_timeout_seconds,
225
+ "worker_poll_interval": self.worker_poll_interval,
226
+ }
227
+
228
+ @classmethod
229
+ def from_dict(cls, data: Dict[str, Any]) -> "QueueConfig":
230
+ """Create from dictionary."""
231
+ priority = data.get("default_priority", 1)
232
+ if isinstance(priority, int):
233
+ priority = RunPriority(priority)
234
+
235
+ return cls(
236
+ max_concurrent_global=data.get("max_concurrent_global", 4),
237
+ max_concurrent_per_agent=data.get("max_concurrent_per_agent", 2),
238
+ max_concurrent_per_workspace=data.get("max_concurrent_per_workspace", 4),
239
+ max_queue_size=data.get("max_queue_size", 100),
240
+ default_priority=priority,
241
+ default_max_retries=data.get("default_max_retries", 3),
242
+ enable_persistence=data.get("enable_persistence", True),
243
+ db_path=data.get("db_path", ".praison/queue.db"),
244
+ autosave_interval_seconds=data.get("autosave_interval_seconds", 30.0),
245
+ stream_buffer_size=data.get("stream_buffer_size", 1000),
246
+ drop_strategy=data.get("drop_strategy", "oldest"),
247
+ run_timeout_seconds=data.get("run_timeout_seconds"),
248
+ worker_poll_interval=data.get("worker_poll_interval", 0.1),
249
+ )
250
+
251
+
252
+ @dataclass
253
+ class QueueStats:
254
+ """Statistics about the queue."""
255
+ queued_count: int = 0
256
+ running_count: int = 0
257
+ succeeded_count: int = 0
258
+ failed_count: int = 0
259
+ cancelled_count: int = 0
260
+ total_runs: int = 0
261
+ avg_wait_seconds: float = 0.0
262
+ avg_duration_seconds: float = 0.0
263
+
264
+ @property
265
+ def active_count(self) -> int:
266
+ """Count of active (non-terminal) runs."""
267
+ return self.queued_count + self.running_count
268
+
269
+
270
+ @dataclass
271
+ class StreamChunk:
272
+ """A chunk of streaming output."""
273
+ run_id: str
274
+ content: str
275
+ timestamp: float = field(default_factory=time.time)
276
+ chunk_index: int = 0
277
+ is_final: bool = False
278
+
279
+
280
+ @dataclass
281
+ class QueueEvent:
282
+ """An event from the queue system."""
283
+ event_type: str # "run_started", "run_completed", "run_failed", "output_chunk", "state_changed"
284
+ run_id: str
285
+ timestamp: float = field(default_factory=time.time)
286
+ data: Dict[str, Any] = field(default_factory=dict)