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,335 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Centralized logging configuration for the worker process.
|
|
3
|
+
|
|
4
|
+
This module provides:
|
|
5
|
+
- Dynamic log level configuration via KUBIYA_CLI_LOG_LEVEL environment variable
|
|
6
|
+
- Multiple log format support (pretty, json, text) via KUBIYA_LOG_FORMAT
|
|
7
|
+
- Backward compatibility with CLAUDE_CODE_DEBUG
|
|
8
|
+
- Sensitive data sanitization helpers
|
|
9
|
+
- Unified structlog configuration
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import os
|
|
13
|
+
import logging
|
|
14
|
+
import time
|
|
15
|
+
import structlog
|
|
16
|
+
from typing import Any, Dict
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def get_log_level() -> int:
|
|
20
|
+
"""
|
|
21
|
+
Get log level from environment variables with fallback hierarchy.
|
|
22
|
+
|
|
23
|
+
Priority:
|
|
24
|
+
1. KUBIYA_CLI_LOG_LEVEL (new, preferred)
|
|
25
|
+
2. LOG_LEVEL (generic fallback)
|
|
26
|
+
3. CLAUDE_CODE_DEBUG=true → DEBUG (deprecated, with warning)
|
|
27
|
+
4. Default: INFO
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
int: Python logging level constant (logging.DEBUG, logging.INFO, etc.)
|
|
31
|
+
"""
|
|
32
|
+
logger = structlog.get_logger(__name__)
|
|
33
|
+
|
|
34
|
+
# Check KUBIYA_CLI_LOG_LEVEL first (preferred)
|
|
35
|
+
kubiya_log_level = os.getenv("KUBIYA_CLI_LOG_LEVEL", "").upper()
|
|
36
|
+
if kubiya_log_level:
|
|
37
|
+
level_map = {
|
|
38
|
+
"DEBUG": logging.DEBUG,
|
|
39
|
+
"INFO": logging.INFO,
|
|
40
|
+
"WARNING": logging.WARNING,
|
|
41
|
+
"ERROR": logging.ERROR,
|
|
42
|
+
"CRITICAL": logging.CRITICAL,
|
|
43
|
+
}
|
|
44
|
+
if kubiya_log_level in level_map:
|
|
45
|
+
return level_map[kubiya_log_level]
|
|
46
|
+
|
|
47
|
+
# Check generic LOG_LEVEL
|
|
48
|
+
log_level = os.getenv("LOG_LEVEL", "").upper()
|
|
49
|
+
if log_level:
|
|
50
|
+
level_map = {
|
|
51
|
+
"DEBUG": logging.DEBUG,
|
|
52
|
+
"INFO": logging.INFO,
|
|
53
|
+
"WARNING": logging.WARNING,
|
|
54
|
+
"ERROR": logging.ERROR,
|
|
55
|
+
"CRITICAL": logging.CRITICAL,
|
|
56
|
+
}
|
|
57
|
+
if log_level in level_map:
|
|
58
|
+
return level_map[log_level]
|
|
59
|
+
|
|
60
|
+
# Check deprecated CLAUDE_CODE_DEBUG
|
|
61
|
+
if os.getenv("CLAUDE_CODE_DEBUG", "false").lower() == "true":
|
|
62
|
+
# Show deprecation warning (but only once, using a module-level flag)
|
|
63
|
+
if not hasattr(get_log_level, "_warned"):
|
|
64
|
+
logger.warning(
|
|
65
|
+
"deprecated_env_var",
|
|
66
|
+
old_var="CLAUDE_CODE_DEBUG",
|
|
67
|
+
new_var="KUBIYA_CLI_LOG_LEVEL",
|
|
68
|
+
message="CLAUDE_CODE_DEBUG is deprecated. Please use KUBIYA_CLI_LOG_LEVEL=DEBUG instead."
|
|
69
|
+
)
|
|
70
|
+
get_log_level._warned = True
|
|
71
|
+
return logging.DEBUG
|
|
72
|
+
|
|
73
|
+
# Default to INFO
|
|
74
|
+
return logging.INFO
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def get_log_format() -> str:
|
|
78
|
+
"""
|
|
79
|
+
Get log format from environment variable.
|
|
80
|
+
|
|
81
|
+
Priority:
|
|
82
|
+
1. KUBIYA_LOG_FORMAT
|
|
83
|
+
2. Default: "pretty"
|
|
84
|
+
|
|
85
|
+
Supported formats:
|
|
86
|
+
- pretty: Human-readable colored output with emojis (default)
|
|
87
|
+
- json: JSON-formatted for log aggregation
|
|
88
|
+
- text: Simple text without colors
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
str: Log format ("pretty", "json", or "text")
|
|
92
|
+
"""
|
|
93
|
+
log_format = os.getenv("KUBIYA_LOG_FORMAT", "pretty").lower()
|
|
94
|
+
if log_format not in ["pretty", "json", "text"]:
|
|
95
|
+
# Invalid format, default to pretty
|
|
96
|
+
return "pretty"
|
|
97
|
+
return log_format
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def sanitize_value(key: str, value: Any) -> str:
|
|
101
|
+
"""
|
|
102
|
+
Sanitize sensitive values based on key name.
|
|
103
|
+
|
|
104
|
+
Keys containing TOKEN, SECRET, PASSWORD, KEY, or CREDENTIAL will be masked.
|
|
105
|
+
Shows first 10 and last 5 characters for values longer than 15 chars,
|
|
106
|
+
otherwise shows "***".
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
key: The key/variable name
|
|
110
|
+
value: The value to potentially sanitize
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
str: Sanitized value if key is sensitive, otherwise original value as string
|
|
114
|
+
"""
|
|
115
|
+
sensitive_keywords = ["TOKEN", "SECRET", "PASSWORD", "KEY", "CREDENTIAL"]
|
|
116
|
+
|
|
117
|
+
# Check if key contains any sensitive keyword
|
|
118
|
+
is_sensitive = any(keyword in key.upper() for keyword in sensitive_keywords)
|
|
119
|
+
|
|
120
|
+
if not is_sensitive:
|
|
121
|
+
return str(value)
|
|
122
|
+
|
|
123
|
+
# Sanitize sensitive value
|
|
124
|
+
value_str = str(value)
|
|
125
|
+
if len(value_str) > 15:
|
|
126
|
+
return f"{value_str[:10]}...{value_str[-5:]}"
|
|
127
|
+
else:
|
|
128
|
+
return "***"
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def pretty_console_renderer(logger, name, event_dict):
|
|
132
|
+
"""
|
|
133
|
+
Render logs in a pretty, human-readable format instead of JSON.
|
|
134
|
+
Uses colors and emojis for better readability.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
logger: The logger instance
|
|
138
|
+
name: The logger name
|
|
139
|
+
event_dict: Dictionary containing log event data
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
str: Formatted log message
|
|
143
|
+
"""
|
|
144
|
+
level = event_dict.get("level", "info").upper()
|
|
145
|
+
event = event_dict.get("event", "")
|
|
146
|
+
timestamp = event_dict.get("timestamp", "")
|
|
147
|
+
|
|
148
|
+
# Extract timestamp (just time part)
|
|
149
|
+
if timestamp:
|
|
150
|
+
try:
|
|
151
|
+
time_part = timestamp.split("T")[1].split(".")[0] # HH:MM:SS
|
|
152
|
+
except:
|
|
153
|
+
time_part = timestamp
|
|
154
|
+
else:
|
|
155
|
+
time_part = time.strftime("%H:%M:%S")
|
|
156
|
+
|
|
157
|
+
# Color codes
|
|
158
|
+
RESET = "\033[0m"
|
|
159
|
+
GRAY = "\033[90m"
|
|
160
|
+
GREEN = "\033[92m"
|
|
161
|
+
YELLOW = "\033[93m"
|
|
162
|
+
RED = "\033[91m"
|
|
163
|
+
CYAN = "\033[96m"
|
|
164
|
+
BOLD = "\033[1m"
|
|
165
|
+
|
|
166
|
+
# Level icons and colors
|
|
167
|
+
level_config = {
|
|
168
|
+
"INFO": ("ℹ️", CYAN),
|
|
169
|
+
"WARNING": ("⚠️", YELLOW),
|
|
170
|
+
"ERROR": ("❌", RED),
|
|
171
|
+
"DEBUG": ("🔍", GRAY),
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
icon, color = level_config.get(level, ("•", RESET))
|
|
175
|
+
|
|
176
|
+
# Format the main message
|
|
177
|
+
message = f"{GRAY}[{time_part}]{RESET} {icon} {event}"
|
|
178
|
+
|
|
179
|
+
# Add relevant context (skip internal keys)
|
|
180
|
+
skip_keys = {"level", "event", "timestamp", "logger"}
|
|
181
|
+
context_parts = []
|
|
182
|
+
|
|
183
|
+
for key, value in event_dict.items():
|
|
184
|
+
if key in skip_keys:
|
|
185
|
+
continue
|
|
186
|
+
# Format value nicely
|
|
187
|
+
if isinstance(value, bool):
|
|
188
|
+
value_str = "✓" if value else "✗"
|
|
189
|
+
elif isinstance(value, str) and len(value) > 60:
|
|
190
|
+
value_str = value[:57] + "..."
|
|
191
|
+
else:
|
|
192
|
+
value_str = str(value)
|
|
193
|
+
|
|
194
|
+
context_parts.append(f"{GRAY}{key}={RESET}{value_str}")
|
|
195
|
+
|
|
196
|
+
if context_parts:
|
|
197
|
+
message += f" {GRAY}({', '.join(context_parts)}){RESET}"
|
|
198
|
+
|
|
199
|
+
return message
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def json_renderer(logger, name, event_dict):
|
|
203
|
+
"""
|
|
204
|
+
Render logs in JSON format for log aggregation systems.
|
|
205
|
+
|
|
206
|
+
Args:
|
|
207
|
+
logger: The logger instance
|
|
208
|
+
name: The logger name
|
|
209
|
+
event_dict: Dictionary containing log event data
|
|
210
|
+
|
|
211
|
+
Returns:
|
|
212
|
+
str: JSON-formatted log message
|
|
213
|
+
"""
|
|
214
|
+
import json
|
|
215
|
+
return json.dumps(event_dict)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def text_renderer(logger, name, event_dict):
|
|
219
|
+
"""
|
|
220
|
+
Render logs in simple text format without colors.
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
logger: The logger instance
|
|
224
|
+
name: The logger name
|
|
225
|
+
event_dict: Dictionary containing log event data
|
|
226
|
+
|
|
227
|
+
Returns:
|
|
228
|
+
str: Simple text log message
|
|
229
|
+
"""
|
|
230
|
+
level = event_dict.get("level", "info").upper()
|
|
231
|
+
event = event_dict.get("event", "")
|
|
232
|
+
timestamp = event_dict.get("timestamp", "")
|
|
233
|
+
|
|
234
|
+
# Extract timestamp (just time part)
|
|
235
|
+
if timestamp:
|
|
236
|
+
try:
|
|
237
|
+
time_part = timestamp.split("T")[1].split(".")[0] # HH:MM:SS
|
|
238
|
+
except:
|
|
239
|
+
time_part = timestamp
|
|
240
|
+
else:
|
|
241
|
+
time_part = time.strftime("%H:%M:%S")
|
|
242
|
+
|
|
243
|
+
# Format the main message
|
|
244
|
+
message = f"[{time_part}] {level:8} {event}"
|
|
245
|
+
|
|
246
|
+
# Add relevant context (skip internal keys)
|
|
247
|
+
skip_keys = {"level", "event", "timestamp", "logger"}
|
|
248
|
+
context_parts = []
|
|
249
|
+
|
|
250
|
+
for key, value in event_dict.items():
|
|
251
|
+
if key in skip_keys:
|
|
252
|
+
continue
|
|
253
|
+
context_parts.append(f"{key}={value}")
|
|
254
|
+
|
|
255
|
+
if context_parts:
|
|
256
|
+
message += f" ({', '.join(context_parts)})"
|
|
257
|
+
|
|
258
|
+
return message
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def add_trace_context(logger, method_name, event_dict):
|
|
262
|
+
"""
|
|
263
|
+
Processor that automatically adds OpenTelemetry trace context to all logs.
|
|
264
|
+
|
|
265
|
+
This enables trace-log correlation by including:
|
|
266
|
+
- trace_id: The current trace ID (32-character hex)
|
|
267
|
+
- span_id: The current span ID (16-character hex)
|
|
268
|
+
|
|
269
|
+
These IDs allow you to:
|
|
270
|
+
1. Copy trace_id from logs → Search in Jaeger UI
|
|
271
|
+
2. See which logs belong to which trace
|
|
272
|
+
3. Correlate application logs with distributed traces
|
|
273
|
+
|
|
274
|
+
Args:
|
|
275
|
+
logger: The logger instance
|
|
276
|
+
method_name: The method being called (info, warning, etc.)
|
|
277
|
+
event_dict: Dictionary containing log event data
|
|
278
|
+
|
|
279
|
+
Returns:
|
|
280
|
+
dict: Updated event_dict with trace context
|
|
281
|
+
"""
|
|
282
|
+
try:
|
|
283
|
+
from control_plane_api.app.observability.optional import get_current_span
|
|
284
|
+
|
|
285
|
+
span = get_current_span()
|
|
286
|
+
if span and span.is_recording():
|
|
287
|
+
span_context = span.get_span_context()
|
|
288
|
+
if span_context and span_context.is_valid:
|
|
289
|
+
# Add trace context to every log message
|
|
290
|
+
event_dict["trace_id"] = format(span_context.trace_id, '032x')
|
|
291
|
+
event_dict["span_id"] = format(span_context.span_id, '016x')
|
|
292
|
+
except Exception:
|
|
293
|
+
# If OTEL not available or error, silently continue
|
|
294
|
+
# (graceful degradation - logs still work without tracing)
|
|
295
|
+
pass
|
|
296
|
+
|
|
297
|
+
return event_dict
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def configure_logging() -> None:
|
|
301
|
+
"""
|
|
302
|
+
Configure structlog with dynamic settings from environment variables.
|
|
303
|
+
|
|
304
|
+
This function sets up structlog with:
|
|
305
|
+
- Dynamic log level from KUBIYA_CLI_LOG_LEVEL (or fallback)
|
|
306
|
+
- Dynamic log format from KUBIYA_LOG_FORMAT
|
|
307
|
+
- Appropriate renderer based on format
|
|
308
|
+
- Standard processors for context and timestamps
|
|
309
|
+
- Automatic trace context injection (trace_id, span_id)
|
|
310
|
+
|
|
311
|
+
Should be called once at application startup before any logging occurs.
|
|
312
|
+
"""
|
|
313
|
+
log_level = get_log_level()
|
|
314
|
+
log_format = get_log_format()
|
|
315
|
+
|
|
316
|
+
# Select renderer based on format
|
|
317
|
+
if log_format == "json":
|
|
318
|
+
renderer = json_renderer
|
|
319
|
+
elif log_format == "text":
|
|
320
|
+
renderer = text_renderer
|
|
321
|
+
else: # "pretty" (default)
|
|
322
|
+
renderer = pretty_console_renderer
|
|
323
|
+
|
|
324
|
+
# Configure structlog with trace context processor
|
|
325
|
+
structlog.configure(
|
|
326
|
+
processors=[
|
|
327
|
+
structlog.contextvars.merge_contextvars,
|
|
328
|
+
structlog.processors.add_log_level,
|
|
329
|
+
add_trace_context, # ← Automatically adds trace_id and span_id to all logs
|
|
330
|
+
structlog.processors.TimeStamper(fmt="iso"),
|
|
331
|
+
renderer,
|
|
332
|
+
],
|
|
333
|
+
wrapper_class=structlog.make_filtering_bound_logger(log_level),
|
|
334
|
+
logger_factory=structlog.PrintLoggerFactory(),
|
|
335
|
+
)
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
"""Unified logging helper for clear and structured logs across the worker.
|
|
2
|
+
|
|
3
|
+
This module provides consistent logging utilities to make logs easy to read and understand.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import structlog
|
|
7
|
+
from typing import Optional, Dict, Any
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ExecutionLogger:
|
|
12
|
+
"""Unified logger for execution workflows with clear, human-readable output."""
|
|
13
|
+
|
|
14
|
+
def __init__(self, logger_name: str = "worker"):
|
|
15
|
+
self.logger = structlog.get_logger(logger_name)
|
|
16
|
+
|
|
17
|
+
@staticmethod
|
|
18
|
+
def _format_execution_id(execution_id: str) -> str:
|
|
19
|
+
"""Format execution ID for display (show first 8 chars)."""
|
|
20
|
+
if not execution_id:
|
|
21
|
+
return "unknown"
|
|
22
|
+
return execution_id[:8] if len(execution_id) > 8 else execution_id
|
|
23
|
+
|
|
24
|
+
@staticmethod
|
|
25
|
+
def _get_emoji(status: str) -> str:
|
|
26
|
+
"""Get emoji for status."""
|
|
27
|
+
emoji_map = {
|
|
28
|
+
"started": "🚀",
|
|
29
|
+
"running": "⚙️",
|
|
30
|
+
"streaming": "📡",
|
|
31
|
+
"waiting": "⏳",
|
|
32
|
+
"completed": "✅",
|
|
33
|
+
"failed": "❌",
|
|
34
|
+
"cancelled": "🛑",
|
|
35
|
+
"retry": "🔄",
|
|
36
|
+
"warning": "⚠️",
|
|
37
|
+
"info": "ℹ️",
|
|
38
|
+
}
|
|
39
|
+
return emoji_map.get(status.lower(), "•")
|
|
40
|
+
|
|
41
|
+
def execution_started(
|
|
42
|
+
self,
|
|
43
|
+
execution_id: str,
|
|
44
|
+
agent_id: Optional[str] = None,
|
|
45
|
+
model: Optional[str] = None,
|
|
46
|
+
runtime: Optional[str] = None,
|
|
47
|
+
):
|
|
48
|
+
"""Log execution start."""
|
|
49
|
+
exec_short = self._format_execution_id(execution_id)
|
|
50
|
+
msg = f"🚀 Execution Started: {exec_short}"
|
|
51
|
+
|
|
52
|
+
details = []
|
|
53
|
+
if agent_id:
|
|
54
|
+
details.append(f"agent={agent_id[:8]}")
|
|
55
|
+
if model:
|
|
56
|
+
details.append(f"model={model}")
|
|
57
|
+
if runtime:
|
|
58
|
+
details.append(f"runtime={runtime}")
|
|
59
|
+
|
|
60
|
+
if details:
|
|
61
|
+
msg += f" ({', '.join(details)})"
|
|
62
|
+
|
|
63
|
+
self.logger.info(msg, execution_id=execution_id)
|
|
64
|
+
|
|
65
|
+
# Print clear separator to distinguish between executions
|
|
66
|
+
print(f"\n{'─' * 80}")
|
|
67
|
+
print(f"🚀 Execution Started: {exec_short}")
|
|
68
|
+
if details:
|
|
69
|
+
print(f" {' | '.join(details)}")
|
|
70
|
+
print(f"{'─' * 80}")
|
|
71
|
+
|
|
72
|
+
def execution_completed(self, execution_id: str, duration_ms: Optional[int] = None):
|
|
73
|
+
"""Log execution completion."""
|
|
74
|
+
exec_short = self._format_execution_id(execution_id)
|
|
75
|
+
msg = f"✅ Execution Completed: {exec_short}"
|
|
76
|
+
|
|
77
|
+
if duration_ms:
|
|
78
|
+
duration_sec = duration_ms / 1000
|
|
79
|
+
msg += f" (took {duration_sec:.2f}s)"
|
|
80
|
+
|
|
81
|
+
self.logger.info(msg, execution_id=execution_id)
|
|
82
|
+
|
|
83
|
+
# Print clear end separator
|
|
84
|
+
print(f"\n{'─' * 80}")
|
|
85
|
+
print(msg)
|
|
86
|
+
print(f"{'─' * 80}\n")
|
|
87
|
+
|
|
88
|
+
def execution_failed(
|
|
89
|
+
self,
|
|
90
|
+
execution_id: str,
|
|
91
|
+
error: str,
|
|
92
|
+
error_type: Optional[str] = None,
|
|
93
|
+
recoverable: bool = False,
|
|
94
|
+
):
|
|
95
|
+
"""Log execution failure."""
|
|
96
|
+
exec_short = self._format_execution_id(execution_id)
|
|
97
|
+
|
|
98
|
+
if recoverable:
|
|
99
|
+
msg = f"⚠️ Execution Error (Recoverable): {exec_short}"
|
|
100
|
+
else:
|
|
101
|
+
msg = f"❌ Execution Failed: {exec_short}"
|
|
102
|
+
|
|
103
|
+
self.logger.error(
|
|
104
|
+
msg,
|
|
105
|
+
execution_id=execution_id,
|
|
106
|
+
error=error,
|
|
107
|
+
error_type=error_type,
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
# Print clear failure separator
|
|
111
|
+
print(f"\n{'─' * 80}")
|
|
112
|
+
print(msg)
|
|
113
|
+
if error_type:
|
|
114
|
+
print(f" Error Type: {error_type}")
|
|
115
|
+
print(f" Error: {error}")
|
|
116
|
+
print(f"{'─' * 80}\n")
|
|
117
|
+
|
|
118
|
+
def activity_started(
|
|
119
|
+
self,
|
|
120
|
+
activity_name: str,
|
|
121
|
+
execution_id: str,
|
|
122
|
+
details: Optional[Dict[str, Any]] = None,
|
|
123
|
+
):
|
|
124
|
+
"""Log activity start."""
|
|
125
|
+
exec_short = self._format_execution_id(execution_id)
|
|
126
|
+
msg = f"⚙️ {activity_name}: {exec_short}"
|
|
127
|
+
|
|
128
|
+
if details:
|
|
129
|
+
detail_str = ", ".join([f"{k}={v}" for k, v in details.items()])
|
|
130
|
+
msg += f" ({detail_str})"
|
|
131
|
+
|
|
132
|
+
self.logger.info(msg, execution_id=execution_id, activity=activity_name)
|
|
133
|
+
print(f" → {msg}")
|
|
134
|
+
|
|
135
|
+
def activity_completed(
|
|
136
|
+
self,
|
|
137
|
+
activity_name: str,
|
|
138
|
+
execution_id: str,
|
|
139
|
+
result: Optional[str] = None,
|
|
140
|
+
):
|
|
141
|
+
"""Log activity completion."""
|
|
142
|
+
exec_short = self._format_execution_id(execution_id)
|
|
143
|
+
msg = f"✅ {activity_name}: {exec_short}"
|
|
144
|
+
|
|
145
|
+
if result:
|
|
146
|
+
msg += f" - {result}"
|
|
147
|
+
|
|
148
|
+
self.logger.info(msg, execution_id=execution_id, activity=activity_name)
|
|
149
|
+
print(f" ✓ {msg}")
|
|
150
|
+
|
|
151
|
+
def activity_failed(
|
|
152
|
+
self,
|
|
153
|
+
activity_name: str,
|
|
154
|
+
execution_id: str,
|
|
155
|
+
error: str,
|
|
156
|
+
will_retry: bool = False,
|
|
157
|
+
):
|
|
158
|
+
"""Log activity failure."""
|
|
159
|
+
exec_short = self._format_execution_id(execution_id)
|
|
160
|
+
|
|
161
|
+
if will_retry:
|
|
162
|
+
msg = f"🔄 {activity_name} Failed (Retrying): {exec_short}"
|
|
163
|
+
else:
|
|
164
|
+
msg = f"❌ {activity_name} Failed: {exec_short}"
|
|
165
|
+
|
|
166
|
+
self.logger.error(
|
|
167
|
+
msg,
|
|
168
|
+
execution_id=execution_id,
|
|
169
|
+
activity=activity_name,
|
|
170
|
+
error=error,
|
|
171
|
+
)
|
|
172
|
+
print(f" ✗ {msg}")
|
|
173
|
+
print(f" Error: {error}")
|
|
174
|
+
|
|
175
|
+
def streaming_started(self, execution_id: str):
|
|
176
|
+
"""Log streaming start."""
|
|
177
|
+
exec_short = self._format_execution_id(execution_id)
|
|
178
|
+
msg = f"📡 Streaming Response: {exec_short}"
|
|
179
|
+
self.logger.info(msg, execution_id=execution_id)
|
|
180
|
+
print(f" → {msg}")
|
|
181
|
+
|
|
182
|
+
def streaming_progress(
|
|
183
|
+
self,
|
|
184
|
+
execution_id: str,
|
|
185
|
+
chunks_received: int,
|
|
186
|
+
response_length: int,
|
|
187
|
+
):
|
|
188
|
+
"""Log streaming progress (only at milestones)."""
|
|
189
|
+
exec_short = self._format_execution_id(execution_id)
|
|
190
|
+
|
|
191
|
+
# Only log at milestones to avoid spam
|
|
192
|
+
if chunks_received % 50 == 0 or chunks_received < 5:
|
|
193
|
+
msg = f"📡 Streaming: {exec_short} - {chunks_received} chunks, {response_length} chars"
|
|
194
|
+
self.logger.debug(msg, execution_id=execution_id)
|
|
195
|
+
# Only print for first few chunks or milestones
|
|
196
|
+
if chunks_received < 5:
|
|
197
|
+
print(f" {msg}")
|
|
198
|
+
|
|
199
|
+
def status_update(
|
|
200
|
+
self,
|
|
201
|
+
execution_id: str,
|
|
202
|
+
old_status: Optional[str],
|
|
203
|
+
new_status: str,
|
|
204
|
+
reason: Optional[str] = None,
|
|
205
|
+
):
|
|
206
|
+
"""Log status update."""
|
|
207
|
+
exec_short = self._format_execution_id(execution_id)
|
|
208
|
+
emoji = self._get_emoji(new_status)
|
|
209
|
+
|
|
210
|
+
if old_status:
|
|
211
|
+
msg = f"{emoji} Status Change: {exec_short} ({old_status} → {new_status})"
|
|
212
|
+
else:
|
|
213
|
+
msg = f"{emoji} Status: {exec_short} ({new_status})"
|
|
214
|
+
|
|
215
|
+
if reason:
|
|
216
|
+
msg += f" - {reason}"
|
|
217
|
+
|
|
218
|
+
self.logger.info(msg, execution_id=execution_id, status=new_status)
|
|
219
|
+
print(f" {msg}")
|
|
220
|
+
|
|
221
|
+
def workflow_signal(
|
|
222
|
+
self,
|
|
223
|
+
execution_id: str,
|
|
224
|
+
signal_name: str,
|
|
225
|
+
details: Optional[str] = None,
|
|
226
|
+
):
|
|
227
|
+
"""Log workflow signal received."""
|
|
228
|
+
exec_short = self._format_execution_id(execution_id)
|
|
229
|
+
msg = f"📨 Signal Received: {exec_short} ({signal_name})"
|
|
230
|
+
|
|
231
|
+
if details:
|
|
232
|
+
msg += f" - {details}"
|
|
233
|
+
|
|
234
|
+
self.logger.info(msg, execution_id=execution_id, signal=signal_name)
|
|
235
|
+
print(f" {msg}")
|
|
236
|
+
|
|
237
|
+
def turn_started(self, execution_id: str, turn_number: int):
|
|
238
|
+
"""Log conversation turn start."""
|
|
239
|
+
exec_short = self._format_execution_id(execution_id)
|
|
240
|
+
msg = f"💬 Turn {turn_number} Started: {exec_short}"
|
|
241
|
+
self.logger.info(msg, execution_id=execution_id, turn=turn_number)
|
|
242
|
+
print(f"\n {msg}")
|
|
243
|
+
|
|
244
|
+
def turn_completed(
|
|
245
|
+
self,
|
|
246
|
+
execution_id: str,
|
|
247
|
+
turn_number: int,
|
|
248
|
+
tokens_used: Optional[int] = None,
|
|
249
|
+
):
|
|
250
|
+
"""Log conversation turn completion."""
|
|
251
|
+
exec_short = self._format_execution_id(execution_id)
|
|
252
|
+
msg = f"💬 Turn {turn_number} Completed: {exec_short}"
|
|
253
|
+
|
|
254
|
+
if tokens_used:
|
|
255
|
+
msg += f" ({tokens_used} tokens)"
|
|
256
|
+
|
|
257
|
+
self.logger.info(msg, execution_id=execution_id, turn=turn_number)
|
|
258
|
+
print(f" {msg}")
|
|
259
|
+
|
|
260
|
+
def tool_call_started(
|
|
261
|
+
self,
|
|
262
|
+
execution_id: str,
|
|
263
|
+
tool_name: str,
|
|
264
|
+
tool_id: Optional[str] = None,
|
|
265
|
+
):
|
|
266
|
+
"""Log tool call start."""
|
|
267
|
+
exec_short = self._format_execution_id(execution_id)
|
|
268
|
+
msg = f"🔧 Tool Called: {tool_name}"
|
|
269
|
+
|
|
270
|
+
if tool_id:
|
|
271
|
+
msg += f" (id: {tool_id[:8]})"
|
|
272
|
+
|
|
273
|
+
self.logger.info(
|
|
274
|
+
msg,
|
|
275
|
+
execution_id=execution_id,
|
|
276
|
+
tool_name=tool_name,
|
|
277
|
+
tool_id=tool_id,
|
|
278
|
+
)
|
|
279
|
+
print(f" → {msg}")
|
|
280
|
+
|
|
281
|
+
def tool_call_completed(
|
|
282
|
+
self,
|
|
283
|
+
execution_id: str,
|
|
284
|
+
tool_name: str,
|
|
285
|
+
success: bool,
|
|
286
|
+
duration_ms: Optional[int] = None,
|
|
287
|
+
):
|
|
288
|
+
"""Log tool call completion."""
|
|
289
|
+
exec_short = self._format_execution_id(execution_id)
|
|
290
|
+
|
|
291
|
+
if success:
|
|
292
|
+
emoji = "✅"
|
|
293
|
+
status = "completed"
|
|
294
|
+
else:
|
|
295
|
+
emoji = "❌"
|
|
296
|
+
status = "failed"
|
|
297
|
+
|
|
298
|
+
msg = f"{emoji} Tool {status}: {tool_name}"
|
|
299
|
+
|
|
300
|
+
if duration_ms:
|
|
301
|
+
msg += f" (took {duration_ms}ms)"
|
|
302
|
+
|
|
303
|
+
self.logger.info(
|
|
304
|
+
msg,
|
|
305
|
+
execution_id=execution_id,
|
|
306
|
+
tool_name=tool_name,
|
|
307
|
+
success=success,
|
|
308
|
+
)
|
|
309
|
+
print(f" {msg}")
|
|
310
|
+
|
|
311
|
+
def warning(self, execution_id: str, message: str, details: Optional[Dict] = None):
|
|
312
|
+
"""Log warning."""
|
|
313
|
+
exec_short = self._format_execution_id(execution_id)
|
|
314
|
+
msg = f"⚠️ Warning: {exec_short} - {message}"
|
|
315
|
+
|
|
316
|
+
self.logger.warning(msg, execution_id=execution_id, **(details or {}))
|
|
317
|
+
print(f" {msg}")
|
|
318
|
+
|
|
319
|
+
def debug(self, execution_id: str, message: str):
|
|
320
|
+
"""Log debug message (only in debug mode)."""
|
|
321
|
+
exec_short = self._format_execution_id(execution_id)
|
|
322
|
+
self.logger.debug(message, execution_id=execution_id)
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
# Global logger instance
|
|
326
|
+
execution_logger = ExecutionLogger()
|