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,632 @@
1
+ """
2
+ Jobs CLI Feature for PraisonAI.
3
+
4
+ Provides CLI commands for managing async jobs.
5
+
6
+ Commands:
7
+ - praisonai run submit <prompt> # Submit a new job
8
+ - praisonai run status <job_id> # Get job status
9
+ - praisonai run result <job_id> # Get job result
10
+ - praisonai run cancel <job_id> # Cancel a job
11
+ - praisonai run list # List jobs
12
+ """
13
+
14
+ import sys
15
+ import json
16
+ import time
17
+ from typing import Optional, List
18
+
19
+
20
+ class JobsHandler:
21
+ """
22
+ Handler for jobs CLI commands.
23
+
24
+ Provides functionality to:
25
+ - Submit jobs to the API
26
+ - Check job status
27
+ - Get job results
28
+ - Cancel jobs
29
+ - List jobs
30
+ """
31
+
32
+ def __init__(
33
+ self,
34
+ api_url: str = "http://127.0.0.1:8005",
35
+ verbose: bool = False
36
+ ):
37
+ self.api_url = api_url.rstrip("/")
38
+ self.verbose = verbose
39
+
40
+ @property
41
+ def feature_name(self) -> str:
42
+ return "jobs"
43
+
44
+ def _get_client(self):
45
+ """Get HTTP client."""
46
+ try:
47
+ import httpx
48
+ return httpx.Client(timeout=30.0)
49
+ except ImportError:
50
+ raise RuntimeError("httpx is required. Install with: pip install httpx")
51
+
52
+ def submit(
53
+ self,
54
+ prompt: str,
55
+ agent_file: Optional[str] = None,
56
+ recipe_name: Optional[str] = None,
57
+ recipe_config: Optional[dict] = None,
58
+ framework: str = "praisonai",
59
+ timeout: int = 3600,
60
+ wait: bool = False,
61
+ poll_interval: int = 5,
62
+ idempotency_key: Optional[str] = None,
63
+ idempotency_scope: str = "none",
64
+ webhook_url: Optional[str] = None,
65
+ session_id: Optional[str] = None,
66
+ output_json: bool = False
67
+ ) -> dict:
68
+ """
69
+ Submit a new job.
70
+
71
+ Args:
72
+ prompt: The prompt/task for the agent
73
+ agent_file: Optional path to agents.yaml
74
+ recipe_name: Optional recipe name (mutually exclusive with agent_file)
75
+ recipe_config: Optional recipe configuration overrides
76
+ framework: Framework to use
77
+ timeout: Job timeout in seconds
78
+ wait: If True, wait for completion
79
+ poll_interval: Seconds between status polls
80
+ idempotency_key: Key for deduplication
81
+ idempotency_scope: Scope for deduplication (none, session, global)
82
+ webhook_url: URL for completion webhook
83
+ session_id: Session ID for conversation continuity
84
+ output_json: If True, output JSON format
85
+
86
+ Returns:
87
+ Job submission response
88
+ """
89
+ # Validate mutually exclusive options
90
+ if agent_file and recipe_name:
91
+ raise ValueError("Cannot specify both --agent-file and --recipe. Choose one.")
92
+
93
+ client = self._get_client()
94
+
95
+ payload = {
96
+ "prompt": prompt,
97
+ "framework": framework,
98
+ "timeout": timeout
99
+ }
100
+
101
+ if agent_file:
102
+ payload["agent_file"] = agent_file
103
+ if recipe_name:
104
+ payload["recipe_name"] = recipe_name
105
+ if recipe_config:
106
+ payload["recipe_config"] = recipe_config
107
+ if webhook_url:
108
+ payload["webhook_url"] = webhook_url
109
+ if session_id:
110
+ payload["session_id"] = session_id
111
+ if idempotency_key:
112
+ payload["idempotency_key"] = idempotency_key
113
+ if idempotency_scope and idempotency_scope != "none":
114
+ payload["idempotency_scope"] = idempotency_scope
115
+
116
+ headers = {}
117
+ if idempotency_key:
118
+ headers["Idempotency-Key"] = idempotency_key
119
+
120
+ try:
121
+ response = client.post(
122
+ f"{self.api_url}/api/v1/runs",
123
+ json=payload,
124
+ headers=headers if headers else None
125
+ )
126
+ response.raise_for_status()
127
+ result = response.json()
128
+
129
+ if output_json:
130
+ print(json.dumps(result, indent=2))
131
+ else:
132
+ self._print_success(f"Job submitted: {result['job_id']}")
133
+ self._print_info(f"Status URL: {result['poll_url']}")
134
+
135
+ if wait:
136
+ return self._wait_for_completion(result['job_id'], poll_interval, output_json)
137
+
138
+ return result
139
+
140
+ except Exception as e:
141
+ self._print_error(f"Failed to submit job: {e}")
142
+ raise
143
+
144
+ def status(self, job_id: str) -> dict:
145
+ """
146
+ Get job status.
147
+
148
+ Args:
149
+ job_id: Job ID
150
+
151
+ Returns:
152
+ Job status response
153
+ """
154
+ client = self._get_client()
155
+
156
+ try:
157
+ response = client.get(f"{self.api_url}/api/v1/runs/{job_id}")
158
+ response.raise_for_status()
159
+ result = response.json()
160
+
161
+ self._print_job_status(result)
162
+ return result
163
+
164
+ except Exception as e:
165
+ self._print_error(f"Failed to get status: {e}")
166
+ raise
167
+
168
+ def result(self, job_id: str) -> dict:
169
+ """
170
+ Get job result.
171
+
172
+ Args:
173
+ job_id: Job ID
174
+
175
+ Returns:
176
+ Job result response
177
+ """
178
+ client = self._get_client()
179
+
180
+ try:
181
+ response = client.get(f"{self.api_url}/api/v1/runs/{job_id}/result")
182
+ response.raise_for_status()
183
+ result = response.json()
184
+
185
+ self._print_job_result(result)
186
+ return result
187
+
188
+ except Exception as e:
189
+ self._print_error(f"Failed to get result: {e}")
190
+ raise
191
+
192
+ def cancel(self, job_id: str) -> dict:
193
+ """
194
+ Cancel a job.
195
+
196
+ Args:
197
+ job_id: Job ID
198
+
199
+ Returns:
200
+ Updated job status
201
+ """
202
+ client = self._get_client()
203
+
204
+ try:
205
+ response = client.post(f"{self.api_url}/api/v1/runs/{job_id}/cancel")
206
+ response.raise_for_status()
207
+ result = response.json()
208
+
209
+ self._print_success(f"Job cancelled: {job_id}")
210
+ return result
211
+
212
+ except Exception as e:
213
+ self._print_error(f"Failed to cancel job: {e}")
214
+ raise
215
+
216
+ def list_jobs(
217
+ self,
218
+ status: Optional[str] = None,
219
+ page: int = 1,
220
+ page_size: int = 20
221
+ ) -> dict:
222
+ """
223
+ List jobs.
224
+
225
+ Args:
226
+ status: Optional status filter
227
+ page: Page number
228
+ page_size: Jobs per page
229
+
230
+ Returns:
231
+ Job list response
232
+ """
233
+ client = self._get_client()
234
+
235
+ params = {"page": page, "page_size": page_size}
236
+ if status:
237
+ params["status"] = status
238
+
239
+ try:
240
+ response = client.get(
241
+ f"{self.api_url}/api/v1/runs",
242
+ params=params
243
+ )
244
+ response.raise_for_status()
245
+ result = response.json()
246
+
247
+ self._print_job_list(result)
248
+ return result
249
+
250
+ except Exception as e:
251
+ self._print_error(f"Failed to list jobs: {e}")
252
+ raise
253
+
254
+ def _wait_for_completion(self, job_id: str, poll_interval: int = 5, output_json: bool = False) -> dict:
255
+ """Wait for a job to complete."""
256
+ client = self._get_client()
257
+
258
+ if not output_json:
259
+ self._print_info("Waiting for job completion...")
260
+
261
+ while True:
262
+ try:
263
+ response = client.get(f"{self.api_url}/api/v1/runs/{job_id}")
264
+ response.raise_for_status()
265
+ result = response.json()
266
+
267
+ status = result.get("status")
268
+ progress = result.get("progress", {}).get("percentage", 0)
269
+
270
+ if not output_json:
271
+ self._print_progress(status, progress)
272
+
273
+ if status in ("succeeded", "failed", "cancelled"):
274
+ if output_json:
275
+ # Get full result for succeeded jobs
276
+ if status == "succeeded":
277
+ res = client.get(f"{self.api_url}/api/v1/runs/{job_id}/result")
278
+ res.raise_for_status()
279
+ print(json.dumps(res.json(), indent=2))
280
+ else:
281
+ print(json.dumps(result, indent=2))
282
+ return result
283
+ else:
284
+ if status == "succeeded":
285
+ return self.result(job_id)
286
+ else:
287
+ return result
288
+
289
+ # Honor Retry-After header if present
290
+ retry_after = result.get("retry_after") or poll_interval
291
+ time.sleep(retry_after)
292
+
293
+ except KeyboardInterrupt:
294
+ if not output_json:
295
+ self._print_info("\nInterrupted. Job continues running in background.")
296
+ return result
297
+ except Exception as e:
298
+ if not output_json:
299
+ self._print_error(f"Error polling status: {e}")
300
+ time.sleep(poll_interval)
301
+
302
+ def stream(self, job_id: str, output_json: bool = False) -> None:
303
+ """
304
+ Stream job progress via SSE.
305
+
306
+ Args:
307
+ job_id: Job ID
308
+ output_json: If True, output raw JSON events
309
+ """
310
+ try:
311
+ import httpx
312
+
313
+ with httpx.stream(
314
+ "GET",
315
+ f"{self.api_url}/api/v1/runs/{job_id}/stream",
316
+ timeout=None
317
+ ) as response:
318
+ response.raise_for_status()
319
+
320
+ for line in response.iter_lines():
321
+ if not line:
322
+ continue
323
+ if line.startswith("data:"):
324
+ data = line[5:].strip()
325
+ if data == "[DONE]":
326
+ if not output_json:
327
+ self._print_success("Stream completed")
328
+ break
329
+ try:
330
+ event = json.loads(data)
331
+ if output_json:
332
+ print(json.dumps(event))
333
+ else:
334
+ event_type = event.get("event", "unknown")
335
+ if event_type == "progress":
336
+ pct = event.get("data", {}).get("percentage", 0)
337
+ step = event.get("data", {}).get("current_step", "")
338
+ self._print_progress(f"{step}", pct)
339
+ elif event_type == "complete":
340
+ status = event.get("data", {}).get("status", "")
341
+ if status == "succeeded":
342
+ self._print_success(f"Job completed: {status}")
343
+ else:
344
+ self._print_error(f"Job ended: {status}")
345
+ elif event_type == "error":
346
+ self._print_error(event.get("data", {}).get("error", "Unknown error"))
347
+ except json.JSONDecodeError:
348
+ if not output_json:
349
+ print(data)
350
+ elif line.startswith(":"):
351
+ # Heartbeat comment, ignore
352
+ pass
353
+
354
+ except Exception as e:
355
+ self._print_error(f"Stream error: {e}")
356
+ raise
357
+
358
+ def _print_job_status(self, job: dict):
359
+ """Print job status."""
360
+ try:
361
+ from rich.console import Console
362
+ from rich.panel import Panel
363
+
364
+ console = Console()
365
+
366
+ status = job.get("status", "unknown")
367
+ status_color = {
368
+ "queued": "dim",
369
+ "running": "yellow",
370
+ "succeeded": "green",
371
+ "failed": "red",
372
+ "cancelled": "dim"
373
+ }.get(status, "white")
374
+
375
+ progress = job.get("progress", {})
376
+
377
+ content = f"""
378
+ [bold]Job ID:[/bold] {job.get('job_id')}
379
+ [bold]Status:[/bold] [{status_color}]{status}[/{status_color}]
380
+ [bold]Progress:[/bold] {progress.get('percentage', 0):.0f}%
381
+ [bold]Step:[/bold] {progress.get('current_step') or '-'}
382
+ [bold]Created:[/bold] {job.get('created_at')}
383
+ [bold]Started:[/bold] {job.get('started_at') or '-'}
384
+ """
385
+ if job.get("error"):
386
+ content += f"[bold]Error:[/bold] [red]{job.get('error')}[/red]\n"
387
+
388
+ console.print(Panel(content.strip(), title="Job Status"))
389
+ except ImportError:
390
+ print(f"Job: {job.get('job_id')}")
391
+ print(f" Status: {job.get('status')}")
392
+ print(f" Progress: {job.get('progress', {}).get('percentage', 0):.0f}%")
393
+
394
+ def _print_job_result(self, job: dict):
395
+ """Print job result."""
396
+ try:
397
+ from rich.console import Console
398
+ from rich.panel import Panel
399
+ from rich.markdown import Markdown
400
+
401
+ console = Console()
402
+
403
+ status = job.get("status")
404
+
405
+ if status == "succeeded":
406
+ result = job.get("result", "")
407
+ if isinstance(result, str):
408
+ console.print(Panel(Markdown(result), title="Result"))
409
+ else:
410
+ console.print(Panel(json.dumps(result, indent=2), title="Result"))
411
+ else:
412
+ console.print(f"[red]Job {status}: {job.get('error')}[/red]")
413
+
414
+ except ImportError:
415
+ print(f"Result: {job.get('result')}")
416
+
417
+ def _print_job_list(self, response: dict):
418
+ """Print job list."""
419
+ try:
420
+ from rich.console import Console
421
+ from rich.table import Table
422
+
423
+ console = Console()
424
+ jobs = response.get("jobs", [])
425
+
426
+ if not jobs:
427
+ console.print("[yellow]No jobs found[/yellow]")
428
+ return
429
+
430
+ table = Table(title=f"Jobs (Page {response.get('page')}/{(response.get('total', 0) + response.get('page_size', 20) - 1) // response.get('page_size', 20)})")
431
+ table.add_column("ID", style="cyan")
432
+ table.add_column("Status", style="yellow")
433
+ table.add_column("Progress", style="blue")
434
+ table.add_column("Created", style="dim")
435
+
436
+ for job in jobs:
437
+ status = job.get("status", "unknown")
438
+ status_color = {
439
+ "queued": "dim",
440
+ "running": "yellow",
441
+ "succeeded": "green",
442
+ "failed": "red",
443
+ "cancelled": "dim"
444
+ }.get(status, "white")
445
+
446
+ progress = job.get("progress", {}).get("percentage", 0)
447
+
448
+ table.add_row(
449
+ job.get("job_id", "?"),
450
+ f"[{status_color}]{status}[/{status_color}]",
451
+ f"{progress:.0f}%",
452
+ job.get("created_at", "")[:19]
453
+ )
454
+
455
+ console.print(table)
456
+ console.print(f"Total: {response.get('total', 0)} jobs")
457
+
458
+ except ImportError:
459
+ print("Jobs:")
460
+ for job in response.get("jobs", []):
461
+ print(f" {job.get('job_id')} - {job.get('status')}")
462
+
463
+ def _print_progress(self, status: str, percentage: float):
464
+ """Print progress update."""
465
+ try:
466
+ from rich.console import Console
467
+ console = Console()
468
+ console.print(f" Status: {status} | Progress: {percentage:.0f}%", end="\r")
469
+ except ImportError:
470
+ print(f" Status: {status} | Progress: {percentage:.0f}%", end="\r")
471
+
472
+ def _print_success(self, message: str):
473
+ """Print success message."""
474
+ try:
475
+ from rich.console import Console
476
+ Console().print(f"[green]✓[/green] {message}")
477
+ except ImportError:
478
+ print(f"✓ {message}")
479
+
480
+ def _print_error(self, message: str):
481
+ """Print error message."""
482
+ try:
483
+ from rich.console import Console
484
+ Console().print(f"[red]✗[/red] {message}")
485
+ except ImportError:
486
+ print(f"✗ {message}")
487
+
488
+ def _print_info(self, message: str):
489
+ """Print info message."""
490
+ try:
491
+ from rich.console import Console
492
+ Console().print(f"[blue]ℹ[/blue] {message}")
493
+ except ImportError:
494
+ print(f"ℹ {message}")
495
+
496
+
497
+ def handle_run_command(args: List[str], verbose: bool = False):
498
+ """
499
+ Handle run CLI commands.
500
+
501
+ Usage:
502
+ praisonai run submit "Your prompt here" [--wait] [--agent-file agents.yaml]
503
+ praisonai run status <job_id>
504
+ praisonai run result <job_id>
505
+ praisonai run cancel <job_id>
506
+ praisonai run list [--status <status>]
507
+ """
508
+ import argparse
509
+
510
+ parser = argparse.ArgumentParser(prog="praisonai run", description="Manage async jobs")
511
+ subparsers = parser.add_subparsers(dest="subcommand", help="Available commands")
512
+
513
+ # Submit command
514
+ submit_parser = subparsers.add_parser("submit", help="Submit a new job")
515
+ submit_parser.add_argument("prompt", help="The prompt/task for the agent")
516
+ submit_parser.add_argument("--agent-file", help="Path to agents.yaml")
517
+ submit_parser.add_argument("--recipe", dest="recipe_name", help="Recipe name to execute (mutually exclusive with --agent-file)")
518
+ submit_parser.add_argument("--recipe-config", help="Recipe config as JSON string")
519
+ submit_parser.add_argument("--framework", default="praisonai", help="Framework to use")
520
+ submit_parser.add_argument("--timeout", type=int, default=3600, help="Timeout in seconds")
521
+ submit_parser.add_argument("--wait", action="store_true", help="Wait for completion")
522
+ submit_parser.add_argument("--stream", action="store_true", help="Stream job progress after submission")
523
+ submit_parser.add_argument("--idempotency-key", help="Idempotency key to prevent duplicates")
524
+ submit_parser.add_argument("--idempotency-scope", default="none", choices=["none", "session", "global"], help="Idempotency scope")
525
+ submit_parser.add_argument("--webhook-url", help="Webhook URL for completion callback")
526
+ submit_parser.add_argument("--session-id", help="Session ID for grouping jobs")
527
+ submit_parser.add_argument("--metadata", action="append", metavar="KEY=VALUE", help="Custom metadata (can be used multiple times)")
528
+ submit_parser.add_argument("--json", dest="output_json", action="store_true", help="Output JSON for scripting")
529
+ submit_parser.add_argument("--api-url", default="http://127.0.0.1:8005", help="API server URL")
530
+
531
+ # Status command
532
+ status_parser = subparsers.add_parser("status", help="Get job status")
533
+ status_parser.add_argument("job_id", help="Job ID")
534
+ status_parser.add_argument("--json", dest="output_json", action="store_true", help="Output JSON")
535
+ status_parser.add_argument("--api-url", default="http://127.0.0.1:8005", help="API server URL")
536
+
537
+ # Result command
538
+ result_parser = subparsers.add_parser("result", help="Get job result")
539
+ result_parser.add_argument("job_id", help="Job ID")
540
+ result_parser.add_argument("--json", dest="output_json", action="store_true", help="Output JSON")
541
+ result_parser.add_argument("--api-url", default="http://127.0.0.1:8005", help="API server URL")
542
+
543
+ # Cancel command
544
+ cancel_parser = subparsers.add_parser("cancel", help="Cancel a job")
545
+ cancel_parser.add_argument("job_id", help="Job ID")
546
+ cancel_parser.add_argument("--json", dest="output_json", action="store_true", help="Output JSON")
547
+ cancel_parser.add_argument("--api-url", default="http://127.0.0.1:8005", help="API server URL")
548
+
549
+ # List command
550
+ list_parser = subparsers.add_parser("list", help="List jobs")
551
+ list_parser.add_argument("--status", help="Filter by status")
552
+ list_parser.add_argument("--page", type=int, default=1, help="Page number")
553
+ list_parser.add_argument("--page-size", type=int, default=20, help="Jobs per page")
554
+ list_parser.add_argument("--json", dest="output_json", action="store_true", help="Output JSON")
555
+ list_parser.add_argument("--api-url", default="http://127.0.0.1:8005", help="API server URL")
556
+
557
+ # Stream command
558
+ stream_parser = subparsers.add_parser("stream", help="Stream job progress via SSE")
559
+ stream_parser.add_argument("job_id", help="Job ID")
560
+ stream_parser.add_argument("--json", dest="output_json", action="store_true", help="Output raw JSON events")
561
+ stream_parser.add_argument("--api-url", default="http://127.0.0.1:8005", help="API server URL")
562
+
563
+ if not args:
564
+ parser.print_help()
565
+ return
566
+
567
+ parsed = parser.parse_args(args)
568
+
569
+ if not parsed.subcommand:
570
+ parser.print_help()
571
+ return
572
+
573
+ handler = JobsHandler(
574
+ api_url=getattr(parsed, "api_url", "http://127.0.0.1:8005"),
575
+ verbose=verbose
576
+ )
577
+
578
+ try:
579
+ if parsed.subcommand == "submit":
580
+ # Parse recipe config if provided
581
+ recipe_config = None
582
+ if getattr(parsed, "recipe_config", None):
583
+ try:
584
+ recipe_config = json.loads(parsed.recipe_config)
585
+ except json.JSONDecodeError:
586
+ print("Error: --recipe-config must be valid JSON")
587
+ sys.exit(1)
588
+
589
+ handler.submit(
590
+ prompt=parsed.prompt,
591
+ agent_file=parsed.agent_file,
592
+ recipe_name=getattr(parsed, "recipe_name", None),
593
+ recipe_config=recipe_config,
594
+ framework=parsed.framework,
595
+ timeout=parsed.timeout,
596
+ wait=parsed.wait,
597
+ idempotency_key=getattr(parsed, "idempotency_key", None),
598
+ idempotency_scope=getattr(parsed, "idempotency_scope", "none"),
599
+ webhook_url=getattr(parsed, "webhook_url", None),
600
+ session_id=getattr(parsed, "session_id", None),
601
+ output_json=getattr(parsed, "output_json", False)
602
+ )
603
+ elif parsed.subcommand == "status":
604
+ result = handler.status(parsed.job_id)
605
+ if getattr(parsed, "output_json", False):
606
+ print(json.dumps(result, indent=2))
607
+ elif parsed.subcommand == "result":
608
+ result = handler.result(parsed.job_id)
609
+ if getattr(parsed, "output_json", False):
610
+ print(json.dumps(result, indent=2))
611
+ elif parsed.subcommand == "cancel":
612
+ result = handler.cancel(parsed.job_id)
613
+ if getattr(parsed, "output_json", False):
614
+ print(json.dumps(result, indent=2))
615
+ elif parsed.subcommand == "list":
616
+ result = handler.list_jobs(
617
+ status=parsed.status,
618
+ page=parsed.page,
619
+ page_size=parsed.page_size
620
+ )
621
+ if getattr(parsed, "output_json", False):
622
+ print(json.dumps(result, indent=2))
623
+ elif parsed.subcommand == "stream":
624
+ handler.stream(
625
+ job_id=parsed.job_id,
626
+ output_json=getattr(parsed, "output_json", False)
627
+ )
628
+ except Exception:
629
+ if verbose:
630
+ import traceback
631
+ traceback.print_exc()
632
+ sys.exit(1)