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,611 @@
|
|
|
1
|
+
"""Team execution workflow for Temporal"""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from datetime import timedelta
|
|
5
|
+
from typing import Optional, List, Dict, Any
|
|
6
|
+
from temporalio import workflow
|
|
7
|
+
from temporalio.common import RetryPolicy
|
|
8
|
+
import asyncio
|
|
9
|
+
import os
|
|
10
|
+
|
|
11
|
+
with workflow.unsafe.imports_passed_through():
|
|
12
|
+
from control_plane_api.worker.activities.team_activities import (
|
|
13
|
+
get_team_agents,
|
|
14
|
+
execute_team_coordination,
|
|
15
|
+
ActivityGetTeamAgentsInput,
|
|
16
|
+
ActivityExecuteTeamInput,
|
|
17
|
+
)
|
|
18
|
+
from control_plane_api.worker.activities.agent_activities import (
|
|
19
|
+
update_execution_status,
|
|
20
|
+
get_execution_details,
|
|
21
|
+
submit_runtime_analytics_activity,
|
|
22
|
+
ActivityUpdateExecutionInput,
|
|
23
|
+
ActivityGetExecutionInput,
|
|
24
|
+
AnalyticsActivityInput,
|
|
25
|
+
)
|
|
26
|
+
from control_plane_api.worker.activities.runtime_activities import (
|
|
27
|
+
publish_user_message,
|
|
28
|
+
PublishUserMessageInput,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
# Heartbeat timeout: Prove activity is alive (default 30 minutes)
|
|
33
|
+
# This should be reasonable - heartbeats confirm the activity hasn't crashed
|
|
34
|
+
HEARTBEAT_TIMEOUT_SECONDS = int(os.environ.get("TEAM_ACTIVITY_HEARTBEAT_TIMEOUT_SECONDS", "1800"))
|
|
35
|
+
|
|
36
|
+
# Activity execution timeout: Total time for activity to complete (default 24 hours)
|
|
37
|
+
# This is the maximum time an activity can run. For streaming workflows, this should be VERY long
|
|
38
|
+
# since the activity may stream for hours while the user interacts with the team
|
|
39
|
+
ACTIVITY_EXECUTION_TIMEOUT_SECONDS = int(os.environ.get("TEAM_ACTIVITY_EXECUTION_TIMEOUT_SECONDS", "86400"))
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@dataclass
|
|
43
|
+
class TeamExecutionInput:
|
|
44
|
+
"""Input for team execution workflow"""
|
|
45
|
+
execution_id: str
|
|
46
|
+
team_id: str
|
|
47
|
+
organization_id: str
|
|
48
|
+
prompt: str
|
|
49
|
+
system_prompt: Optional[str] = None
|
|
50
|
+
team_config: dict = None
|
|
51
|
+
user_metadata: dict = None
|
|
52
|
+
mcp_servers: dict = None # MCP servers configuration
|
|
53
|
+
initial_message_timestamp: Optional[str] = None # Timestamp for initial user message
|
|
54
|
+
|
|
55
|
+
def __post_init__(self):
|
|
56
|
+
if self.team_config is None:
|
|
57
|
+
self.team_config = {}
|
|
58
|
+
if self.user_metadata is None:
|
|
59
|
+
self.user_metadata = {}
|
|
60
|
+
if self.mcp_servers is None:
|
|
61
|
+
self.mcp_servers = {}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@dataclass
|
|
65
|
+
class ChatMessage:
|
|
66
|
+
"""Represents a message in the conversation"""
|
|
67
|
+
role: str # "user", "assistant", "system", "tool"
|
|
68
|
+
content: str
|
|
69
|
+
timestamp: str
|
|
70
|
+
tool_name: Optional[str] = None
|
|
71
|
+
tool_input: Optional[Dict[str, Any]] = None
|
|
72
|
+
tool_output: Optional[Dict[str, Any]] = None
|
|
73
|
+
message_id: Optional[str] = None # Unique identifier for deduplication
|
|
74
|
+
user_id: Optional[str] = None # User who sent the message
|
|
75
|
+
user_name: Optional[str] = None
|
|
76
|
+
user_email: Optional[str] = None
|
|
77
|
+
user_avatar: Optional[str] = None
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@dataclass
|
|
81
|
+
class ExecutionState:
|
|
82
|
+
"""Current state of the execution for queries"""
|
|
83
|
+
status: str # "pending", "running", "waiting_for_input", "paused", "completed", "failed"
|
|
84
|
+
messages: List[ChatMessage] = field(default_factory=list)
|
|
85
|
+
current_response: str = ""
|
|
86
|
+
error_message: Optional[str] = None
|
|
87
|
+
usage: Dict[str, Any] = field(default_factory=dict)
|
|
88
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
89
|
+
is_waiting_for_input: bool = False
|
|
90
|
+
should_complete: bool = False
|
|
91
|
+
is_paused: bool = False
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@workflow.defn
|
|
95
|
+
class TeamExecutionWorkflow:
|
|
96
|
+
"""
|
|
97
|
+
Workflow for executing a team of agents with HITL support.
|
|
98
|
+
|
|
99
|
+
This workflow:
|
|
100
|
+
1. Gets team agents
|
|
101
|
+
2. Coordinates execution across agents
|
|
102
|
+
3. Aggregates results
|
|
103
|
+
4. Updates execution status
|
|
104
|
+
5. Supports queries for real-time state access
|
|
105
|
+
6. Supports signals for adding followup messages
|
|
106
|
+
"""
|
|
107
|
+
|
|
108
|
+
def __init__(self) -> None:
|
|
109
|
+
"""Initialize workflow state"""
|
|
110
|
+
self._state = ExecutionState(status="pending")
|
|
111
|
+
self._lock = asyncio.Lock()
|
|
112
|
+
self._new_message_count = 0
|
|
113
|
+
self._processed_message_count = 0
|
|
114
|
+
|
|
115
|
+
@workflow.query
|
|
116
|
+
def get_state(self) -> ExecutionState:
|
|
117
|
+
"""Query handler: Get current execution state including messages and status"""
|
|
118
|
+
return self._state
|
|
119
|
+
|
|
120
|
+
@workflow.signal
|
|
121
|
+
async def add_message(self, message: ChatMessage) -> None:
|
|
122
|
+
"""
|
|
123
|
+
Signal handler: Add a message to the conversation.
|
|
124
|
+
This allows clients to send followup messages while the workflow is running.
|
|
125
|
+
"""
|
|
126
|
+
async with self._lock:
|
|
127
|
+
self._state.messages.append(message)
|
|
128
|
+
self._new_message_count += 1
|
|
129
|
+
self._state.is_waiting_for_input = False
|
|
130
|
+
workflow.logger.info(
|
|
131
|
+
f"Message added to team conversation",
|
|
132
|
+
extra={
|
|
133
|
+
"role": message.role,
|
|
134
|
+
"content_preview": message.content[:100] if message.content else "",
|
|
135
|
+
"total_messages": len(self._state.messages)
|
|
136
|
+
}
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
@workflow.signal
|
|
140
|
+
async def mark_as_done(self) -> None:
|
|
141
|
+
"""
|
|
142
|
+
Signal handler: Mark the workflow as complete.
|
|
143
|
+
"""
|
|
144
|
+
async with self._lock:
|
|
145
|
+
self._state.should_complete = True
|
|
146
|
+
self._state.is_waiting_for_input = False
|
|
147
|
+
workflow.logger.info("Team workflow marked as done by user")
|
|
148
|
+
|
|
149
|
+
@workflow.signal
|
|
150
|
+
async def update_streaming_response(self, current_response: str) -> None:
|
|
151
|
+
"""
|
|
152
|
+
Signal handler: Update current streaming response.
|
|
153
|
+
Activity sends this periodically during execution for state tracking.
|
|
154
|
+
"""
|
|
155
|
+
async with self._lock:
|
|
156
|
+
self._state.current_response = current_response
|
|
157
|
+
workflow.logger.info(
|
|
158
|
+
f"Streaming response updated",
|
|
159
|
+
extra={"response_length": len(current_response)}
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
@workflow.signal
|
|
163
|
+
async def pause_execution(self) -> None:
|
|
164
|
+
"""
|
|
165
|
+
Signal handler: Pause the workflow execution.
|
|
166
|
+
This pauses the workflow - it will stop processing but remain active.
|
|
167
|
+
Resume can be called to continue execution.
|
|
168
|
+
"""
|
|
169
|
+
async with self._lock:
|
|
170
|
+
if not self._state.is_paused:
|
|
171
|
+
self._state.is_paused = True
|
|
172
|
+
self._state.status = "paused"
|
|
173
|
+
workflow.logger.info("Team workflow paused by user")
|
|
174
|
+
|
|
175
|
+
@workflow.signal
|
|
176
|
+
async def resume_execution(self) -> None:
|
|
177
|
+
"""
|
|
178
|
+
Signal handler: Resume a paused workflow execution.
|
|
179
|
+
This resumes the workflow from where it was paused.
|
|
180
|
+
"""
|
|
181
|
+
async with self._lock:
|
|
182
|
+
if self._state.is_paused:
|
|
183
|
+
self._state.is_paused = False
|
|
184
|
+
# Restore previous status (either running or waiting_for_input)
|
|
185
|
+
self._state.status = "waiting_for_input" if self._state.is_waiting_for_input else "running"
|
|
186
|
+
workflow.logger.info("Team workflow resumed by user")
|
|
187
|
+
|
|
188
|
+
@workflow.run
|
|
189
|
+
async def run(self, input: TeamExecutionInput) -> dict:
|
|
190
|
+
"""
|
|
191
|
+
Run the team execution workflow with HITL pattern.
|
|
192
|
+
|
|
193
|
+
This workflow implements a continuous conversation loop:
|
|
194
|
+
1. Process the initial user message
|
|
195
|
+
2. Execute team coordination and return response
|
|
196
|
+
3. Wait for user input (signals)
|
|
197
|
+
4. Process followup messages in a loop
|
|
198
|
+
5. Only complete when user explicitly marks as done
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
input: Workflow input with team execution details
|
|
202
|
+
|
|
203
|
+
Returns:
|
|
204
|
+
Team execution result dict
|
|
205
|
+
"""
|
|
206
|
+
workflow.logger.info(
|
|
207
|
+
f"Starting team execution workflow with HITL pattern",
|
|
208
|
+
extra={
|
|
209
|
+
"execution_id": input.execution_id,
|
|
210
|
+
"team_id": input.team_id,
|
|
211
|
+
"organization_id": input.organization_id,
|
|
212
|
+
}
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
# Initialize state with user's initial message
|
|
216
|
+
# CRITICAL: Use real-time timestamp (not workflow.now()) to ensure chronological ordering
|
|
217
|
+
# This prevents timestamp mismatches between initial and follow-up messages
|
|
218
|
+
message_timestamp = input.initial_message_timestamp or workflow.now().isoformat()
|
|
219
|
+
|
|
220
|
+
initial_user_message = ChatMessage(
|
|
221
|
+
role="user",
|
|
222
|
+
content=input.prompt,
|
|
223
|
+
timestamp=message_timestamp,
|
|
224
|
+
message_id=f"{input.execution_id}_user_1", # Generate deterministic ID
|
|
225
|
+
)
|
|
226
|
+
self._state.messages.append(initial_user_message)
|
|
227
|
+
self._state.status = "running"
|
|
228
|
+
self._new_message_count = 1
|
|
229
|
+
self._processed_message_count = 0
|
|
230
|
+
|
|
231
|
+
try:
|
|
232
|
+
# Step 1: Update execution status to running
|
|
233
|
+
await workflow.execute_activity(
|
|
234
|
+
update_execution_status,
|
|
235
|
+
ActivityUpdateExecutionInput(
|
|
236
|
+
execution_id=input.execution_id,
|
|
237
|
+
status="running",
|
|
238
|
+
started_at=workflow.now().isoformat(),
|
|
239
|
+
execution_metadata={
|
|
240
|
+
"workflow_started": True,
|
|
241
|
+
"hitl_enabled": True,
|
|
242
|
+
},
|
|
243
|
+
),
|
|
244
|
+
start_to_close_timeout=timedelta(seconds=30),
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
# Step 1.5: Publish initial user message to stream immediately
|
|
248
|
+
# This ensures the user message appears in UI before assistant response
|
|
249
|
+
#
|
|
250
|
+
# IMPORTANT: Use workflow patching to handle existing workflows that don't have this activity
|
|
251
|
+
# Existing workflows will skip this during replay; new workflows will execute it
|
|
252
|
+
if workflow.patched("publish-user-message-v1"):
|
|
253
|
+
workflow.logger.info(
|
|
254
|
+
f"Publishing initial user message to stream",
|
|
255
|
+
extra={
|
|
256
|
+
"execution_id": str(input.execution_id)[:8] if input.execution_id else "unknown",
|
|
257
|
+
"message_id": initial_user_message.message_id,
|
|
258
|
+
}
|
|
259
|
+
)
|
|
260
|
+
await workflow.execute_activity(
|
|
261
|
+
publish_user_message,
|
|
262
|
+
PublishUserMessageInput(
|
|
263
|
+
execution_id=input.execution_id,
|
|
264
|
+
prompt=input.prompt,
|
|
265
|
+
timestamp=initial_user_message.timestamp,
|
|
266
|
+
message_id=initial_user_message.message_id,
|
|
267
|
+
user_id=input.user_metadata.get("user_id") if input.user_metadata else None,
|
|
268
|
+
user_name=input.user_metadata.get("user_name") if input.user_metadata else None,
|
|
269
|
+
user_email=input.user_metadata.get("user_email") if input.user_metadata else None,
|
|
270
|
+
user_avatar=input.user_metadata.get("user_avatar") if input.user_metadata else None,
|
|
271
|
+
),
|
|
272
|
+
start_to_close_timeout=timedelta(seconds=10),
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
# Step 2: Get team agents once at the beginning
|
|
276
|
+
workflow.logger.info(
|
|
277
|
+
f"[WORKFLOW] About to call get_team_agents",
|
|
278
|
+
extra={
|
|
279
|
+
"team_id": input.team_id,
|
|
280
|
+
"organization_id": input.organization_id,
|
|
281
|
+
}
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
team_agents = await workflow.execute_activity(
|
|
285
|
+
get_team_agents,
|
|
286
|
+
ActivityGetTeamAgentsInput(
|
|
287
|
+
team_id=input.team_id,
|
|
288
|
+
organization_id=input.organization_id,
|
|
289
|
+
),
|
|
290
|
+
start_to_close_timeout=timedelta(seconds=30),
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
workflow.logger.info(
|
|
294
|
+
f"[WORKFLOW] get_team_agents returned",
|
|
295
|
+
extra={
|
|
296
|
+
"result": team_agents,
|
|
297
|
+
"agents_count": len(team_agents.get("agents", [])) if team_agents else 0,
|
|
298
|
+
}
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
if not team_agents.get("agents"):
|
|
302
|
+
workflow.logger.error(
|
|
303
|
+
f"[WORKFLOW] NO AGENTS RETURNED!",
|
|
304
|
+
extra={
|
|
305
|
+
"team_agents": team_agents,
|
|
306
|
+
"team_id": input.team_id,
|
|
307
|
+
"organization_id": input.organization_id,
|
|
308
|
+
}
|
|
309
|
+
)
|
|
310
|
+
raise ValueError("No agents found in team")
|
|
311
|
+
|
|
312
|
+
# HITL Conversation Loop - Continue until user marks as done
|
|
313
|
+
conversation_turn = 0
|
|
314
|
+
while not self._state.should_complete:
|
|
315
|
+
# Check if workflow is paused - wait until resumed
|
|
316
|
+
if self._state.is_paused:
|
|
317
|
+
workflow.logger.info(
|
|
318
|
+
"Team workflow is paused, waiting for resume signal",
|
|
319
|
+
extra={"execution_id": str(input.execution_id)[:8] if input.execution_id else "unknown"}
|
|
320
|
+
)
|
|
321
|
+
await workflow.wait_condition(
|
|
322
|
+
lambda: not self._state.is_paused or self._state.should_complete,
|
|
323
|
+
timeout=timedelta(hours=24)
|
|
324
|
+
)
|
|
325
|
+
if self._state.should_complete:
|
|
326
|
+
break
|
|
327
|
+
workflow.logger.info(
|
|
328
|
+
"Team workflow resumed, continuing execution",
|
|
329
|
+
extra={"execution_id": str(input.execution_id)[:8] if input.execution_id else "unknown"}
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
conversation_turn += 1
|
|
333
|
+
workflow.logger.info(
|
|
334
|
+
f"Starting team conversation turn {conversation_turn}",
|
|
335
|
+
extra={"turn": conversation_turn, "message_count": len(self._state.messages)}
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
# Get the latest user message
|
|
339
|
+
latest_message = self._state.messages[-1] if self._state.messages else None
|
|
340
|
+
latest_prompt = latest_message.content if latest_message and latest_message.role == "user" else input.prompt
|
|
341
|
+
|
|
342
|
+
# Capture turn start time for analytics
|
|
343
|
+
# workflow.time() already returns a float timestamp, no need for .timestamp()
|
|
344
|
+
turn_start_time = workflow.time()
|
|
345
|
+
|
|
346
|
+
# Step 3: Execute team coordination
|
|
347
|
+
team_result = await workflow.execute_activity(
|
|
348
|
+
execute_team_coordination,
|
|
349
|
+
ActivityExecuteTeamInput(
|
|
350
|
+
execution_id=input.execution_id,
|
|
351
|
+
team_id=input.team_id,
|
|
352
|
+
organization_id=input.organization_id,
|
|
353
|
+
prompt=latest_prompt,
|
|
354
|
+
system_prompt=input.system_prompt,
|
|
355
|
+
agents=team_agents["agents"],
|
|
356
|
+
team_config=input.team_config,
|
|
357
|
+
mcp_servers=input.mcp_servers, # Pass MCP servers
|
|
358
|
+
session_id=input.execution_id, # Use execution_id as session_id
|
|
359
|
+
user_id=input.user_metadata.get("user_id") if input.user_metadata else None,
|
|
360
|
+
model_id=input.team_config.get("llm", {}).get("model") if input.team_config else None,
|
|
361
|
+
model_config=input.team_config.get("llm", {}) if input.team_config else None,
|
|
362
|
+
# Activity reads CONTROL_PLANE_URL and KUBIYA_API_KEY from worker environment
|
|
363
|
+
),
|
|
364
|
+
start_to_close_timeout=timedelta(seconds=ACTIVITY_EXECUTION_TIMEOUT_SECONDS), # Configurable, default 24 hours for long-running streaming
|
|
365
|
+
heartbeat_timeout=timedelta(seconds=HEARTBEAT_TIMEOUT_SECONDS), # Configurable, default 30 min for long-running tasks
|
|
366
|
+
)
|
|
367
|
+
|
|
368
|
+
# Add tool execution status messages (real-time updates)
|
|
369
|
+
if team_result.get("tool_execution_messages"):
|
|
370
|
+
async with self._lock:
|
|
371
|
+
for tool_msg in team_result["tool_execution_messages"]:
|
|
372
|
+
self._state.messages.append(ChatMessage(
|
|
373
|
+
role="system",
|
|
374
|
+
content=tool_msg.get("content", ""),
|
|
375
|
+
timestamp=tool_msg.get("timestamp", workflow.now().isoformat()),
|
|
376
|
+
tool_name=tool_msg.get("tool_name"),
|
|
377
|
+
))
|
|
378
|
+
|
|
379
|
+
# Add tool messages to state (detailed tool info)
|
|
380
|
+
if team_result.get("tool_messages"):
|
|
381
|
+
async with self._lock:
|
|
382
|
+
for tool_msg in team_result["tool_messages"]:
|
|
383
|
+
self._state.messages.append(ChatMessage(
|
|
384
|
+
role="tool",
|
|
385
|
+
content=tool_msg.get("content", ""),
|
|
386
|
+
timestamp=tool_msg.get("timestamp", workflow.now().isoformat()),
|
|
387
|
+
tool_name=tool_msg.get("tool_name"),
|
|
388
|
+
tool_input=tool_msg.get("tool_input"),
|
|
389
|
+
))
|
|
390
|
+
|
|
391
|
+
# Update state with team response
|
|
392
|
+
if team_result.get("response"):
|
|
393
|
+
async with self._lock:
|
|
394
|
+
# CRITICAL: Use real-time timestamp from team_result if available
|
|
395
|
+
# This ensures chronological ordering with streaming events
|
|
396
|
+
# Fallback to workflow.now() (deterministic) if not provided
|
|
397
|
+
response_timestamp = team_result.get("response_timestamp") or workflow.now().isoformat()
|
|
398
|
+
|
|
399
|
+
self._state.messages.append(ChatMessage(
|
|
400
|
+
role="assistant",
|
|
401
|
+
content=team_result["response"],
|
|
402
|
+
timestamp=response_timestamp,
|
|
403
|
+
))
|
|
404
|
+
self._state.current_response = team_result["response"]
|
|
405
|
+
self._processed_message_count += 1
|
|
406
|
+
|
|
407
|
+
# Update usage and metadata (accumulate across turns)
|
|
408
|
+
if team_result.get("usage"):
|
|
409
|
+
current_usage = self._state.usage
|
|
410
|
+
new_usage = team_result.get("usage", {})
|
|
411
|
+
self._state.usage = {
|
|
412
|
+
"input_tokens": current_usage.get("input_tokens", 0) + new_usage.get("input_tokens", 0),
|
|
413
|
+
"output_tokens": current_usage.get("output_tokens", 0) + new_usage.get("output_tokens", 0),
|
|
414
|
+
"total_tokens": current_usage.get("total_tokens", 0) + new_usage.get("total_tokens", 0),
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
# Update metadata
|
|
418
|
+
self._state.metadata.update({
|
|
419
|
+
"agent_count": len(team_agents["agents"]),
|
|
420
|
+
"coordination_type": team_result.get("coordination_type"),
|
|
421
|
+
"conversation_turns": conversation_turn,
|
|
422
|
+
})
|
|
423
|
+
|
|
424
|
+
# Check if team execution failed
|
|
425
|
+
if not team_result.get("success"):
|
|
426
|
+
self._state.status = "failed"
|
|
427
|
+
self._state.error_message = team_result.get("error")
|
|
428
|
+
break
|
|
429
|
+
|
|
430
|
+
# Submit turn analytics (fire-and-forget)
|
|
431
|
+
# This triggers the control plane's intelligent state transition system
|
|
432
|
+
workflow.start_activity(
|
|
433
|
+
submit_runtime_analytics_activity,
|
|
434
|
+
AnalyticsActivityInput(
|
|
435
|
+
execution_id=input.execution_id,
|
|
436
|
+
turn_number=conversation_turn,
|
|
437
|
+
result=team_result,
|
|
438
|
+
turn_start_time=turn_start_time,
|
|
439
|
+
),
|
|
440
|
+
start_to_close_timeout=timedelta(seconds=30),
|
|
441
|
+
retry_policy=RetryPolicy(
|
|
442
|
+
maximum_attempts=3,
|
|
443
|
+
initial_interval=timedelta(seconds=2),
|
|
444
|
+
maximum_interval=timedelta(seconds=10),
|
|
445
|
+
backoff_coefficient=2.0,
|
|
446
|
+
non_retryable_error_types=["ValueError", "TypeError"],
|
|
447
|
+
),
|
|
448
|
+
)
|
|
449
|
+
|
|
450
|
+
# Wait for control plane to make intelligent state decision
|
|
451
|
+
# The control plane AI analyzes the turn and determines the appropriate state
|
|
452
|
+
workflow.logger.info(
|
|
453
|
+
f"⏳ Waiting for control plane state decision for team turn {conversation_turn}",
|
|
454
|
+
extra={"turn": conversation_turn, "execution_id": str(input.execution_id)[:8] if input.execution_id else "unknown"}
|
|
455
|
+
)
|
|
456
|
+
|
|
457
|
+
# Give control plane time to make AI decision (up to 6 seconds with retries)
|
|
458
|
+
max_retries = 3
|
|
459
|
+
retry_delay = 2 # seconds
|
|
460
|
+
|
|
461
|
+
for retry in range(max_retries):
|
|
462
|
+
await asyncio.sleep(retry_delay)
|
|
463
|
+
|
|
464
|
+
# Query execution state from control plane
|
|
465
|
+
try:
|
|
466
|
+
current_execution = await workflow.execute_activity(
|
|
467
|
+
get_execution_details,
|
|
468
|
+
ActivityGetExecutionInput(execution_id=input.execution_id),
|
|
469
|
+
start_to_close_timeout=timedelta(seconds=10),
|
|
470
|
+
)
|
|
471
|
+
|
|
472
|
+
control_plane_status = current_execution.get("status", "unknown")
|
|
473
|
+
|
|
474
|
+
# Check if status has been updated from "running" (indicates AI made a decision)
|
|
475
|
+
if control_plane_status != "running":
|
|
476
|
+
workflow.logger.info(
|
|
477
|
+
f"✅ Control plane decided state: {control_plane_status}",
|
|
478
|
+
extra={
|
|
479
|
+
"execution_id": input.execution_id,
|
|
480
|
+
"turn": conversation_turn,
|
|
481
|
+
"decided_status": control_plane_status,
|
|
482
|
+
"retry": retry + 1
|
|
483
|
+
}
|
|
484
|
+
)
|
|
485
|
+
break
|
|
486
|
+
else:
|
|
487
|
+
if retry < max_retries - 1:
|
|
488
|
+
workflow.logger.info(
|
|
489
|
+
f"⏳ Control plane still processing, retry {retry + 1}/{max_retries}",
|
|
490
|
+
extra={"turn": conversation_turn}
|
|
491
|
+
)
|
|
492
|
+
except Exception as e:
|
|
493
|
+
workflow.logger.warning(
|
|
494
|
+
f"⚠️ Failed to query execution state: {str(e)}",
|
|
495
|
+
extra={"turn": conversation_turn, "retry": retry + 1}
|
|
496
|
+
)
|
|
497
|
+
if retry == max_retries - 1:
|
|
498
|
+
# Final retry failed - default to waiting_for_input (safe fallback)
|
|
499
|
+
control_plane_status = "waiting_for_input"
|
|
500
|
+
workflow.logger.warning(
|
|
501
|
+
"Using safe fallback state: waiting_for_input",
|
|
502
|
+
extra={"turn": conversation_turn}
|
|
503
|
+
)
|
|
504
|
+
|
|
505
|
+
# Update internal state based on control plane decision
|
|
506
|
+
self._state.status = control_plane_status
|
|
507
|
+
self._state.is_waiting_for_input = (control_plane_status == "waiting_for_input")
|
|
508
|
+
|
|
509
|
+
workflow.logger.info(
|
|
510
|
+
f"Team turn {conversation_turn} complete - state: {control_plane_status}",
|
|
511
|
+
extra={"turn": conversation_turn, "status": control_plane_status}
|
|
512
|
+
)
|
|
513
|
+
|
|
514
|
+
# Wait for either new message, mark as done, or pause signal
|
|
515
|
+
await workflow.wait_condition(
|
|
516
|
+
lambda: self._new_message_count > self._processed_message_count or self._state.should_complete or self._state.is_paused,
|
|
517
|
+
timeout=timedelta(hours=24)
|
|
518
|
+
)
|
|
519
|
+
|
|
520
|
+
if self._state.should_complete:
|
|
521
|
+
workflow.logger.info("User marked team workflow as done")
|
|
522
|
+
break
|
|
523
|
+
|
|
524
|
+
# If paused while waiting, loop back to check pause condition at top of while loop
|
|
525
|
+
if self._state.is_paused:
|
|
526
|
+
workflow.logger.info(
|
|
527
|
+
"Team workflow paused while waiting for input",
|
|
528
|
+
extra={"execution_id": str(input.execution_id)[:8] if input.execution_id else "unknown"}
|
|
529
|
+
)
|
|
530
|
+
continue
|
|
531
|
+
|
|
532
|
+
# Continue loop to process new message
|
|
533
|
+
self._state.status = "running"
|
|
534
|
+
|
|
535
|
+
# Conversation complete - finalize workflow
|
|
536
|
+
final_status = "failed" if self._state.status == "failed" else "completed"
|
|
537
|
+
self._state.status = final_status
|
|
538
|
+
|
|
539
|
+
await workflow.execute_activity(
|
|
540
|
+
update_execution_status,
|
|
541
|
+
ActivityUpdateExecutionInput(
|
|
542
|
+
execution_id=input.execution_id,
|
|
543
|
+
status=final_status,
|
|
544
|
+
completed_at=workflow.now().isoformat(),
|
|
545
|
+
response=self._state.current_response,
|
|
546
|
+
error_message=self._state.error_message,
|
|
547
|
+
usage=self._state.usage,
|
|
548
|
+
execution_metadata={
|
|
549
|
+
**self._state.metadata,
|
|
550
|
+
"workflow_completed": True,
|
|
551
|
+
"total_conversation_turns": conversation_turn,
|
|
552
|
+
},
|
|
553
|
+
),
|
|
554
|
+
start_to_close_timeout=timedelta(seconds=30),
|
|
555
|
+
)
|
|
556
|
+
|
|
557
|
+
workflow.logger.info(
|
|
558
|
+
f"Team execution workflow completed with HITL",
|
|
559
|
+
extra={
|
|
560
|
+
"execution_id": input.execution_id,
|
|
561
|
+
"status": final_status,
|
|
562
|
+
"conversation_turns": conversation_turn,
|
|
563
|
+
}
|
|
564
|
+
)
|
|
565
|
+
|
|
566
|
+
return {
|
|
567
|
+
"success": final_status == "completed",
|
|
568
|
+
"execution_id": input.execution_id,
|
|
569
|
+
"status": final_status,
|
|
570
|
+
"response": self._state.current_response,
|
|
571
|
+
"usage": self._state.usage,
|
|
572
|
+
"conversation_turns": conversation_turn,
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
except Exception as e:
|
|
576
|
+
# Update state with error
|
|
577
|
+
self._state.status = "failed"
|
|
578
|
+
self._state.error_message = str(e)
|
|
579
|
+
self._state.metadata["error_type"] = type(e).__name__
|
|
580
|
+
|
|
581
|
+
workflow.logger.error(
|
|
582
|
+
f"Team execution workflow failed",
|
|
583
|
+
extra={
|
|
584
|
+
"execution_id": input.execution_id,
|
|
585
|
+
"error": str(e),
|
|
586
|
+
}
|
|
587
|
+
)
|
|
588
|
+
|
|
589
|
+
# Update execution as failed
|
|
590
|
+
try:
|
|
591
|
+
await workflow.execute_activity(
|
|
592
|
+
update_execution_status,
|
|
593
|
+
ActivityUpdateExecutionInput(
|
|
594
|
+
execution_id=input.execution_id,
|
|
595
|
+
status="failed",
|
|
596
|
+
completed_at=workflow.now().isoformat(),
|
|
597
|
+
error_message=f"Workflow error: {str(e)}",
|
|
598
|
+
execution_metadata={
|
|
599
|
+
"workflow_error": True,
|
|
600
|
+
"error_type": type(e).__name__,
|
|
601
|
+
},
|
|
602
|
+
),
|
|
603
|
+
start_to_close_timeout=timedelta(seconds=30),
|
|
604
|
+
)
|
|
605
|
+
except Exception as update_error:
|
|
606
|
+
workflow.logger.error(
|
|
607
|
+
f"Failed to update status after error",
|
|
608
|
+
extra={"error": str(update_error)}
|
|
609
|
+
)
|
|
610
|
+
|
|
611
|
+
raise
|