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,266 @@
1
+ """
2
+ Recipe Data Models
3
+
4
+ Defines the core data structures for recipe execution.
5
+ """
6
+
7
+ from dataclasses import dataclass, field
8
+ from datetime import datetime, timezone
9
+ from enum import IntEnum
10
+ from typing import Any, Dict, List, Optional
11
+
12
+
13
+ class ExitCode(IntEnum):
14
+ """Stable exit codes for CLI operations."""
15
+ SUCCESS = 0
16
+ GENERAL_ERROR = 1
17
+ VALIDATION_ERROR = 2
18
+ RUNTIME_ERROR = 3
19
+ POLICY_DENIED = 4
20
+ TIMEOUT = 5
21
+ MISSING_DEPS = 6
22
+ NOT_FOUND = 7
23
+
24
+
25
+ class RecipeStatus:
26
+ """Recipe execution status constants."""
27
+ SUCCESS = "success"
28
+ FAILED = "failed"
29
+ DRY_RUN = "dry_run"
30
+ POLICY_DENIED = "policy_denied"
31
+ TIMEOUT = "timeout"
32
+ MISSING_DEPS = "missing_deps"
33
+ VALIDATION_ERROR = "validation_error"
34
+
35
+
36
+ @dataclass
37
+ class RecipeResult:
38
+ """
39
+ Result of a recipe execution.
40
+
41
+ Attributes:
42
+ run_id: Unique identifier for this execution
43
+ recipe: Recipe name
44
+ version: Recipe version
45
+ status: Execution status (success, failed, dry_run, policy_denied, etc.)
46
+ output: Recipe-specific output data
47
+ metrics: Execution metrics (duration, tokens, etc.)
48
+ error: Error message if failed
49
+ trace: Tracing identifiers (run_id, trace_id, session_id, agent_id)
50
+ """
51
+ run_id: str
52
+ recipe: str
53
+ version: str
54
+ status: str
55
+ output: Any = None
56
+ metrics: Dict[str, Any] = field(default_factory=dict)
57
+ error: Optional[str] = None
58
+ trace: Dict[str, str] = field(default_factory=dict)
59
+
60
+ @property
61
+ def ok(self) -> bool:
62
+ """Check if execution was successful."""
63
+ return self.status in (RecipeStatus.SUCCESS, RecipeStatus.DRY_RUN)
64
+
65
+ def to_dict(self) -> Dict[str, Any]:
66
+ """Convert to dictionary for JSON serialization."""
67
+ return {
68
+ "ok": self.ok,
69
+ "run_id": self.run_id,
70
+ "recipe": self.recipe,
71
+ "version": self.version,
72
+ "status": self.status,
73
+ "output": self.output,
74
+ "metrics": self.metrics,
75
+ "error": self.error,
76
+ "trace": self.trace,
77
+ }
78
+
79
+ def to_exit_code(self) -> int:
80
+ """Convert status to CLI exit code."""
81
+ status_to_code = {
82
+ RecipeStatus.SUCCESS: ExitCode.SUCCESS,
83
+ RecipeStatus.DRY_RUN: ExitCode.SUCCESS,
84
+ RecipeStatus.FAILED: ExitCode.RUNTIME_ERROR,
85
+ RecipeStatus.POLICY_DENIED: ExitCode.POLICY_DENIED,
86
+ RecipeStatus.TIMEOUT: ExitCode.TIMEOUT,
87
+ RecipeStatus.MISSING_DEPS: ExitCode.MISSING_DEPS,
88
+ RecipeStatus.VALIDATION_ERROR: ExitCode.VALIDATION_ERROR,
89
+ }
90
+ return status_to_code.get(self.status, ExitCode.GENERAL_ERROR)
91
+
92
+
93
+ @dataclass
94
+ class RecipeEvent:
95
+ """
96
+ Streaming event from recipe execution.
97
+
98
+ Attributes:
99
+ event_type: Type of event (started, progress, log, output, completed, error)
100
+ data: Event-specific data
101
+ timestamp: ISO format timestamp
102
+ """
103
+ event_type: str
104
+ data: Dict[str, Any] = field(default_factory=dict)
105
+ timestamp: str = ""
106
+
107
+ def __post_init__(self):
108
+ if not self.timestamp:
109
+ self.timestamp = datetime.now(timezone.utc).isoformat()
110
+
111
+ def to_dict(self) -> Dict[str, Any]:
112
+ """Convert to dictionary for JSON/SSE serialization."""
113
+ return {
114
+ "event": self.event_type,
115
+ "data": self.data,
116
+ "timestamp": self.timestamp,
117
+ }
118
+
119
+ def to_sse(self) -> str:
120
+ """Convert to Server-Sent Events format."""
121
+ import json
122
+ return f"event: {self.event_type}\ndata: {json.dumps(self.data)}\n\n"
123
+
124
+
125
+ @dataclass
126
+ class RecipeConfig:
127
+ """
128
+ Recipe configuration and metadata.
129
+
130
+ Attributes:
131
+ name: Recipe name (kebab-case)
132
+ version: SemVer version string
133
+ description: Recipe description
134
+ author: Author name or identifier
135
+ license: License identifier (e.g., Apache-2.0)
136
+ tags: Discovery tags
137
+ requires: Dependencies (packages, env, tools, external)
138
+ tools: Tool permissions (allow, deny)
139
+ config_schema: JSON Schema for input configuration
140
+ defaults: Default configuration values
141
+ outputs: Expected output definitions
142
+ governance: Governance settings (approval, cost limits, audit)
143
+ data_policy: Data handling policy (PII, retention)
144
+ path: Source path of the recipe
145
+ """
146
+ name: str
147
+ version: str = "1.0.0"
148
+ description: str = ""
149
+ author: Optional[str] = None
150
+ license: Optional[str] = None
151
+ tags: List[str] = field(default_factory=list)
152
+
153
+ # Dependencies
154
+ requires: Dict[str, Any] = field(default_factory=dict)
155
+
156
+ # Tool permissions
157
+ tools: Dict[str, List[str]] = field(default_factory=dict)
158
+
159
+ # Configuration
160
+ config_schema: Dict[str, Any] = field(default_factory=dict)
161
+ defaults: Dict[str, Any] = field(default_factory=dict)
162
+
163
+ # Outputs
164
+ outputs: List[Dict[str, Any]] = field(default_factory=list)
165
+
166
+ # Governance
167
+ governance: Dict[str, Any] = field(default_factory=dict)
168
+
169
+ # Data policy
170
+ data_policy: Dict[str, Any] = field(default_factory=dict)
171
+
172
+ # Source path
173
+ path: Optional[str] = None
174
+
175
+ # Raw config dict
176
+ raw: Dict[str, Any] = field(default_factory=dict)
177
+
178
+ def get_allowed_tools(self) -> List[str]:
179
+ """Get list of explicitly allowed tools."""
180
+ return self.tools.get("allow", [])
181
+
182
+ def get_denied_tools(self) -> List[str]:
183
+ """Get list of explicitly denied tools."""
184
+ return self.tools.get("deny", [])
185
+
186
+ def get_required_packages(self) -> List[str]:
187
+ """Get list of required Python packages."""
188
+ packages = self.requires.get("packages", [])
189
+ return [packages] if isinstance(packages, str) else packages
190
+
191
+ def get_required_env(self) -> List[str]:
192
+ """Get list of required environment variables."""
193
+ env = self.requires.get("env", [])
194
+ return [env] if isinstance(env, str) else env
195
+
196
+ def get_required_tools(self) -> List[str]:
197
+ """Get list of required tools."""
198
+ tools = self.requires.get("tools", [])
199
+ return [tools] if isinstance(tools, str) else tools
200
+
201
+ def get_external_deps(self) -> List[Dict[str, Any]]:
202
+ """Get list of external dependencies (ffmpeg, etc.)."""
203
+ return self.requires.get("external", [])
204
+
205
+ def to_dict(self) -> Dict[str, Any]:
206
+ """Convert to dictionary."""
207
+ return {
208
+ "name": self.name,
209
+ "version": self.version,
210
+ "description": self.description,
211
+ "author": self.author,
212
+ "license": self.license,
213
+ "tags": self.tags,
214
+ "requires": self.requires,
215
+ "tools": self.tools,
216
+ "config_schema": self.config_schema,
217
+ "defaults": self.defaults,
218
+ "outputs": self.outputs,
219
+ "governance": self.governance,
220
+ "data_policy": self.data_policy,
221
+ "path": self.path,
222
+ }
223
+
224
+
225
+ @dataclass
226
+ class ValidationResult:
227
+ """Result of recipe validation."""
228
+ valid: bool
229
+ recipe: str
230
+ version: str
231
+ errors: List[str] = field(default_factory=list)
232
+ warnings: List[str] = field(default_factory=list)
233
+ dependencies: Dict[str, Any] = field(default_factory=dict)
234
+
235
+ def to_dict(self) -> Dict[str, Any]:
236
+ """Convert to dictionary."""
237
+ return {
238
+ "valid": self.valid,
239
+ "recipe": self.recipe,
240
+ "version": self.version,
241
+ "errors": self.errors,
242
+ "warnings": self.warnings,
243
+ "dependencies": self.dependencies,
244
+ }
245
+
246
+
247
+ @dataclass
248
+ class RecipeInfo:
249
+ """Recipe information for listing/discovery."""
250
+ name: str
251
+ version: str
252
+ description: str
253
+ tags: List[str]
254
+ path: str
255
+ source: str # local, package, github
256
+
257
+ def to_dict(self) -> Dict[str, Any]:
258
+ """Convert to dictionary."""
259
+ return {
260
+ "name": self.name,
261
+ "version": self.version,
262
+ "description": self.description,
263
+ "tags": self.tags,
264
+ "path": self.path,
265
+ "source": self.source,
266
+ }
@@ -0,0 +1,440 @@
1
+ """
2
+ Recipe Operations Module.
3
+
4
+ Provides high-level APIs for running recipes in different operational modes:
5
+ - run_background(): Execute recipe as a background task
6
+ - submit_job(): Submit recipe to async jobs server
7
+ - schedule(): Create a scheduled recipe executor
8
+
9
+ All functions are lazy-loaded and have zero performance impact when not used.
10
+ """
11
+
12
+ import logging
13
+ from dataclasses import dataclass, field
14
+ from typing import Any, Dict, Optional
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ # Safe defaults
20
+ DEFAULT_TIMEOUT_SEC = 300
21
+ DEFAULT_MAX_COST_USD = 1.00
22
+ DEFAULT_MAX_RETRIES = 3
23
+
24
+
25
+ @dataclass
26
+ class BackgroundTaskHandle:
27
+ """Handle for a background recipe task."""
28
+ task_id: str
29
+ recipe_name: str
30
+ session_id: Optional[str] = None
31
+ _runner: Any = field(default=None, repr=False)
32
+ _task: Any = field(default=None, repr=False)
33
+
34
+ async def status(self) -> str:
35
+ """Get task status."""
36
+ if self._runner and self._task:
37
+ return self._task.status.value
38
+ return "unknown"
39
+
40
+ async def wait(self, timeout: Optional[float] = None) -> Any:
41
+ """Wait for task completion and return result."""
42
+ if self._runner and self._task:
43
+ return await self._runner.wait_for_task(self._task.id, timeout=timeout)
44
+ return None
45
+
46
+ async def cancel(self) -> bool:
47
+ """Cancel the task."""
48
+ if self._runner:
49
+ return await self._runner.cancel(self.task_id)
50
+ return False
51
+
52
+
53
+ @dataclass
54
+ class JobHandle:
55
+ """Handle for an async job."""
56
+ job_id: str
57
+ recipe_name: str
58
+ status: str = "queued"
59
+ poll_url: Optional[str] = None
60
+ stream_url: Optional[str] = None
61
+ api_url: str = "http://127.0.0.1:8005"
62
+
63
+ def get_status(self) -> Dict[str, Any]:
64
+ """Get job status from API."""
65
+ try:
66
+ import httpx
67
+ with httpx.Client(timeout=30.0) as client:
68
+ response = client.get(f"{self.api_url}/api/v1/runs/{self.job_id}")
69
+ response.raise_for_status()
70
+ return response.json()
71
+ except Exception as e:
72
+ logger.error(f"Failed to get job status: {e}")
73
+ return {"job_id": self.job_id, "status": "unknown", "error": str(e)}
74
+
75
+ def get_result(self) -> Any:
76
+ """Get job result from API."""
77
+ try:
78
+ import httpx
79
+ with httpx.Client(timeout=30.0) as client:
80
+ response = client.get(f"{self.api_url}/api/v1/runs/{self.job_id}/result")
81
+ response.raise_for_status()
82
+ return response.json()
83
+ except Exception as e:
84
+ logger.error(f"Failed to get job result: {e}")
85
+ return {"job_id": self.job_id, "error": str(e)}
86
+
87
+ def cancel(self) -> bool:
88
+ """Cancel the job."""
89
+ try:
90
+ import httpx
91
+ with httpx.Client(timeout=30.0) as client:
92
+ response = client.post(f"{self.api_url}/api/v1/runs/{self.job_id}/cancel")
93
+ return response.status_code < 400
94
+ except Exception as e:
95
+ logger.error(f"Failed to cancel job: {e}")
96
+ return False
97
+
98
+ def wait(self, poll_interval: int = 5, timeout: Optional[int] = None) -> Dict[str, Any]:
99
+ """Wait for job completion by polling."""
100
+ import time
101
+ start_time = time.time()
102
+
103
+ while True:
104
+ status = self.get_status()
105
+ if status.get("status") in ("succeeded", "failed", "cancelled"):
106
+ return self.get_result()
107
+
108
+ if timeout and (time.time() - start_time) > timeout:
109
+ return {"job_id": self.job_id, "status": "timeout", "error": "Wait timeout exceeded"}
110
+
111
+ time.sleep(poll_interval)
112
+
113
+
114
+ @dataclass
115
+ class RecipeScheduler:
116
+ """Wrapper around AgentScheduler for recipe-based scheduling."""
117
+ recipe_name: str
118
+ interval: str = "hourly"
119
+ max_retries: int = DEFAULT_MAX_RETRIES
120
+ timeout_sec: int = DEFAULT_TIMEOUT_SEC
121
+ max_cost_usd: float = DEFAULT_MAX_COST_USD
122
+ run_immediately: bool = False
123
+ _scheduler: Any = field(default=None, repr=False)
124
+ _resolved: Any = field(default=None, repr=False)
125
+
126
+ def start(self) -> bool:
127
+ """Start the scheduler."""
128
+ if self._scheduler:
129
+ return self._scheduler.start(
130
+ schedule_expr=self.interval,
131
+ max_retries=self.max_retries,
132
+ run_immediately=self.run_immediately
133
+ )
134
+ return False
135
+
136
+ def stop(self) -> bool:
137
+ """Stop the scheduler."""
138
+ if self._scheduler:
139
+ return self._scheduler.stop()
140
+ return False
141
+
142
+ def get_stats(self) -> Dict[str, Any]:
143
+ """Get scheduler statistics."""
144
+ if self._scheduler:
145
+ return self._scheduler.get_stats()
146
+ return {}
147
+
148
+ @property
149
+ def is_running(self) -> bool:
150
+ """Check if scheduler is running."""
151
+ if self._scheduler:
152
+ return self._scheduler.is_running
153
+ return False
154
+
155
+
156
+ def run_background(
157
+ name: str,
158
+ *,
159
+ input: Any = None,
160
+ config: Optional[Dict[str, Any]] = None,
161
+ session_id: Optional[str] = None,
162
+ timeout_sec: Optional[int] = None,
163
+ max_concurrent: int = 5,
164
+ on_complete: Optional[callable] = None,
165
+ ) -> BackgroundTaskHandle:
166
+ """
167
+ Run a recipe as a background task.
168
+
169
+ Args:
170
+ name: Recipe name
171
+ input: Input data for the recipe
172
+ config: Configuration overrides
173
+ session_id: Session ID for conversation continuity
174
+ timeout_sec: Timeout in seconds (default: 300)
175
+ max_concurrent: Max concurrent tasks (default: 5)
176
+ on_complete: Callback when task completes
177
+
178
+ Returns:
179
+ BackgroundTaskHandle for tracking the task
180
+
181
+ Example:
182
+ task = recipe.run_background("my-recipe", input={"query": "test"})
183
+ print(f"Task ID: {task.task_id}")
184
+ result = await task.wait()
185
+ """
186
+ import asyncio
187
+ from .bridge import resolve, execute_resolved_recipe
188
+
189
+ # Resolve the recipe
190
+ resolved = resolve(
191
+ name,
192
+ input_data=input,
193
+ config=config,
194
+ session_id=session_id,
195
+ options={'timeout_sec': timeout_sec or DEFAULT_TIMEOUT_SEC},
196
+ )
197
+
198
+ # Import BackgroundRunner lazily
199
+ try:
200
+ from praisonaiagents.background import BackgroundRunner
201
+ except ImportError:
202
+ raise RuntimeError(
203
+ "Background tasks require praisonaiagents. "
204
+ "Install with: pip install praisonaiagents"
205
+ )
206
+
207
+ # Create or get runner
208
+ runner = BackgroundRunner(max_concurrent_tasks=max_concurrent)
209
+
210
+ # Define the task function
211
+ def recipe_task():
212
+ return execute_resolved_recipe(resolved)
213
+
214
+ # Submit the task
215
+ loop = asyncio.get_event_loop()
216
+ if loop.is_running():
217
+ # We're in an async context, use create_task
218
+ import concurrent.futures
219
+ future = concurrent.futures.Future()
220
+
221
+ async def submit_and_return():
222
+ task = await runner.submit(
223
+ recipe_task,
224
+ name=f"recipe:{resolved.name}",
225
+ timeout=timeout_sec,
226
+ on_complete=on_complete,
227
+ )
228
+ return task
229
+
230
+ asyncio.ensure_future(submit_and_return()).add_done_callback(
231
+ lambda f: future.set_result(f.result())
232
+ )
233
+ task = future.result(timeout=10)
234
+ else:
235
+ # Sync context
236
+ task = loop.run_until_complete(runner.submit(
237
+ recipe_task,
238
+ name=f"recipe:{resolved.name}",
239
+ timeout=timeout_sec,
240
+ on_complete=on_complete,
241
+ ))
242
+
243
+ return BackgroundTaskHandle(
244
+ task_id=task.id,
245
+ recipe_name=resolved.name,
246
+ session_id=resolved.session_id,
247
+ _runner=runner,
248
+ _task=task,
249
+ )
250
+
251
+
252
+ def submit_job(
253
+ name: str,
254
+ *,
255
+ input: Any = None,
256
+ config: Optional[Dict[str, Any]] = None,
257
+ session_id: Optional[str] = None,
258
+ timeout_sec: Optional[int] = None,
259
+ idempotency_key: Optional[str] = None,
260
+ webhook_url: Optional[str] = None,
261
+ api_url: str = "http://127.0.0.1:8005",
262
+ wait: bool = False,
263
+ poll_interval: int = 5,
264
+ ) -> JobHandle:
265
+ """
266
+ Submit a recipe to the async jobs server.
267
+
268
+ Args:
269
+ name: Recipe name
270
+ input: Input data for the recipe
271
+ config: Configuration overrides
272
+ session_id: Session ID for conversation continuity
273
+ timeout_sec: Timeout in seconds (default: 3600)
274
+ idempotency_key: Key for deduplication
275
+ webhook_url: URL for completion webhook
276
+ api_url: Jobs API URL (default: http://127.0.0.1:8005)
277
+ wait: If True, wait for completion before returning
278
+ poll_interval: Polling interval when waiting (seconds)
279
+
280
+ Returns:
281
+ JobHandle for tracking the job
282
+
283
+ Example:
284
+ job = recipe.submit_job("my-recipe", input={"query": "test"}, wait=True)
285
+ print(f"Result: {job.get_result()}")
286
+ """
287
+ try:
288
+ import httpx
289
+ except ImportError:
290
+ raise RuntimeError(
291
+ "Job submission requires httpx. "
292
+ "Install with: pip install httpx"
293
+ )
294
+
295
+ # Build request payload
296
+ payload = {
297
+ "prompt": str(input) if input else f"Execute recipe: {name}",
298
+ "recipe_name": name,
299
+ "timeout": timeout_sec or 3600,
300
+ }
301
+
302
+ if config:
303
+ payload["config"] = config
304
+ if session_id:
305
+ payload["session_id"] = session_id
306
+ if idempotency_key:
307
+ payload["idempotency_key"] = idempotency_key
308
+ if webhook_url:
309
+ payload["webhook_url"] = webhook_url
310
+
311
+ # Submit to API
312
+ with httpx.Client(timeout=30.0) as client:
313
+ response = client.post(
314
+ f"{api_url}/api/v1/runs",
315
+ json=payload,
316
+ )
317
+ response.raise_for_status()
318
+ data = response.json()
319
+
320
+ handle = JobHandle(
321
+ job_id=data["job_id"],
322
+ recipe_name=name,
323
+ status=data.get("status", "queued"),
324
+ poll_url=data.get("poll_url"),
325
+ stream_url=data.get("stream_url"),
326
+ api_url=api_url,
327
+ )
328
+
329
+ if wait:
330
+ handle.wait(poll_interval=poll_interval, timeout=timeout_sec)
331
+
332
+ return handle
333
+
334
+
335
+ def schedule(
336
+ name: str,
337
+ *,
338
+ input: Any = None,
339
+ config: Optional[Dict[str, Any]] = None,
340
+ interval: Optional[str] = None,
341
+ max_retries: Optional[int] = None,
342
+ run_immediately: Optional[bool] = None,
343
+ timeout_sec: Optional[int] = None,
344
+ max_cost_usd: Optional[float] = None,
345
+ on_success: Optional[callable] = None,
346
+ on_failure: Optional[callable] = None,
347
+ ) -> RecipeScheduler:
348
+ """
349
+ Create a scheduler for periodic recipe execution.
350
+
351
+ Args:
352
+ name: Recipe name
353
+ input: Input data for the recipe
354
+ config: Configuration overrides
355
+ interval: Schedule interval (hourly, daily, */30m, etc.)
356
+ max_retries: Max retry attempts (default: 3)
357
+ run_immediately: Run once immediately (default: False)
358
+ timeout_sec: Timeout per execution (default: 300)
359
+ max_cost_usd: Budget limit (default: $1.00)
360
+ on_success: Callback on successful execution
361
+ on_failure: Callback on failed execution
362
+
363
+ Returns:
364
+ RecipeScheduler instance (call .start() to begin)
365
+
366
+ Example:
367
+ scheduler = recipe.schedule("news-monitor", interval="hourly")
368
+ scheduler.start()
369
+ # ... later ...
370
+ scheduler.stop()
371
+ """
372
+ from .bridge import resolve, execute_resolved_recipe, get_recipe_task_description
373
+
374
+ # Resolve the recipe
375
+ resolved = resolve(
376
+ name,
377
+ input_data=input,
378
+ config=config,
379
+ options={'timeout_sec': timeout_sec or DEFAULT_TIMEOUT_SEC},
380
+ )
381
+
382
+ # Get runtime config defaults
383
+ runtime = resolved.runtime_config
384
+ if runtime and hasattr(runtime, 'schedule'):
385
+ sched_config = runtime.schedule
386
+ interval = interval or sched_config.interval
387
+ max_retries = max_retries if max_retries is not None else sched_config.max_retries
388
+ run_immediately = run_immediately if run_immediately is not None else sched_config.run_immediately
389
+ timeout_sec = timeout_sec or sched_config.timeout_sec
390
+ max_cost_usd = max_cost_usd if max_cost_usd is not None else sched_config.max_cost_usd
391
+
392
+ # Apply defaults
393
+ interval = interval or "hourly"
394
+ max_retries = max_retries if max_retries is not None else DEFAULT_MAX_RETRIES
395
+ run_immediately = run_immediately if run_immediately is not None else False
396
+ timeout_sec = timeout_sec or DEFAULT_TIMEOUT_SEC
397
+ max_cost_usd = max_cost_usd if max_cost_usd is not None else DEFAULT_MAX_COST_USD
398
+
399
+ # Import AgentScheduler lazily
400
+ try:
401
+ from praisonai.scheduler import AgentScheduler
402
+ except ImportError:
403
+ raise RuntimeError(
404
+ "Scheduling requires praisonai scheduler module."
405
+ )
406
+
407
+ # Create a recipe executor agent wrapper
408
+ class RecipeExecutorAgent:
409
+ """Wrapper that makes a recipe look like an agent for the scheduler."""
410
+ def __init__(self, resolved_recipe):
411
+ self.resolved = resolved_recipe
412
+ self.name = f"RecipeAgent:{resolved_recipe.name}"
413
+
414
+ def start(self, task: str) -> Any:
415
+ return execute_resolved_recipe(self.resolved)
416
+
417
+ # Create the agent wrapper
418
+ agent = RecipeExecutorAgent(resolved)
419
+ task = get_recipe_task_description(resolved)
420
+
421
+ # Create the scheduler
422
+ scheduler = AgentScheduler(
423
+ agent=agent,
424
+ task=task,
425
+ timeout=timeout_sec,
426
+ max_cost=max_cost_usd,
427
+ on_success=on_success,
428
+ on_failure=on_failure,
429
+ )
430
+
431
+ return RecipeScheduler(
432
+ recipe_name=resolved.name,
433
+ interval=interval,
434
+ max_retries=max_retries,
435
+ timeout_sec=timeout_sec,
436
+ max_cost_usd=max_cost_usd,
437
+ run_immediately=run_immediately,
438
+ _scheduler=scheduler,
439
+ _resolved=resolved,
440
+ )