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,964 @@
|
|
|
1
|
+
"""
|
|
2
|
+
EventFormatter - Centralized SSE event formatting for execution streaming.
|
|
3
|
+
|
|
4
|
+
This module provides a clean interface for formatting Server-Sent Events (SSE)
|
|
5
|
+
according to the SSE specification with proper event IDs for gap recovery.
|
|
6
|
+
|
|
7
|
+
SSE Format:
|
|
8
|
+
id: {execution_id}_{counter}_{timestamp_micros}
|
|
9
|
+
event: {event_type}
|
|
10
|
+
data: {json_payload}
|
|
11
|
+
{blank line}
|
|
12
|
+
|
|
13
|
+
Event Types:
|
|
14
|
+
- connected: Initial connection confirmation
|
|
15
|
+
- message: Chat messages (user, assistant, tool, system)
|
|
16
|
+
- history_complete: History loading complete
|
|
17
|
+
- status: Execution status updates
|
|
18
|
+
- tool_started: Tool execution started
|
|
19
|
+
- tool_completed: Tool execution completed
|
|
20
|
+
- member_tool_started: Team member tool execution started
|
|
21
|
+
- member_tool_completed: Team member tool execution completed
|
|
22
|
+
- message_chunk: Streaming message chunks (for real-time token streaming)
|
|
23
|
+
- member_message_chunk: Team member message chunks
|
|
24
|
+
- member_message_complete: Team member message streaming complete
|
|
25
|
+
- thinking_start: Beginning of thinking/reasoning block
|
|
26
|
+
- thinking_delta: Incremental thinking content
|
|
27
|
+
- thinking_complete: End of thinking block with signature
|
|
28
|
+
- member_thinking_start: Team member thinking start
|
|
29
|
+
- member_thinking_delta: Team member thinking content
|
|
30
|
+
- member_thinking_complete: Team member thinking end
|
|
31
|
+
- done: Execution completed successfully
|
|
32
|
+
- error: Execution failed
|
|
33
|
+
- degraded: Degraded mode notification (Redis down, worker down)
|
|
34
|
+
- reconnect: Server requesting client reconnect
|
|
35
|
+
- timeout_warning: Connection timeout warning
|
|
36
|
+
- gap_detected: Event gap detected, client should reconnect
|
|
37
|
+
|
|
38
|
+
Test Strategy:
|
|
39
|
+
- Unit test each format method for SSE compliance
|
|
40
|
+
- Verify event ID generation uniqueness and format
|
|
41
|
+
- Test JSON serialization edge cases (None, nested objects, special chars)
|
|
42
|
+
- Test multi-line data handling
|
|
43
|
+
- Verify all fields present (id, event, data, blank line)
|
|
44
|
+
- Test thread-safety of counter increment
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
import json
|
|
48
|
+
import time
|
|
49
|
+
from typing import Any, Dict, List, Optional
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class EventFormatter:
|
|
53
|
+
"""
|
|
54
|
+
Formats Server-Sent Events (SSE) for execution streaming.
|
|
55
|
+
|
|
56
|
+
Handles:
|
|
57
|
+
- Event ID generation with sequential counter
|
|
58
|
+
- JSON serialization of payloads
|
|
59
|
+
- Proper SSE format compliance
|
|
60
|
+
- Multiple event types with specialized formatting
|
|
61
|
+
|
|
62
|
+
Example:
|
|
63
|
+
>>> formatter = EventFormatter("exec-123")
|
|
64
|
+
>>> event = formatter.format_connected_event("org-456", "pending")
|
|
65
|
+
>>> print(event)
|
|
66
|
+
id: exec-123_1_1702938457123456
|
|
67
|
+
event: connected
|
|
68
|
+
data: {"execution_id": "exec-123", "organization_id": "org-456", "status": "pending", "connected_at": 1702938457.123456}
|
|
69
|
+
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
def __init__(self, execution_id: str):
|
|
73
|
+
"""
|
|
74
|
+
Initialize event formatter for a specific execution.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
execution_id: Unique execution identifier
|
|
78
|
+
"""
|
|
79
|
+
self.execution_id = execution_id
|
|
80
|
+
self._counter = 0
|
|
81
|
+
|
|
82
|
+
def generate_event_id(self) -> str:
|
|
83
|
+
"""
|
|
84
|
+
Generate unique event ID with sequential counter and microsecond timestamp.
|
|
85
|
+
|
|
86
|
+
Format: {execution_id}_{counter}_{timestamp_micros}
|
|
87
|
+
|
|
88
|
+
The counter ensures sequential ordering within a stream, while the
|
|
89
|
+
microsecond timestamp provides global uniqueness across reconnections.
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
str: Unique event ID
|
|
93
|
+
|
|
94
|
+
Example:
|
|
95
|
+
>>> formatter = EventFormatter("exec-123")
|
|
96
|
+
>>> event_id1 = formatter.generate_event_id()
|
|
97
|
+
>>> event_id2 = formatter.generate_event_id()
|
|
98
|
+
>>> # event_id1: "exec-123_1_1702938457123456"
|
|
99
|
+
>>> # event_id2: "exec-123_2_1702938457124789"
|
|
100
|
+
"""
|
|
101
|
+
self._counter += 1
|
|
102
|
+
timestamp_micros = int(time.time() * 1000000)
|
|
103
|
+
return f"{self.execution_id}_{self._counter}_{timestamp_micros}"
|
|
104
|
+
|
|
105
|
+
def format_event(
|
|
106
|
+
self,
|
|
107
|
+
event_type: str,
|
|
108
|
+
data: Dict[str, Any],
|
|
109
|
+
event_id: Optional[str] = None
|
|
110
|
+
) -> str:
|
|
111
|
+
"""
|
|
112
|
+
Generic SSE event formatter.
|
|
113
|
+
|
|
114
|
+
Generates proper SSE format with id, event, data fields and blank line terminator.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
event_type: Type of event (e.g., "message", "status", "done")
|
|
118
|
+
data: Event payload to JSON-serialize
|
|
119
|
+
event_id: Optional custom event ID (auto-generated if not provided)
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
str: Formatted SSE event string
|
|
123
|
+
|
|
124
|
+
Example:
|
|
125
|
+
>>> formatter = EventFormatter("exec-123")
|
|
126
|
+
>>> event = formatter.format_event("status", {"status": "running"})
|
|
127
|
+
>>> print(event)
|
|
128
|
+
id: exec-123_1_1702938457123456
|
|
129
|
+
event: status
|
|
130
|
+
data: {"status": "running"}
|
|
131
|
+
|
|
132
|
+
"""
|
|
133
|
+
if event_id is None:
|
|
134
|
+
event_id = self.generate_event_id()
|
|
135
|
+
|
|
136
|
+
# JSON serialize data with proper error handling
|
|
137
|
+
try:
|
|
138
|
+
data_json = json.dumps(data)
|
|
139
|
+
except (TypeError, ValueError) as e:
|
|
140
|
+
# Fallback to error payload if serialization fails
|
|
141
|
+
data_json = json.dumps({
|
|
142
|
+
"error": "Failed to serialize event data",
|
|
143
|
+
"error_type": "serialization_error",
|
|
144
|
+
"details": str(e)
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
# SSE format: id, event, data, blank line
|
|
148
|
+
return f"id: {event_id}\nevent: {event_type}\ndata: {data_json}\n\n"
|
|
149
|
+
|
|
150
|
+
def format_connected_event(
|
|
151
|
+
self,
|
|
152
|
+
organization_id: str,
|
|
153
|
+
status: str = "pending"
|
|
154
|
+
) -> str:
|
|
155
|
+
"""
|
|
156
|
+
Format 'connected' event sent immediately on connection.
|
|
157
|
+
|
|
158
|
+
This event is sent first to unblock the EventSource connection before
|
|
159
|
+
any slow operations (Temporal queries, DB lookups) are performed.
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
organization_id: Organization ID for the execution
|
|
163
|
+
status: Current execution status (default: "pending")
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
str: Formatted SSE event
|
|
167
|
+
|
|
168
|
+
Example:
|
|
169
|
+
>>> formatter = EventFormatter("exec-123")
|
|
170
|
+
>>> event = formatter.format_connected_event("org-456", "running")
|
|
171
|
+
>>> print(event)
|
|
172
|
+
id: exec-123_1_1702938457123456
|
|
173
|
+
event: connected
|
|
174
|
+
data: {"execution_id": "exec-123", "organization_id": "org-456", "status": "running", "connected_at": 1702938457.123456}
|
|
175
|
+
|
|
176
|
+
"""
|
|
177
|
+
data = {
|
|
178
|
+
"execution_id": self.execution_id,
|
|
179
|
+
"organization_id": organization_id,
|
|
180
|
+
"status": status,
|
|
181
|
+
"connected_at": time.time()
|
|
182
|
+
}
|
|
183
|
+
return self.format_event("connected", data)
|
|
184
|
+
|
|
185
|
+
def format_message_event(self, message: Dict[str, Any]) -> str:
|
|
186
|
+
"""
|
|
187
|
+
Format 'message' event for chat messages.
|
|
188
|
+
|
|
189
|
+
Handles user, assistant, tool, and system messages with all metadata.
|
|
190
|
+
|
|
191
|
+
Args:
|
|
192
|
+
message: Message dictionary with fields like role, content, timestamp, etc.
|
|
193
|
+
|
|
194
|
+
Returns:
|
|
195
|
+
str: Formatted SSE event
|
|
196
|
+
|
|
197
|
+
Example:
|
|
198
|
+
>>> formatter = EventFormatter("exec-123")
|
|
199
|
+
>>> message = {
|
|
200
|
+
... "role": "user",
|
|
201
|
+
... "content": "Hello",
|
|
202
|
+
... "timestamp": "2024-12-18T10:00:00Z",
|
|
203
|
+
... "message_id": "msg-456"
|
|
204
|
+
... }
|
|
205
|
+
>>> event = formatter.format_message_event(message)
|
|
206
|
+
"""
|
|
207
|
+
return self.format_event("message", message)
|
|
208
|
+
|
|
209
|
+
def format_history_complete_event(
|
|
210
|
+
self,
|
|
211
|
+
message_count: int,
|
|
212
|
+
is_truncated: bool = False,
|
|
213
|
+
has_more: bool = False
|
|
214
|
+
) -> str:
|
|
215
|
+
"""
|
|
216
|
+
Format 'history_complete' event.
|
|
217
|
+
|
|
218
|
+
Signals that historical messages have been fully loaded.
|
|
219
|
+
|
|
220
|
+
Args:
|
|
221
|
+
message_count: Number of messages loaded
|
|
222
|
+
is_truncated: Whether history was truncated (default: False)
|
|
223
|
+
has_more: Whether there are more messages available (default: False)
|
|
224
|
+
|
|
225
|
+
Returns:
|
|
226
|
+
str: Formatted SSE event
|
|
227
|
+
|
|
228
|
+
Example:
|
|
229
|
+
>>> formatter = EventFormatter("exec-123")
|
|
230
|
+
>>> event = formatter.format_history_complete_event(42, is_truncated=True)
|
|
231
|
+
>>> print(event)
|
|
232
|
+
id: exec-123_1_1702938457123456
|
|
233
|
+
event: history_complete
|
|
234
|
+
data: {"execution_id": "exec-123", "message_count": 42, "is_truncated": true, "has_more": false}
|
|
235
|
+
|
|
236
|
+
"""
|
|
237
|
+
data = {
|
|
238
|
+
"execution_id": self.execution_id,
|
|
239
|
+
"message_count": message_count,
|
|
240
|
+
"is_truncated": is_truncated,
|
|
241
|
+
"has_more": has_more
|
|
242
|
+
}
|
|
243
|
+
return self.format_event("history_complete", data)
|
|
244
|
+
|
|
245
|
+
def format_status_event(
|
|
246
|
+
self,
|
|
247
|
+
status: str,
|
|
248
|
+
metadata: Optional[Dict] = None
|
|
249
|
+
) -> str:
|
|
250
|
+
"""
|
|
251
|
+
Format 'status' event for execution status changes.
|
|
252
|
+
|
|
253
|
+
Args:
|
|
254
|
+
status: New status (e.g., "pending", "running", "completed", "failed")
|
|
255
|
+
metadata: Optional additional metadata (e.g., source="database")
|
|
256
|
+
|
|
257
|
+
Returns:
|
|
258
|
+
str: Formatted SSE event
|
|
259
|
+
|
|
260
|
+
Example:
|
|
261
|
+
>>> formatter = EventFormatter("exec-123")
|
|
262
|
+
>>> event = formatter.format_status_event("running")
|
|
263
|
+
>>> print(event)
|
|
264
|
+
id: exec-123_1_1702938457123456
|
|
265
|
+
event: status
|
|
266
|
+
data: {"status": "running", "execution_id": "exec-123"}
|
|
267
|
+
|
|
268
|
+
"""
|
|
269
|
+
data = {
|
|
270
|
+
"status": status,
|
|
271
|
+
"execution_id": self.execution_id
|
|
272
|
+
}
|
|
273
|
+
if metadata:
|
|
274
|
+
data.update(metadata)
|
|
275
|
+
return self.format_event("status", data)
|
|
276
|
+
|
|
277
|
+
def format_tool_started_event(self, tool_data: Dict[str, Any]) -> str:
|
|
278
|
+
"""
|
|
279
|
+
Format 'tool_started' event for tool execution started.
|
|
280
|
+
|
|
281
|
+
Args:
|
|
282
|
+
tool_data: Tool execution data with nested structure:
|
|
283
|
+
{
|
|
284
|
+
"data": {
|
|
285
|
+
"tool_name": str,
|
|
286
|
+
"tool_execution_id": str,
|
|
287
|
+
"tool_input": dict,
|
|
288
|
+
...
|
|
289
|
+
},
|
|
290
|
+
"timestamp": str
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
Returns:
|
|
294
|
+
str: Formatted SSE event
|
|
295
|
+
|
|
296
|
+
Example:
|
|
297
|
+
>>> formatter = EventFormatter("exec-123")
|
|
298
|
+
>>> tool_data = {
|
|
299
|
+
... "data": {
|
|
300
|
+
... "tool_name": "search",
|
|
301
|
+
... "tool_execution_id": "tool-789",
|
|
302
|
+
... "tool_input": {"query": "test"}
|
|
303
|
+
... },
|
|
304
|
+
... "timestamp": "2024-12-18T10:00:00Z"
|
|
305
|
+
... }
|
|
306
|
+
>>> event = formatter.format_tool_started_event(tool_data)
|
|
307
|
+
"""
|
|
308
|
+
return self.format_event("tool_started", tool_data)
|
|
309
|
+
|
|
310
|
+
def format_tool_completed_event(self, tool_data: Dict[str, Any]) -> str:
|
|
311
|
+
"""
|
|
312
|
+
Format 'tool_completed' event for tool execution completed.
|
|
313
|
+
|
|
314
|
+
Args:
|
|
315
|
+
tool_data: Tool execution data with nested structure:
|
|
316
|
+
{
|
|
317
|
+
"data": {
|
|
318
|
+
"tool_name": str,
|
|
319
|
+
"tool_execution_id": str,
|
|
320
|
+
"tool_output": Any,
|
|
321
|
+
"tool_status": str,
|
|
322
|
+
...
|
|
323
|
+
},
|
|
324
|
+
"timestamp": str
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
Returns:
|
|
328
|
+
str: Formatted SSE event
|
|
329
|
+
|
|
330
|
+
Example:
|
|
331
|
+
>>> formatter = EventFormatter("exec-123")
|
|
332
|
+
>>> tool_data = {
|
|
333
|
+
... "data": {
|
|
334
|
+
... "tool_name": "search",
|
|
335
|
+
... "tool_execution_id": "tool-789",
|
|
336
|
+
... "tool_output": {"results": [...]},
|
|
337
|
+
... "tool_status": "completed"
|
|
338
|
+
... },
|
|
339
|
+
... "timestamp": "2024-12-18T10:00:01Z"
|
|
340
|
+
... }
|
|
341
|
+
>>> event = formatter.format_tool_completed_event(tool_data)
|
|
342
|
+
"""
|
|
343
|
+
return self.format_event("tool_completed", tool_data)
|
|
344
|
+
|
|
345
|
+
def format_member_tool_started_event(self, tool_data: Dict[str, Any]) -> str:
|
|
346
|
+
"""
|
|
347
|
+
Format 'member_tool_started' event for team member tool execution started.
|
|
348
|
+
|
|
349
|
+
Used in multi-agent scenarios when a team member starts executing a tool.
|
|
350
|
+
|
|
351
|
+
Args:
|
|
352
|
+
tool_data: Tool execution data with nested structure:
|
|
353
|
+
{
|
|
354
|
+
"data": {
|
|
355
|
+
"tool_name": str,
|
|
356
|
+
"tool_execution_id": str,
|
|
357
|
+
"member_name": str,
|
|
358
|
+
"parent_message_id": str,
|
|
359
|
+
"tool_arguments": dict,
|
|
360
|
+
...
|
|
361
|
+
},
|
|
362
|
+
"timestamp": str
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
Returns:
|
|
366
|
+
str: Formatted SSE event
|
|
367
|
+
|
|
368
|
+
Example:
|
|
369
|
+
>>> formatter = EventFormatter("exec-123")
|
|
370
|
+
>>> tool_data = {
|
|
371
|
+
... "data": {
|
|
372
|
+
... "tool_name": "search",
|
|
373
|
+
... "tool_execution_id": "tool-789",
|
|
374
|
+
... "member_name": "Researcher",
|
|
375
|
+
... "tool_arguments": {"query": "test"}
|
|
376
|
+
... },
|
|
377
|
+
... "timestamp": "2024-12-18T10:00:00Z"
|
|
378
|
+
... }
|
|
379
|
+
>>> event = formatter.format_member_tool_started_event(tool_data)
|
|
380
|
+
"""
|
|
381
|
+
return self.format_event("member_tool_started", tool_data)
|
|
382
|
+
|
|
383
|
+
def format_member_tool_completed_event(self, tool_data: Dict[str, Any]) -> str:
|
|
384
|
+
"""
|
|
385
|
+
Format 'member_tool_completed' event for team member tool execution completed.
|
|
386
|
+
|
|
387
|
+
Used in multi-agent scenarios when a team member finishes executing a tool.
|
|
388
|
+
|
|
389
|
+
Args:
|
|
390
|
+
tool_data: Tool execution data with nested structure:
|
|
391
|
+
{
|
|
392
|
+
"data": {
|
|
393
|
+
"tool_name": str,
|
|
394
|
+
"tool_execution_id": str,
|
|
395
|
+
"member_name": str,
|
|
396
|
+
"status": str,
|
|
397
|
+
"tool_output": Any,
|
|
398
|
+
"tool_error": Any,
|
|
399
|
+
...
|
|
400
|
+
},
|
|
401
|
+
"timestamp": str
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
Returns:
|
|
405
|
+
str: Formatted SSE event
|
|
406
|
+
|
|
407
|
+
Example:
|
|
408
|
+
>>> formatter = EventFormatter("exec-123")
|
|
409
|
+
>>> tool_data = {
|
|
410
|
+
... "data": {
|
|
411
|
+
... "tool_name": "search",
|
|
412
|
+
... "tool_execution_id": "tool-789",
|
|
413
|
+
... "member_name": "Researcher",
|
|
414
|
+
... "status": "success",
|
|
415
|
+
... "tool_output": {"results": [...]}
|
|
416
|
+
... },
|
|
417
|
+
... "timestamp": "2024-12-18T10:00:01Z"
|
|
418
|
+
... }
|
|
419
|
+
>>> event = formatter.format_member_tool_completed_event(tool_data)
|
|
420
|
+
"""
|
|
421
|
+
return self.format_event("member_tool_completed", tool_data)
|
|
422
|
+
|
|
423
|
+
def format_message_chunk_event(self, chunk_data: Dict[str, Any]) -> str:
|
|
424
|
+
"""
|
|
425
|
+
Format 'message_chunk' event for streaming message chunks.
|
|
426
|
+
|
|
427
|
+
Used for real-time token streaming of assistant responses.
|
|
428
|
+
|
|
429
|
+
Args:
|
|
430
|
+
chunk_data: Chunk data with nested structure:
|
|
431
|
+
{
|
|
432
|
+
"data": {
|
|
433
|
+
"content": str,
|
|
434
|
+
"message_id": str,
|
|
435
|
+
"is_final": bool,
|
|
436
|
+
...
|
|
437
|
+
},
|
|
438
|
+
"timestamp": str
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
Returns:
|
|
442
|
+
str: Formatted SSE event
|
|
443
|
+
|
|
444
|
+
Example:
|
|
445
|
+
>>> formatter = EventFormatter("exec-123")
|
|
446
|
+
>>> chunk_data = {
|
|
447
|
+
... "data": {
|
|
448
|
+
... "content": "Hello",
|
|
449
|
+
... "message_id": "msg-456",
|
|
450
|
+
... "is_final": False
|
|
451
|
+
... },
|
|
452
|
+
... "timestamp": "2024-12-18T10:00:00Z"
|
|
453
|
+
... }
|
|
454
|
+
>>> event = formatter.format_message_chunk_event(chunk_data)
|
|
455
|
+
"""
|
|
456
|
+
return self.format_event("message_chunk", chunk_data)
|
|
457
|
+
|
|
458
|
+
def format_member_message_chunk_event(self, chunk_data: Dict[str, Any]) -> str:
|
|
459
|
+
"""
|
|
460
|
+
Format 'member_message_chunk' event for team member message chunks.
|
|
461
|
+
|
|
462
|
+
Used for streaming messages from team members in multi-agent scenarios.
|
|
463
|
+
|
|
464
|
+
Args:
|
|
465
|
+
chunk_data: Chunk data with nested structure similar to message_chunk
|
|
466
|
+
|
|
467
|
+
Returns:
|
|
468
|
+
str: Formatted SSE event
|
|
469
|
+
"""
|
|
470
|
+
return self.format_event("member_message_chunk", chunk_data)
|
|
471
|
+
|
|
472
|
+
def format_member_message_complete_event(self, data: Dict[str, Any]) -> str:
|
|
473
|
+
"""
|
|
474
|
+
Format 'member_message_complete' event for end of team member message.
|
|
475
|
+
|
|
476
|
+
Used to signal that a team member has finished streaming their message.
|
|
477
|
+
|
|
478
|
+
Args:
|
|
479
|
+
data: Event data with nested structure:
|
|
480
|
+
{
|
|
481
|
+
"data": {
|
|
482
|
+
"message_id": str,
|
|
483
|
+
"member_name": str,
|
|
484
|
+
...
|
|
485
|
+
},
|
|
486
|
+
"timestamp": str
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
Returns:
|
|
490
|
+
str: Formatted SSE event
|
|
491
|
+
"""
|
|
492
|
+
return self.format_event("member_message_complete", data)
|
|
493
|
+
|
|
494
|
+
# =========================================================================
|
|
495
|
+
# Thinking/Reasoning Event Methods (Extended Thinking Support)
|
|
496
|
+
# =========================================================================
|
|
497
|
+
|
|
498
|
+
def format_thinking_start_event(
|
|
499
|
+
self,
|
|
500
|
+
message_id: str,
|
|
501
|
+
index: int = 0,
|
|
502
|
+
budget_tokens: Optional[int] = None
|
|
503
|
+
) -> str:
|
|
504
|
+
"""
|
|
505
|
+
Format 'thinking_start' event for beginning of thinking block.
|
|
506
|
+
|
|
507
|
+
Sent when the model begins extended thinking/reasoning before generating
|
|
508
|
+
a response. This event signals the UI to show a thinking indicator.
|
|
509
|
+
|
|
510
|
+
Args:
|
|
511
|
+
message_id: ID of the message being generated
|
|
512
|
+
index: Content block index (default: 0)
|
|
513
|
+
budget_tokens: Optional thinking token budget configured for this request
|
|
514
|
+
|
|
515
|
+
Returns:
|
|
516
|
+
str: Formatted SSE event
|
|
517
|
+
|
|
518
|
+
Example:
|
|
519
|
+
>>> formatter = EventFormatter("exec-123")
|
|
520
|
+
>>> event = formatter.format_thinking_start_event("msg_abc", budget_tokens=10000)
|
|
521
|
+
>>> print(event)
|
|
522
|
+
id: exec-123_1_1702938457123456
|
|
523
|
+
event: thinking_start
|
|
524
|
+
data: {"execution_id": "exec-123", "message_id": "msg_abc", "index": 0, "budget_tokens": 10000}
|
|
525
|
+
|
|
526
|
+
"""
|
|
527
|
+
data = {
|
|
528
|
+
"execution_id": self.execution_id,
|
|
529
|
+
"message_id": message_id,
|
|
530
|
+
"index": index,
|
|
531
|
+
}
|
|
532
|
+
if budget_tokens is not None:
|
|
533
|
+
data["budget_tokens"] = budget_tokens
|
|
534
|
+
return self.format_event("thinking_start", data)
|
|
535
|
+
|
|
536
|
+
def format_thinking_delta_event(
|
|
537
|
+
self,
|
|
538
|
+
message_id: str,
|
|
539
|
+
thinking: str,
|
|
540
|
+
index: int = 0
|
|
541
|
+
) -> str:
|
|
542
|
+
"""
|
|
543
|
+
Format 'thinking_delta' event for incremental thinking content.
|
|
544
|
+
|
|
545
|
+
Streams reasoning content as the model thinks through the problem.
|
|
546
|
+
Clients should accumulate deltas for the same message_id + index.
|
|
547
|
+
|
|
548
|
+
Args:
|
|
549
|
+
message_id: ID of the message being generated
|
|
550
|
+
thinking: Incremental thinking content (may be multiline with markdown)
|
|
551
|
+
index: Content block index (default: 0)
|
|
552
|
+
|
|
553
|
+
Returns:
|
|
554
|
+
str: Formatted SSE event
|
|
555
|
+
|
|
556
|
+
Example:
|
|
557
|
+
>>> formatter = EventFormatter("exec-123")
|
|
558
|
+
>>> event = formatter.format_thinking_delta_event(
|
|
559
|
+
... "msg_abc",
|
|
560
|
+
... "Step 1: Analyze the requirements\\n\\nFirst, I need to..."
|
|
561
|
+
... )
|
|
562
|
+
|
|
563
|
+
"""
|
|
564
|
+
data = {
|
|
565
|
+
"execution_id": self.execution_id,
|
|
566
|
+
"message_id": message_id,
|
|
567
|
+
"thinking": thinking,
|
|
568
|
+
"index": index,
|
|
569
|
+
}
|
|
570
|
+
return self.format_event("thinking_delta", data)
|
|
571
|
+
|
|
572
|
+
def format_thinking_complete_event(
|
|
573
|
+
self,
|
|
574
|
+
message_id: str,
|
|
575
|
+
index: int = 0,
|
|
576
|
+
signature: Optional[str] = None,
|
|
577
|
+
tokens_used: Optional[int] = None
|
|
578
|
+
) -> str:
|
|
579
|
+
"""
|
|
580
|
+
Format 'thinking_complete' event for end of thinking block.
|
|
581
|
+
|
|
582
|
+
Sent when thinking is complete, includes optional signature for verification.
|
|
583
|
+
The signature is opaque and should be stored/passed through unchanged.
|
|
584
|
+
|
|
585
|
+
Args:
|
|
586
|
+
message_id: ID of the message being generated
|
|
587
|
+
index: Content block index (default: 0)
|
|
588
|
+
signature: Optional verification signature from Anthropic API
|
|
589
|
+
tokens_used: Optional actual tokens used for thinking
|
|
590
|
+
|
|
591
|
+
Returns:
|
|
592
|
+
str: Formatted SSE event
|
|
593
|
+
|
|
594
|
+
Example:
|
|
595
|
+
>>> formatter = EventFormatter("exec-123")
|
|
596
|
+
>>> event = formatter.format_thinking_complete_event(
|
|
597
|
+
... "msg_abc",
|
|
598
|
+
... signature="EqQBCgIYAhIM...",
|
|
599
|
+
... tokens_used=1024
|
|
600
|
+
... )
|
|
601
|
+
|
|
602
|
+
"""
|
|
603
|
+
data = {
|
|
604
|
+
"execution_id": self.execution_id,
|
|
605
|
+
"message_id": message_id,
|
|
606
|
+
"index": index,
|
|
607
|
+
}
|
|
608
|
+
if signature is not None:
|
|
609
|
+
data["signature"] = signature
|
|
610
|
+
if tokens_used is not None:
|
|
611
|
+
data["tokens_used"] = tokens_used
|
|
612
|
+
return self.format_event("thinking_complete", data)
|
|
613
|
+
|
|
614
|
+
def format_member_thinking_start_event(
|
|
615
|
+
self,
|
|
616
|
+
member_name: str,
|
|
617
|
+
index: int = 0,
|
|
618
|
+
member_id: Optional[str] = None,
|
|
619
|
+
budget_tokens: Optional[int] = None
|
|
620
|
+
) -> str:
|
|
621
|
+
"""
|
|
622
|
+
Format 'member_thinking_start' event for team member thinking.
|
|
623
|
+
|
|
624
|
+
Sent when a sub-agent/team member begins thinking.
|
|
625
|
+
Note: Sub-agents currently don't support transparent thinking mode,
|
|
626
|
+
this is reserved for future SDK enhancements.
|
|
627
|
+
|
|
628
|
+
Args:
|
|
629
|
+
member_name: Name of the team member/sub-agent
|
|
630
|
+
index: Content block index (default: 0)
|
|
631
|
+
member_id: Optional sub-agent ID
|
|
632
|
+
budget_tokens: Optional thinking token budget
|
|
633
|
+
|
|
634
|
+
Returns:
|
|
635
|
+
str: Formatted SSE event
|
|
636
|
+
"""
|
|
637
|
+
data = {
|
|
638
|
+
"execution_id": self.execution_id,
|
|
639
|
+
"member_name": member_name,
|
|
640
|
+
"index": index,
|
|
641
|
+
}
|
|
642
|
+
if member_id is not None:
|
|
643
|
+
data["member_id"] = member_id
|
|
644
|
+
if budget_tokens is not None:
|
|
645
|
+
data["budget_tokens"] = budget_tokens
|
|
646
|
+
return self.format_event("member_thinking_start", data)
|
|
647
|
+
|
|
648
|
+
def format_member_thinking_delta_event(
|
|
649
|
+
self,
|
|
650
|
+
member_name: str,
|
|
651
|
+
thinking: str,
|
|
652
|
+
index: int = 0,
|
|
653
|
+
member_id: Optional[str] = None
|
|
654
|
+
) -> str:
|
|
655
|
+
"""
|
|
656
|
+
Format 'member_thinking_delta' event for team member thinking content.
|
|
657
|
+
|
|
658
|
+
Streams reasoning content from a sub-agent/team member.
|
|
659
|
+
Note: Sub-agents currently don't support transparent thinking mode,
|
|
660
|
+
this is reserved for future SDK enhancements.
|
|
661
|
+
|
|
662
|
+
Args:
|
|
663
|
+
member_name: Name of the team member/sub-agent
|
|
664
|
+
thinking: Incremental thinking content
|
|
665
|
+
index: Content block index (default: 0)
|
|
666
|
+
member_id: Optional sub-agent ID
|
|
667
|
+
|
|
668
|
+
Returns:
|
|
669
|
+
str: Formatted SSE event
|
|
670
|
+
"""
|
|
671
|
+
data = {
|
|
672
|
+
"execution_id": self.execution_id,
|
|
673
|
+
"member_name": member_name,
|
|
674
|
+
"thinking": thinking,
|
|
675
|
+
"index": index,
|
|
676
|
+
}
|
|
677
|
+
if member_id is not None:
|
|
678
|
+
data["member_id"] = member_id
|
|
679
|
+
return self.format_event("member_thinking_delta", data)
|
|
680
|
+
|
|
681
|
+
def format_member_thinking_complete_event(
|
|
682
|
+
self,
|
|
683
|
+
member_name: str,
|
|
684
|
+
index: int = 0,
|
|
685
|
+
member_id: Optional[str] = None,
|
|
686
|
+
signature: Optional[str] = None,
|
|
687
|
+
tokens_used: Optional[int] = None
|
|
688
|
+
) -> str:
|
|
689
|
+
"""
|
|
690
|
+
Format 'member_thinking_complete' event for end of member thinking.
|
|
691
|
+
|
|
692
|
+
Sent when a sub-agent/team member completes thinking.
|
|
693
|
+
Note: Sub-agents currently don't support transparent thinking mode,
|
|
694
|
+
this is reserved for future SDK enhancements.
|
|
695
|
+
|
|
696
|
+
Args:
|
|
697
|
+
member_name: Name of the team member/sub-agent
|
|
698
|
+
index: Content block index (default: 0)
|
|
699
|
+
member_id: Optional sub-agent ID
|
|
700
|
+
signature: Optional verification signature
|
|
701
|
+
tokens_used: Optional actual tokens used
|
|
702
|
+
|
|
703
|
+
Returns:
|
|
704
|
+
str: Formatted SSE event
|
|
705
|
+
"""
|
|
706
|
+
data = {
|
|
707
|
+
"execution_id": self.execution_id,
|
|
708
|
+
"member_name": member_name,
|
|
709
|
+
"index": index,
|
|
710
|
+
}
|
|
711
|
+
if member_id is not None:
|
|
712
|
+
data["member_id"] = member_id
|
|
713
|
+
if signature is not None:
|
|
714
|
+
data["signature"] = signature
|
|
715
|
+
if tokens_used is not None:
|
|
716
|
+
data["tokens_used"] = tokens_used
|
|
717
|
+
return self.format_event("member_thinking_complete", data)
|
|
718
|
+
|
|
719
|
+
def format_done_event(
|
|
720
|
+
self,
|
|
721
|
+
response: Any = None,
|
|
722
|
+
usage: Optional[Dict] = None,
|
|
723
|
+
workflow_status: Optional[str] = None,
|
|
724
|
+
messages: Optional[List[Dict[str, Any]]] = None
|
|
725
|
+
) -> str:
|
|
726
|
+
"""
|
|
727
|
+
Format 'done' event for successful completion.
|
|
728
|
+
|
|
729
|
+
Args:
|
|
730
|
+
response: Optional response data
|
|
731
|
+
usage: Optional usage statistics (tokens, cost, etc.)
|
|
732
|
+
workflow_status: Optional Temporal workflow status
|
|
733
|
+
messages: Optional full message history (for completed executions with no prior streaming)
|
|
734
|
+
|
|
735
|
+
Returns:
|
|
736
|
+
str: Formatted SSE event
|
|
737
|
+
|
|
738
|
+
Example:
|
|
739
|
+
>>> formatter = EventFormatter("exec-123")
|
|
740
|
+
>>> event = formatter.format_done_event(
|
|
741
|
+
... response="Task completed",
|
|
742
|
+
... usage={"tokens": 150, "cost": 0.003}
|
|
743
|
+
... )
|
|
744
|
+
>>> print(event)
|
|
745
|
+
id: exec-123_1_1702938457123456
|
|
746
|
+
event: done
|
|
747
|
+
data: {"execution_id": "exec-123", "response": "Task completed", "usage": {"tokens": 150, "cost": 0.003}}
|
|
748
|
+
|
|
749
|
+
"""
|
|
750
|
+
data = {
|
|
751
|
+
"execution_id": self.execution_id
|
|
752
|
+
}
|
|
753
|
+
if response is not None:
|
|
754
|
+
data["response"] = response
|
|
755
|
+
if usage is not None:
|
|
756
|
+
data["usage"] = usage
|
|
757
|
+
if workflow_status is not None:
|
|
758
|
+
data["workflow_status"] = workflow_status
|
|
759
|
+
if messages is not None:
|
|
760
|
+
data["messages"] = messages
|
|
761
|
+
return self.format_event("done", data)
|
|
762
|
+
|
|
763
|
+
def format_error_event(
|
|
764
|
+
self,
|
|
765
|
+
error: str,
|
|
766
|
+
error_type: str = "execution_error",
|
|
767
|
+
status: str = "failed"
|
|
768
|
+
) -> str:
|
|
769
|
+
"""
|
|
770
|
+
Format 'error' event for failures.
|
|
771
|
+
|
|
772
|
+
Args:
|
|
773
|
+
error: Error message
|
|
774
|
+
error_type: Type of error (default: "execution_error")
|
|
775
|
+
status: Execution status (default: "failed")
|
|
776
|
+
|
|
777
|
+
Returns:
|
|
778
|
+
str: Formatted SSE event
|
|
779
|
+
|
|
780
|
+
Example:
|
|
781
|
+
>>> formatter = EventFormatter("exec-123")
|
|
782
|
+
>>> event = formatter.format_error_event(
|
|
783
|
+
... "Connection timeout",
|
|
784
|
+
... error_type="timeout_error"
|
|
785
|
+
... )
|
|
786
|
+
>>> print(event)
|
|
787
|
+
id: exec-123_1_1702938457123456
|
|
788
|
+
event: error
|
|
789
|
+
data: {"error": "Connection timeout", "error_type": "timeout_error", "execution_id": "exec-123", "status": "failed"}
|
|
790
|
+
|
|
791
|
+
"""
|
|
792
|
+
data = {
|
|
793
|
+
"error": error,
|
|
794
|
+
"error_type": error_type,
|
|
795
|
+
"execution_id": self.execution_id,
|
|
796
|
+
"status": status
|
|
797
|
+
}
|
|
798
|
+
return self.format_event("error", data)
|
|
799
|
+
|
|
800
|
+
def format_degraded_event(
|
|
801
|
+
self,
|
|
802
|
+
mode: str,
|
|
803
|
+
reason: str,
|
|
804
|
+
message: Optional[str] = None,
|
|
805
|
+
capabilities: Optional[list] = None
|
|
806
|
+
) -> str:
|
|
807
|
+
"""
|
|
808
|
+
Format 'degraded' event for degraded mode notification.
|
|
809
|
+
|
|
810
|
+
Sent when the stream falls back to slower polling modes due to
|
|
811
|
+
infrastructure issues (Redis down, Temporal worker down, etc.).
|
|
812
|
+
|
|
813
|
+
Args:
|
|
814
|
+
mode: Degradation mode (e.g., "history_only", "live_only", "degraded", "unavailable")
|
|
815
|
+
reason: Reason for degraded mode (e.g., "redis_unavailable", "worker_down")
|
|
816
|
+
message: User-friendly explanation (optional)
|
|
817
|
+
capabilities: List of available capabilities in this mode (optional)
|
|
818
|
+
|
|
819
|
+
Returns:
|
|
820
|
+
str: Formatted SSE event
|
|
821
|
+
|
|
822
|
+
Example:
|
|
823
|
+
>>> formatter = EventFormatter("exec-123")
|
|
824
|
+
>>> event = formatter.format_degraded_event(
|
|
825
|
+
... mode="history_only",
|
|
826
|
+
... reason="redis_unavailable",
|
|
827
|
+
... message="Real-time events unavailable, using workflow polling (slower updates)",
|
|
828
|
+
... capabilities=["history"]
|
|
829
|
+
... )
|
|
830
|
+
"""
|
|
831
|
+
data = {
|
|
832
|
+
"execution_id": self.execution_id,
|
|
833
|
+
"mode": mode,
|
|
834
|
+
"reason": reason,
|
|
835
|
+
}
|
|
836
|
+
if message:
|
|
837
|
+
data["message"] = message
|
|
838
|
+
if capabilities is not None:
|
|
839
|
+
data["capabilities"] = capabilities
|
|
840
|
+
return self.format_event("degraded", data)
|
|
841
|
+
|
|
842
|
+
def format_recovered_event(
|
|
843
|
+
self,
|
|
844
|
+
message: str = "Services recovered, resuming full functionality"
|
|
845
|
+
) -> str:
|
|
846
|
+
"""
|
|
847
|
+
Format 'recovered' event for service recovery notification.
|
|
848
|
+
|
|
849
|
+
Sent when services recover from degraded mode back to full functionality.
|
|
850
|
+
|
|
851
|
+
Args:
|
|
852
|
+
message: Recovery notification message
|
|
853
|
+
|
|
854
|
+
Returns:
|
|
855
|
+
str: Formatted SSE event
|
|
856
|
+
|
|
857
|
+
Example:
|
|
858
|
+
>>> formatter = EventFormatter("exec-123")
|
|
859
|
+
>>> event = formatter.format_recovered_event()
|
|
860
|
+
>>> print(event)
|
|
861
|
+
id: exec-123_1_1702938457123456
|
|
862
|
+
event: recovered
|
|
863
|
+
data: {"execution_id": "exec-123", "message": "Services recovered, resuming full functionality"}
|
|
864
|
+
|
|
865
|
+
"""
|
|
866
|
+
data = {
|
|
867
|
+
"execution_id": self.execution_id,
|
|
868
|
+
"message": message
|
|
869
|
+
}
|
|
870
|
+
return self.format_event("recovered", data)
|
|
871
|
+
|
|
872
|
+
def format_reconnect_event(
|
|
873
|
+
self,
|
|
874
|
+
reason: str,
|
|
875
|
+
duration: Optional[float] = None
|
|
876
|
+
) -> str:
|
|
877
|
+
"""
|
|
878
|
+
Format 'reconnect' event for server-requested reconnection.
|
|
879
|
+
|
|
880
|
+
Tells the client to gracefully reconnect (won't count as failed attempt).
|
|
881
|
+
|
|
882
|
+
Args:
|
|
883
|
+
reason: Reason for reconnect (e.g., "timeout", "server_restart")
|
|
884
|
+
duration: Optional duration in seconds before timeout
|
|
885
|
+
|
|
886
|
+
Returns:
|
|
887
|
+
str: Formatted SSE event
|
|
888
|
+
|
|
889
|
+
Example:
|
|
890
|
+
>>> formatter = EventFormatter("exec-123")
|
|
891
|
+
>>> event = formatter.format_reconnect_event("timeout", duration=300.5)
|
|
892
|
+
>>> print(event)
|
|
893
|
+
id: exec-123_1_1702938457123456
|
|
894
|
+
event: reconnect
|
|
895
|
+
data: {"reason": "timeout", "duration": 300.5}
|
|
896
|
+
|
|
897
|
+
"""
|
|
898
|
+
data = {"reason": reason}
|
|
899
|
+
if duration is not None:
|
|
900
|
+
data["duration"] = duration
|
|
901
|
+
return self.format_event("reconnect", data)
|
|
902
|
+
|
|
903
|
+
def format_timeout_warning_event(self, remaining_seconds: int) -> str:
|
|
904
|
+
"""
|
|
905
|
+
Format 'timeout_warning' event for connection timeout warnings.
|
|
906
|
+
|
|
907
|
+
Sent in the last 30 seconds before connection timeout.
|
|
908
|
+
|
|
909
|
+
Args:
|
|
910
|
+
remaining_seconds: Seconds remaining before timeout
|
|
911
|
+
|
|
912
|
+
Returns:
|
|
913
|
+
str: Formatted SSE event
|
|
914
|
+
|
|
915
|
+
Example:
|
|
916
|
+
>>> formatter = EventFormatter("exec-123")
|
|
917
|
+
>>> event = formatter.format_timeout_warning_event(15)
|
|
918
|
+
"""
|
|
919
|
+
data = {"remaining_seconds": remaining_seconds}
|
|
920
|
+
return self.format_event("timeout_warning", data)
|
|
921
|
+
|
|
922
|
+
def format_gap_detected_event(
|
|
923
|
+
self,
|
|
924
|
+
reason: str,
|
|
925
|
+
buffer_oldest: Optional[str] = None
|
|
926
|
+
) -> str:
|
|
927
|
+
"""
|
|
928
|
+
Format 'gap_detected' event when event buffer can't recover gaps.
|
|
929
|
+
|
|
930
|
+
Signals client should reconnect to get missing events.
|
|
931
|
+
|
|
932
|
+
Args:
|
|
933
|
+
reason: Reason for gap (e.g., "Event buffer miss - events too old")
|
|
934
|
+
buffer_oldest: Oldest event ID in buffer
|
|
935
|
+
|
|
936
|
+
Returns:
|
|
937
|
+
str: Formatted SSE event
|
|
938
|
+
"""
|
|
939
|
+
data = {
|
|
940
|
+
"execution_id": self.execution_id,
|
|
941
|
+
"reason": reason
|
|
942
|
+
}
|
|
943
|
+
if buffer_oldest is not None:
|
|
944
|
+
data["buffer_oldest"] = buffer_oldest
|
|
945
|
+
return self.format_event("gap_detected", data)
|
|
946
|
+
|
|
947
|
+
def format_keepalive(self) -> str:
|
|
948
|
+
"""
|
|
949
|
+
Format keepalive comment (not an event).
|
|
950
|
+
|
|
951
|
+
SSE comments start with ':' and don't have id/event/data fields.
|
|
952
|
+
Prevents connection timeout during periods of no activity.
|
|
953
|
+
|
|
954
|
+
Returns:
|
|
955
|
+
str: SSE comment string
|
|
956
|
+
|
|
957
|
+
Example:
|
|
958
|
+
>>> formatter = EventFormatter("exec-123")
|
|
959
|
+
>>> keepalive = formatter.format_keepalive()
|
|
960
|
+
>>> print(keepalive)
|
|
961
|
+
: keepalive
|
|
962
|
+
|
|
963
|
+
"""
|
|
964
|
+
return ": keepalive\n\n"
|