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,136 @@
|
|
|
1
|
+
"""Base classes and interfaces for event bus providers."""
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from typing import Dict, Any, Optional, Callable
|
|
5
|
+
from pydantic import BaseModel, Field
|
|
6
|
+
import structlog
|
|
7
|
+
|
|
8
|
+
logger = structlog.get_logger(__name__)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class EventBusConfig(BaseModel):
|
|
12
|
+
"""Base configuration for event bus providers."""
|
|
13
|
+
|
|
14
|
+
enabled: bool = Field(default=True, description="Whether this provider is enabled")
|
|
15
|
+
timeout_seconds: int = Field(
|
|
16
|
+
default=30, description="Timeout for operations in seconds"
|
|
17
|
+
)
|
|
18
|
+
retry_attempts: int = Field(
|
|
19
|
+
default=3, description="Number of retry attempts on failure"
|
|
20
|
+
)
|
|
21
|
+
retry_backoff_seconds: float = Field(
|
|
22
|
+
default=0.5, description="Backoff time between retries in seconds"
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class EventBusProvider(ABC):
|
|
27
|
+
"""Abstract base class for event bus providers."""
|
|
28
|
+
|
|
29
|
+
def __init__(self, config: EventBusConfig):
|
|
30
|
+
self.config = config
|
|
31
|
+
self.logger = structlog.get_logger(
|
|
32
|
+
__name__, provider=self.__class__.__name__
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
@abstractmethod
|
|
36
|
+
async def initialize(self) -> None:
|
|
37
|
+
"""
|
|
38
|
+
Initialize provider connection/resources.
|
|
39
|
+
|
|
40
|
+
Raises:
|
|
41
|
+
Exception: If initialization fails
|
|
42
|
+
"""
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
@abstractmethod
|
|
46
|
+
async def publish_event(
|
|
47
|
+
self,
|
|
48
|
+
execution_id: str,
|
|
49
|
+
event_type: str,
|
|
50
|
+
data: Dict[str, Any],
|
|
51
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
52
|
+
) -> bool:
|
|
53
|
+
"""
|
|
54
|
+
Publish event to the bus.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
execution_id: Unique execution identifier
|
|
58
|
+
event_type: Type of event (e.g., "message_chunk", "tool_started")
|
|
59
|
+
data: Event payload data
|
|
60
|
+
metadata: Optional metadata (organization_id, worker_id, etc.)
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
True if successful, False otherwise
|
|
64
|
+
"""
|
|
65
|
+
pass
|
|
66
|
+
|
|
67
|
+
@abstractmethod
|
|
68
|
+
async def subscribe(
|
|
69
|
+
self, pattern: str, callback: Callable[[Dict[str, Any]], None]
|
|
70
|
+
) -> None:
|
|
71
|
+
"""
|
|
72
|
+
Subscribe to events matching pattern.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
pattern: Pattern to match events (provider-specific format)
|
|
76
|
+
callback: Async callback function to handle events
|
|
77
|
+
"""
|
|
78
|
+
pass
|
|
79
|
+
|
|
80
|
+
@abstractmethod
|
|
81
|
+
async def health_check(self) -> Dict[str, Any]:
|
|
82
|
+
"""
|
|
83
|
+
Check provider health status.
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
Dict with health info: {"healthy": bool, "details": {...}}
|
|
87
|
+
"""
|
|
88
|
+
pass
|
|
89
|
+
|
|
90
|
+
@abstractmethod
|
|
91
|
+
async def shutdown(self) -> None:
|
|
92
|
+
"""Cleanup resources and close connections."""
|
|
93
|
+
pass
|
|
94
|
+
|
|
95
|
+
async def _retry_operation(
|
|
96
|
+
self, operation: Callable, operation_name: str
|
|
97
|
+
) -> Any:
|
|
98
|
+
"""
|
|
99
|
+
Retry an operation with exponential backoff.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
operation: Async operation to retry
|
|
103
|
+
operation_name: Name for logging
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
Result of operation
|
|
107
|
+
|
|
108
|
+
Raises:
|
|
109
|
+
Last exception if all retries fail
|
|
110
|
+
"""
|
|
111
|
+
import asyncio
|
|
112
|
+
|
|
113
|
+
last_exception = None
|
|
114
|
+
for attempt in range(self.config.retry_attempts):
|
|
115
|
+
try:
|
|
116
|
+
return await operation()
|
|
117
|
+
except Exception as e:
|
|
118
|
+
last_exception = e
|
|
119
|
+
if attempt < self.config.retry_attempts - 1:
|
|
120
|
+
backoff = self.config.retry_backoff_seconds * (2**attempt)
|
|
121
|
+
self.logger.warning(
|
|
122
|
+
f"{operation_name}_retry",
|
|
123
|
+
attempt=attempt + 1,
|
|
124
|
+
max_attempts=self.config.retry_attempts,
|
|
125
|
+
backoff_seconds=backoff,
|
|
126
|
+
error=str(e),
|
|
127
|
+
)
|
|
128
|
+
await asyncio.sleep(backoff)
|
|
129
|
+
else:
|
|
130
|
+
self.logger.error(
|
|
131
|
+
f"{operation_name}_failed_all_retries",
|
|
132
|
+
attempts=self.config.retry_attempts,
|
|
133
|
+
error=str(e),
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
raise last_exception
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
"""Event bus manager for orchestrating multiple providers."""
|
|
2
|
+
|
|
3
|
+
from typing import Dict, Any, Optional
|
|
4
|
+
from pydantic import BaseModel, Field
|
|
5
|
+
import asyncio
|
|
6
|
+
import structlog
|
|
7
|
+
|
|
8
|
+
from control_plane_api.app.lib.event_bus.base import EventBusProvider
|
|
9
|
+
|
|
10
|
+
logger = structlog.get_logger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class EventBusManagerConfig(BaseModel):
|
|
14
|
+
"""Configuration for event bus manager with all providers."""
|
|
15
|
+
|
|
16
|
+
# Import provider configs (will be defined in provider modules)
|
|
17
|
+
http: Optional[Any] = Field(default=None, description="HTTP provider config")
|
|
18
|
+
websocket: Optional[Any] = Field(
|
|
19
|
+
default=None, description="WebSocket provider config"
|
|
20
|
+
)
|
|
21
|
+
redis: Optional[Any] = Field(default=None, description="Redis provider config")
|
|
22
|
+
nats: Optional[Any] = Field(default=None, description="NATS provider config")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class EventBusManager:
|
|
26
|
+
"""
|
|
27
|
+
Manages multiple event bus providers with fallback support.
|
|
28
|
+
Publishes to all enabled providers simultaneously.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def __init__(self, config: EventBusManagerConfig):
|
|
32
|
+
self.config = config
|
|
33
|
+
self.providers: Dict[str, EventBusProvider] = {}
|
|
34
|
+
self.logger = structlog.get_logger(__name__)
|
|
35
|
+
self._initialized = False
|
|
36
|
+
|
|
37
|
+
async def initialize(self) -> None:
|
|
38
|
+
"""Initialize all enabled providers."""
|
|
39
|
+
if self._initialized:
|
|
40
|
+
self.logger.warning("event_bus_manager_already_initialized")
|
|
41
|
+
return
|
|
42
|
+
|
|
43
|
+
initialized_providers = []
|
|
44
|
+
|
|
45
|
+
# HTTP Provider
|
|
46
|
+
if self.config.http and self.config.http.enabled:
|
|
47
|
+
try:
|
|
48
|
+
from control_plane_api.app.lib.event_bus.providers.http_provider import (
|
|
49
|
+
HTTPEventProvider,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
self.providers["http"] = HTTPEventProvider(self.config.http)
|
|
53
|
+
await self.providers["http"].initialize()
|
|
54
|
+
initialized_providers.append("http")
|
|
55
|
+
self.logger.info("http_provider_initialized")
|
|
56
|
+
except Exception as e:
|
|
57
|
+
self.logger.error("http_provider_init_failed", error=str(e))
|
|
58
|
+
# HTTP is critical, re-raise
|
|
59
|
+
raise
|
|
60
|
+
|
|
61
|
+
# WebSocket Provider
|
|
62
|
+
if self.config.websocket and self.config.websocket.enabled:
|
|
63
|
+
try:
|
|
64
|
+
from control_plane_api.app.lib.event_bus.providers.websocket_provider import (
|
|
65
|
+
WebSocketEventProvider,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
self.providers["websocket"] = WebSocketEventProvider(
|
|
69
|
+
self.config.websocket
|
|
70
|
+
)
|
|
71
|
+
await self.providers["websocket"].initialize()
|
|
72
|
+
initialized_providers.append("websocket")
|
|
73
|
+
self.logger.info("websocket_provider_initialized")
|
|
74
|
+
except Exception as e:
|
|
75
|
+
self.logger.warning(
|
|
76
|
+
"websocket_provider_init_failed_continuing", error=str(e)
|
|
77
|
+
)
|
|
78
|
+
# WebSocket is optional, continue without it
|
|
79
|
+
|
|
80
|
+
# Redis Provider
|
|
81
|
+
if self.config.redis and self.config.redis.enabled:
|
|
82
|
+
try:
|
|
83
|
+
from control_plane_api.app.lib.event_bus.providers.redis_provider import (
|
|
84
|
+
RedisEventProvider,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
self.providers["redis"] = RedisEventProvider(self.config.redis)
|
|
88
|
+
await self.providers["redis"].initialize()
|
|
89
|
+
initialized_providers.append("redis")
|
|
90
|
+
self.logger.info("redis_provider_initialized")
|
|
91
|
+
except Exception as e:
|
|
92
|
+
self.logger.warning(
|
|
93
|
+
"redis_provider_init_failed_continuing", error=str(e)
|
|
94
|
+
)
|
|
95
|
+
# Redis is optional, continue without it
|
|
96
|
+
|
|
97
|
+
# NATS Provider (Optional)
|
|
98
|
+
if self.config.nats and self.config.nats.enabled:
|
|
99
|
+
try:
|
|
100
|
+
from control_plane_api.app.lib.event_bus.providers.nats_provider import (
|
|
101
|
+
NATSEventProvider,
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
self.providers["nats"] = NATSEventProvider(self.config.nats)
|
|
105
|
+
await self.providers["nats"].initialize()
|
|
106
|
+
initialized_providers.append("nats")
|
|
107
|
+
self.logger.info("nats_provider_initialized")
|
|
108
|
+
except Exception as e:
|
|
109
|
+
self.logger.warning(
|
|
110
|
+
"nats_provider_init_failed_optional", error=str(e)
|
|
111
|
+
)
|
|
112
|
+
# NATS is optional, continue without it
|
|
113
|
+
|
|
114
|
+
if not self.providers:
|
|
115
|
+
raise RuntimeError("No event bus providers initialized")
|
|
116
|
+
|
|
117
|
+
self._initialized = True
|
|
118
|
+
self.logger.info(
|
|
119
|
+
"event_bus_manager_initialized",
|
|
120
|
+
providers=initialized_providers,
|
|
121
|
+
count=len(self.providers),
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
async def publish_event(
|
|
125
|
+
self,
|
|
126
|
+
execution_id: str,
|
|
127
|
+
event_type: str,
|
|
128
|
+
data: Dict[str, Any],
|
|
129
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
130
|
+
) -> Dict[str, bool]:
|
|
131
|
+
"""
|
|
132
|
+
Publish event to all enabled providers in parallel.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
execution_id: Unique execution identifier
|
|
136
|
+
event_type: Type of event
|
|
137
|
+
data: Event payload
|
|
138
|
+
metadata: Optional metadata
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
Dict mapping provider name to success status
|
|
142
|
+
"""
|
|
143
|
+
if not self._initialized:
|
|
144
|
+
self.logger.error("event_bus_manager_not_initialized")
|
|
145
|
+
return {}
|
|
146
|
+
|
|
147
|
+
# Log start of publishing
|
|
148
|
+
self.logger.info(
|
|
149
|
+
"event_bus_publishing_start",
|
|
150
|
+
execution_id=execution_id,
|
|
151
|
+
event_type=event_type,
|
|
152
|
+
providers=list(self.providers.keys()),
|
|
153
|
+
provider_count=len(self.providers)
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
# PERFORMANCE OPTIMIZATION: Prioritize WebSocket for instant delivery
|
|
157
|
+
# Other providers (Redis, HTTP) run in background without blocking
|
|
158
|
+
websocket_task = None
|
|
159
|
+
websocket_name = None
|
|
160
|
+
background_tasks = []
|
|
161
|
+
background_names = []
|
|
162
|
+
|
|
163
|
+
for name, provider in self.providers.items():
|
|
164
|
+
task = asyncio.create_task(
|
|
165
|
+
provider.publish_event(execution_id, event_type, data, metadata)
|
|
166
|
+
)
|
|
167
|
+
if name == "websocket":
|
|
168
|
+
websocket_task = task
|
|
169
|
+
websocket_name = name
|
|
170
|
+
else:
|
|
171
|
+
background_tasks.append(task)
|
|
172
|
+
background_names.append(name)
|
|
173
|
+
|
|
174
|
+
results = {}
|
|
175
|
+
|
|
176
|
+
# Wait only for WebSocket (real-time delivery to client)
|
|
177
|
+
if websocket_task:
|
|
178
|
+
try:
|
|
179
|
+
ws_result = await websocket_task
|
|
180
|
+
results[websocket_name] = ws_result
|
|
181
|
+
if ws_result:
|
|
182
|
+
self.logger.debug(
|
|
183
|
+
"websocket_publish_success",
|
|
184
|
+
execution_id=execution_id,
|
|
185
|
+
event_type=event_type
|
|
186
|
+
)
|
|
187
|
+
else:
|
|
188
|
+
self.logger.warning(
|
|
189
|
+
"websocket_publish_failed",
|
|
190
|
+
execution_id=execution_id,
|
|
191
|
+
event_type=event_type
|
|
192
|
+
)
|
|
193
|
+
except Exception as e:
|
|
194
|
+
self.logger.error(
|
|
195
|
+
"websocket_publish_exception",
|
|
196
|
+
provider=websocket_name,
|
|
197
|
+
error=str(e),
|
|
198
|
+
execution_id=execution_id,
|
|
199
|
+
event_type=event_type,
|
|
200
|
+
)
|
|
201
|
+
results[websocket_name] = False
|
|
202
|
+
|
|
203
|
+
# Background providers - don't await, let them complete asynchronously
|
|
204
|
+
# Use asyncio.gather with return_exceptions to prevent unhandled exception warnings
|
|
205
|
+
if background_tasks:
|
|
206
|
+
async def process_background_providers():
|
|
207
|
+
try:
|
|
208
|
+
bg_results = await asyncio.gather(*background_tasks, return_exceptions=True)
|
|
209
|
+
for name, result in zip(background_names, bg_results):
|
|
210
|
+
if isinstance(result, Exception):
|
|
211
|
+
self.logger.warning(
|
|
212
|
+
"background_provider_publish_exception",
|
|
213
|
+
provider=name,
|
|
214
|
+
error=str(result),
|
|
215
|
+
execution_id=execution_id,
|
|
216
|
+
event_type=event_type,
|
|
217
|
+
)
|
|
218
|
+
elif not result:
|
|
219
|
+
self.logger.warning(
|
|
220
|
+
"background_provider_publish_failed",
|
|
221
|
+
provider=name,
|
|
222
|
+
execution_id=execution_id,
|
|
223
|
+
event_type=event_type
|
|
224
|
+
)
|
|
225
|
+
except Exception as e:
|
|
226
|
+
self.logger.error(
|
|
227
|
+
"background_providers_gather_failed",
|
|
228
|
+
error=str(e),
|
|
229
|
+
execution_id=execution_id,
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
# Fire and forget - schedule but don't await
|
|
233
|
+
asyncio.create_task(process_background_providers())
|
|
234
|
+
|
|
235
|
+
# Mark background providers as pending (we won't wait for results)
|
|
236
|
+
for name in background_names:
|
|
237
|
+
results[name] = True # Optimistically assume success
|
|
238
|
+
|
|
239
|
+
# Log overall status
|
|
240
|
+
success_count = sum(1 for success in results.values() if success)
|
|
241
|
+
if success_count == 0:
|
|
242
|
+
self.logger.error(
|
|
243
|
+
"event_bus_all_providers_failed",
|
|
244
|
+
execution_id=execution_id,
|
|
245
|
+
event_type=event_type,
|
|
246
|
+
results=results,
|
|
247
|
+
)
|
|
248
|
+
elif success_count < len(results):
|
|
249
|
+
self.logger.warning(
|
|
250
|
+
"event_bus_partial_success",
|
|
251
|
+
execution_id=execution_id,
|
|
252
|
+
event_type=event_type,
|
|
253
|
+
success_count=success_count,
|
|
254
|
+
total_count=len(results),
|
|
255
|
+
results=results,
|
|
256
|
+
)
|
|
257
|
+
else:
|
|
258
|
+
self.logger.info(
|
|
259
|
+
"event_bus_all_providers_success",
|
|
260
|
+
execution_id=execution_id,
|
|
261
|
+
event_type=event_type,
|
|
262
|
+
provider_count=len(results),
|
|
263
|
+
providers=list(results.keys())
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
return results
|
|
267
|
+
|
|
268
|
+
async def health_check(self) -> Dict[str, Any]:
|
|
269
|
+
"""
|
|
270
|
+
Check health of all providers.
|
|
271
|
+
|
|
272
|
+
Returns:
|
|
273
|
+
Dict with health status for each provider
|
|
274
|
+
"""
|
|
275
|
+
health = {}
|
|
276
|
+
|
|
277
|
+
for name, provider in self.providers.items():
|
|
278
|
+
try:
|
|
279
|
+
provider_health = await provider.health_check()
|
|
280
|
+
health[name] = provider_health
|
|
281
|
+
except Exception as e:
|
|
282
|
+
self.logger.error(
|
|
283
|
+
"provider_health_check_failed", provider=name, error=str(e)
|
|
284
|
+
)
|
|
285
|
+
health[name] = {"healthy": False, "error": str(e)}
|
|
286
|
+
|
|
287
|
+
# Overall status
|
|
288
|
+
healthy_count = sum(
|
|
289
|
+
1 for h in health.values() if h.get("healthy", False)
|
|
290
|
+
)
|
|
291
|
+
health["_overall"] = {
|
|
292
|
+
"healthy": healthy_count > 0, # At least one provider healthy
|
|
293
|
+
"total_providers": len(self.providers),
|
|
294
|
+
"healthy_providers": healthy_count,
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return health
|
|
298
|
+
|
|
299
|
+
async def shutdown(self) -> None:
|
|
300
|
+
"""Shutdown all providers gracefully."""
|
|
301
|
+
if not self._initialized:
|
|
302
|
+
return
|
|
303
|
+
|
|
304
|
+
self.logger.info("shutting_down_event_bus_manager")
|
|
305
|
+
|
|
306
|
+
# Shutdown all providers in parallel
|
|
307
|
+
tasks = []
|
|
308
|
+
for name, provider in self.providers.items():
|
|
309
|
+
tasks.append(self._shutdown_provider(name, provider))
|
|
310
|
+
|
|
311
|
+
await asyncio.gather(*tasks, return_exceptions=True)
|
|
312
|
+
|
|
313
|
+
self.providers.clear()
|
|
314
|
+
self._initialized = False
|
|
315
|
+
self.logger.info("event_bus_manager_shutdown_complete")
|
|
316
|
+
|
|
317
|
+
async def _shutdown_provider(
|
|
318
|
+
self, name: str, provider: EventBusProvider
|
|
319
|
+
) -> None:
|
|
320
|
+
"""Shutdown a single provider with error handling."""
|
|
321
|
+
try:
|
|
322
|
+
await provider.shutdown()
|
|
323
|
+
self.logger.info("provider_shutdown", provider=name)
|
|
324
|
+
except Exception as e:
|
|
325
|
+
self.logger.error(
|
|
326
|
+
"provider_shutdown_failed", provider=name, error=str(e)
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
def is_initialized(self) -> bool:
|
|
330
|
+
"""Check if manager is initialized."""
|
|
331
|
+
return self._initialized
|
|
332
|
+
|
|
333
|
+
def get_provider_names(self) -> list[str]:
|
|
334
|
+
"""Get list of initialized provider names."""
|
|
335
|
+
return list(self.providers.keys())
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"""HTTP-based event bus provider."""
|
|
2
|
+
|
|
3
|
+
from typing import Dict, Any, Optional, Callable
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
import httpx
|
|
6
|
+
import structlog
|
|
7
|
+
from pydantic import Field
|
|
8
|
+
|
|
9
|
+
from control_plane_api.app.lib.event_bus.base import EventBusProvider, EventBusConfig
|
|
10
|
+
|
|
11
|
+
logger = structlog.get_logger(__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class HTTPConfig(EventBusConfig):
|
|
15
|
+
"""Configuration for HTTP event provider."""
|
|
16
|
+
|
|
17
|
+
base_url: str = Field(..., description="Base URL for control plane API")
|
|
18
|
+
batching_enabled: bool = Field(
|
|
19
|
+
default=True, description="Enable event batching"
|
|
20
|
+
)
|
|
21
|
+
batch_window_ms: int = Field(
|
|
22
|
+
default=100, description="Batch window in milliseconds"
|
|
23
|
+
)
|
|
24
|
+
batch_max_size_bytes: int = Field(
|
|
25
|
+
default=1000, description="Maximum batch size in bytes"
|
|
26
|
+
)
|
|
27
|
+
api_key: Optional[str] = Field(
|
|
28
|
+
default=None, description="API key for authentication"
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class HTTPEventProvider(EventBusProvider):
|
|
33
|
+
"""HTTP-based event publishing (default provider)."""
|
|
34
|
+
|
|
35
|
+
def __init__(self, config: HTTPConfig):
|
|
36
|
+
super().__init__(config)
|
|
37
|
+
self.config: HTTPConfig = config
|
|
38
|
+
self.base_url = config.base_url.rstrip("/")
|
|
39
|
+
self.client: Optional[httpx.AsyncClient] = None
|
|
40
|
+
|
|
41
|
+
async def initialize(self) -> None:
|
|
42
|
+
"""Initialize HTTP client."""
|
|
43
|
+
self.client = httpx.AsyncClient(
|
|
44
|
+
timeout=self.config.timeout_seconds,
|
|
45
|
+
follow_redirects=False, # Fail fast on redirects
|
|
46
|
+
)
|
|
47
|
+
self.logger.info(
|
|
48
|
+
"http_provider_initialized",
|
|
49
|
+
base_url=self.base_url,
|
|
50
|
+
batching_enabled=self.config.batching_enabled,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
async def publish_event(
|
|
54
|
+
self,
|
|
55
|
+
execution_id: str,
|
|
56
|
+
event_type: str,
|
|
57
|
+
data: Dict[str, Any],
|
|
58
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
59
|
+
) -> bool:
|
|
60
|
+
"""
|
|
61
|
+
Publish event via HTTP POST.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
execution_id: Execution ID
|
|
65
|
+
event_type: Event type
|
|
66
|
+
data: Event data
|
|
67
|
+
metadata: Optional metadata
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
True if successful (202 Accepted)
|
|
71
|
+
"""
|
|
72
|
+
if not self.client:
|
|
73
|
+
self.logger.error("http_client_not_initialized")
|
|
74
|
+
return False
|
|
75
|
+
|
|
76
|
+
url = f"{self.base_url}/api/v1/executions/{execution_id}/events"
|
|
77
|
+
|
|
78
|
+
payload = {
|
|
79
|
+
"event_type": event_type,
|
|
80
|
+
"data": data,
|
|
81
|
+
"timestamp": datetime.utcnow().isoformat(),
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
headers = {}
|
|
85
|
+
if self.config.api_key:
|
|
86
|
+
headers["Authorization"] = f"Bearer {self.config.api_key}"
|
|
87
|
+
|
|
88
|
+
try:
|
|
89
|
+
response = await self.client.post(url, json=payload, headers=headers)
|
|
90
|
+
|
|
91
|
+
if response.status_code == 202:
|
|
92
|
+
self.logger.debug(
|
|
93
|
+
"http_event_published",
|
|
94
|
+
execution_id=execution_id,
|
|
95
|
+
event_type=event_type,
|
|
96
|
+
)
|
|
97
|
+
return True
|
|
98
|
+
else:
|
|
99
|
+
self.logger.warning(
|
|
100
|
+
"http_event_publish_failed",
|
|
101
|
+
execution_id=execution_id,
|
|
102
|
+
event_type=event_type,
|
|
103
|
+
status_code=response.status_code,
|
|
104
|
+
response=response.text[:200],
|
|
105
|
+
)
|
|
106
|
+
return False
|
|
107
|
+
|
|
108
|
+
except httpx.TimeoutException:
|
|
109
|
+
self.logger.error(
|
|
110
|
+
"http_event_publish_timeout",
|
|
111
|
+
execution_id=execution_id,
|
|
112
|
+
event_type=event_type,
|
|
113
|
+
timeout=self.config.timeout_seconds,
|
|
114
|
+
)
|
|
115
|
+
return False
|
|
116
|
+
except Exception as e:
|
|
117
|
+
self.logger.error(
|
|
118
|
+
"http_event_publish_exception",
|
|
119
|
+
execution_id=execution_id,
|
|
120
|
+
event_type=event_type,
|
|
121
|
+
error=str(e),
|
|
122
|
+
)
|
|
123
|
+
return False
|
|
124
|
+
|
|
125
|
+
async def subscribe(
|
|
126
|
+
self, pattern: str, callback: Callable[[Dict[str, Any]], None]
|
|
127
|
+
) -> None:
|
|
128
|
+
"""HTTP provider does not support subscriptions."""
|
|
129
|
+
self.logger.warning(
|
|
130
|
+
"http_provider_subscribe_not_supported", pattern=pattern
|
|
131
|
+
)
|
|
132
|
+
raise NotImplementedError(
|
|
133
|
+
"HTTP provider does not support subscriptions"
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
async def health_check(self) -> Dict[str, Any]:
|
|
137
|
+
"""Check HTTP client health."""
|
|
138
|
+
if not self.client:
|
|
139
|
+
return {
|
|
140
|
+
"healthy": False,
|
|
141
|
+
"error": "client_not_initialized",
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
try:
|
|
145
|
+
# Try to make a simple request to health endpoint
|
|
146
|
+
health_url = f"{self.base_url}/api/health"
|
|
147
|
+
response = await self.client.get(health_url, timeout=5.0)
|
|
148
|
+
|
|
149
|
+
return {
|
|
150
|
+
"healthy": response.status_code == 200,
|
|
151
|
+
"base_url": self.base_url,
|
|
152
|
+
"status_code": response.status_code,
|
|
153
|
+
}
|
|
154
|
+
except Exception as e:
|
|
155
|
+
return {
|
|
156
|
+
"healthy": False,
|
|
157
|
+
"base_url": self.base_url,
|
|
158
|
+
"error": str(e),
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async def shutdown(self) -> None:
|
|
162
|
+
"""Close HTTP client."""
|
|
163
|
+
if self.client:
|
|
164
|
+
await self.client.aclose()
|
|
165
|
+
self.client = None
|
|
166
|
+
self.logger.info("http_provider_shutdown")
|