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,372 @@
1
+ """
2
+ Worker Pool for PraisonAI Queue System.
3
+
4
+ Manages async workers that execute queued runs.
5
+ """
6
+
7
+ import asyncio
8
+ import logging
9
+ import time
10
+ from collections import deque
11
+ from typing import Any, Callable, Coroutine, Dict, List, Optional, Set
12
+
13
+ from .models import QueuedRun, RunState, StreamChunk, QueueEvent
14
+ from .scheduler import QueueScheduler
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ class StreamBuffer:
20
+ """Bounded buffer with backpressure for streaming output."""
21
+
22
+ def __init__(self, max_size: int = 1000, drop_strategy: str = "oldest"):
23
+ """
24
+ Initialize stream buffer.
25
+
26
+ Args:
27
+ max_size: Maximum number of chunks to buffer.
28
+ drop_strategy: "oldest" to drop oldest, "newest" to reject new.
29
+ """
30
+ self._buffer: deque = deque(maxlen=max_size)
31
+ self._lock = asyncio.Lock()
32
+ self._drop_strategy = drop_strategy
33
+ self._dropped_count = 0
34
+ self._max_size = max_size
35
+
36
+ async def push(self, chunk: StreamChunk) -> bool:
37
+ """
38
+ Push a chunk to the buffer.
39
+
40
+ Returns:
41
+ True if added, False if dropped.
42
+ """
43
+ async with self._lock:
44
+ if len(self._buffer) >= self._max_size:
45
+ if self._drop_strategy == "oldest":
46
+ self._buffer.popleft()
47
+ self._dropped_count += 1
48
+ elif self._drop_strategy == "newest":
49
+ self._dropped_count += 1
50
+ return False
51
+ self._buffer.append(chunk)
52
+ return True
53
+
54
+ async def drain(self, batch_size: int = 50) -> List[StreamChunk]:
55
+ """Drain up to batch_size chunks."""
56
+ async with self._lock:
57
+ result = []
58
+ for _ in range(min(batch_size, len(self._buffer))):
59
+ result.append(self._buffer.popleft())
60
+ return result
61
+
62
+ async def drain_all(self) -> List[StreamChunk]:
63
+ """Drain all chunks."""
64
+ async with self._lock:
65
+ result = list(self._buffer)
66
+ self._buffer.clear()
67
+ return result
68
+
69
+ @property
70
+ def size(self) -> int:
71
+ """Current buffer size."""
72
+ return len(self._buffer)
73
+
74
+ @property
75
+ def dropped_count(self) -> int:
76
+ """Number of dropped chunks."""
77
+ return self._dropped_count
78
+
79
+
80
+ class WorkerPool:
81
+ """
82
+ Manages worker tasks for executing queued runs.
83
+
84
+ Workers poll the scheduler for runs and execute them with streaming output.
85
+ """
86
+
87
+ def __init__(
88
+ self,
89
+ scheduler: QueueScheduler,
90
+ on_output: Optional[Callable[[str, str], Coroutine[Any, Any, None]]] = None,
91
+ on_complete: Optional[Callable[[str, QueuedRun], Coroutine[Any, Any, None]]] = None,
92
+ on_error: Optional[Callable[[str, Exception], Coroutine[Any, Any, None]]] = None,
93
+ on_event: Optional[Callable[[QueueEvent], Coroutine[Any, Any, None]]] = None,
94
+ max_workers: int = 4,
95
+ poll_interval: float = 0.1,
96
+ stream_buffer_size: int = 1000,
97
+ ):
98
+ """
99
+ Initialize worker pool.
100
+
101
+ Args:
102
+ scheduler: The queue scheduler.
103
+ on_output: Callback for streaming output (run_id, chunk).
104
+ on_complete: Callback when run completes (run_id, run).
105
+ on_error: Callback when run fails (run_id, exception).
106
+ on_event: Callback for queue events.
107
+ max_workers: Maximum number of concurrent workers.
108
+ poll_interval: Seconds between scheduler polls.
109
+ stream_buffer_size: Size of stream buffer per run.
110
+ """
111
+ self.scheduler = scheduler
112
+ self.on_output = on_output
113
+ self.on_complete = on_complete
114
+ self.on_error = on_error
115
+ self.on_event = on_event
116
+ self.max_workers = max_workers
117
+ self.poll_interval = poll_interval
118
+ self.stream_buffer_size = stream_buffer_size
119
+
120
+ self._workers: List[asyncio.Task] = []
121
+ self._running = False
122
+ self._stream_buffers: Dict[str, StreamBuffer] = {}
123
+ self._active_runs: Set[str] = set()
124
+
125
+ async def start(self) -> None:
126
+ """Start the worker pool."""
127
+ if self._running:
128
+ return
129
+
130
+ self._running = True
131
+
132
+ for i in range(self.max_workers):
133
+ task = asyncio.create_task(
134
+ self._worker_loop(i),
135
+ name=f"queue-worker-{i}"
136
+ )
137
+ self._workers.append(task)
138
+
139
+ logger.info(f"Started {self.max_workers} queue workers")
140
+
141
+ async def stop(self, timeout: float = 5.0) -> None:
142
+ """
143
+ Stop all workers gracefully.
144
+
145
+ Args:
146
+ timeout: Seconds to wait for workers to finish.
147
+ """
148
+ self._running = False
149
+
150
+ if not self._workers:
151
+ return
152
+
153
+ # Wait for workers to finish current work
154
+ try:
155
+ await asyncio.wait_for(
156
+ asyncio.gather(*self._workers, return_exceptions=True),
157
+ timeout=timeout
158
+ )
159
+ except asyncio.TimeoutError:
160
+ # Cancel remaining workers
161
+ for task in self._workers:
162
+ if not task.done():
163
+ task.cancel()
164
+
165
+ await asyncio.gather(*self._workers, return_exceptions=True)
166
+
167
+ self._workers.clear()
168
+ logger.info("Stopped queue workers")
169
+
170
+ async def _worker_loop(self, worker_id: int) -> None:
171
+ """Main worker loop."""
172
+ logger.debug(f"Worker {worker_id} started")
173
+
174
+ while self._running:
175
+ try:
176
+ # Get next run from scheduler
177
+ run = await self.scheduler.next()
178
+
179
+ if run is None:
180
+ # No work available, wait and retry
181
+ await asyncio.sleep(self.poll_interval)
182
+ continue
183
+
184
+ # Execute the run
185
+ self._active_runs.add(run.run_id)
186
+ try:
187
+ await self._execute_run(run, worker_id)
188
+ finally:
189
+ self._active_runs.discard(run.run_id)
190
+
191
+ except asyncio.CancelledError:
192
+ logger.debug(f"Worker {worker_id} cancelled")
193
+ break
194
+ except Exception as e:
195
+ logger.error(f"Worker {worker_id} error: {e}", exc_info=True)
196
+ await asyncio.sleep(self.poll_interval)
197
+
198
+ logger.debug(f"Worker {worker_id} stopped")
199
+
200
+ async def _execute_run(self, run: QueuedRun, worker_id: int) -> None:
201
+ """Execute a single run with streaming output."""
202
+ logger.debug(f"Worker {worker_id} executing run {run.run_id}")
203
+
204
+ # Create stream buffer for this run
205
+ buffer = StreamBuffer(max_size=self.stream_buffer_size)
206
+ self._stream_buffers[run.run_id] = buffer
207
+
208
+ output_chunks: List[str] = []
209
+
210
+ try:
211
+ # Import agent lazily to avoid circular imports
212
+ from praisonaiagents import Agent
213
+
214
+ # Get agent configuration
215
+ agent_config = run.config.get("agent_config", {})
216
+ agent_name = run.agent_name or agent_config.get("name", "Assistant")
217
+
218
+ # Create agent
219
+ agent = Agent(
220
+ name=agent_name,
221
+ instructions=agent_config.get("instructions", "You are a helpful assistant."),
222
+ model=agent_config.get("model"),
223
+ tools=agent_config.get("tools", []),
224
+ verbose=agent_config.get("verbose", False),
225
+ )
226
+
227
+ # Check for cancellation before starting
228
+ if self.scheduler.is_cancelled(run.run_id):
229
+ logger.debug(f"Run {run.run_id} was cancelled before execution")
230
+ return
231
+
232
+ # Execute with streaming if available
233
+ chunk_index = 0
234
+
235
+ # Try streaming first
236
+ try:
237
+ async for chunk in self._stream_agent(agent, run.input_content):
238
+ # Check for cancellation
239
+ if self.scheduler.is_cancelled(run.run_id):
240
+ logger.debug(f"Run {run.run_id} cancelled during execution")
241
+ self.scheduler.clear_cancel_token(run.run_id)
242
+ return
243
+
244
+ # Check for pause
245
+ while run.state == RunState.PAUSED:
246
+ await asyncio.sleep(0.1)
247
+ if self.scheduler.is_cancelled(run.run_id):
248
+ return
249
+
250
+ output_chunks.append(chunk)
251
+
252
+ # Buffer the chunk
253
+ stream_chunk = StreamChunk(
254
+ run_id=run.run_id,
255
+ content=chunk,
256
+ chunk_index=chunk_index,
257
+ )
258
+ await buffer.push(stream_chunk)
259
+ chunk_index += 1
260
+
261
+ # Emit output callback
262
+ if self.on_output:
263
+ try:
264
+ await self.on_output(run.run_id, chunk)
265
+ except Exception as e:
266
+ logger.error(f"Error in output callback: {e}")
267
+
268
+ except Exception as stream_error:
269
+ # Fallback to non-streaming
270
+ logger.debug(f"Streaming failed, falling back to sync: {stream_error}")
271
+ result = agent.chat(run.input_content)
272
+ output_chunks = [result]
273
+
274
+ if self.on_output:
275
+ try:
276
+ await self.on_output(run.run_id, result)
277
+ except Exception as e:
278
+ logger.error(f"Error in output callback: {e}")
279
+
280
+ # Combine output
281
+ full_output = "".join(output_chunks)
282
+
283
+ # Mark final chunk
284
+ final_chunk = StreamChunk(
285
+ run_id=run.run_id,
286
+ content="",
287
+ chunk_index=chunk_index,
288
+ is_final=True,
289
+ )
290
+ await buffer.push(final_chunk)
291
+
292
+ # Complete the run
293
+ completed_run = await self.scheduler.complete(
294
+ run.run_id,
295
+ output=full_output,
296
+ metrics={
297
+ "chunks": chunk_index,
298
+ "output_length": len(full_output),
299
+ }
300
+ )
301
+
302
+ if completed_run and self.on_complete:
303
+ try:
304
+ await self.on_complete(run.run_id, completed_run)
305
+ except Exception as e:
306
+ logger.error(f"Error in complete callback: {e}")
307
+
308
+ except asyncio.CancelledError:
309
+ # Worker was cancelled
310
+ await self.scheduler.cancel(run.run_id)
311
+ raise
312
+
313
+ except Exception as e:
314
+ logger.error(f"Run {run.run_id} failed: {e}", exc_info=True)
315
+
316
+ # Mark as failed
317
+ failed_run = await self.scheduler.fail(run.run_id, str(e))
318
+
319
+ if self.on_error:
320
+ try:
321
+ await self.on_error(run.run_id, e)
322
+ except Exception as callback_error:
323
+ logger.error(f"Error in error callback: {callback_error}")
324
+
325
+ finally:
326
+ # Cleanup buffer
327
+ self._stream_buffers.pop(run.run_id, None)
328
+
329
+ async def _stream_agent(self, agent, input_content: str):
330
+ """
331
+ Stream output from agent.
332
+
333
+ Yields chunks of output text.
334
+ """
335
+ # Try to use agent's async streaming if available
336
+ if hasattr(agent, 'astream'):
337
+ async for chunk in agent.astream(input_content):
338
+ yield chunk
339
+ elif hasattr(agent, 'stream'):
340
+ # Wrap sync stream in async
341
+ for chunk in agent.stream(input_content):
342
+ yield chunk
343
+ await asyncio.sleep(0) # Yield control
344
+ else:
345
+ # Fallback: get full response and yield in chunks
346
+ result = agent.chat(input_content)
347
+
348
+ # Simulate streaming by yielding words
349
+ words = result.split()
350
+ chunk_size = 3 # Words per chunk
351
+
352
+ for i in range(0, len(words), chunk_size):
353
+ chunk_words = words[i:i + chunk_size]
354
+ chunk = " ".join(chunk_words)
355
+ if i + chunk_size < len(words):
356
+ chunk += " "
357
+ yield chunk
358
+ await asyncio.sleep(0.05) # Small delay for streaming effect
359
+
360
+ def get_stream_buffer(self, run_id: str) -> Optional[StreamBuffer]:
361
+ """Get the stream buffer for a run."""
362
+ return self._stream_buffers.get(run_id)
363
+
364
+ @property
365
+ def active_run_count(self) -> int:
366
+ """Number of currently executing runs."""
367
+ return len(self._active_runs)
368
+
369
+ @property
370
+ def is_running(self) -> bool:
371
+ """Whether the worker pool is running."""
372
+ return self._running