kubiya-control-plane-api 0.9.15__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.
- control_plane_api/LICENSE +676 -0
- control_plane_api/README.md +350 -0
- control_plane_api/__init__.py +4 -0
- control_plane_api/__version__.py +8 -0
- control_plane_api/alembic/README +1 -0
- control_plane_api/alembic/env.py +121 -0
- control_plane_api/alembic/script.py.mako +28 -0
- control_plane_api/alembic/versions/2613c65c3dbe_initial_database_setup.py +32 -0
- control_plane_api/alembic/versions/2df520d4927d_merge_heads.py +28 -0
- control_plane_api/alembic/versions/43abf98d6a01_add_paused_status_to_executions.py +73 -0
- control_plane_api/alembic/versions/6289854264cb_merge_multiple_heads.py +28 -0
- control_plane_api/alembic/versions/6a4d4dc3d8dc_generate_execution_transitions.py +50 -0
- control_plane_api/alembic/versions/87d11cf0a783_add_disconnected_status_to_worker_.py +44 -0
- control_plane_api/alembic/versions/add_ephemeral_queue_support.py +85 -0
- control_plane_api/alembic/versions/add_model_type_to_llm_models.py +31 -0
- control_plane_api/alembic/versions/add_plan_executions_table.py +114 -0
- control_plane_api/alembic/versions/add_trace_span_tables.py +154 -0
- control_plane_api/alembic/versions/add_user_info_to_traces.py +36 -0
- control_plane_api/alembic/versions/adjusting_foreign_keys.py +32 -0
- control_plane_api/alembic/versions/b4983d976db2_initial_tables.py +1128 -0
- control_plane_api/alembic/versions/d181a3b40e71_rename_custom_metadata_to_metadata_in_.py +50 -0
- control_plane_api/alembic/versions/df9117888e82_add_missing_columns.py +82 -0
- control_plane_api/alembic/versions/f25de6ad895a_missing_migrations.py +34 -0
- control_plane_api/alembic/versions/f71305fb69b9_fix_ephemeral_queue_deletion_foreign_key.py +54 -0
- control_plane_api/alembic/versions/mark_local_exec_queues_as_ephemeral.py +68 -0
- control_plane_api/alembic.ini +148 -0
- control_plane_api/api/index.py +12 -0
- control_plane_api/app/__init__.py +11 -0
- control_plane_api/app/activities/__init__.py +20 -0
- control_plane_api/app/activities/agent_activities.py +384 -0
- control_plane_api/app/activities/plan_generation_activities.py +499 -0
- control_plane_api/app/activities/team_activities.py +424 -0
- control_plane_api/app/activities/temporal_cloud_activities.py +588 -0
- control_plane_api/app/config/__init__.py +35 -0
- control_plane_api/app/config/api_config.py +469 -0
- control_plane_api/app/config/config_loader.py +224 -0
- control_plane_api/app/config/model_pricing.py +323 -0
- control_plane_api/app/config/storage_config.py +159 -0
- control_plane_api/app/config.py +115 -0
- control_plane_api/app/controllers/__init__.py +0 -0
- control_plane_api/app/controllers/execution_environment_controller.py +1315 -0
- control_plane_api/app/database.py +135 -0
- control_plane_api/app/exceptions.py +408 -0
- control_plane_api/app/lib/__init__.py +11 -0
- control_plane_api/app/lib/environment.py +65 -0
- control_plane_api/app/lib/event_bus/__init__.py +17 -0
- control_plane_api/app/lib/event_bus/base.py +136 -0
- control_plane_api/app/lib/event_bus/manager.py +335 -0
- control_plane_api/app/lib/event_bus/providers/__init__.py +6 -0
- control_plane_api/app/lib/event_bus/providers/http_provider.py +166 -0
- control_plane_api/app/lib/event_bus/providers/nats_provider.py +324 -0
- control_plane_api/app/lib/event_bus/providers/redis_provider.py +233 -0
- control_plane_api/app/lib/event_bus/providers/websocket_provider.py +497 -0
- control_plane_api/app/lib/job_executor.py +330 -0
- control_plane_api/app/lib/kubiya_client.py +293 -0
- control_plane_api/app/lib/litellm_pricing.py +166 -0
- control_plane_api/app/lib/mcp_validation.py +163 -0
- control_plane_api/app/lib/nats/__init__.py +13 -0
- control_plane_api/app/lib/nats/credentials_manager.py +288 -0
- control_plane_api/app/lib/nats/listener.py +374 -0
- control_plane_api/app/lib/planning_prompt_builder.py +153 -0
- control_plane_api/app/lib/planning_tools/__init__.py +41 -0
- control_plane_api/app/lib/planning_tools/agents.py +409 -0
- control_plane_api/app/lib/planning_tools/agno_toolkit.py +836 -0
- control_plane_api/app/lib/planning_tools/base.py +119 -0
- control_plane_api/app/lib/planning_tools/cognitive_memory_tools.py +403 -0
- control_plane_api/app/lib/planning_tools/context_graph_tools.py +545 -0
- control_plane_api/app/lib/planning_tools/environments.py +218 -0
- control_plane_api/app/lib/planning_tools/knowledge.py +204 -0
- control_plane_api/app/lib/planning_tools/models.py +93 -0
- control_plane_api/app/lib/planning_tools/planning_service.py +646 -0
- control_plane_api/app/lib/planning_tools/resources.py +242 -0
- control_plane_api/app/lib/planning_tools/teams.py +334 -0
- control_plane_api/app/lib/policy_enforcer_client.py +1016 -0
- control_plane_api/app/lib/redis_client.py +803 -0
- control_plane_api/app/lib/sqlalchemy_utils.py +486 -0
- control_plane_api/app/lib/state_transition_tools/__init__.py +7 -0
- control_plane_api/app/lib/state_transition_tools/execution_context.py +388 -0
- control_plane_api/app/lib/storage/__init__.py +20 -0
- control_plane_api/app/lib/storage/base_provider.py +274 -0
- control_plane_api/app/lib/storage/provider_factory.py +157 -0
- control_plane_api/app/lib/storage/vercel_blob_provider.py +468 -0
- control_plane_api/app/lib/supabase.py +71 -0
- control_plane_api/app/lib/supabase_utils.py +138 -0
- control_plane_api/app/lib/task_planning/__init__.py +138 -0
- control_plane_api/app/lib/task_planning/agent_factory.py +308 -0
- control_plane_api/app/lib/task_planning/agents.py +389 -0
- control_plane_api/app/lib/task_planning/cache.py +218 -0
- control_plane_api/app/lib/task_planning/entity_resolver.py +273 -0
- control_plane_api/app/lib/task_planning/helpers.py +293 -0
- control_plane_api/app/lib/task_planning/hooks.py +474 -0
- control_plane_api/app/lib/task_planning/models.py +503 -0
- control_plane_api/app/lib/task_planning/plan_validator.py +166 -0
- control_plane_api/app/lib/task_planning/planning_workflow.py +2911 -0
- control_plane_api/app/lib/task_planning/runner.py +656 -0
- control_plane_api/app/lib/task_planning/streaming_hook.py +213 -0
- control_plane_api/app/lib/task_planning/workflow.py +424 -0
- control_plane_api/app/lib/templating/__init__.py +88 -0
- control_plane_api/app/lib/templating/compiler.py +278 -0
- control_plane_api/app/lib/templating/engine.py +178 -0
- control_plane_api/app/lib/templating/parsers/__init__.py +29 -0
- control_plane_api/app/lib/templating/parsers/base.py +96 -0
- control_plane_api/app/lib/templating/parsers/env.py +85 -0
- control_plane_api/app/lib/templating/parsers/graph.py +112 -0
- control_plane_api/app/lib/templating/parsers/secret.py +87 -0
- control_plane_api/app/lib/templating/parsers/simple.py +81 -0
- control_plane_api/app/lib/templating/resolver.py +366 -0
- control_plane_api/app/lib/templating/types.py +214 -0
- control_plane_api/app/lib/templating/validator.py +201 -0
- control_plane_api/app/lib/temporal_client.py +232 -0
- control_plane_api/app/lib/temporal_credentials_cache.py +178 -0
- control_plane_api/app/lib/temporal_credentials_service.py +203 -0
- control_plane_api/app/lib/validation/__init__.py +24 -0
- control_plane_api/app/lib/validation/runtime_validation.py +388 -0
- control_plane_api/app/main.py +531 -0
- control_plane_api/app/middleware/__init__.py +10 -0
- control_plane_api/app/middleware/auth.py +645 -0
- control_plane_api/app/middleware/exception_handler.py +267 -0
- control_plane_api/app/middleware/prometheus_middleware.py +173 -0
- control_plane_api/app/middleware/rate_limiting.py +384 -0
- control_plane_api/app/middleware/request_id.py +202 -0
- control_plane_api/app/models/__init__.py +40 -0
- control_plane_api/app/models/agent.py +90 -0
- control_plane_api/app/models/analytics.py +206 -0
- control_plane_api/app/models/associations.py +107 -0
- control_plane_api/app/models/auth_user.py +73 -0
- control_plane_api/app/models/context.py +161 -0
- control_plane_api/app/models/custom_integration.py +99 -0
- control_plane_api/app/models/environment.py +64 -0
- control_plane_api/app/models/execution.py +125 -0
- control_plane_api/app/models/execution_transition.py +50 -0
- control_plane_api/app/models/job.py +159 -0
- control_plane_api/app/models/llm_model.py +78 -0
- control_plane_api/app/models/orchestration.py +66 -0
- control_plane_api/app/models/plan_execution.py +102 -0
- control_plane_api/app/models/presence.py +49 -0
- control_plane_api/app/models/project.py +61 -0
- control_plane_api/app/models/project_management.py +85 -0
- control_plane_api/app/models/session.py +29 -0
- control_plane_api/app/models/skill.py +155 -0
- control_plane_api/app/models/system_tables.py +43 -0
- control_plane_api/app/models/task_planning.py +372 -0
- control_plane_api/app/models/team.py +86 -0
- control_plane_api/app/models/trace.py +257 -0
- control_plane_api/app/models/user_profile.py +54 -0
- control_plane_api/app/models/worker.py +221 -0
- control_plane_api/app/models/workflow.py +161 -0
- control_plane_api/app/models/workspace.py +50 -0
- control_plane_api/app/observability/__init__.py +177 -0
- control_plane_api/app/observability/context_logging.py +475 -0
- control_plane_api/app/observability/decorators.py +337 -0
- control_plane_api/app/observability/local_span_processor.py +702 -0
- control_plane_api/app/observability/metrics.py +303 -0
- control_plane_api/app/observability/middleware.py +246 -0
- control_plane_api/app/observability/optional.py +115 -0
- control_plane_api/app/observability/tracing.py +382 -0
- control_plane_api/app/policies/README.md +149 -0
- control_plane_api/app/policies/approved_users.rego +62 -0
- control_plane_api/app/policies/business_hours.rego +51 -0
- control_plane_api/app/policies/rate_limiting.rego +100 -0
- control_plane_api/app/policies/tool_enforcement/README.md +336 -0
- control_plane_api/app/policies/tool_enforcement/bash_command_validation.rego +71 -0
- control_plane_api/app/policies/tool_enforcement/business_hours_enforcement.rego +82 -0
- control_plane_api/app/policies/tool_enforcement/mcp_tool_allowlist.rego +58 -0
- control_plane_api/app/policies/tool_enforcement/production_safeguards.rego +80 -0
- control_plane_api/app/policies/tool_enforcement/role_based_tool_access.rego +44 -0
- control_plane_api/app/policies/tool_restrictions.rego +86 -0
- control_plane_api/app/routers/__init__.py +4 -0
- control_plane_api/app/routers/agents.py +382 -0
- control_plane_api/app/routers/agents_v2.py +1598 -0
- control_plane_api/app/routers/analytics.py +1310 -0
- control_plane_api/app/routers/auth.py +59 -0
- control_plane_api/app/routers/client_config.py +57 -0
- control_plane_api/app/routers/context_graph.py +561 -0
- control_plane_api/app/routers/context_manager.py +577 -0
- control_plane_api/app/routers/custom_integrations.py +490 -0
- control_plane_api/app/routers/enforcer.py +132 -0
- control_plane_api/app/routers/environment_context.py +252 -0
- control_plane_api/app/routers/environments.py +761 -0
- control_plane_api/app/routers/execution_environment.py +847 -0
- control_plane_api/app/routers/executions/__init__.py +28 -0
- control_plane_api/app/routers/executions/router.py +286 -0
- control_plane_api/app/routers/executions/services/__init__.py +22 -0
- control_plane_api/app/routers/executions/services/demo_worker_health.py +156 -0
- control_plane_api/app/routers/executions/services/status_service.py +420 -0
- control_plane_api/app/routers/executions/services/test_worker_health.py +480 -0
- control_plane_api/app/routers/executions/services/worker_health.py +514 -0
- control_plane_api/app/routers/executions/streaming/__init__.py +22 -0
- control_plane_api/app/routers/executions/streaming/deduplication.py +352 -0
- control_plane_api/app/routers/executions/streaming/event_buffer.py +353 -0
- control_plane_api/app/routers/executions/streaming/event_formatter.py +964 -0
- control_plane_api/app/routers/executions/streaming/history_loader.py +588 -0
- control_plane_api/app/routers/executions/streaming/live_source.py +693 -0
- control_plane_api/app/routers/executions/streaming/streamer.py +849 -0
- control_plane_api/app/routers/executions.py +4888 -0
- control_plane_api/app/routers/health.py +165 -0
- control_plane_api/app/routers/health_v2.py +394 -0
- control_plane_api/app/routers/integration_templates.py +496 -0
- control_plane_api/app/routers/integrations.py +287 -0
- control_plane_api/app/routers/jobs.py +1809 -0
- control_plane_api/app/routers/metrics.py +517 -0
- control_plane_api/app/routers/models.py +82 -0
- control_plane_api/app/routers/models_v2.py +628 -0
- control_plane_api/app/routers/plan_executions.py +1481 -0
- control_plane_api/app/routers/plan_generation_async.py +304 -0
- control_plane_api/app/routers/policies.py +669 -0
- control_plane_api/app/routers/presence.py +234 -0
- control_plane_api/app/routers/projects.py +987 -0
- control_plane_api/app/routers/runners.py +379 -0
- control_plane_api/app/routers/runtimes.py +172 -0
- control_plane_api/app/routers/secrets.py +171 -0
- control_plane_api/app/routers/skills.py +1010 -0
- control_plane_api/app/routers/skills_definitions.py +140 -0
- control_plane_api/app/routers/storage.py +456 -0
- control_plane_api/app/routers/task_planning.py +611 -0
- control_plane_api/app/routers/task_queues.py +650 -0
- control_plane_api/app/routers/team_context.py +274 -0
- control_plane_api/app/routers/teams.py +1747 -0
- control_plane_api/app/routers/templates.py +248 -0
- control_plane_api/app/routers/traces.py +571 -0
- control_plane_api/app/routers/websocket_client.py +479 -0
- control_plane_api/app/routers/websocket_executions_status.py +437 -0
- control_plane_api/app/routers/websocket_gateway.py +323 -0
- control_plane_api/app/routers/websocket_traces.py +576 -0
- control_plane_api/app/routers/worker_queues.py +2555 -0
- control_plane_api/app/routers/worker_websocket.py +419 -0
- control_plane_api/app/routers/workers.py +1004 -0
- control_plane_api/app/routers/workflows.py +204 -0
- control_plane_api/app/runtimes/__init__.py +6 -0
- control_plane_api/app/runtimes/validation.py +344 -0
- control_plane_api/app/schemas/__init__.py +1 -0
- control_plane_api/app/schemas/job_schemas.py +302 -0
- control_plane_api/app/schemas/mcp_schemas.py +311 -0
- control_plane_api/app/schemas/template_schemas.py +133 -0
- control_plane_api/app/schemas/trace_schemas.py +168 -0
- control_plane_api/app/schemas/worker_queue_observability_schemas.py +165 -0
- control_plane_api/app/services/__init__.py +1 -0
- control_plane_api/app/services/agno_planning_strategy.py +233 -0
- control_plane_api/app/services/agno_service.py +838 -0
- control_plane_api/app/services/claude_code_planning_service.py +203 -0
- control_plane_api/app/services/context_graph_client.py +224 -0
- control_plane_api/app/services/custom_integration_service.py +415 -0
- control_plane_api/app/services/integration_resolution_service.py +345 -0
- control_plane_api/app/services/litellm_service.py +394 -0
- control_plane_api/app/services/plan_generator.py +79 -0
- control_plane_api/app/services/planning_strategy.py +66 -0
- control_plane_api/app/services/planning_strategy_factory.py +118 -0
- control_plane_api/app/services/policy_service.py +615 -0
- control_plane_api/app/services/state_transition_service.py +755 -0
- control_plane_api/app/services/storage_service.py +593 -0
- control_plane_api/app/services/temporal_cloud_provisioning.py +150 -0
- control_plane_api/app/services/toolsets/context_graph_skill.py +432 -0
- control_plane_api/app/services/trace_retention.py +354 -0
- control_plane_api/app/services/worker_queue_metrics_service.py +190 -0
- control_plane_api/app/services/workflow_cancellation_manager.py +135 -0
- control_plane_api/app/services/workflow_operations_service.py +611 -0
- control_plane_api/app/skills/__init__.py +100 -0
- control_plane_api/app/skills/base.py +239 -0
- control_plane_api/app/skills/builtin/__init__.py +37 -0
- control_plane_api/app/skills/builtin/agent_communication/__init__.py +8 -0
- control_plane_api/app/skills/builtin/agent_communication/skill.py +246 -0
- control_plane_api/app/skills/builtin/code_ingestion/__init__.py +4 -0
- control_plane_api/app/skills/builtin/code_ingestion/skill.py +267 -0
- control_plane_api/app/skills/builtin/cognitive_memory/__init__.py +4 -0
- control_plane_api/app/skills/builtin/cognitive_memory/skill.py +174 -0
- control_plane_api/app/skills/builtin/contextual_awareness/__init__.py +4 -0
- control_plane_api/app/skills/builtin/contextual_awareness/skill.py +387 -0
- control_plane_api/app/skills/builtin/data_visualization/__init__.py +4 -0
- control_plane_api/app/skills/builtin/data_visualization/skill.py +154 -0
- control_plane_api/app/skills/builtin/docker/__init__.py +4 -0
- control_plane_api/app/skills/builtin/docker/skill.py +104 -0
- control_plane_api/app/skills/builtin/file_generation/__init__.py +4 -0
- control_plane_api/app/skills/builtin/file_generation/skill.py +94 -0
- control_plane_api/app/skills/builtin/file_system/__init__.py +4 -0
- control_plane_api/app/skills/builtin/file_system/skill.py +110 -0
- control_plane_api/app/skills/builtin/knowledge_api/__init__.py +5 -0
- control_plane_api/app/skills/builtin/knowledge_api/skill.py +124 -0
- control_plane_api/app/skills/builtin/python/__init__.py +4 -0
- control_plane_api/app/skills/builtin/python/skill.py +92 -0
- control_plane_api/app/skills/builtin/remote_filesystem/__init__.py +5 -0
- control_plane_api/app/skills/builtin/remote_filesystem/skill.py +170 -0
- control_plane_api/app/skills/builtin/shell/__init__.py +4 -0
- control_plane_api/app/skills/builtin/shell/skill.py +161 -0
- control_plane_api/app/skills/builtin/slack/__init__.py +3 -0
- control_plane_api/app/skills/builtin/slack/skill.py +302 -0
- control_plane_api/app/skills/builtin/workflow_executor/__init__.py +4 -0
- control_plane_api/app/skills/builtin/workflow_executor/skill.py +469 -0
- control_plane_api/app/skills/business_intelligence.py +189 -0
- control_plane_api/app/skills/config.py +63 -0
- control_plane_api/app/skills/loaders/__init__.py +14 -0
- control_plane_api/app/skills/loaders/base.py +73 -0
- control_plane_api/app/skills/loaders/filesystem_loader.py +199 -0
- control_plane_api/app/skills/registry.py +125 -0
- control_plane_api/app/utils/helpers.py +12 -0
- control_plane_api/app/utils/workflow_executor.py +354 -0
- control_plane_api/app/workflows/__init__.py +11 -0
- control_plane_api/app/workflows/agent_execution.py +520 -0
- control_plane_api/app/workflows/agent_execution_with_skills.py +223 -0
- control_plane_api/app/workflows/namespace_provisioning.py +326 -0
- control_plane_api/app/workflows/plan_generation.py +254 -0
- control_plane_api/app/workflows/team_execution.py +442 -0
- control_plane_api/scripts/seed_models.py +240 -0
- control_plane_api/scripts/validate_existing_tool_names.py +492 -0
- control_plane_api/shared/__init__.py +8 -0
- control_plane_api/shared/version.py +17 -0
- control_plane_api/test_deduplication.py +274 -0
- control_plane_api/test_executor_deduplication_e2e.py +309 -0
- control_plane_api/test_job_execution_e2e.py +283 -0
- control_plane_api/test_real_integration.py +193 -0
- control_plane_api/version.py +38 -0
- control_plane_api/worker/__init__.py +0 -0
- control_plane_api/worker/activities/__init__.py +0 -0
- control_plane_api/worker/activities/agent_activities.py +1585 -0
- control_plane_api/worker/activities/approval_activities.py +234 -0
- control_plane_api/worker/activities/job_activities.py +199 -0
- control_plane_api/worker/activities/runtime_activities.py +1167 -0
- control_plane_api/worker/activities/skill_activities.py +282 -0
- control_plane_api/worker/activities/team_activities.py +479 -0
- control_plane_api/worker/agent_runtime_server.py +370 -0
- control_plane_api/worker/binary_manager.py +333 -0
- control_plane_api/worker/config/__init__.py +31 -0
- control_plane_api/worker/config/worker_config.py +273 -0
- control_plane_api/worker/control_plane_client.py +1491 -0
- control_plane_api/worker/examples/analytics_integration_example.py +362 -0
- control_plane_api/worker/health_monitor.py +159 -0
- control_plane_api/worker/metrics.py +237 -0
- control_plane_api/worker/models/__init__.py +1 -0
- control_plane_api/worker/models/error_events.py +105 -0
- control_plane_api/worker/models/inputs.py +89 -0
- control_plane_api/worker/runtimes/__init__.py +35 -0
- control_plane_api/worker/runtimes/agent_runtime/runtime.py +485 -0
- control_plane_api/worker/runtimes/agno/__init__.py +34 -0
- control_plane_api/worker/runtimes/agno/config.py +248 -0
- control_plane_api/worker/runtimes/agno/hooks.py +385 -0
- control_plane_api/worker/runtimes/agno/mcp_builder.py +195 -0
- control_plane_api/worker/runtimes/agno/runtime.py +1063 -0
- control_plane_api/worker/runtimes/agno/utils.py +163 -0
- control_plane_api/worker/runtimes/base.py +979 -0
- control_plane_api/worker/runtimes/claude_code/__init__.py +38 -0
- control_plane_api/worker/runtimes/claude_code/cleanup.py +184 -0
- control_plane_api/worker/runtimes/claude_code/client_pool.py +529 -0
- control_plane_api/worker/runtimes/claude_code/config.py +829 -0
- control_plane_api/worker/runtimes/claude_code/hooks.py +482 -0
- control_plane_api/worker/runtimes/claude_code/litellm_proxy.py +1702 -0
- control_plane_api/worker/runtimes/claude_code/mcp_builder.py +467 -0
- control_plane_api/worker/runtimes/claude_code/mcp_discovery.py +558 -0
- control_plane_api/worker/runtimes/claude_code/runtime.py +1546 -0
- control_plane_api/worker/runtimes/claude_code/tool_mapper.py +403 -0
- control_plane_api/worker/runtimes/claude_code/utils.py +149 -0
- control_plane_api/worker/runtimes/factory.py +173 -0
- control_plane_api/worker/runtimes/model_utils.py +107 -0
- control_plane_api/worker/runtimes/validation.py +93 -0
- control_plane_api/worker/services/__init__.py +1 -0
- control_plane_api/worker/services/agent_communication_tools.py +908 -0
- control_plane_api/worker/services/agent_executor.py +485 -0
- control_plane_api/worker/services/agent_executor_v2.py +793 -0
- control_plane_api/worker/services/analytics_collector.py +457 -0
- control_plane_api/worker/services/analytics_service.py +464 -0
- control_plane_api/worker/services/approval_tools.py +310 -0
- control_plane_api/worker/services/approval_tools_agno.py +207 -0
- control_plane_api/worker/services/cancellation_manager.py +177 -0
- control_plane_api/worker/services/code_ingestion_tools.py +465 -0
- control_plane_api/worker/services/contextual_awareness_tools.py +405 -0
- control_plane_api/worker/services/data_visualization.py +834 -0
- control_plane_api/worker/services/event_publisher.py +531 -0
- control_plane_api/worker/services/jira_tools.py +257 -0
- control_plane_api/worker/services/remote_filesystem_tools.py +498 -0
- control_plane_api/worker/services/runtime_analytics.py +328 -0
- control_plane_api/worker/services/session_service.py +365 -0
- control_plane_api/worker/services/skill_context_enhancement.py +181 -0
- control_plane_api/worker/services/skill_factory.py +471 -0
- control_plane_api/worker/services/system_prompt_enhancement.py +410 -0
- control_plane_api/worker/services/team_executor.py +715 -0
- control_plane_api/worker/services/team_executor_v2.py +1866 -0
- control_plane_api/worker/services/tool_enforcement.py +254 -0
- control_plane_api/worker/services/workflow_executor/__init__.py +52 -0
- control_plane_api/worker/services/workflow_executor/event_processor.py +287 -0
- control_plane_api/worker/services/workflow_executor/event_publisher.py +210 -0
- control_plane_api/worker/services/workflow_executor/executors/__init__.py +15 -0
- control_plane_api/worker/services/workflow_executor/executors/base.py +270 -0
- control_plane_api/worker/services/workflow_executor/executors/json_executor.py +50 -0
- control_plane_api/worker/services/workflow_executor/executors/python_executor.py +50 -0
- control_plane_api/worker/services/workflow_executor/models.py +142 -0
- control_plane_api/worker/services/workflow_executor_tools.py +1748 -0
- control_plane_api/worker/skills/__init__.py +12 -0
- control_plane_api/worker/skills/builtin/context_graph_search/README.md +213 -0
- control_plane_api/worker/skills/builtin/context_graph_search/__init__.py +5 -0
- control_plane_api/worker/skills/builtin/context_graph_search/agno_impl.py +808 -0
- control_plane_api/worker/skills/builtin/context_graph_search/skill.yaml +67 -0
- control_plane_api/worker/skills/builtin/contextual_awareness/__init__.py +4 -0
- control_plane_api/worker/skills/builtin/contextual_awareness/agno_impl.py +62 -0
- control_plane_api/worker/skills/builtin/data_visualization/agno_impl.py +18 -0
- control_plane_api/worker/skills/builtin/data_visualization/skill.yaml +84 -0
- control_plane_api/worker/skills/builtin/docker/agno_impl.py +65 -0
- control_plane_api/worker/skills/builtin/docker/skill.yaml +60 -0
- control_plane_api/worker/skills/builtin/file_generation/agno_impl.py +47 -0
- control_plane_api/worker/skills/builtin/file_generation/skill.yaml +64 -0
- control_plane_api/worker/skills/builtin/file_system/agno_impl.py +32 -0
- control_plane_api/worker/skills/builtin/file_system/skill.yaml +54 -0
- control_plane_api/worker/skills/builtin/knowledge_api/__init__.py +4 -0
- control_plane_api/worker/skills/builtin/knowledge_api/agno_impl.py +50 -0
- control_plane_api/worker/skills/builtin/knowledge_api/skill.yaml +66 -0
- control_plane_api/worker/skills/builtin/python/agno_impl.py +25 -0
- control_plane_api/worker/skills/builtin/python/skill.yaml +60 -0
- control_plane_api/worker/skills/builtin/schema_fix_mixin.py +260 -0
- control_plane_api/worker/skills/builtin/shell/agno_impl.py +31 -0
- control_plane_api/worker/skills/builtin/shell/skill.yaml +60 -0
- control_plane_api/worker/skills/builtin/slack/__init__.py +3 -0
- control_plane_api/worker/skills/builtin/slack/agno_impl.py +1282 -0
- control_plane_api/worker/skills/builtin/slack/skill.yaml +276 -0
- control_plane_api/worker/skills/builtin/workflow_executor/agno_impl.py +62 -0
- control_plane_api/worker/skills/builtin/workflow_executor/skill.yaml +79 -0
- control_plane_api/worker/skills/loaders/__init__.py +5 -0
- control_plane_api/worker/skills/loaders/base.py +23 -0
- control_plane_api/worker/skills/loaders/filesystem_loader.py +357 -0
- control_plane_api/worker/skills/registry.py +208 -0
- control_plane_api/worker/tests/__init__.py +1 -0
- control_plane_api/worker/tests/conftest.py +12 -0
- control_plane_api/worker/tests/e2e/__init__.py +0 -0
- control_plane_api/worker/tests/e2e/test_context_graph_real_api.py +338 -0
- control_plane_api/worker/tests/e2e/test_context_graph_templates_e2e.py +523 -0
- control_plane_api/worker/tests/e2e/test_enforcement_e2e.py +344 -0
- control_plane_api/worker/tests/e2e/test_execution_flow.py +571 -0
- control_plane_api/worker/tests/e2e/test_single_execution_mode.py +656 -0
- control_plane_api/worker/tests/integration/__init__.py +0 -0
- control_plane_api/worker/tests/integration/test_builtin_skills_fixes.py +245 -0
- control_plane_api/worker/tests/integration/test_context_graph_search_integration.py +365 -0
- control_plane_api/worker/tests/integration/test_control_plane_integration.py +308 -0
- control_plane_api/worker/tests/integration/test_hook_enforcement_integration.py +579 -0
- control_plane_api/worker/tests/integration/test_scheduled_job_workflow.py +237 -0
- control_plane_api/worker/tests/integration/test_system_prompt_enhancement_integration.py +343 -0
- control_plane_api/worker/tests/unit/__init__.py +0 -0
- control_plane_api/worker/tests/unit/test_builtin_skill_autoload.py +396 -0
- control_plane_api/worker/tests/unit/test_context_graph_search.py +450 -0
- control_plane_api/worker/tests/unit/test_context_graph_templates.py +403 -0
- control_plane_api/worker/tests/unit/test_control_plane_client.py +401 -0
- control_plane_api/worker/tests/unit/test_control_plane_client_jobs.py +345 -0
- control_plane_api/worker/tests/unit/test_job_activities.py +353 -0
- control_plane_api/worker/tests/unit/test_skill_context_enhancement.py +321 -0
- control_plane_api/worker/tests/unit/test_system_prompt_enhancement.py +415 -0
- control_plane_api/worker/tests/unit/test_tool_enforcement.py +324 -0
- control_plane_api/worker/utils/__init__.py +1 -0
- control_plane_api/worker/utils/chunk_batcher.py +330 -0
- control_plane_api/worker/utils/environment.py +65 -0
- control_plane_api/worker/utils/error_publisher.py +260 -0
- control_plane_api/worker/utils/event_batcher.py +256 -0
- control_plane_api/worker/utils/logging_config.py +335 -0
- control_plane_api/worker/utils/logging_helper.py +326 -0
- control_plane_api/worker/utils/parameter_validator.py +120 -0
- control_plane_api/worker/utils/retry_utils.py +60 -0
- control_plane_api/worker/utils/streaming_utils.py +665 -0
- control_plane_api/worker/utils/tool_validation.py +332 -0
- control_plane_api/worker/utils/workspace_manager.py +163 -0
- control_plane_api/worker/websocket_client.py +393 -0
- control_plane_api/worker/worker.py +1297 -0
- control_plane_api/worker/workflows/__init__.py +0 -0
- control_plane_api/worker/workflows/agent_execution.py +909 -0
- control_plane_api/worker/workflows/scheduled_job_wrapper.py +332 -0
- control_plane_api/worker/workflows/team_execution.py +611 -0
- kubiya_control_plane_api-0.9.15.dist-info/METADATA +354 -0
- kubiya_control_plane_api-0.9.15.dist-info/RECORD +479 -0
- kubiya_control_plane_api-0.9.15.dist-info/WHEEL +5 -0
- kubiya_control_plane_api-0.9.15.dist-info/entry_points.txt +5 -0
- kubiya_control_plane_api-0.9.15.dist-info/licenses/LICENSE +676 -0
- kubiya_control_plane_api-0.9.15.dist-info/top_level.txt +3 -0
- scripts/__init__.py +1 -0
- scripts/migrations.py +39 -0
- scripts/seed_worker_queues.py +128 -0
- scripts/setup_agent_runtime.py +142 -0
- worker_internal/__init__.py +1 -0
- worker_internal/planner/__init__.py +1 -0
- worker_internal/planner/activities.py +1499 -0
- worker_internal/planner/agent_tools.py +197 -0
- worker_internal/planner/event_models.py +148 -0
- worker_internal/planner/event_publisher.py +67 -0
- worker_internal/planner/models.py +199 -0
- worker_internal/planner/retry_logic.py +134 -0
- worker_internal/planner/worker.py +300 -0
- worker_internal/planner/workflows.py +970 -0
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Execution context tools for state transition decisions
|
|
3
|
+
|
|
4
|
+
Provides tools for the AI agent to gather context about executions,
|
|
5
|
+
turns, conversations, and tool usage patterns.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Optional, Dict, Any, List
|
|
9
|
+
import structlog
|
|
10
|
+
import httpx
|
|
11
|
+
from agno.tools.toolkit import Toolkit
|
|
12
|
+
|
|
13
|
+
logger = structlog.get_logger()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ExecutionContextTools(Toolkit):
|
|
17
|
+
"""
|
|
18
|
+
Tools for gathering execution context to inform state transition decisions
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
base_url: str = "http://localhost:8000",
|
|
24
|
+
organization_id: Optional[str] = None,
|
|
25
|
+
timeout: int = 30,
|
|
26
|
+
):
|
|
27
|
+
"""
|
|
28
|
+
Initialize execution context tools
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
base_url: Base URL for the control plane API
|
|
32
|
+
organization_id: Organization context for filtering
|
|
33
|
+
timeout: HTTP request timeout in seconds
|
|
34
|
+
"""
|
|
35
|
+
super().__init__(name="execution_context_tools")
|
|
36
|
+
self.base_url = base_url.rstrip("/")
|
|
37
|
+
self.organization_id = organization_id
|
|
38
|
+
self.timeout = timeout
|
|
39
|
+
self._client: Optional[httpx.AsyncClient] = None
|
|
40
|
+
|
|
41
|
+
async def _get_client(self) -> httpx.AsyncClient:
|
|
42
|
+
"""Get or create HTTP client"""
|
|
43
|
+
if self._client is None:
|
|
44
|
+
self._client = httpx.AsyncClient(
|
|
45
|
+
base_url=self.base_url,
|
|
46
|
+
timeout=self.timeout,
|
|
47
|
+
)
|
|
48
|
+
return self._client
|
|
49
|
+
|
|
50
|
+
async def _make_request(
|
|
51
|
+
self,
|
|
52
|
+
method: str,
|
|
53
|
+
endpoint: str,
|
|
54
|
+
params: Optional[Dict[str, Any]] = None,
|
|
55
|
+
json: Optional[Dict[str, Any]] = None,
|
|
56
|
+
) -> Dict[str, Any]:
|
|
57
|
+
"""Make an HTTP request to the control plane API"""
|
|
58
|
+
client = await self._get_client()
|
|
59
|
+
response = await client.request(
|
|
60
|
+
method=method,
|
|
61
|
+
url=endpoint,
|
|
62
|
+
params=params,
|
|
63
|
+
json=json,
|
|
64
|
+
)
|
|
65
|
+
response.raise_for_status()
|
|
66
|
+
return response.json()
|
|
67
|
+
|
|
68
|
+
async def get_execution_details(self, execution_id: str) -> str:
|
|
69
|
+
"""
|
|
70
|
+
Get detailed information about an execution
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
execution_id: The execution ID to fetch
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
Formatted string with execution details including status, metadata, timestamps
|
|
77
|
+
"""
|
|
78
|
+
try:
|
|
79
|
+
logger.info("fetching_execution_details", execution_id=execution_id)
|
|
80
|
+
|
|
81
|
+
result = await self._make_request(
|
|
82
|
+
method="GET",
|
|
83
|
+
endpoint=f"/api/v1/executions/{execution_id}",
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
execution = result.get("execution", result)
|
|
87
|
+
|
|
88
|
+
# Format key information
|
|
89
|
+
output = [
|
|
90
|
+
f"Execution Details (ID: {execution_id}):",
|
|
91
|
+
f" Status: {execution.get('status', 'unknown')}",
|
|
92
|
+
f" Prompt: {execution.get('prompt', 'N/A')[:200]}...",
|
|
93
|
+
f" Started: {execution.get('started_at', 'N/A')}",
|
|
94
|
+
f" Completed: {execution.get('completed_at', 'N/A')}",
|
|
95
|
+
f" Response Preview: {execution.get('response', '')[:300]}...",
|
|
96
|
+
f" Error Message: {execution.get('error_message', 'None')}",
|
|
97
|
+
f" Agent ID: {execution.get('agent_id', 'N/A')}",
|
|
98
|
+
f" Team ID: {execution.get('team_id', 'N/A')}",
|
|
99
|
+
]
|
|
100
|
+
|
|
101
|
+
# Add metadata if available
|
|
102
|
+
if execution.get('execution_metadata'):
|
|
103
|
+
output.append(f" Metadata: {execution['execution_metadata']}")
|
|
104
|
+
|
|
105
|
+
return "\n".join(output)
|
|
106
|
+
|
|
107
|
+
except Exception as e:
|
|
108
|
+
logger.error(
|
|
109
|
+
"get_execution_details_failed",
|
|
110
|
+
execution_id=execution_id,
|
|
111
|
+
error=str(e),
|
|
112
|
+
)
|
|
113
|
+
return f"Error fetching execution details: {str(e)}"
|
|
114
|
+
|
|
115
|
+
async def get_recent_turns(self, execution_id: str, limit: int = 5) -> str:
|
|
116
|
+
"""
|
|
117
|
+
Get recent turns for an execution with metrics
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
execution_id: The execution ID
|
|
121
|
+
limit: Maximum number of recent turns to fetch (default: 5)
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
Formatted string with turn information including tokens, costs, finish reasons, tools called
|
|
125
|
+
"""
|
|
126
|
+
try:
|
|
127
|
+
logger.info(
|
|
128
|
+
"fetching_recent_turns",
|
|
129
|
+
execution_id=execution_id,
|
|
130
|
+
limit=limit,
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
result = await self._make_request(
|
|
134
|
+
method="GET",
|
|
135
|
+
endpoint="/api/v1/analytics/turns",
|
|
136
|
+
params={"execution_id": execution_id, "limit": limit},
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
turns = result.get("turns", [])
|
|
140
|
+
|
|
141
|
+
if not turns:
|
|
142
|
+
return f"No turns found for execution {execution_id}"
|
|
143
|
+
|
|
144
|
+
output = [f"Recent Turns ({len(turns)} total, showing last {min(len(turns), limit)}):"]
|
|
145
|
+
|
|
146
|
+
for turn in turns[-limit:]:
|
|
147
|
+
turn_num = turn.get("turn_number", "?")
|
|
148
|
+
output.append(f"\n--- Turn {turn_num} ---")
|
|
149
|
+
output.append(f" Model: {turn.get('model', 'N/A')}")
|
|
150
|
+
output.append(f" Finish Reason: {turn.get('finish_reason', 'N/A')}")
|
|
151
|
+
output.append(f" Duration: {turn.get('duration_ms', 0)}ms")
|
|
152
|
+
output.append(f" Tokens (in/out): {turn.get('input_tokens', 0)}/{turn.get('output_tokens', 0)}")
|
|
153
|
+
output.append(f" Tools Called: {turn.get('tools_called_count', 0)}")
|
|
154
|
+
|
|
155
|
+
if turn.get('tools_called_names'):
|
|
156
|
+
output.append(f" Tool Names: {', '.join(turn['tools_called_names'])}")
|
|
157
|
+
|
|
158
|
+
if turn.get('response_preview'):
|
|
159
|
+
output.append(f" Response: {turn['response_preview'][:150]}...")
|
|
160
|
+
|
|
161
|
+
if turn.get('error_message'):
|
|
162
|
+
output.append(f" Error: {turn['error_message'][:200]}")
|
|
163
|
+
|
|
164
|
+
return "\n".join(output)
|
|
165
|
+
|
|
166
|
+
except Exception as e:
|
|
167
|
+
logger.error(
|
|
168
|
+
"get_recent_turns_failed",
|
|
169
|
+
execution_id=execution_id,
|
|
170
|
+
error=str(e),
|
|
171
|
+
)
|
|
172
|
+
return f"Error fetching recent turns: {str(e)}"
|
|
173
|
+
|
|
174
|
+
async def get_conversation_messages(self, execution_id: str, limit: int = 10) -> str:
|
|
175
|
+
"""
|
|
176
|
+
Get conversation message history for an execution
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
execution_id: The execution ID
|
|
180
|
+
limit: Maximum number of recent messages (default: 10)
|
|
181
|
+
|
|
182
|
+
Returns:
|
|
183
|
+
Formatted string with conversation messages
|
|
184
|
+
"""
|
|
185
|
+
try:
|
|
186
|
+
logger.info(
|
|
187
|
+
"fetching_conversation_messages",
|
|
188
|
+
execution_id=execution_id,
|
|
189
|
+
limit=limit,
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
result = await self._make_request(
|
|
193
|
+
method="GET",
|
|
194
|
+
endpoint=f"/api/v1/executions/{execution_id}/messages",
|
|
195
|
+
params={"limit": limit},
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
messages = result.get("messages", [])
|
|
199
|
+
|
|
200
|
+
if not messages:
|
|
201
|
+
return f"No conversation messages found for execution {execution_id}"
|
|
202
|
+
|
|
203
|
+
output = [f"Conversation Messages ({len(messages)} total, showing last {limit}):"]
|
|
204
|
+
|
|
205
|
+
for idx, msg in enumerate(messages[-limit:], 1):
|
|
206
|
+
role = msg.get("role", "unknown")
|
|
207
|
+
content = msg.get("content", "")
|
|
208
|
+
timestamp = msg.get("timestamp", "N/A")
|
|
209
|
+
|
|
210
|
+
output.append(f"\n{idx}. [{role.upper()}] at {timestamp}:")
|
|
211
|
+
output.append(f" {content[:300]}...")
|
|
212
|
+
|
|
213
|
+
return "\n".join(output)
|
|
214
|
+
|
|
215
|
+
except Exception as e:
|
|
216
|
+
logger.error(
|
|
217
|
+
"get_conversation_messages_failed",
|
|
218
|
+
execution_id=execution_id,
|
|
219
|
+
error=str(e),
|
|
220
|
+
)
|
|
221
|
+
# Return empty conversation if endpoint doesn't exist yet
|
|
222
|
+
return f"Conversation history not available: {str(e)}"
|
|
223
|
+
|
|
224
|
+
async def get_tool_call_patterns(self, execution_id: str) -> str:
|
|
225
|
+
"""
|
|
226
|
+
Analyze tool call patterns for an execution
|
|
227
|
+
|
|
228
|
+
Args:
|
|
229
|
+
execution_id: The execution ID
|
|
230
|
+
|
|
231
|
+
Returns:
|
|
232
|
+
Formatted string with tool usage statistics and patterns
|
|
233
|
+
"""
|
|
234
|
+
try:
|
|
235
|
+
logger.info(
|
|
236
|
+
"analyzing_tool_call_patterns",
|
|
237
|
+
execution_id=execution_id,
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
result = await self._make_request(
|
|
241
|
+
method="GET",
|
|
242
|
+
endpoint="/api/v1/analytics/tool-calls",
|
|
243
|
+
params={"execution_id": execution_id},
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
tool_calls = result.get("tool_calls", [])
|
|
247
|
+
|
|
248
|
+
if not tool_calls:
|
|
249
|
+
return f"No tool calls found for execution {execution_id}"
|
|
250
|
+
|
|
251
|
+
# Analyze patterns
|
|
252
|
+
total_calls = len(tool_calls)
|
|
253
|
+
successful_calls = sum(1 for tc in tool_calls if tc.get("success", False))
|
|
254
|
+
failed_calls = total_calls - successful_calls
|
|
255
|
+
|
|
256
|
+
# Count by tool name
|
|
257
|
+
tool_counts: Dict[str, int] = {}
|
|
258
|
+
tool_failures: Dict[str, int] = {}
|
|
259
|
+
|
|
260
|
+
for tc in tool_calls:
|
|
261
|
+
tool_name = tc.get("tool_name", "unknown")
|
|
262
|
+
tool_counts[tool_name] = tool_counts.get(tool_name, 0) + 1
|
|
263
|
+
if not tc.get("success", False):
|
|
264
|
+
tool_failures[tool_name] = tool_failures.get(tool_name, 0) + 1
|
|
265
|
+
|
|
266
|
+
output = [
|
|
267
|
+
f"Tool Call Analysis:",
|
|
268
|
+
f" Total Calls: {total_calls}",
|
|
269
|
+
f" Successful: {successful_calls} ({100*successful_calls//total_calls if total_calls > 0 else 0}%)",
|
|
270
|
+
f" Failed: {failed_calls} ({100*failed_calls//total_calls if total_calls > 0 else 0}%)",
|
|
271
|
+
f"\nMost Used Tools:",
|
|
272
|
+
]
|
|
273
|
+
|
|
274
|
+
for tool_name, count in sorted(tool_counts.items(), key=lambda x: x[1], reverse=True)[:5]:
|
|
275
|
+
failures = tool_failures.get(tool_name, 0)
|
|
276
|
+
output.append(f" - {tool_name}: {count} calls ({failures} failed)")
|
|
277
|
+
|
|
278
|
+
return "\n".join(output)
|
|
279
|
+
|
|
280
|
+
except Exception as e:
|
|
281
|
+
logger.error(
|
|
282
|
+
"get_tool_call_patterns_failed",
|
|
283
|
+
execution_id=execution_id,
|
|
284
|
+
error=str(e),
|
|
285
|
+
)
|
|
286
|
+
return f"Tool call analysis not available: {str(e)}"
|
|
287
|
+
|
|
288
|
+
async def check_error_recoverability(self, execution_id: str, error_message: Optional[str]) -> str:
|
|
289
|
+
"""
|
|
290
|
+
Determine if an error is recoverable or requires user intervention
|
|
291
|
+
|
|
292
|
+
Args:
|
|
293
|
+
execution_id: The execution ID
|
|
294
|
+
error_message: The error message to analyze
|
|
295
|
+
|
|
296
|
+
Returns:
|
|
297
|
+
Assessment of error recoverability
|
|
298
|
+
"""
|
|
299
|
+
try:
|
|
300
|
+
if not error_message:
|
|
301
|
+
return "No error detected - execution appears healthy"
|
|
302
|
+
|
|
303
|
+
error_lower = error_message.lower()
|
|
304
|
+
|
|
305
|
+
# Unrecoverable errors (need user intervention)
|
|
306
|
+
unrecoverable_patterns = [
|
|
307
|
+
"authentication failed",
|
|
308
|
+
"unauthorized",
|
|
309
|
+
"forbidden",
|
|
310
|
+
"access denied",
|
|
311
|
+
"invalid credentials",
|
|
312
|
+
"permission denied",
|
|
313
|
+
"quota exceeded",
|
|
314
|
+
"rate limit exceeded",
|
|
315
|
+
"timeout",
|
|
316
|
+
"network unreachable",
|
|
317
|
+
"connection refused",
|
|
318
|
+
"not found",
|
|
319
|
+
"does not exist",
|
|
320
|
+
]
|
|
321
|
+
|
|
322
|
+
# Recoverable errors (can retry)
|
|
323
|
+
recoverable_patterns = [
|
|
324
|
+
"temporary",
|
|
325
|
+
"retry",
|
|
326
|
+
"throttled",
|
|
327
|
+
"busy",
|
|
328
|
+
"unavailable",
|
|
329
|
+
]
|
|
330
|
+
|
|
331
|
+
is_unrecoverable = any(pattern in error_lower for pattern in unrecoverable_patterns)
|
|
332
|
+
is_recoverable = any(pattern in error_lower for pattern in recoverable_patterns)
|
|
333
|
+
|
|
334
|
+
if is_unrecoverable:
|
|
335
|
+
return (
|
|
336
|
+
f"Error Assessment: UNRECOVERABLE\n"
|
|
337
|
+
f" Error: {error_message[:200]}\n"
|
|
338
|
+
f" Recommendation: Requires user intervention. Execution should be marked as FAILED."
|
|
339
|
+
)
|
|
340
|
+
elif is_recoverable:
|
|
341
|
+
return (
|
|
342
|
+
f"Error Assessment: RECOVERABLE\n"
|
|
343
|
+
f" Error: {error_message[:200]}\n"
|
|
344
|
+
f" Recommendation: Can be retried. Execution can continue or wait for retry."
|
|
345
|
+
)
|
|
346
|
+
else:
|
|
347
|
+
return (
|
|
348
|
+
f"Error Assessment: UNCERTAIN\n"
|
|
349
|
+
f" Error: {error_message[:200]}\n"
|
|
350
|
+
f" Recommendation: Use caution. Consider context and turn history to decide."
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
except Exception as e:
|
|
354
|
+
logger.error(
|
|
355
|
+
"check_error_recoverability_failed",
|
|
356
|
+
execution_id=execution_id,
|
|
357
|
+
error=str(e),
|
|
358
|
+
)
|
|
359
|
+
return f"Error recoverability check failed: {str(e)}"
|
|
360
|
+
|
|
361
|
+
async def get_current_state(self, execution_id: str) -> str:
|
|
362
|
+
"""
|
|
363
|
+
Get the current state of an execution
|
|
364
|
+
|
|
365
|
+
Args:
|
|
366
|
+
execution_id: The execution ID
|
|
367
|
+
|
|
368
|
+
Returns:
|
|
369
|
+
Current execution state
|
|
370
|
+
"""
|
|
371
|
+
try:
|
|
372
|
+
result = await self._make_request(
|
|
373
|
+
method="GET",
|
|
374
|
+
endpoint=f"/api/v1/executions/{execution_id}",
|
|
375
|
+
)
|
|
376
|
+
|
|
377
|
+
execution = result.get("execution", result)
|
|
378
|
+
current_status = execution.get("status", "unknown")
|
|
379
|
+
|
|
380
|
+
return f"Current State: {current_status}"
|
|
381
|
+
|
|
382
|
+
except Exception as e:
|
|
383
|
+
logger.error(
|
|
384
|
+
"get_current_state_failed",
|
|
385
|
+
execution_id=execution_id,
|
|
386
|
+
error=str(e),
|
|
387
|
+
)
|
|
388
|
+
return f"Unable to determine current state: {str(e)}"
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Storage provider module for remote filesystem support.
|
|
3
|
+
|
|
4
|
+
Provides abstract base classes and implementations for different storage providers:
|
|
5
|
+
- Vercel Blob Storage
|
|
6
|
+
- AWS S3 (future)
|
|
7
|
+
- Azure Blob Storage (future)
|
|
8
|
+
- Google Cloud Storage (future)
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from .base_provider import StorageProvider, StorageFile, UploadResult
|
|
12
|
+
from .provider_factory import StorageProviderFactory, get_storage_provider
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"StorageProvider",
|
|
16
|
+
"StorageFile",
|
|
17
|
+
"UploadResult",
|
|
18
|
+
"StorageProviderFactory",
|
|
19
|
+
"get_storage_provider",
|
|
20
|
+
]
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Abstract base class for storage providers.
|
|
3
|
+
|
|
4
|
+
All storage provider implementations must inherit from StorageProvider
|
|
5
|
+
and implement all abstract methods to ensure consistent behavior.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from abc import ABC, abstractmethod
|
|
9
|
+
from typing import BinaryIO, Dict, List, Optional, Tuple
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from datetime import datetime
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class StorageFile:
|
|
16
|
+
"""
|
|
17
|
+
File metadata returned from storage operations.
|
|
18
|
+
|
|
19
|
+
Attributes:
|
|
20
|
+
file_id: Provider-specific unique identifier
|
|
21
|
+
file_name: Original filename
|
|
22
|
+
file_path: Organization-scoped virtual path
|
|
23
|
+
content_type: MIME type of the file
|
|
24
|
+
file_size_bytes: File size in bytes
|
|
25
|
+
checksum: File checksum (MD5 or SHA256)
|
|
26
|
+
created_at: Creation timestamp
|
|
27
|
+
provider_metadata: Provider-specific additional metadata
|
|
28
|
+
"""
|
|
29
|
+
file_id: str
|
|
30
|
+
file_name: str
|
|
31
|
+
file_path: str
|
|
32
|
+
content_type: str
|
|
33
|
+
file_size_bytes: int
|
|
34
|
+
checksum: Optional[str] = None
|
|
35
|
+
created_at: Optional[datetime] = None
|
|
36
|
+
provider_metadata: Optional[Dict[str, any]] = None
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass
|
|
40
|
+
class UploadResult:
|
|
41
|
+
"""
|
|
42
|
+
Result of file upload operation.
|
|
43
|
+
|
|
44
|
+
Attributes:
|
|
45
|
+
file_id: Provider-specific unique identifier for the uploaded file
|
|
46
|
+
url: Access URL for the file (may be provider-specific)
|
|
47
|
+
file_size_bytes: Actual size of uploaded file
|
|
48
|
+
checksum: File checksum for integrity verification
|
|
49
|
+
provider_metadata: Provider-specific metadata from upload
|
|
50
|
+
"""
|
|
51
|
+
file_id: str
|
|
52
|
+
url: str
|
|
53
|
+
file_size_bytes: int
|
|
54
|
+
checksum: str
|
|
55
|
+
provider_metadata: Dict[str, any]
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class StorageProvider(ABC):
|
|
59
|
+
"""
|
|
60
|
+
Abstract base class for storage providers.
|
|
61
|
+
|
|
62
|
+
All providers must implement this interface to ensure
|
|
63
|
+
consistent behavior across different storage backends.
|
|
64
|
+
|
|
65
|
+
Each provider should handle:
|
|
66
|
+
- Organization-level isolation (typically via path prefixes or separate buckets)
|
|
67
|
+
- Authentication with the underlying storage service
|
|
68
|
+
- Error handling and retry logic
|
|
69
|
+
- Streaming support for large files
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
@abstractmethod
|
|
73
|
+
async def upload(
|
|
74
|
+
self,
|
|
75
|
+
file_content: BinaryIO,
|
|
76
|
+
file_name: str,
|
|
77
|
+
file_path: str,
|
|
78
|
+
content_type: str,
|
|
79
|
+
organization_id: str,
|
|
80
|
+
metadata: Optional[Dict[str, str]] = None
|
|
81
|
+
) -> UploadResult:
|
|
82
|
+
"""
|
|
83
|
+
Upload a file to storage.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
file_content: Binary file content (file-like object)
|
|
87
|
+
file_name: Original filename
|
|
88
|
+
file_path: Organization-scoped virtual path (e.g., /folder/file.txt)
|
|
89
|
+
content_type: MIME type of the file
|
|
90
|
+
organization_id: Organization ID for namespacing
|
|
91
|
+
metadata: Optional additional metadata to store with the file
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
UploadResult with file ID, URL, and metadata
|
|
95
|
+
|
|
96
|
+
Raises:
|
|
97
|
+
Exception: If upload fails (provider-specific exceptions)
|
|
98
|
+
"""
|
|
99
|
+
pass
|
|
100
|
+
|
|
101
|
+
@abstractmethod
|
|
102
|
+
async def download(
|
|
103
|
+
self,
|
|
104
|
+
file_id: str,
|
|
105
|
+
organization_id: str
|
|
106
|
+
) -> Tuple[BinaryIO, str, int]:
|
|
107
|
+
"""
|
|
108
|
+
Download file content.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
file_id: Provider-specific file identifier
|
|
112
|
+
organization_id: Organization ID for validation
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
Tuple of (file_stream, content_type, file_size_bytes)
|
|
116
|
+
|
|
117
|
+
Raises:
|
|
118
|
+
FileNotFoundError: If file doesn't exist
|
|
119
|
+
Exception: If download fails
|
|
120
|
+
"""
|
|
121
|
+
pass
|
|
122
|
+
|
|
123
|
+
@abstractmethod
|
|
124
|
+
async def delete(
|
|
125
|
+
self,
|
|
126
|
+
file_id: str,
|
|
127
|
+
organization_id: str
|
|
128
|
+
) -> bool:
|
|
129
|
+
"""
|
|
130
|
+
Delete a file from storage.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
file_id: Provider-specific file identifier
|
|
134
|
+
organization_id: Organization ID for validation
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
True if deletion was successful, False otherwise
|
|
138
|
+
|
|
139
|
+
Raises:
|
|
140
|
+
Exception: If deletion fails
|
|
141
|
+
"""
|
|
142
|
+
pass
|
|
143
|
+
|
|
144
|
+
@abstractmethod
|
|
145
|
+
async def list_files(
|
|
146
|
+
self,
|
|
147
|
+
organization_id: str,
|
|
148
|
+
prefix: Optional[str] = None,
|
|
149
|
+
limit: int = 100,
|
|
150
|
+
cursor: Optional[str] = None
|
|
151
|
+
) -> Tuple[List[StorageFile], Optional[str]]:
|
|
152
|
+
"""
|
|
153
|
+
List files with pagination support.
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
organization_id: Organization ID for filtering
|
|
157
|
+
prefix: Optional path prefix filter (e.g., /folder/)
|
|
158
|
+
limit: Maximum number of files to return
|
|
159
|
+
cursor: Pagination cursor from previous request
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
Tuple of (files_list, next_cursor)
|
|
163
|
+
next_cursor is None if no more files
|
|
164
|
+
|
|
165
|
+
Raises:
|
|
166
|
+
Exception: If listing fails
|
|
167
|
+
"""
|
|
168
|
+
pass
|
|
169
|
+
|
|
170
|
+
@abstractmethod
|
|
171
|
+
async def get_metadata(
|
|
172
|
+
self,
|
|
173
|
+
file_id: str,
|
|
174
|
+
organization_id: str
|
|
175
|
+
) -> StorageFile:
|
|
176
|
+
"""
|
|
177
|
+
Get file metadata without downloading content.
|
|
178
|
+
|
|
179
|
+
Args:
|
|
180
|
+
file_id: Provider-specific file identifier
|
|
181
|
+
organization_id: Organization ID for validation
|
|
182
|
+
|
|
183
|
+
Returns:
|
|
184
|
+
StorageFile with metadata
|
|
185
|
+
|
|
186
|
+
Raises:
|
|
187
|
+
FileNotFoundError: If file doesn't exist
|
|
188
|
+
Exception: If metadata retrieval fails
|
|
189
|
+
"""
|
|
190
|
+
pass
|
|
191
|
+
|
|
192
|
+
@abstractmethod
|
|
193
|
+
async def copy(
|
|
194
|
+
self,
|
|
195
|
+
source_file_id: str,
|
|
196
|
+
destination_path: str,
|
|
197
|
+
organization_id: str
|
|
198
|
+
) -> UploadResult:
|
|
199
|
+
"""
|
|
200
|
+
Copy file to new location within same organization.
|
|
201
|
+
|
|
202
|
+
Args:
|
|
203
|
+
source_file_id: Source file identifier
|
|
204
|
+
destination_path: Destination virtual path
|
|
205
|
+
organization_id: Organization ID
|
|
206
|
+
|
|
207
|
+
Returns:
|
|
208
|
+
UploadResult for the new file
|
|
209
|
+
|
|
210
|
+
Raises:
|
|
211
|
+
FileNotFoundError: If source file doesn't exist
|
|
212
|
+
Exception: If copy fails
|
|
213
|
+
"""
|
|
214
|
+
pass
|
|
215
|
+
|
|
216
|
+
@abstractmethod
|
|
217
|
+
async def move(
|
|
218
|
+
self,
|
|
219
|
+
file_id: str,
|
|
220
|
+
new_path: str,
|
|
221
|
+
organization_id: str
|
|
222
|
+
) -> StorageFile:
|
|
223
|
+
"""
|
|
224
|
+
Move/rename file to new location.
|
|
225
|
+
|
|
226
|
+
Args:
|
|
227
|
+
file_id: File identifier to move
|
|
228
|
+
new_path: New virtual path
|
|
229
|
+
organization_id: Organization ID
|
|
230
|
+
|
|
231
|
+
Returns:
|
|
232
|
+
StorageFile with updated metadata
|
|
233
|
+
|
|
234
|
+
Raises:
|
|
235
|
+
FileNotFoundError: If file doesn't exist
|
|
236
|
+
Exception: If move fails
|
|
237
|
+
"""
|
|
238
|
+
pass
|
|
239
|
+
|
|
240
|
+
@abstractmethod
|
|
241
|
+
def get_provider_name(self) -> str:
|
|
242
|
+
"""
|
|
243
|
+
Return provider name identifier.
|
|
244
|
+
|
|
245
|
+
Returns:
|
|
246
|
+
Provider name (e.g., 'vercel_blob', 's3', 'azure_blob')
|
|
247
|
+
"""
|
|
248
|
+
pass
|
|
249
|
+
|
|
250
|
+
async def exists(
|
|
251
|
+
self,
|
|
252
|
+
file_id: str,
|
|
253
|
+
organization_id: str
|
|
254
|
+
) -> bool:
|
|
255
|
+
"""
|
|
256
|
+
Check if a file exists.
|
|
257
|
+
|
|
258
|
+
Default implementation uses get_metadata, but providers
|
|
259
|
+
can override with more efficient implementation.
|
|
260
|
+
|
|
261
|
+
Args:
|
|
262
|
+
file_id: File identifier
|
|
263
|
+
organization_id: Organization ID
|
|
264
|
+
|
|
265
|
+
Returns:
|
|
266
|
+
True if file exists, False otherwise
|
|
267
|
+
"""
|
|
268
|
+
try:
|
|
269
|
+
await self.get_metadata(file_id, organization_id)
|
|
270
|
+
return True
|
|
271
|
+
except FileNotFoundError:
|
|
272
|
+
return False
|
|
273
|
+
except Exception:
|
|
274
|
+
return False
|