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,54 @@
1
+ """
2
+ Recipe Exceptions
3
+
4
+ Custom exceptions for recipe operations.
5
+ """
6
+
7
+
8
+ class RecipeError(Exception):
9
+ """Base exception for recipe operations."""
10
+
11
+ def __init__(self, message: str, recipe: str = None, details: dict = None):
12
+ super().__init__(message)
13
+ self.message = message
14
+ self.recipe = recipe
15
+ self.details = details or {}
16
+
17
+
18
+ class RecipeNotFoundError(RecipeError):
19
+ """Recipe not found."""
20
+ pass
21
+
22
+
23
+ class RecipeDependencyError(RecipeError):
24
+ """Missing dependencies for recipe execution."""
25
+
26
+ def __init__(self, message: str, recipe: str = None, missing: list = None):
27
+ super().__init__(message, recipe, {"missing": missing or []})
28
+ self.missing = missing or []
29
+
30
+
31
+ class RecipePolicyError(RecipeError):
32
+ """Security policy blocked execution."""
33
+
34
+ def __init__(self, message: str, recipe: str = None, policy: str = None):
35
+ super().__init__(message, recipe, {"policy": policy})
36
+ self.policy = policy
37
+
38
+
39
+ class RecipeValidationError(RecipeError):
40
+ """Recipe validation failed."""
41
+
42
+ def __init__(self, message: str, recipe: str = None, errors: list = None):
43
+ super().__init__(message, recipe, {"errors": errors or []})
44
+ self.errors = errors or []
45
+
46
+
47
+ class RecipeTimeoutError(RecipeError):
48
+ """Recipe execution timed out."""
49
+ pass
50
+
51
+
52
+ class RecipeExecutionError(RecipeError):
53
+ """Recipe execution failed."""
54
+ pass
@@ -0,0 +1,402 @@
1
+ """
2
+ Run History Storage Module
3
+
4
+ Provides storage and retrieval of recipe run history for:
5
+ - Export/replay functionality
6
+ - Audit trails
7
+ - Debugging and analysis
8
+ """
9
+
10
+ import json
11
+ import shutil
12
+ from datetime import datetime, timezone, timedelta
13
+ from pathlib import Path
14
+ from typing import Any, Dict, List, Optional
15
+
16
+ from .models import RecipeResult
17
+
18
+
19
+ # Default storage path
20
+ DEFAULT_RUNS_PATH = Path.home() / ".praison" / "runs"
21
+
22
+
23
+ class RunHistoryError(Exception):
24
+ """Base exception for run history operations."""
25
+ pass
26
+
27
+
28
+ class RunNotFoundError(RunHistoryError):
29
+ """Run not found in history."""
30
+ pass
31
+
32
+
33
+ def _get_timestamp() -> str:
34
+ """Get current timestamp in ISO format."""
35
+ return datetime.now(timezone.utc).isoformat()
36
+
37
+
38
+ class RunHistory:
39
+ """
40
+ Local run history storage.
41
+
42
+ Storage structure:
43
+ ~/.praison/runs/
44
+ ├── index.json # Run index for fast lookup
45
+ └── <run_id>/
46
+ ├── run.json # Run metadata and result
47
+ ├── input.json # Input data
48
+ ├── output.json # Output data
49
+ └── events.jsonl # Event stream (if streaming)
50
+ """
51
+
52
+ def __init__(self, path: Optional[Path] = None):
53
+ """Initialize run history storage."""
54
+ self.path = Path(path) if path else DEFAULT_RUNS_PATH
55
+ self.index_path = self.path / "index.json"
56
+ self._ensure_structure()
57
+
58
+ def _ensure_structure(self):
59
+ """Ensure storage directory structure exists."""
60
+ self.path.mkdir(parents=True, exist_ok=True)
61
+ if not self.index_path.exists():
62
+ self._save_index({"runs": {}, "updated": _get_timestamp()})
63
+
64
+ def _load_index(self) -> Dict[str, Any]:
65
+ """Load run index."""
66
+ if self.index_path.exists():
67
+ with open(self.index_path) as f:
68
+ return json.load(f)
69
+ return {"runs": {}, "updated": _get_timestamp()}
70
+
71
+ def _save_index(self, index: Dict[str, Any]):
72
+ """Save run index."""
73
+ index["updated"] = _get_timestamp()
74
+ with open(self.index_path, "w") as f:
75
+ json.dump(index, f, indent=2)
76
+
77
+ def store(
78
+ self,
79
+ result: RecipeResult,
80
+ input_data: Optional[Dict[str, Any]] = None,
81
+ events: Optional[List[Dict[str, Any]]] = None,
82
+ data_policy: Optional[Dict[str, Any]] = None,
83
+ ) -> str:
84
+ """
85
+ Store a run result in history.
86
+
87
+ Args:
88
+ result: RecipeResult to store
89
+ input_data: Original input data
90
+ events: List of streaming events (if any)
91
+ data_policy: Data policy for retention/export
92
+
93
+ Returns:
94
+ run_id
95
+ """
96
+ run_id = result.run_id
97
+ run_dir = self.path / run_id
98
+ run_dir.mkdir(parents=True, exist_ok=True)
99
+
100
+ # Check data policy
101
+ export_allowed = True
102
+ retention_days = None
103
+ if data_policy:
104
+ export_allowed = data_policy.get("export_allowed", True)
105
+ retention_days = data_policy.get("retention_days")
106
+
107
+ # Store run metadata
108
+ run_data = {
109
+ "run_id": run_id,
110
+ "recipe": result.recipe,
111
+ "version": result.version,
112
+ "status": result.status,
113
+ "ok": result.ok,
114
+ "error": result.error,
115
+ "metrics": result.metrics,
116
+ "trace": result.trace,
117
+ "stored_at": _get_timestamp(),
118
+ "export_allowed": export_allowed,
119
+ "retention_days": retention_days,
120
+ }
121
+
122
+ with open(run_dir / "run.json", "w") as f:
123
+ json.dump(run_data, f, indent=2)
124
+
125
+ # Store input (if allowed)
126
+ if input_data and export_allowed:
127
+ with open(run_dir / "input.json", "w") as f:
128
+ json.dump(input_data, f, indent=2)
129
+
130
+ # Store output (if allowed)
131
+ if result.output and export_allowed:
132
+ with open(run_dir / "output.json", "w") as f:
133
+ json.dump(result.output, f, indent=2)
134
+
135
+ # Store events (if any)
136
+ if events:
137
+ with open(run_dir / "events.jsonl", "w") as f:
138
+ for event in events:
139
+ f.write(json.dumps(event) + "\n")
140
+
141
+ # Update index
142
+ index = self._load_index()
143
+ index["runs"][run_id] = {
144
+ "recipe": result.recipe,
145
+ "version": result.version,
146
+ "status": result.status,
147
+ "stored_at": run_data["stored_at"],
148
+ "session_id": result.trace.get("session_id"),
149
+ }
150
+ self._save_index(index)
151
+
152
+ return run_id
153
+
154
+ def get(self, run_id: str) -> Dict[str, Any]:
155
+ """
156
+ Get a run from history.
157
+
158
+ Args:
159
+ run_id: Run ID to retrieve
160
+
161
+ Returns:
162
+ Dict with run data, input, output, events
163
+
164
+ Raises:
165
+ RunNotFoundError: If run not found
166
+ """
167
+ run_dir = self.path / run_id
168
+ if not run_dir.exists():
169
+ raise RunNotFoundError(f"Run not found: {run_id}")
170
+
171
+ # Load run metadata
172
+ run_path = run_dir / "run.json"
173
+ if not run_path.exists():
174
+ raise RunNotFoundError(f"Run metadata missing: {run_id}")
175
+
176
+ with open(run_path) as f:
177
+ run_data = json.load(f)
178
+
179
+ # Load input
180
+ input_path = run_dir / "input.json"
181
+ if input_path.exists():
182
+ with open(input_path) as f:
183
+ run_data["input"] = json.load(f)
184
+
185
+ # Load output
186
+ output_path = run_dir / "output.json"
187
+ if output_path.exists():
188
+ with open(output_path) as f:
189
+ run_data["output"] = json.load(f)
190
+
191
+ # Load events
192
+ events_path = run_dir / "events.jsonl"
193
+ if events_path.exists():
194
+ events = []
195
+ with open(events_path) as f:
196
+ for line in f:
197
+ if line.strip():
198
+ events.append(json.loads(line))
199
+ run_data["events"] = events
200
+
201
+ return run_data
202
+
203
+ def list_runs(
204
+ self,
205
+ recipe: Optional[str] = None,
206
+ session_id: Optional[str] = None,
207
+ status: Optional[str] = None,
208
+ limit: int = 100,
209
+ offset: int = 0,
210
+ ) -> List[Dict[str, Any]]:
211
+ """
212
+ List runs from history.
213
+
214
+ Args:
215
+ recipe: Filter by recipe name
216
+ session_id: Filter by session ID
217
+ status: Filter by status
218
+ limit: Maximum number of results
219
+ offset: Offset for pagination
220
+
221
+ Returns:
222
+ List of run summaries
223
+ """
224
+ index = self._load_index()
225
+ runs = []
226
+
227
+ for run_id, info in index["runs"].items():
228
+ # Apply filters
229
+ if recipe and info.get("recipe") != recipe:
230
+ continue
231
+ if session_id and info.get("session_id") != session_id:
232
+ continue
233
+ if status and info.get("status") != status:
234
+ continue
235
+
236
+ runs.append({
237
+ "run_id": run_id,
238
+ **info,
239
+ })
240
+
241
+ # Sort by stored_at descending
242
+ runs.sort(key=lambda x: x.get("stored_at", ""), reverse=True)
243
+
244
+ # Apply pagination
245
+ return runs[offset:offset + limit]
246
+
247
+ def export(
248
+ self,
249
+ run_id: str,
250
+ output_path: Optional[Path] = None,
251
+ ) -> Path:
252
+ """
253
+ Export a run as a JSON bundle.
254
+
255
+ Args:
256
+ run_id: Run ID to export
257
+ output_path: Output file path (default: <run_id>.export.json)
258
+
259
+ Returns:
260
+ Path to exported file
261
+
262
+ Raises:
263
+ RunNotFoundError: If run not found
264
+ RunHistoryError: If export not allowed
265
+ """
266
+ run_data = self.get(run_id)
267
+
268
+ # Check if export is allowed
269
+ if not run_data.get("export_allowed", True):
270
+ raise RunHistoryError(f"Export not allowed for run: {run_id}")
271
+
272
+ # Prepare export bundle
273
+ export_bundle = {
274
+ "format": "praison-run-export",
275
+ "version": "1.0",
276
+ "exported_at": _get_timestamp(),
277
+ "run": run_data,
278
+ }
279
+
280
+ # Write to file
281
+ output_path = Path(output_path) if output_path else Path(f"{run_id}.export.json")
282
+ with open(output_path, "w") as f:
283
+ json.dump(export_bundle, f, indent=2)
284
+
285
+ return output_path
286
+
287
+ def delete(self, run_id: str) -> bool:
288
+ """
289
+ Delete a run from history.
290
+
291
+ Args:
292
+ run_id: Run ID to delete
293
+
294
+ Returns:
295
+ True if deleted
296
+ """
297
+ run_dir = self.path / run_id
298
+ if run_dir.exists():
299
+ shutil.rmtree(run_dir)
300
+
301
+ index = self._load_index()
302
+ if run_id in index["runs"]:
303
+ del index["runs"][run_id]
304
+ self._save_index(index)
305
+
306
+ return True
307
+
308
+ def cleanup(self, retention_days: Optional[int] = None) -> int:
309
+ """
310
+ Clean up old runs based on retention policy.
311
+
312
+ Args:
313
+ retention_days: Override retention days (default: use per-run policy)
314
+
315
+ Returns:
316
+ Number of runs deleted
317
+ """
318
+ index = self._load_index()
319
+ deleted = 0
320
+ now = datetime.now(timezone.utc)
321
+
322
+ for run_id in list(index["runs"].keys()):
323
+ run_dir = self.path / run_id
324
+ run_path = run_dir / "run.json"
325
+
326
+ if not run_path.exists():
327
+ # Clean up orphaned index entry
328
+ del index["runs"][run_id]
329
+ deleted += 1
330
+ continue
331
+
332
+ with open(run_path) as f:
333
+ run_data = json.load(f)
334
+
335
+ # Check retention
336
+ run_retention = retention_days or run_data.get("retention_days")
337
+ if run_retention:
338
+ stored_at = datetime.fromisoformat(run_data["stored_at"].replace("Z", "+00:00"))
339
+ if now - stored_at > timedelta(days=run_retention):
340
+ self.delete(run_id)
341
+ deleted += 1
342
+
343
+ return deleted
344
+
345
+ def get_stats(self) -> Dict[str, Any]:
346
+ """Get storage statistics."""
347
+ index = self._load_index()
348
+
349
+ total_runs = len(index["runs"])
350
+ total_size = 0
351
+
352
+ for run_id in index["runs"]:
353
+ run_dir = self.path / run_id
354
+ if run_dir.exists():
355
+ for f in run_dir.iterdir():
356
+ if f.is_file():
357
+ total_size += f.stat().st_size
358
+
359
+ return {
360
+ "total_runs": total_runs,
361
+ "total_size_bytes": total_size,
362
+ "storage_path": str(self.path),
363
+ }
364
+
365
+
366
+ # Global instance for convenience
367
+ _default_history: Optional[RunHistory] = None
368
+
369
+
370
+ def get_history(path: Optional[Path] = None) -> RunHistory:
371
+ """Get or create default run history instance."""
372
+ global _default_history
373
+ if path:
374
+ return RunHistory(path)
375
+ if _default_history is None:
376
+ _default_history = RunHistory()
377
+ return _default_history
378
+
379
+
380
+ def store_run(
381
+ result: RecipeResult,
382
+ input_data: Optional[Dict[str, Any]] = None,
383
+ events: Optional[List[Dict[str, Any]]] = None,
384
+ data_policy: Optional[Dict[str, Any]] = None,
385
+ ) -> str:
386
+ """Convenience function to store a run."""
387
+ return get_history().store(result, input_data, events, data_policy)
388
+
389
+
390
+ def get_run(run_id: str) -> Dict[str, Any]:
391
+ """Convenience function to get a run."""
392
+ return get_history().get(run_id)
393
+
394
+
395
+ def list_runs(**kwargs) -> List[Dict[str, Any]]:
396
+ """Convenience function to list runs."""
397
+ return get_history().list_runs(**kwargs)
398
+
399
+
400
+ def export_run(run_id: str, output_path: Optional[Path] = None) -> Path:
401
+ """Convenience function to export a run."""
402
+ return get_history().export(run_id, output_path)