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,65 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Environment detection utilities for worker deployment context.
|
|
3
|
+
|
|
4
|
+
Provides functions to detect serverless environments and determine
|
|
5
|
+
whether WebSocket connections should be used.
|
|
6
|
+
|
|
7
|
+
Serverless environments detected:
|
|
8
|
+
- AWS Lambda
|
|
9
|
+
- Vercel Functions
|
|
10
|
+
- Google Cloud Functions
|
|
11
|
+
- Azure Functions
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from typing import Literal
|
|
15
|
+
import os
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def detect_environment() -> Literal["standard", "serverless"]:
|
|
19
|
+
"""
|
|
20
|
+
Detect if running in a serverless environment.
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
"serverless" if running in a serverless environment (Lambda, Vercel, GCF, Azure Functions)
|
|
24
|
+
"standard" for traditional server environments
|
|
25
|
+
"""
|
|
26
|
+
# AWS Lambda
|
|
27
|
+
if os.environ.get("AWS_LAMBDA_FUNCTION_NAME"):
|
|
28
|
+
return "serverless"
|
|
29
|
+
|
|
30
|
+
# Vercel Functions
|
|
31
|
+
if os.environ.get("VERCEL"):
|
|
32
|
+
return "serverless"
|
|
33
|
+
|
|
34
|
+
# Google Cloud Functions
|
|
35
|
+
if os.environ.get("FUNCTION_TARGET") or os.environ.get("K_SERVICE"):
|
|
36
|
+
return "serverless"
|
|
37
|
+
|
|
38
|
+
# Azure Functions
|
|
39
|
+
if os.environ.get("AZURE_FUNCTIONS_ENVIRONMENT"):
|
|
40
|
+
return "serverless"
|
|
41
|
+
|
|
42
|
+
return "standard"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def should_use_websocket() -> bool:
|
|
46
|
+
"""
|
|
47
|
+
Determine if WebSocket should be used based on environment.
|
|
48
|
+
|
|
49
|
+
WebSocket is disabled in serverless environments due to limitations:
|
|
50
|
+
- Short-lived execution contexts
|
|
51
|
+
- No persistent connections
|
|
52
|
+
- Function timeout constraints
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
True if WebSocket should be used, False otherwise
|
|
56
|
+
"""
|
|
57
|
+
# Never use WebSocket in serverless
|
|
58
|
+
if detect_environment() == "serverless":
|
|
59
|
+
return False
|
|
60
|
+
|
|
61
|
+
# Check explicit disable
|
|
62
|
+
if os.environ.get("WEBSOCKET_ENABLED", "true").lower() == "false":
|
|
63
|
+
return False
|
|
64
|
+
|
|
65
|
+
return True
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Error event publisher for standardized error handling.
|
|
3
|
+
|
|
4
|
+
This module provides utilities for publishing error events to the Control Plane
|
|
5
|
+
with consistent structure, user-friendly messages, and recovery suggestions.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import structlog
|
|
9
|
+
import traceback
|
|
10
|
+
from typing import Optional, List, Dict, Any
|
|
11
|
+
from datetime import datetime, timezone
|
|
12
|
+
|
|
13
|
+
from control_plane_api.worker.models.error_events import (
|
|
14
|
+
ErrorEvent, ErrorSeverity, ErrorCategory, ErrorContext,
|
|
15
|
+
ErrorDetails, ErrorRecovery
|
|
16
|
+
)
|
|
17
|
+
from control_plane_api.worker.control_plane_client import ControlPlaneClient
|
|
18
|
+
|
|
19
|
+
logger = structlog.get_logger()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ErrorEventPublisher:
|
|
23
|
+
"""Helper class for publishing standardized error events."""
|
|
24
|
+
|
|
25
|
+
def __init__(self, control_plane: ControlPlaneClient):
|
|
26
|
+
self.control_plane = control_plane
|
|
27
|
+
|
|
28
|
+
async def publish_error(
|
|
29
|
+
self,
|
|
30
|
+
execution_id: str,
|
|
31
|
+
exception: Exception,
|
|
32
|
+
severity: ErrorSeverity,
|
|
33
|
+
category: ErrorCategory,
|
|
34
|
+
stage: str,
|
|
35
|
+
component: str,
|
|
36
|
+
operation: Optional[str] = None,
|
|
37
|
+
recovery_actions: Optional[List[str]] = None,
|
|
38
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
39
|
+
include_stack_trace: bool = True,
|
|
40
|
+
) -> bool:
|
|
41
|
+
"""
|
|
42
|
+
Publish a standardized error event to Control Plane.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
execution_id: Execution ID
|
|
46
|
+
exception: The exception that occurred
|
|
47
|
+
severity: Error severity level
|
|
48
|
+
category: Error category
|
|
49
|
+
stage: Execution stage where error occurred
|
|
50
|
+
component: Component that generated error
|
|
51
|
+
operation: Specific operation that failed
|
|
52
|
+
recovery_actions: List of user-actionable recovery steps
|
|
53
|
+
metadata: Additional metadata
|
|
54
|
+
include_stack_trace: Whether to include stack trace
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
True if published successfully
|
|
58
|
+
"""
|
|
59
|
+
try:
|
|
60
|
+
# Extract stack trace
|
|
61
|
+
stack_trace = None
|
|
62
|
+
code_location = None
|
|
63
|
+
if include_stack_trace:
|
|
64
|
+
tb_lines = traceback.format_exception(
|
|
65
|
+
type(exception), exception, exception.__traceback__
|
|
66
|
+
)
|
|
67
|
+
stack_trace = ''.join(tb_lines)[-2000:] # Last 2000 chars
|
|
68
|
+
|
|
69
|
+
# Extract code location from traceback
|
|
70
|
+
tb = exception.__traceback__
|
|
71
|
+
if tb:
|
|
72
|
+
frame = traceback.extract_tb(tb)[-1]
|
|
73
|
+
code_location = f"{frame.filename}:{frame.lineno}"
|
|
74
|
+
|
|
75
|
+
# Create user-friendly error message
|
|
76
|
+
user_message = self._create_user_friendly_message(
|
|
77
|
+
exception, category, component
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
# Determine if retryable
|
|
81
|
+
is_retryable = self._is_retryable_error(exception, category)
|
|
82
|
+
|
|
83
|
+
# Get default recovery actions if none provided
|
|
84
|
+
if not recovery_actions:
|
|
85
|
+
recovery_actions = self._get_default_recovery_actions(
|
|
86
|
+
category, is_retryable
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
# Get documentation URL
|
|
90
|
+
doc_url = self._get_documentation_url(category)
|
|
91
|
+
|
|
92
|
+
# Build error event
|
|
93
|
+
error_event = ErrorEvent(
|
|
94
|
+
severity=severity,
|
|
95
|
+
category=category,
|
|
96
|
+
context=ErrorContext(
|
|
97
|
+
execution_id=execution_id,
|
|
98
|
+
timestamp=datetime.now(timezone.utc).isoformat(),
|
|
99
|
+
stage=stage,
|
|
100
|
+
component=component,
|
|
101
|
+
operation=operation,
|
|
102
|
+
),
|
|
103
|
+
details=ErrorDetails(
|
|
104
|
+
error_type=type(exception).__name__,
|
|
105
|
+
error_message=user_message,
|
|
106
|
+
technical_message=str(exception),
|
|
107
|
+
stack_trace=stack_trace,
|
|
108
|
+
code_location=code_location,
|
|
109
|
+
),
|
|
110
|
+
recovery=ErrorRecovery(
|
|
111
|
+
is_retryable=is_retryable,
|
|
112
|
+
retry_suggested=is_retryable and severity != ErrorSeverity.CRITICAL,
|
|
113
|
+
recovery_actions=recovery_actions,
|
|
114
|
+
documentation_url=doc_url,
|
|
115
|
+
),
|
|
116
|
+
metadata=metadata,
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
# Publish error event
|
|
120
|
+
success = await self.control_plane.publish_event_async(
|
|
121
|
+
execution_id=execution_id,
|
|
122
|
+
event_type="error",
|
|
123
|
+
data=error_event.to_event_data(),
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
if success:
|
|
127
|
+
logger.info(
|
|
128
|
+
"error_event_published",
|
|
129
|
+
execution_id=execution_id[:8] if len(execution_id) >= 8 else execution_id,
|
|
130
|
+
severity=severity.value,
|
|
131
|
+
category=category.value,
|
|
132
|
+
error_type=type(exception).__name__,
|
|
133
|
+
)
|
|
134
|
+
else:
|
|
135
|
+
logger.warning(
|
|
136
|
+
"error_event_publish_failed",
|
|
137
|
+
execution_id=execution_id[:8] if len(execution_id) >= 8 else execution_id,
|
|
138
|
+
error=str(exception)[:200],
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
return success
|
|
142
|
+
|
|
143
|
+
except Exception as publish_error:
|
|
144
|
+
# Never let error publishing break the main execution
|
|
145
|
+
logger.error(
|
|
146
|
+
"failed_to_publish_error_event",
|
|
147
|
+
execution_id=execution_id[:8] if len(execution_id) >= 8 else execution_id,
|
|
148
|
+
error=str(publish_error),
|
|
149
|
+
)
|
|
150
|
+
return False
|
|
151
|
+
|
|
152
|
+
def _create_user_friendly_message(
|
|
153
|
+
self, exception: Exception, category: ErrorCategory, component: str
|
|
154
|
+
) -> str:
|
|
155
|
+
"""Create user-friendly error message."""
|
|
156
|
+
|
|
157
|
+
messages = {
|
|
158
|
+
ErrorCategory.RUNTIME_INIT: f"Failed to initialize {component}",
|
|
159
|
+
ErrorCategory.SKILL_LOADING: f"Could not load skill: {str(exception)[:100]}",
|
|
160
|
+
ErrorCategory.MCP_CONNECTION: f"Failed to connect to MCP server",
|
|
161
|
+
ErrorCategory.TOOL_EXECUTION: f"Tool execution failed: {str(exception)[:100]}",
|
|
162
|
+
ErrorCategory.TIMEOUT: f"Operation timed out in {component}",
|
|
163
|
+
ErrorCategory.MODEL_ERROR: f"LLM model error: {str(exception)[:100]}",
|
|
164
|
+
ErrorCategory.AUTHENTICATION: "Authentication failed - please check credentials",
|
|
165
|
+
ErrorCategory.NETWORK: "Network connection error",
|
|
166
|
+
ErrorCategory.VALIDATION: f"Validation error: {str(exception)[:100]}",
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return messages.get(category, str(exception)[:200])
|
|
170
|
+
|
|
171
|
+
def _is_retryable_error(
|
|
172
|
+
self, exception: Exception, category: ErrorCategory
|
|
173
|
+
) -> bool:
|
|
174
|
+
"""Determine if error is retryable."""
|
|
175
|
+
|
|
176
|
+
# Network and timeout errors are generally retryable
|
|
177
|
+
if category in [ErrorCategory.NETWORK, ErrorCategory.TIMEOUT]:
|
|
178
|
+
return True
|
|
179
|
+
|
|
180
|
+
# Authentication errors not retryable without credential changes
|
|
181
|
+
if category == ErrorCategory.AUTHENTICATION:
|
|
182
|
+
return False
|
|
183
|
+
|
|
184
|
+
# Validation errors not retryable without input changes
|
|
185
|
+
if category == ErrorCategory.VALIDATION:
|
|
186
|
+
return False
|
|
187
|
+
|
|
188
|
+
# Check exception type
|
|
189
|
+
retryable_types = [
|
|
190
|
+
"TimeoutError",
|
|
191
|
+
"ConnectionError",
|
|
192
|
+
"HTTPError",
|
|
193
|
+
"TemporaryFailure",
|
|
194
|
+
]
|
|
195
|
+
|
|
196
|
+
return type(exception).__name__ in retryable_types
|
|
197
|
+
|
|
198
|
+
def _get_default_recovery_actions(
|
|
199
|
+
self, category: ErrorCategory, is_retryable: bool
|
|
200
|
+
) -> List[str]:
|
|
201
|
+
"""Get default recovery actions for error category."""
|
|
202
|
+
|
|
203
|
+
actions = {
|
|
204
|
+
ErrorCategory.RUNTIME_INIT: [
|
|
205
|
+
"Check that all required dependencies are installed",
|
|
206
|
+
"Verify runtime configuration is correct",
|
|
207
|
+
"Check worker logs for detailed error information",
|
|
208
|
+
],
|
|
209
|
+
ErrorCategory.SKILL_LOADING: [
|
|
210
|
+
"Verify skill configuration is correct",
|
|
211
|
+
"Check that skill dependencies are available",
|
|
212
|
+
"Review skill permissions and access",
|
|
213
|
+
],
|
|
214
|
+
ErrorCategory.MCP_CONNECTION: [
|
|
215
|
+
"Verify MCP server is running and accessible",
|
|
216
|
+
"Check network connectivity",
|
|
217
|
+
"Review MCP server configuration and credentials",
|
|
218
|
+
],
|
|
219
|
+
ErrorCategory.TIMEOUT: [
|
|
220
|
+
"Consider increasing timeout settings",
|
|
221
|
+
"Check system resources and load",
|
|
222
|
+
"Simplify the operation if possible",
|
|
223
|
+
],
|
|
224
|
+
ErrorCategory.MODEL_ERROR: [
|
|
225
|
+
"Verify API credentials are valid",
|
|
226
|
+
"Check API rate limits and quotas",
|
|
227
|
+
"Try a different model if available",
|
|
228
|
+
],
|
|
229
|
+
ErrorCategory.AUTHENTICATION: [
|
|
230
|
+
"Verify API credentials are correct",
|
|
231
|
+
"Check that credentials have required permissions",
|
|
232
|
+
"Regenerate credentials if expired",
|
|
233
|
+
],
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
default_actions = actions.get(category, [
|
|
237
|
+
"Review error details above",
|
|
238
|
+
"Check system logs for more information",
|
|
239
|
+
"Contact support if issue persists",
|
|
240
|
+
])
|
|
241
|
+
|
|
242
|
+
if is_retryable:
|
|
243
|
+
default_actions.insert(0, "Retry the operation")
|
|
244
|
+
|
|
245
|
+
return default_actions
|
|
246
|
+
|
|
247
|
+
def _get_documentation_url(self, category: ErrorCategory) -> Optional[str]:
|
|
248
|
+
"""Get documentation URL for error category."""
|
|
249
|
+
|
|
250
|
+
base_url = "https://docs.kubiya.ai/troubleshooting"
|
|
251
|
+
|
|
252
|
+
urls = {
|
|
253
|
+
ErrorCategory.RUNTIME_INIT: f"{base_url}/runtime-initialization",
|
|
254
|
+
ErrorCategory.SKILL_LOADING: f"{base_url}/skill-configuration",
|
|
255
|
+
ErrorCategory.MCP_CONNECTION: f"{base_url}/mcp-servers",
|
|
256
|
+
ErrorCategory.MODEL_ERROR: f"{base_url}/llm-configuration",
|
|
257
|
+
ErrorCategory.AUTHENTICATION: f"{base_url}/authentication",
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return urls.get(category, base_url)
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Event Batcher for efficient HTTP event publishing.
|
|
3
|
+
|
|
4
|
+
Batches multiple events into a single HTTP request to reduce overhead,
|
|
5
|
+
especially important for serverless deployments like Vercel where you
|
|
6
|
+
pay per request.
|
|
7
|
+
|
|
8
|
+
Features:
|
|
9
|
+
- Time-based flushing (default 50ms)
|
|
10
|
+
- Size-based flushing (default 10 events)
|
|
11
|
+
- Priority-based immediate flushing for critical events
|
|
12
|
+
- Automatic background flushing task
|
|
13
|
+
- Graceful shutdown with pending event flush
|
|
14
|
+
|
|
15
|
+
Usage:
|
|
16
|
+
batcher = EventBatcher(
|
|
17
|
+
flush_callback=send_batch_to_server,
|
|
18
|
+
max_batch_size=10,
|
|
19
|
+
max_batch_time_ms=50
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
await batcher.start()
|
|
23
|
+
await batcher.add_event(event_data, priority="normal")
|
|
24
|
+
await batcher.add_event(critical_event, priority="critical") # Flushes immediately
|
|
25
|
+
await batcher.stop()
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
import asyncio
|
|
29
|
+
import time
|
|
30
|
+
from typing import Dict, Any, Callable, Literal, List, Optional
|
|
31
|
+
import structlog
|
|
32
|
+
|
|
33
|
+
logger = structlog.get_logger()
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class EventBatcher:
|
|
37
|
+
"""
|
|
38
|
+
Batches events for efficient HTTP publishing.
|
|
39
|
+
|
|
40
|
+
Reduces HTTP request count by batching multiple events together,
|
|
41
|
+
with smart flushing based on time, size, and priority.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
def __init__(
|
|
45
|
+
self,
|
|
46
|
+
flush_callback: Callable[[List[Dict[str, Any]]], Any],
|
|
47
|
+
max_batch_size: int = 10,
|
|
48
|
+
max_batch_time_ms: int = 50,
|
|
49
|
+
enabled: bool = True
|
|
50
|
+
):
|
|
51
|
+
"""
|
|
52
|
+
Initialize event batcher.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
flush_callback: Async callback to send batch (receives list of events)
|
|
56
|
+
max_batch_size: Maximum events per batch before auto-flush
|
|
57
|
+
max_batch_time_ms: Maximum milliseconds to wait before auto-flush
|
|
58
|
+
enabled: Whether batching is enabled (if False, events flush immediately)
|
|
59
|
+
"""
|
|
60
|
+
self.flush_callback = flush_callback
|
|
61
|
+
self.max_batch_size = max_batch_size
|
|
62
|
+
self.max_batch_time_ms = max_batch_time_ms
|
|
63
|
+
self.enabled = enabled
|
|
64
|
+
|
|
65
|
+
# Batch state
|
|
66
|
+
self.batch: List[Dict[str, Any]] = []
|
|
67
|
+
self.batch_lock = asyncio.Lock()
|
|
68
|
+
self.last_flush_time = time.time()
|
|
69
|
+
|
|
70
|
+
# Background flush task
|
|
71
|
+
self._flush_task: Optional[asyncio.Task] = None
|
|
72
|
+
self._running = False
|
|
73
|
+
|
|
74
|
+
# Statistics
|
|
75
|
+
self.stats = {
|
|
76
|
+
"total_events": 0,
|
|
77
|
+
"total_batches": 0,
|
|
78
|
+
"total_flushes": 0,
|
|
79
|
+
"time_based_flushes": 0,
|
|
80
|
+
"size_based_flushes": 0,
|
|
81
|
+
"critical_flushes": 0,
|
|
82
|
+
"events_per_batch_avg": 0.0
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async def start(self):
|
|
86
|
+
"""Start the background flush task."""
|
|
87
|
+
if not self.enabled:
|
|
88
|
+
logger.info("event_batching_disabled")
|
|
89
|
+
return
|
|
90
|
+
|
|
91
|
+
if self._running:
|
|
92
|
+
logger.warning("event_batcher_already_running")
|
|
93
|
+
return
|
|
94
|
+
|
|
95
|
+
self._running = True
|
|
96
|
+
self._flush_task = asyncio.create_task(self._flush_loop())
|
|
97
|
+
logger.info(
|
|
98
|
+
"event_batcher_started",
|
|
99
|
+
max_batch_size=self.max_batch_size,
|
|
100
|
+
max_batch_time_ms=self.max_batch_time_ms
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
async def stop(self):
|
|
104
|
+
"""Stop the batcher and flush any pending events."""
|
|
105
|
+
self._running = False
|
|
106
|
+
|
|
107
|
+
# Cancel background task
|
|
108
|
+
if self._flush_task and not self._flush_task.done():
|
|
109
|
+
self._flush_task.cancel()
|
|
110
|
+
try:
|
|
111
|
+
await self._flush_task
|
|
112
|
+
except asyncio.CancelledError:
|
|
113
|
+
pass
|
|
114
|
+
|
|
115
|
+
# Flush any remaining events
|
|
116
|
+
await self._flush_batch("shutdown")
|
|
117
|
+
|
|
118
|
+
logger.info(
|
|
119
|
+
"event_batcher_stopped",
|
|
120
|
+
stats=self.stats
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
async def add_event(
|
|
124
|
+
self,
|
|
125
|
+
event: Dict[str, Any],
|
|
126
|
+
priority: Literal["normal", "critical"] = "normal"
|
|
127
|
+
) -> bool:
|
|
128
|
+
"""
|
|
129
|
+
Add an event to the batch.
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
event: Event data to batch
|
|
133
|
+
priority: "normal" for regular events, "critical" for immediate flush
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
True if event was added successfully
|
|
137
|
+
"""
|
|
138
|
+
if not self.enabled:
|
|
139
|
+
# If batching disabled, send immediately
|
|
140
|
+
try:
|
|
141
|
+
result = self.flush_callback([event])
|
|
142
|
+
if asyncio.iscoroutine(result):
|
|
143
|
+
await result
|
|
144
|
+
return True
|
|
145
|
+
except Exception as e:
|
|
146
|
+
logger.error("event_send_failed", error=str(e))
|
|
147
|
+
return False
|
|
148
|
+
|
|
149
|
+
async with self.batch_lock:
|
|
150
|
+
self.batch.append(event)
|
|
151
|
+
self.stats["total_events"] += 1
|
|
152
|
+
|
|
153
|
+
# Critical events flush immediately
|
|
154
|
+
if priority == "critical":
|
|
155
|
+
await self._flush_batch("critical")
|
|
156
|
+
return True
|
|
157
|
+
|
|
158
|
+
# Size-based flush
|
|
159
|
+
if len(self.batch) >= self.max_batch_size:
|
|
160
|
+
await self._flush_batch("size")
|
|
161
|
+
return True
|
|
162
|
+
|
|
163
|
+
return True
|
|
164
|
+
|
|
165
|
+
async def _flush_loop(self):
|
|
166
|
+
"""Background task that flushes batches based on time."""
|
|
167
|
+
while self._running:
|
|
168
|
+
try:
|
|
169
|
+
# Check every 10ms (more responsive than max_batch_time_ms)
|
|
170
|
+
await asyncio.sleep(0.01)
|
|
171
|
+
|
|
172
|
+
# Check if we need time-based flush
|
|
173
|
+
async with self.batch_lock:
|
|
174
|
+
if not self.batch:
|
|
175
|
+
continue
|
|
176
|
+
|
|
177
|
+
elapsed_ms = (time.time() - self.last_flush_time) * 1000
|
|
178
|
+
if elapsed_ms >= self.max_batch_time_ms:
|
|
179
|
+
await self._flush_batch("time")
|
|
180
|
+
|
|
181
|
+
except asyncio.CancelledError:
|
|
182
|
+
break
|
|
183
|
+
except Exception as e:
|
|
184
|
+
logger.error("flush_loop_error", error=str(e))
|
|
185
|
+
# Continue running even if flush fails
|
|
186
|
+
|
|
187
|
+
async def _flush_batch(self, reason: str):
|
|
188
|
+
"""
|
|
189
|
+
Flush the current batch.
|
|
190
|
+
|
|
191
|
+
Args:
|
|
192
|
+
reason: Reason for flush (time/size/critical/shutdown)
|
|
193
|
+
|
|
194
|
+
Note: Caller must hold batch_lock
|
|
195
|
+
"""
|
|
196
|
+
if not self.batch:
|
|
197
|
+
return
|
|
198
|
+
|
|
199
|
+
batch_to_send = self.batch.copy()
|
|
200
|
+
batch_size = len(batch_to_send)
|
|
201
|
+
|
|
202
|
+
# Clear batch immediately (unlock for new events)
|
|
203
|
+
self.batch.clear()
|
|
204
|
+
self.last_flush_time = time.time()
|
|
205
|
+
|
|
206
|
+
# Update statistics
|
|
207
|
+
self.stats["total_batches"] += 1
|
|
208
|
+
self.stats["total_flushes"] += 1
|
|
209
|
+
|
|
210
|
+
if reason == "time":
|
|
211
|
+
self.stats["time_based_flushes"] += 1
|
|
212
|
+
elif reason == "size":
|
|
213
|
+
self.stats["size_based_flushes"] += 1
|
|
214
|
+
elif reason == "critical":
|
|
215
|
+
self.stats["critical_flushes"] += 1
|
|
216
|
+
|
|
217
|
+
# Calculate average batch size
|
|
218
|
+
self.stats["events_per_batch_avg"] = (
|
|
219
|
+
self.stats["total_events"] / self.stats["total_batches"]
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
# Send batch (outside lock)
|
|
223
|
+
try:
|
|
224
|
+
logger.debug(
|
|
225
|
+
"flushing_event_batch",
|
|
226
|
+
batch_size=batch_size,
|
|
227
|
+
reason=reason,
|
|
228
|
+
avg_batch_size=f"{self.stats['events_per_batch_avg']:.1f}"
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
result = self.flush_callback(batch_to_send)
|
|
232
|
+
if asyncio.iscoroutine(result):
|
|
233
|
+
await result
|
|
234
|
+
|
|
235
|
+
logger.debug(
|
|
236
|
+
"event_batch_flushed",
|
|
237
|
+
batch_size=batch_size,
|
|
238
|
+
reason=reason
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
except Exception as e:
|
|
242
|
+
logger.error(
|
|
243
|
+
"batch_flush_failed",
|
|
244
|
+
error=str(e),
|
|
245
|
+
batch_size=batch_size,
|
|
246
|
+
reason=reason
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
def get_stats(self) -> Dict[str, Any]:
|
|
250
|
+
"""Get batching statistics."""
|
|
251
|
+
return self.stats.copy()
|
|
252
|
+
|
|
253
|
+
async def force_flush(self):
|
|
254
|
+
"""Force flush any pending events (useful for testing or critical sections)."""
|
|
255
|
+
async with self.batch_lock:
|
|
256
|
+
await self._flush_batch("manual")
|