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,225 @@
1
+ """
2
+ Daemon process manager for running schedulers in background.
3
+ """
4
+ import os
5
+ import sys
6
+ import signal
7
+ import subprocess
8
+ from pathlib import Path
9
+ from typing import Optional, Dict, List
10
+ from datetime import datetime
11
+
12
+
13
+ class DaemonManager:
14
+ """Manages daemon processes for schedulers."""
15
+
16
+ def __init__(self, log_dir: Optional[Path] = None, max_log_size_mb: float = 10.0):
17
+ """
18
+ Initialize daemon manager.
19
+
20
+ Args:
21
+ log_dir: Directory for log files. Defaults to ~/.praisonai/logs
22
+ max_log_size_mb: Maximum log file size in MB before rotation
23
+ """
24
+ if log_dir is None:
25
+ home = Path.home()
26
+ log_dir = home / ".praisonai" / "logs"
27
+
28
+ self.log_dir = Path(log_dir)
29
+ self.log_dir.mkdir(parents=True, exist_ok=True)
30
+ self.max_log_size_bytes = int(max_log_size_mb * 1024 * 1024)
31
+
32
+ def start_daemon(
33
+ self,
34
+ name: str,
35
+ task: str,
36
+ interval: str,
37
+ command: List[str]
38
+ ) -> int:
39
+ """
40
+ Start a daemon process.
41
+
42
+ Args:
43
+ name: Daemon name
44
+ task: Task description
45
+ interval: Schedule interval
46
+ command: Command to run as list
47
+
48
+ Returns:
49
+ Process ID
50
+ """
51
+ log_file = self.log_dir / f"{name}.log"
52
+
53
+ # Open log file - use original working approach
54
+ with open(log_file, 'a') as log:
55
+ log.flush()
56
+
57
+ # Start process as daemon
58
+ proc = subprocess.Popen(
59
+ command,
60
+ stdout=log,
61
+ stderr=subprocess.STDOUT,
62
+ stdin=subprocess.DEVNULL,
63
+ start_new_session=True, # Detach from terminal
64
+ cwd=os.getcwd()
65
+ )
66
+
67
+ return proc.pid
68
+
69
+ def start_scheduler_daemon(
70
+ self,
71
+ name: str,
72
+ task: Optional[str] = None,
73
+ recipe_name: Optional[str] = None,
74
+ interval: str = "hourly",
75
+ max_cost: Optional[float] = None,
76
+ timeout: Optional[int] = None,
77
+ max_retries: int = 3
78
+ ) -> int:
79
+ """
80
+ Start a PraisonAI scheduler as daemon.
81
+
82
+ Args:
83
+ name: Scheduler name
84
+ task: Task to schedule (mutually exclusive with recipe_name)
85
+ recipe_name: Recipe name to schedule (mutually exclusive with task)
86
+ interval: Schedule interval
87
+ max_cost: Maximum cost budget
88
+ timeout: Timeout per execution
89
+ max_retries: Maximum retry attempts
90
+
91
+ Returns:
92
+ Process ID
93
+ """
94
+ # Build command - use CLI without --daemon flag (original working approach)
95
+ command = [
96
+ sys.executable,
97
+ "-m",
98
+ "praisonai.cli.main",
99
+ "schedule",
100
+ ]
101
+
102
+ # Add task or recipe
103
+ if recipe_name:
104
+ command.append(recipe_name) # Recipe name will be auto-detected
105
+ elif task:
106
+ command.append(task)
107
+ else:
108
+ raise ValueError("Either task or recipe_name must be provided")
109
+
110
+ command.extend(["--interval", interval, "--max-retries", str(max_retries)])
111
+
112
+ if timeout:
113
+ command.extend(["--timeout", str(timeout)])
114
+
115
+ if max_cost:
116
+ command.extend(["--max-cost", str(max_cost)])
117
+
118
+ display_task = recipe_name or task
119
+ return self.start_daemon(name, display_task, interval, command)
120
+
121
+ def stop_daemon(self, pid: int, timeout: int = 10) -> bool:
122
+ """
123
+ Stop a daemon process gracefully.
124
+
125
+ Args:
126
+ pid: Process ID
127
+ timeout: Timeout in seconds
128
+
129
+ Returns:
130
+ True if stopped successfully
131
+ """
132
+ try:
133
+ # Try graceful shutdown first (SIGTERM)
134
+ os.kill(pid, signal.SIGTERM)
135
+
136
+ # Wait for process to terminate
137
+ import time
138
+ for _ in range(timeout * 10):
139
+ try:
140
+ os.kill(pid, 0) # Check if still alive
141
+ time.sleep(0.1)
142
+ except (OSError, ProcessLookupError):
143
+ return True # Process terminated
144
+
145
+ # Force kill if still alive
146
+ try:
147
+ os.kill(pid, signal.SIGKILL)
148
+ time.sleep(0.2) # Give it time to die
149
+ except (OSError, ProcessLookupError):
150
+ pass
151
+
152
+ return True
153
+
154
+ except (OSError, ProcessLookupError):
155
+ return False
156
+
157
+ def get_status(self, pid: int) -> Optional[Dict]:
158
+ """
159
+ Get daemon process status.
160
+
161
+ Args:
162
+ pid: Process ID
163
+
164
+ Returns:
165
+ Status dictionary or None if not found
166
+ """
167
+ try:
168
+ os.kill(pid, 0) # Check if process exists
169
+
170
+ return {
171
+ "pid": pid,
172
+ "is_alive": True
173
+ }
174
+ except (OSError, ProcessLookupError):
175
+ return {
176
+ "pid": pid,
177
+ "is_alive": False
178
+ }
179
+
180
+ def read_logs(self, name: str, lines: int = 50) -> Optional[str]:
181
+ """
182
+ Read daemon logs.
183
+
184
+ Args:
185
+ name: Daemon name
186
+ lines: Number of lines to read from end
187
+
188
+ Returns:
189
+ Log content or None if not found
190
+ """
191
+ log_file = self.log_dir / f"{name}.log"
192
+
193
+ if not log_file.exists():
194
+ return None
195
+
196
+ try:
197
+ with open(log_file) as f:
198
+ all_lines = f.readlines()
199
+ return ''.join(all_lines[-lines:])
200
+ except IOError:
201
+ return None
202
+
203
+ def rotate_log(self, name: str) -> bool:
204
+ """
205
+ Rotate log file if it exceeds max size.
206
+
207
+ Args:
208
+ name: Daemon name
209
+
210
+ Returns:
211
+ True if rotated
212
+ """
213
+ log_file = self.log_dir / f"{name}.log"
214
+
215
+ if not log_file.exists():
216
+ return False
217
+
218
+ if log_file.stat().st_size > self.max_log_size_bytes:
219
+ # Rotate: rename to .log.1, .log.2, etc.
220
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
221
+ rotated_file = self.log_dir / f"{name}.log.{timestamp}"
222
+ log_file.rename(rotated_file)
223
+ return True
224
+
225
+ return False
@@ -0,0 +1,155 @@
1
+ """
2
+ Scheduler state manager for persistent storage of scheduler processes.
3
+ """
4
+ import json
5
+ import os
6
+ import signal
7
+ from pathlib import Path
8
+ from typing import Dict, List, Optional
9
+ from datetime import datetime
10
+
11
+
12
+ class SchedulerStateManager:
13
+ """Manages persistent state for scheduler processes."""
14
+
15
+ def __init__(self, state_dir: Optional[Path] = None):
16
+ """
17
+ Initialize state manager.
18
+
19
+ Args:
20
+ state_dir: Directory to store state files. Defaults to ~/.praisonai/schedulers
21
+ """
22
+ if state_dir is None:
23
+ home = Path.home()
24
+ state_dir = home / ".praisonai" / "schedulers"
25
+
26
+ self.state_dir = Path(state_dir)
27
+ self.state_dir.mkdir(parents=True, exist_ok=True)
28
+
29
+ def save_state(self, name: str, state: Dict) -> None:
30
+ """
31
+ Save scheduler state to JSON file.
32
+
33
+ Args:
34
+ name: Scheduler name
35
+ state: State dictionary to save
36
+ """
37
+ state_file = self.state_dir / f"{name}.json"
38
+
39
+ with open(state_file, 'w') as f:
40
+ json.dump(state, f, indent=2)
41
+
42
+ def load_state(self, name: str) -> Optional[Dict]:
43
+ """
44
+ Load scheduler state from JSON file.
45
+
46
+ Args:
47
+ name: Scheduler name
48
+
49
+ Returns:
50
+ State dictionary or None if not found
51
+ """
52
+ state_file = self.state_dir / f"{name}.json"
53
+
54
+ if not state_file.exists():
55
+ return None
56
+
57
+ try:
58
+ with open(state_file) as f:
59
+ return json.load(f)
60
+ except (json.JSONDecodeError, IOError):
61
+ return None
62
+
63
+ def delete_state(self, name: str) -> bool:
64
+ """
65
+ Delete scheduler state file.
66
+
67
+ Args:
68
+ name: Scheduler name
69
+
70
+ Returns:
71
+ True if deleted, False if not found
72
+ """
73
+ state_file = self.state_dir / f"{name}.json"
74
+
75
+ if state_file.exists():
76
+ state_file.unlink()
77
+ return True
78
+
79
+ return False
80
+
81
+ def list_all(self) -> List[Dict]:
82
+ """
83
+ List all scheduler states.
84
+
85
+ Returns:
86
+ List of state dictionaries
87
+ """
88
+ states = []
89
+
90
+ for state_file in self.state_dir.glob("*.json"):
91
+ try:
92
+ with open(state_file) as f:
93
+ state = json.load(f)
94
+ states.append(state)
95
+ except (json.JSONDecodeError, IOError):
96
+ continue
97
+
98
+ return states
99
+
100
+ def generate_unique_name(self, base_name: str = "scheduler") -> str:
101
+ """
102
+ Generate a unique scheduler name.
103
+
104
+ Args:
105
+ base_name: Base name for the scheduler
106
+
107
+ Returns:
108
+ Unique name like "scheduler-0", "scheduler-1", etc.
109
+ """
110
+ existing_states = self.list_all()
111
+ existing_names = {s.get("name", "") for s in existing_states}
112
+
113
+ counter = 0
114
+ while True:
115
+ name = f"{base_name}-{counter}"
116
+ if name not in existing_names:
117
+ return name
118
+ counter += 1
119
+
120
+ def is_process_alive(self, pid: int) -> bool:
121
+ """
122
+ Check if a process is still alive.
123
+
124
+ Args:
125
+ pid: Process ID
126
+
127
+ Returns:
128
+ True if process is alive, False otherwise
129
+ """
130
+ try:
131
+ # Send signal 0 to check if process exists
132
+ os.kill(pid, 0)
133
+ return True
134
+ except (OSError, ProcessLookupError):
135
+ return False
136
+
137
+ def cleanup_dead_processes(self) -> int:
138
+ """
139
+ Clean up state files for dead processes.
140
+
141
+ Returns:
142
+ Number of dead processes cleaned up
143
+ """
144
+ cleaned = 0
145
+ states = self.list_all()
146
+
147
+ for state in states:
148
+ pid = state.get("pid")
149
+ name = state.get("name")
150
+
151
+ if pid and name and not self.is_process_alive(pid):
152
+ self.delete_state(name)
153
+ cleaned += 1
154
+
155
+ return cleaned
@@ -0,0 +1,193 @@
1
+ """
2
+ YAML loader for agent scheduler configuration.
3
+
4
+ Loads agent and schedule configuration from agents.yaml files.
5
+ """
6
+
7
+ import yaml
8
+ import logging
9
+ from typing import Dict, Any, Tuple
10
+ from pathlib import Path
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ def load_agent_yaml_with_schedule(yaml_path: str) -> Tuple[Dict[str, Any], Dict[str, Any]]:
16
+ """
17
+ Load agents.yaml file and extract agent and schedule configuration.
18
+
19
+ Args:
20
+ yaml_path: Path to agents.yaml file
21
+
22
+ Returns:
23
+ Tuple of (agent_config, schedule_config)
24
+
25
+ Raises:
26
+ FileNotFoundError: If YAML file doesn't exist
27
+ ValueError: If YAML is invalid or missing required fields
28
+
29
+ Example YAML structure:
30
+ framework: praisonai
31
+
32
+ agents:
33
+ - name: "AI News Monitor"
34
+ role: "News Analyst"
35
+ goal: "Monitor AI news"
36
+ instructions: "Search and summarize"
37
+ tools:
38
+ - search_tool
39
+ verbose: true
40
+
41
+ task: "Search for latest AI news"
42
+
43
+ schedule:
44
+ interval: "hourly"
45
+ max_retries: 3
46
+ run_immediately: true
47
+ """
48
+ yaml_path = Path(yaml_path)
49
+
50
+ if not yaml_path.exists():
51
+ raise FileNotFoundError(f"YAML file not found: {yaml_path}")
52
+
53
+ try:
54
+ with open(yaml_path, 'r') as f:
55
+ config = yaml.safe_load(f)
56
+ except yaml.YAMLError as e:
57
+ raise ValueError(f"Invalid YAML file: {e}")
58
+
59
+ if not config:
60
+ raise ValueError("Empty YAML file")
61
+
62
+ # Extract agent configuration
63
+ agent_config = {}
64
+
65
+ # Get agents list (can be single agent or multiple)
66
+ agents = config.get('agents', [])
67
+ if not agents:
68
+ raise ValueError("No agents defined in YAML file")
69
+
70
+ # Use first agent for scheduling (can be extended for multi-agent later)
71
+ if isinstance(agents, list):
72
+ agent_config = agents[0]
73
+ else:
74
+ agent_config = agents
75
+
76
+ # Get task
77
+ task = config.get('task', '')
78
+ if not task:
79
+ # Try to get from agent's tasks
80
+ if 'tasks' in agent_config and agent_config['tasks']:
81
+ first_task = list(agent_config['tasks'].values())[0]
82
+ task = first_task.get('description', '')
83
+
84
+ agent_config['task'] = task
85
+
86
+ # Get framework (default to praisonai)
87
+ agent_config['framework'] = config.get('framework', 'praisonai')
88
+
89
+ # Extract schedule configuration (optional)
90
+ schedule_section = config.get('schedule', {})
91
+
92
+ # Set defaults for schedule if not provided
93
+ schedule_config = {
94
+ 'interval': schedule_section.get('interval', 'hourly'),
95
+ 'max_retries': schedule_section.get('max_retries', 3),
96
+ 'run_immediately': schedule_section.get('run_immediately', False),
97
+ 'timeout': schedule_section.get('timeout'), # Optional timeout in seconds
98
+ 'max_cost': schedule_section.get('max_cost', 1.00) # Default $1.00 budget limit for safety
99
+ }
100
+
101
+ logger.info(f"Loaded agent '{agent_config.get('name', 'Unknown')}' with schedule interval '{schedule_config['interval']}'")
102
+
103
+ return agent_config, schedule_config
104
+
105
+
106
+ def create_agent_from_config(agent_config: Dict[str, Any]) -> Any:
107
+ """
108
+ Create a PraisonAI Agent instance from configuration.
109
+
110
+ Args:
111
+ agent_config: Agent configuration dictionary
112
+
113
+ Returns:
114
+ Agent instance
115
+
116
+ Raises:
117
+ ImportError: If praisonaiagents is not installed
118
+ """
119
+ try:
120
+ from praisonaiagents import Agent
121
+ except ImportError:
122
+ raise ImportError("praisonaiagents package is required. Install with: pip install praisonaiagents")
123
+
124
+ # Extract agent parameters
125
+ name = agent_config.get('name', 'Scheduled Agent')
126
+ role = agent_config.get('role', '')
127
+ goal = agent_config.get('goal', '')
128
+ instructions = agent_config.get('instructions', agent_config.get('backstory', ''))
129
+ verbose = agent_config.get('verbose', False)
130
+
131
+ # Handle tools
132
+ tools = []
133
+ tool_names = agent_config.get('tools', [])
134
+
135
+ if tool_names:
136
+ # Try to import tools
137
+ for tool_name in tool_names:
138
+ try:
139
+ # Try common tool imports
140
+ if tool_name == 'search_tool' or tool_name == 'InternetSearchTool':
141
+ try:
142
+ from tools import search_tool
143
+ tools.append(search_tool)
144
+ except ImportError:
145
+ logger.warning(f"Could not import {tool_name}, skipping")
146
+ # Add more tool imports as needed
147
+ except Exception as e:
148
+ logger.warning(f"Error loading tool {tool_name}: {e}")
149
+
150
+ # Create agent
151
+ agent = Agent(
152
+ name=name,
153
+ role=role,
154
+ goal=goal,
155
+ instructions=instructions,
156
+ tools=tools,
157
+ verbose=verbose
158
+ )
159
+
160
+ return agent
161
+
162
+
163
+ def validate_schedule_config(schedule_config: Dict[str, Any]) -> None:
164
+ """
165
+ Validate schedule configuration.
166
+
167
+ Args:
168
+ schedule_config: Schedule configuration dictionary
169
+
170
+ Raises:
171
+ ValueError: If configuration is invalid
172
+ """
173
+ required_fields = ['interval']
174
+ for field in required_fields:
175
+ if field not in schedule_config:
176
+ raise ValueError(f"Missing required schedule field: {field}")
177
+
178
+ # Validate interval format
179
+ interval = schedule_config['interval']
180
+ valid_intervals = ['hourly', 'daily']
181
+
182
+ if interval not in valid_intervals:
183
+ # Check if it's a custom format (*/Nm, */Nh, */Ns, or plain number)
184
+ if not (interval.startswith('*/') or interval.isdigit()):
185
+ raise ValueError(
186
+ f"Invalid interval format: {interval}. "
187
+ f"Use 'hourly', 'daily', '*/30m', '*/6h', or seconds as number"
188
+ )
189
+
190
+ # Validate max_retries
191
+ max_retries = schedule_config.get('max_retries', 3)
192
+ if not isinstance(max_retries, int) or max_retries < 0:
193
+ raise ValueError(f"max_retries must be a non-negative integer, got: {max_retries}")