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.
- praisonai/__init__.py +54 -0
- praisonai/__main__.py +15 -0
- praisonai/acp/__init__.py +54 -0
- praisonai/acp/config.py +159 -0
- praisonai/acp/server.py +587 -0
- praisonai/acp/session.py +219 -0
- praisonai/adapters/__init__.py +50 -0
- praisonai/adapters/readers.py +395 -0
- praisonai/adapters/rerankers.py +315 -0
- praisonai/adapters/retrievers.py +394 -0
- praisonai/adapters/vector_stores.py +409 -0
- praisonai/agent_scheduler.py +337 -0
- praisonai/agents_generator.py +903 -0
- praisonai/api/call.py +292 -0
- praisonai/auto.py +1197 -0
- praisonai/capabilities/__init__.py +275 -0
- praisonai/capabilities/a2a.py +140 -0
- praisonai/capabilities/assistants.py +283 -0
- praisonai/capabilities/audio.py +320 -0
- praisonai/capabilities/batches.py +469 -0
- praisonai/capabilities/completions.py +336 -0
- praisonai/capabilities/container_files.py +155 -0
- praisonai/capabilities/containers.py +93 -0
- praisonai/capabilities/embeddings.py +158 -0
- praisonai/capabilities/files.py +467 -0
- praisonai/capabilities/fine_tuning.py +293 -0
- praisonai/capabilities/guardrails.py +182 -0
- praisonai/capabilities/images.py +330 -0
- praisonai/capabilities/mcp.py +190 -0
- praisonai/capabilities/messages.py +270 -0
- praisonai/capabilities/moderations.py +154 -0
- praisonai/capabilities/ocr.py +217 -0
- praisonai/capabilities/passthrough.py +204 -0
- praisonai/capabilities/rag.py +207 -0
- praisonai/capabilities/realtime.py +160 -0
- praisonai/capabilities/rerank.py +165 -0
- praisonai/capabilities/responses.py +266 -0
- praisonai/capabilities/search.py +109 -0
- praisonai/capabilities/skills.py +133 -0
- praisonai/capabilities/vector_store_files.py +334 -0
- praisonai/capabilities/vector_stores.py +304 -0
- praisonai/capabilities/videos.py +141 -0
- praisonai/chainlit_ui.py +304 -0
- praisonai/chat/__init__.py +106 -0
- praisonai/chat/app.py +125 -0
- praisonai/cli/__init__.py +26 -0
- praisonai/cli/app.py +213 -0
- praisonai/cli/commands/__init__.py +75 -0
- praisonai/cli/commands/acp.py +70 -0
- praisonai/cli/commands/completion.py +333 -0
- praisonai/cli/commands/config.py +166 -0
- praisonai/cli/commands/debug.py +142 -0
- praisonai/cli/commands/diag.py +55 -0
- praisonai/cli/commands/doctor.py +166 -0
- praisonai/cli/commands/environment.py +179 -0
- praisonai/cli/commands/lsp.py +112 -0
- praisonai/cli/commands/mcp.py +210 -0
- praisonai/cli/commands/profile.py +457 -0
- praisonai/cli/commands/run.py +228 -0
- praisonai/cli/commands/schedule.py +150 -0
- praisonai/cli/commands/serve.py +97 -0
- praisonai/cli/commands/session.py +212 -0
- praisonai/cli/commands/traces.py +145 -0
- praisonai/cli/commands/version.py +101 -0
- praisonai/cli/configuration/__init__.py +18 -0
- praisonai/cli/configuration/loader.py +353 -0
- praisonai/cli/configuration/paths.py +114 -0
- praisonai/cli/configuration/schema.py +164 -0
- praisonai/cli/features/__init__.py +268 -0
- praisonai/cli/features/acp.py +236 -0
- praisonai/cli/features/action_orchestrator.py +546 -0
- praisonai/cli/features/agent_scheduler.py +773 -0
- praisonai/cli/features/agent_tools.py +474 -0
- praisonai/cli/features/agents.py +375 -0
- praisonai/cli/features/at_mentions.py +471 -0
- praisonai/cli/features/auto_memory.py +182 -0
- praisonai/cli/features/autonomy_mode.py +490 -0
- praisonai/cli/features/background.py +356 -0
- praisonai/cli/features/base.py +168 -0
- praisonai/cli/features/capabilities.py +1326 -0
- praisonai/cli/features/checkpoints.py +338 -0
- praisonai/cli/features/code_intelligence.py +652 -0
- praisonai/cli/features/compaction.py +294 -0
- praisonai/cli/features/compare.py +534 -0
- praisonai/cli/features/cost_tracker.py +514 -0
- praisonai/cli/features/debug.py +810 -0
- praisonai/cli/features/deploy.py +517 -0
- praisonai/cli/features/diag.py +289 -0
- praisonai/cli/features/doctor/__init__.py +63 -0
- praisonai/cli/features/doctor/checks/__init__.py +24 -0
- praisonai/cli/features/doctor/checks/acp_checks.py +240 -0
- praisonai/cli/features/doctor/checks/config_checks.py +366 -0
- praisonai/cli/features/doctor/checks/db_checks.py +366 -0
- praisonai/cli/features/doctor/checks/env_checks.py +543 -0
- praisonai/cli/features/doctor/checks/lsp_checks.py +199 -0
- praisonai/cli/features/doctor/checks/mcp_checks.py +349 -0
- praisonai/cli/features/doctor/checks/memory_checks.py +268 -0
- praisonai/cli/features/doctor/checks/network_checks.py +251 -0
- praisonai/cli/features/doctor/checks/obs_checks.py +328 -0
- praisonai/cli/features/doctor/checks/performance_checks.py +235 -0
- praisonai/cli/features/doctor/checks/permissions_checks.py +259 -0
- praisonai/cli/features/doctor/checks/selftest_checks.py +322 -0
- praisonai/cli/features/doctor/checks/serve_checks.py +426 -0
- praisonai/cli/features/doctor/checks/skills_checks.py +231 -0
- praisonai/cli/features/doctor/checks/tools_checks.py +371 -0
- praisonai/cli/features/doctor/engine.py +266 -0
- praisonai/cli/features/doctor/formatters.py +310 -0
- praisonai/cli/features/doctor/handler.py +397 -0
- praisonai/cli/features/doctor/models.py +264 -0
- praisonai/cli/features/doctor/registry.py +239 -0
- praisonai/cli/features/endpoints.py +1019 -0
- praisonai/cli/features/eval.py +560 -0
- praisonai/cli/features/external_agents.py +231 -0
- praisonai/cli/features/fast_context.py +410 -0
- praisonai/cli/features/flow_display.py +566 -0
- praisonai/cli/features/git_integration.py +651 -0
- praisonai/cli/features/guardrail.py +171 -0
- praisonai/cli/features/handoff.py +185 -0
- praisonai/cli/features/hooks.py +583 -0
- praisonai/cli/features/image.py +384 -0
- praisonai/cli/features/interactive_runtime.py +585 -0
- praisonai/cli/features/interactive_tools.py +380 -0
- praisonai/cli/features/interactive_tui.py +603 -0
- praisonai/cli/features/jobs.py +632 -0
- praisonai/cli/features/knowledge.py +531 -0
- praisonai/cli/features/lite.py +244 -0
- praisonai/cli/features/lsp_cli.py +225 -0
- praisonai/cli/features/mcp.py +169 -0
- praisonai/cli/features/message_queue.py +587 -0
- praisonai/cli/features/metrics.py +211 -0
- praisonai/cli/features/n8n.py +673 -0
- praisonai/cli/features/observability.py +293 -0
- praisonai/cli/features/ollama.py +361 -0
- praisonai/cli/features/output_style.py +273 -0
- praisonai/cli/features/package.py +631 -0
- praisonai/cli/features/performance.py +308 -0
- praisonai/cli/features/persistence.py +636 -0
- praisonai/cli/features/profile.py +226 -0
- praisonai/cli/features/profiler/__init__.py +81 -0
- praisonai/cli/features/profiler/core.py +558 -0
- praisonai/cli/features/profiler/optimizations.py +652 -0
- praisonai/cli/features/profiler/suite.py +386 -0
- praisonai/cli/features/profiling.py +350 -0
- praisonai/cli/features/queue/__init__.py +73 -0
- praisonai/cli/features/queue/manager.py +395 -0
- praisonai/cli/features/queue/models.py +286 -0
- praisonai/cli/features/queue/persistence.py +564 -0
- praisonai/cli/features/queue/scheduler.py +484 -0
- praisonai/cli/features/queue/worker.py +372 -0
- praisonai/cli/features/recipe.py +1723 -0
- praisonai/cli/features/recipes.py +449 -0
- praisonai/cli/features/registry.py +229 -0
- praisonai/cli/features/repo_map.py +860 -0
- praisonai/cli/features/router.py +466 -0
- praisonai/cli/features/sandbox_executor.py +515 -0
- praisonai/cli/features/serve.py +829 -0
- praisonai/cli/features/session.py +222 -0
- praisonai/cli/features/skills.py +856 -0
- praisonai/cli/features/slash_commands.py +650 -0
- praisonai/cli/features/telemetry.py +179 -0
- praisonai/cli/features/templates.py +1384 -0
- praisonai/cli/features/thinking.py +305 -0
- praisonai/cli/features/todo.py +334 -0
- praisonai/cli/features/tools.py +680 -0
- praisonai/cli/features/tui/__init__.py +83 -0
- praisonai/cli/features/tui/app.py +580 -0
- praisonai/cli/features/tui/cli.py +566 -0
- praisonai/cli/features/tui/debug.py +511 -0
- praisonai/cli/features/tui/events.py +99 -0
- praisonai/cli/features/tui/mock_provider.py +328 -0
- praisonai/cli/features/tui/orchestrator.py +652 -0
- praisonai/cli/features/tui/screens/__init__.py +50 -0
- praisonai/cli/features/tui/screens/main.py +245 -0
- praisonai/cli/features/tui/screens/queue.py +174 -0
- praisonai/cli/features/tui/screens/session.py +124 -0
- praisonai/cli/features/tui/screens/settings.py +148 -0
- praisonai/cli/features/tui/widgets/__init__.py +56 -0
- praisonai/cli/features/tui/widgets/chat.py +261 -0
- praisonai/cli/features/tui/widgets/composer.py +224 -0
- praisonai/cli/features/tui/widgets/queue_panel.py +200 -0
- praisonai/cli/features/tui/widgets/status.py +167 -0
- praisonai/cli/features/tui/widgets/tool_panel.py +248 -0
- praisonai/cli/features/workflow.py +720 -0
- praisonai/cli/legacy.py +236 -0
- praisonai/cli/main.py +5559 -0
- praisonai/cli/schedule_cli.py +54 -0
- praisonai/cli/state/__init__.py +31 -0
- praisonai/cli/state/identifiers.py +161 -0
- praisonai/cli/state/sessions.py +313 -0
- praisonai/code/__init__.py +93 -0
- praisonai/code/agent_tools.py +344 -0
- praisonai/code/diff/__init__.py +21 -0
- praisonai/code/diff/diff_strategy.py +432 -0
- praisonai/code/tools/__init__.py +27 -0
- praisonai/code/tools/apply_diff.py +221 -0
- praisonai/code/tools/execute_command.py +275 -0
- praisonai/code/tools/list_files.py +274 -0
- praisonai/code/tools/read_file.py +206 -0
- praisonai/code/tools/search_replace.py +248 -0
- praisonai/code/tools/write_file.py +217 -0
- praisonai/code/utils/__init__.py +46 -0
- praisonai/code/utils/file_utils.py +307 -0
- praisonai/code/utils/ignore_utils.py +308 -0
- praisonai/code/utils/text_utils.py +276 -0
- praisonai/db/__init__.py +64 -0
- praisonai/db/adapter.py +531 -0
- praisonai/deploy/__init__.py +62 -0
- praisonai/deploy/api.py +231 -0
- praisonai/deploy/docker.py +454 -0
- praisonai/deploy/doctor.py +367 -0
- praisonai/deploy/main.py +327 -0
- praisonai/deploy/models.py +179 -0
- praisonai/deploy/providers/__init__.py +33 -0
- praisonai/deploy/providers/aws.py +331 -0
- praisonai/deploy/providers/azure.py +358 -0
- praisonai/deploy/providers/base.py +101 -0
- praisonai/deploy/providers/gcp.py +314 -0
- praisonai/deploy/schema.py +208 -0
- praisonai/deploy.py +185 -0
- praisonai/endpoints/__init__.py +53 -0
- praisonai/endpoints/a2u_server.py +410 -0
- praisonai/endpoints/discovery.py +165 -0
- praisonai/endpoints/providers/__init__.py +28 -0
- praisonai/endpoints/providers/a2a.py +253 -0
- praisonai/endpoints/providers/a2u.py +208 -0
- praisonai/endpoints/providers/agents_api.py +171 -0
- praisonai/endpoints/providers/base.py +231 -0
- praisonai/endpoints/providers/mcp.py +263 -0
- praisonai/endpoints/providers/recipe.py +206 -0
- praisonai/endpoints/providers/tools_mcp.py +150 -0
- praisonai/endpoints/registry.py +131 -0
- praisonai/endpoints/server.py +161 -0
- praisonai/inbuilt_tools/__init__.py +24 -0
- praisonai/inbuilt_tools/autogen_tools.py +117 -0
- praisonai/inc/__init__.py +2 -0
- praisonai/inc/config.py +96 -0
- praisonai/inc/models.py +155 -0
- praisonai/integrations/__init__.py +56 -0
- praisonai/integrations/base.py +303 -0
- praisonai/integrations/claude_code.py +270 -0
- praisonai/integrations/codex_cli.py +255 -0
- praisonai/integrations/cursor_cli.py +195 -0
- praisonai/integrations/gemini_cli.py +222 -0
- praisonai/jobs/__init__.py +67 -0
- praisonai/jobs/executor.py +425 -0
- praisonai/jobs/models.py +230 -0
- praisonai/jobs/router.py +314 -0
- praisonai/jobs/server.py +186 -0
- praisonai/jobs/store.py +203 -0
- praisonai/llm/__init__.py +66 -0
- praisonai/llm/registry.py +382 -0
- praisonai/mcp_server/__init__.py +152 -0
- praisonai/mcp_server/adapters/__init__.py +74 -0
- praisonai/mcp_server/adapters/agents.py +128 -0
- praisonai/mcp_server/adapters/capabilities.py +168 -0
- praisonai/mcp_server/adapters/cli_tools.py +568 -0
- praisonai/mcp_server/adapters/extended_capabilities.py +462 -0
- praisonai/mcp_server/adapters/knowledge.py +93 -0
- praisonai/mcp_server/adapters/memory.py +104 -0
- praisonai/mcp_server/adapters/prompts.py +306 -0
- praisonai/mcp_server/adapters/resources.py +124 -0
- praisonai/mcp_server/adapters/tools_bridge.py +280 -0
- praisonai/mcp_server/auth/__init__.py +48 -0
- praisonai/mcp_server/auth/api_key.py +291 -0
- praisonai/mcp_server/auth/oauth.py +460 -0
- praisonai/mcp_server/auth/oidc.py +289 -0
- praisonai/mcp_server/auth/scopes.py +260 -0
- praisonai/mcp_server/cli.py +852 -0
- praisonai/mcp_server/elicitation.py +445 -0
- praisonai/mcp_server/icons.py +302 -0
- praisonai/mcp_server/recipe_adapter.py +573 -0
- praisonai/mcp_server/recipe_cli.py +824 -0
- praisonai/mcp_server/registry.py +703 -0
- praisonai/mcp_server/sampling.py +422 -0
- praisonai/mcp_server/server.py +490 -0
- praisonai/mcp_server/tasks.py +443 -0
- praisonai/mcp_server/transports/__init__.py +18 -0
- praisonai/mcp_server/transports/http_stream.py +376 -0
- praisonai/mcp_server/transports/stdio.py +132 -0
- praisonai/persistence/__init__.py +84 -0
- praisonai/persistence/config.py +238 -0
- praisonai/persistence/conversation/__init__.py +25 -0
- praisonai/persistence/conversation/async_mysql.py +427 -0
- praisonai/persistence/conversation/async_postgres.py +410 -0
- praisonai/persistence/conversation/async_sqlite.py +371 -0
- praisonai/persistence/conversation/base.py +151 -0
- praisonai/persistence/conversation/json_store.py +250 -0
- praisonai/persistence/conversation/mysql.py +387 -0
- praisonai/persistence/conversation/postgres.py +401 -0
- praisonai/persistence/conversation/singlestore.py +240 -0
- praisonai/persistence/conversation/sqlite.py +341 -0
- praisonai/persistence/conversation/supabase.py +203 -0
- praisonai/persistence/conversation/surrealdb.py +287 -0
- praisonai/persistence/factory.py +301 -0
- praisonai/persistence/hooks/__init__.py +18 -0
- praisonai/persistence/hooks/agent_hooks.py +297 -0
- praisonai/persistence/knowledge/__init__.py +26 -0
- praisonai/persistence/knowledge/base.py +144 -0
- praisonai/persistence/knowledge/cassandra.py +232 -0
- praisonai/persistence/knowledge/chroma.py +295 -0
- praisonai/persistence/knowledge/clickhouse.py +242 -0
- praisonai/persistence/knowledge/cosmosdb_vector.py +438 -0
- praisonai/persistence/knowledge/couchbase.py +286 -0
- praisonai/persistence/knowledge/lancedb.py +216 -0
- praisonai/persistence/knowledge/langchain_adapter.py +291 -0
- praisonai/persistence/knowledge/lightrag_adapter.py +212 -0
- praisonai/persistence/knowledge/llamaindex_adapter.py +256 -0
- praisonai/persistence/knowledge/milvus.py +277 -0
- praisonai/persistence/knowledge/mongodb_vector.py +306 -0
- praisonai/persistence/knowledge/pgvector.py +335 -0
- praisonai/persistence/knowledge/pinecone.py +253 -0
- praisonai/persistence/knowledge/qdrant.py +301 -0
- praisonai/persistence/knowledge/redis_vector.py +291 -0
- praisonai/persistence/knowledge/singlestore_vector.py +299 -0
- praisonai/persistence/knowledge/surrealdb_vector.py +309 -0
- praisonai/persistence/knowledge/upstash_vector.py +266 -0
- praisonai/persistence/knowledge/weaviate.py +223 -0
- praisonai/persistence/migrations/__init__.py +10 -0
- praisonai/persistence/migrations/manager.py +251 -0
- praisonai/persistence/orchestrator.py +406 -0
- praisonai/persistence/state/__init__.py +21 -0
- praisonai/persistence/state/async_mongodb.py +200 -0
- praisonai/persistence/state/base.py +107 -0
- praisonai/persistence/state/dynamodb.py +226 -0
- praisonai/persistence/state/firestore.py +175 -0
- praisonai/persistence/state/gcs.py +155 -0
- praisonai/persistence/state/memory.py +245 -0
- praisonai/persistence/state/mongodb.py +158 -0
- praisonai/persistence/state/redis.py +190 -0
- praisonai/persistence/state/upstash.py +144 -0
- praisonai/persistence/tests/__init__.py +3 -0
- praisonai/persistence/tests/test_all_backends.py +633 -0
- praisonai/profiler.py +1214 -0
- praisonai/recipe/__init__.py +134 -0
- praisonai/recipe/bridge.py +278 -0
- praisonai/recipe/core.py +893 -0
- praisonai/recipe/exceptions.py +54 -0
- praisonai/recipe/history.py +402 -0
- praisonai/recipe/models.py +266 -0
- praisonai/recipe/operations.py +440 -0
- praisonai/recipe/policy.py +422 -0
- praisonai/recipe/registry.py +849 -0
- praisonai/recipe/runtime.py +214 -0
- praisonai/recipe/security.py +711 -0
- praisonai/recipe/serve.py +859 -0
- praisonai/recipe/server.py +613 -0
- praisonai/scheduler/__init__.py +45 -0
- praisonai/scheduler/agent_scheduler.py +552 -0
- praisonai/scheduler/base.py +124 -0
- praisonai/scheduler/daemon_manager.py +225 -0
- praisonai/scheduler/state_manager.py +155 -0
- praisonai/scheduler/yaml_loader.py +193 -0
- praisonai/scheduler.py +194 -0
- praisonai/setup/__init__.py +1 -0
- praisonai/setup/build.py +21 -0
- praisonai/setup/post_install.py +23 -0
- praisonai/setup/setup_conda_env.py +25 -0
- praisonai/setup.py +16 -0
- praisonai/templates/__init__.py +116 -0
- praisonai/templates/cache.py +364 -0
- praisonai/templates/dependency_checker.py +358 -0
- praisonai/templates/discovery.py +391 -0
- praisonai/templates/loader.py +564 -0
- praisonai/templates/registry.py +511 -0
- praisonai/templates/resolver.py +206 -0
- praisonai/templates/security.py +327 -0
- praisonai/templates/tool_override.py +498 -0
- praisonai/templates/tools_doctor.py +256 -0
- praisonai/test.py +105 -0
- praisonai/train.py +562 -0
- praisonai/train_vision.py +306 -0
- praisonai/ui/agents.py +824 -0
- praisonai/ui/callbacks.py +57 -0
- praisonai/ui/chainlit_compat.py +246 -0
- praisonai/ui/chat.py +532 -0
- praisonai/ui/code.py +717 -0
- praisonai/ui/colab.py +474 -0
- praisonai/ui/colab_chainlit.py +81 -0
- praisonai/ui/components/aicoder.py +284 -0
- praisonai/ui/context.py +283 -0
- praisonai/ui/database_config.py +56 -0
- praisonai/ui/db.py +294 -0
- praisonai/ui/realtime.py +488 -0
- praisonai/ui/realtimeclient/__init__.py +756 -0
- praisonai/ui/realtimeclient/tools.py +242 -0
- praisonai/ui/sql_alchemy.py +710 -0
- praisonai/upload_vision.py +140 -0
- praisonai/version.py +1 -0
- praisonai-3.0.0.dist-info/METADATA +3493 -0
- praisonai-3.0.0.dist-info/RECORD +393 -0
- praisonai-3.0.0.dist-info/WHEEL +5 -0
- praisonai-3.0.0.dist-info/entry_points.txt +4 -0
- 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)
|