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,254 @@
|
|
|
1
|
+
"""Plan generation workflow for Temporal"""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from datetime import timedelta
|
|
5
|
+
from typing import Optional, Dict, Any, List
|
|
6
|
+
from temporalio import workflow
|
|
7
|
+
from temporalio.common import RetryPolicy
|
|
8
|
+
import asyncio
|
|
9
|
+
|
|
10
|
+
with workflow.unsafe.imports_passed_through():
|
|
11
|
+
from control_plane_api.app.activities.plan_generation_activities import (
|
|
12
|
+
generate_plan_activity,
|
|
13
|
+
store_plan_activity,
|
|
14
|
+
update_plan_generation_status,
|
|
15
|
+
ActivityGeneratePlanInput,
|
|
16
|
+
ActivityStorePlanInput,
|
|
17
|
+
ActivityUpdatePlanGenerationInput,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class PlanGenerationInput:
|
|
23
|
+
"""Input for plan generation workflow"""
|
|
24
|
+
execution_id: str
|
|
25
|
+
organization_id: str
|
|
26
|
+
task_request: Dict[str, Any] # TaskPlanRequest as dict (for serialization)
|
|
27
|
+
user_metadata: Optional[Dict[str, Any]] = None
|
|
28
|
+
api_token: Optional[str] = None # API token for accessing resources
|
|
29
|
+
|
|
30
|
+
def __post_init__(self):
|
|
31
|
+
if self.user_metadata is None:
|
|
32
|
+
self.user_metadata = {}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass
|
|
36
|
+
class PlanGenerationState:
|
|
37
|
+
"""Current state of plan generation for queries"""
|
|
38
|
+
status: str # "pending", "analyzing", "generating", "completed", "failed"
|
|
39
|
+
current_step: str = ""
|
|
40
|
+
error_message: Optional[str] = None
|
|
41
|
+
plan_json: Optional[Dict[str, Any]] = None
|
|
42
|
+
progress_percentage: int = 0
|
|
43
|
+
|
|
44
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
45
|
+
"""Convert state to dict for serialization"""
|
|
46
|
+
return {
|
|
47
|
+
"status": self.status,
|
|
48
|
+
"current_step": self.current_step,
|
|
49
|
+
"error_message": self.error_message,
|
|
50
|
+
"plan_json": self.plan_json,
|
|
51
|
+
"progress_percentage": self.progress_percentage,
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@workflow.defn
|
|
56
|
+
class PlanGenerationWorkflow:
|
|
57
|
+
"""
|
|
58
|
+
Workflow for generating a task plan asynchronously.
|
|
59
|
+
|
|
60
|
+
This workflow:
|
|
61
|
+
1. Updates execution status to running
|
|
62
|
+
2. Generates the plan using the planning strategy
|
|
63
|
+
3. Stores the generated plan in the execution record
|
|
64
|
+
4. Updates execution with results
|
|
65
|
+
5. Supports queries for real-time state access
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
def __init__(self) -> None:
|
|
69
|
+
"""Initialize workflow state"""
|
|
70
|
+
self._state = PlanGenerationState(status="pending")
|
|
71
|
+
self._lock = asyncio.Lock()
|
|
72
|
+
|
|
73
|
+
@workflow.query
|
|
74
|
+
def get_state(self) -> PlanGenerationState:
|
|
75
|
+
"""Query handler: Get current plan generation state"""
|
|
76
|
+
return self._state
|
|
77
|
+
|
|
78
|
+
@workflow.run
|
|
79
|
+
async def run(self, input: PlanGenerationInput) -> Dict[str, Any]:
|
|
80
|
+
"""
|
|
81
|
+
Run the plan generation workflow.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
input: Workflow input with plan generation details
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
Result dict with generated plan and metadata
|
|
88
|
+
"""
|
|
89
|
+
workflow.logger.info(
|
|
90
|
+
f"Starting plan generation workflow",
|
|
91
|
+
extra={
|
|
92
|
+
"execution_id": input.execution_id,
|
|
93
|
+
"organization_id": input.organization_id,
|
|
94
|
+
"quick_mode": input.task_request.get("quick_mode", False),
|
|
95
|
+
}
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
try:
|
|
99
|
+
# Step 1: Update execution status to running
|
|
100
|
+
async with self._lock:
|
|
101
|
+
self._state.status = "running"
|
|
102
|
+
self._state.current_step = "Initializing plan generation"
|
|
103
|
+
self._state.progress_percentage = 10
|
|
104
|
+
|
|
105
|
+
await workflow.execute_activity(
|
|
106
|
+
update_plan_generation_status,
|
|
107
|
+
ActivityUpdatePlanGenerationInput(
|
|
108
|
+
execution_id=input.execution_id,
|
|
109
|
+
status="running",
|
|
110
|
+
current_step="Initializing plan generation",
|
|
111
|
+
),
|
|
112
|
+
start_to_close_timeout=timedelta(seconds=30),
|
|
113
|
+
retry_policy=RetryPolicy(
|
|
114
|
+
maximum_attempts=3,
|
|
115
|
+
initial_interval=timedelta(seconds=1),
|
|
116
|
+
maximum_interval=timedelta(seconds=10),
|
|
117
|
+
),
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
# Step 2: Analyze and generate plan
|
|
121
|
+
async with self._lock:
|
|
122
|
+
self._state.status = "analyzing"
|
|
123
|
+
self._state.current_step = "Analyzing task requirements"
|
|
124
|
+
self._state.progress_percentage = 30
|
|
125
|
+
|
|
126
|
+
await workflow.execute_activity(
|
|
127
|
+
update_plan_generation_status,
|
|
128
|
+
ActivityUpdatePlanGenerationInput(
|
|
129
|
+
execution_id=input.execution_id,
|
|
130
|
+
status="analyzing",
|
|
131
|
+
current_step="Analyzing task requirements",
|
|
132
|
+
),
|
|
133
|
+
start_to_close_timeout=timedelta(seconds=30),
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
# Generate the plan (this is the long-running step: 1-3 minutes)
|
|
137
|
+
async with self._lock:
|
|
138
|
+
self._state.status = "generating"
|
|
139
|
+
self._state.current_step = "Generating execution plan"
|
|
140
|
+
self._state.progress_percentage = 50
|
|
141
|
+
|
|
142
|
+
await workflow.execute_activity(
|
|
143
|
+
update_plan_generation_status,
|
|
144
|
+
ActivityUpdatePlanGenerationInput(
|
|
145
|
+
execution_id=input.execution_id,
|
|
146
|
+
status="generating",
|
|
147
|
+
current_step="Generating execution plan",
|
|
148
|
+
),
|
|
149
|
+
start_to_close_timeout=timedelta(seconds=30),
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
workflow.logger.info("Executing plan generation activity")
|
|
153
|
+
plan_result = await workflow.execute_activity(
|
|
154
|
+
generate_plan_activity,
|
|
155
|
+
ActivityGeneratePlanInput(
|
|
156
|
+
execution_id=input.execution_id,
|
|
157
|
+
organization_id=input.organization_id,
|
|
158
|
+
task_request=input.task_request,
|
|
159
|
+
api_token=input.api_token,
|
|
160
|
+
),
|
|
161
|
+
start_to_close_timeout=timedelta(minutes=10), # Plan generation can take 1-3 minutes, allow extra buffer
|
|
162
|
+
# No heartbeat timeout - plan generation is a long-running operation
|
|
163
|
+
retry_policy=RetryPolicy(
|
|
164
|
+
maximum_attempts=2,
|
|
165
|
+
initial_interval=timedelta(seconds=5),
|
|
166
|
+
maximum_interval=timedelta(seconds=30),
|
|
167
|
+
),
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
async with self._lock:
|
|
171
|
+
self._state.plan_json = plan_result.get("plan_json")
|
|
172
|
+
self._state.progress_percentage = 80
|
|
173
|
+
|
|
174
|
+
workflow.logger.info("Plan generated successfully")
|
|
175
|
+
|
|
176
|
+
# Step 3: Store the plan
|
|
177
|
+
async with self._lock:
|
|
178
|
+
self._state.current_step = "Storing generated plan"
|
|
179
|
+
self._state.progress_percentage = 90
|
|
180
|
+
|
|
181
|
+
await workflow.execute_activity(
|
|
182
|
+
store_plan_activity,
|
|
183
|
+
ActivityStorePlanInput(
|
|
184
|
+
execution_id=input.execution_id,
|
|
185
|
+
plan_json=plan_result["plan_json"],
|
|
186
|
+
metadata=plan_result.get("metadata", {}),
|
|
187
|
+
),
|
|
188
|
+
start_to_close_timeout=timedelta(seconds=30),
|
|
189
|
+
retry_policy=RetryPolicy(
|
|
190
|
+
maximum_attempts=3,
|
|
191
|
+
initial_interval=timedelta(seconds=1),
|
|
192
|
+
maximum_interval=timedelta(seconds=10),
|
|
193
|
+
),
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
workflow.logger.info("Plan stored successfully")
|
|
197
|
+
|
|
198
|
+
# Step 4: Mark as completed
|
|
199
|
+
async with self._lock:
|
|
200
|
+
self._state.status = "completed"
|
|
201
|
+
self._state.current_step = "Plan generation complete"
|
|
202
|
+
self._state.progress_percentage = 100
|
|
203
|
+
|
|
204
|
+
await workflow.execute_activity(
|
|
205
|
+
update_plan_generation_status,
|
|
206
|
+
ActivityUpdatePlanGenerationInput(
|
|
207
|
+
execution_id=input.execution_id,
|
|
208
|
+
status="completed",
|
|
209
|
+
current_step="Plan generation complete",
|
|
210
|
+
plan_json=plan_result["plan_json"],
|
|
211
|
+
),
|
|
212
|
+
start_to_close_timeout=timedelta(seconds=30),
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
workflow.logger.info(
|
|
216
|
+
"Plan generation workflow completed successfully",
|
|
217
|
+
extra={"execution_id": input.execution_id}
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
return {
|
|
221
|
+
"status": "completed",
|
|
222
|
+
"plan_json": plan_result["plan_json"],
|
|
223
|
+
"metadata": plan_result.get("metadata", {}),
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
except Exception as e:
|
|
227
|
+
workflow.logger.error(
|
|
228
|
+
f"Plan generation workflow failed: {str(e)}",
|
|
229
|
+
extra={"execution_id": input.execution_id},
|
|
230
|
+
exc_info=True,
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
async with self._lock:
|
|
234
|
+
self._state.status = "failed"
|
|
235
|
+
self._state.error_message = str(e)
|
|
236
|
+
|
|
237
|
+
# Update execution status to failed
|
|
238
|
+
try:
|
|
239
|
+
await workflow.execute_activity(
|
|
240
|
+
update_plan_generation_status,
|
|
241
|
+
ActivityUpdatePlanGenerationInput(
|
|
242
|
+
execution_id=input.execution_id,
|
|
243
|
+
status="failed",
|
|
244
|
+
error_message=str(e),
|
|
245
|
+
),
|
|
246
|
+
start_to_close_timeout=timedelta(seconds=30),
|
|
247
|
+
)
|
|
248
|
+
except Exception as update_error:
|
|
249
|
+
workflow.logger.error(f"Failed to update error status: {str(update_error)}")
|
|
250
|
+
|
|
251
|
+
return {
|
|
252
|
+
"status": "failed",
|
|
253
|
+
"error": str(e),
|
|
254
|
+
}
|
|
@@ -0,0 +1,442 @@
|
|
|
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
|
+
import asyncio
|
|
8
|
+
|
|
9
|
+
with workflow.unsafe.imports_passed_through():
|
|
10
|
+
from control_plane_api.app.activities.team_activities import (
|
|
11
|
+
get_team_agents,
|
|
12
|
+
execute_team_coordination,
|
|
13
|
+
update_execution_status,
|
|
14
|
+
ActivityGetTeamAgentsInput,
|
|
15
|
+
ActivityExecuteTeamInput,
|
|
16
|
+
ActivityUpdateExecutionInput,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class TeamExecutionInput:
|
|
22
|
+
"""Input for team execution workflow"""
|
|
23
|
+
execution_id: str
|
|
24
|
+
team_id: str
|
|
25
|
+
organization_id: str
|
|
26
|
+
prompt: str
|
|
27
|
+
system_prompt: Optional[str] = None
|
|
28
|
+
model_id: Optional[str] = None
|
|
29
|
+
model_config: dict = None
|
|
30
|
+
team_config: dict = None
|
|
31
|
+
mcp_servers: dict = None
|
|
32
|
+
user_metadata: dict = None
|
|
33
|
+
|
|
34
|
+
def __post_init__(self):
|
|
35
|
+
if self.model_config is None:
|
|
36
|
+
self.model_config = {}
|
|
37
|
+
if self.team_config is None:
|
|
38
|
+
self.team_config = {}
|
|
39
|
+
if self.mcp_servers is None:
|
|
40
|
+
self.mcp_servers = {}
|
|
41
|
+
if self.user_metadata is None:
|
|
42
|
+
self.user_metadata = {}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass
|
|
46
|
+
class ChatMessage:
|
|
47
|
+
"""Represents a message in the conversation"""
|
|
48
|
+
role: str # "user", "assistant", "system", "tool"
|
|
49
|
+
content: str
|
|
50
|
+
timestamp: str
|
|
51
|
+
tool_name: Optional[str] = None
|
|
52
|
+
tool_input: Optional[Dict[str, Any]] = None
|
|
53
|
+
tool_output: Optional[Dict[str, Any]] = None
|
|
54
|
+
# User attribution for messages
|
|
55
|
+
user_id: Optional[str] = None
|
|
56
|
+
user_name: Optional[str] = None
|
|
57
|
+
user_email: Optional[str] = None
|
|
58
|
+
user_avatar: Optional[str] = None
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@dataclass
|
|
62
|
+
class ExecutionState:
|
|
63
|
+
"""Current state of the execution for queries"""
|
|
64
|
+
status: str # "pending", "running", "waiting_for_input", "paused", "completed", "failed"
|
|
65
|
+
messages: List[ChatMessage] = field(default_factory=list)
|
|
66
|
+
current_response: str = ""
|
|
67
|
+
error_message: Optional[str] = None
|
|
68
|
+
usage: Dict[str, Any] = field(default_factory=dict)
|
|
69
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
70
|
+
is_waiting_for_input: bool = False
|
|
71
|
+
should_complete: bool = False
|
|
72
|
+
is_paused: bool = False
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@workflow.defn
|
|
76
|
+
class TeamExecutionWorkflow:
|
|
77
|
+
"""
|
|
78
|
+
Workflow for executing a team of agents with HITL support.
|
|
79
|
+
|
|
80
|
+
This workflow:
|
|
81
|
+
1. Gets team agents
|
|
82
|
+
2. Coordinates execution across agents
|
|
83
|
+
3. Aggregates results
|
|
84
|
+
4. Updates execution status
|
|
85
|
+
5. Supports queries for real-time state access
|
|
86
|
+
6. Supports signals for adding followup messages
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
def __init__(self) -> None:
|
|
90
|
+
"""Initialize workflow state"""
|
|
91
|
+
self._state = ExecutionState(status="pending")
|
|
92
|
+
self._lock = asyncio.Lock()
|
|
93
|
+
self._new_message_count = 0
|
|
94
|
+
self._processed_message_count = 0
|
|
95
|
+
|
|
96
|
+
@workflow.query
|
|
97
|
+
def get_state(self) -> ExecutionState:
|
|
98
|
+
"""Query handler: Get current execution state including messages and status"""
|
|
99
|
+
return self._state
|
|
100
|
+
|
|
101
|
+
@workflow.signal
|
|
102
|
+
async def add_message(self, message: ChatMessage) -> None:
|
|
103
|
+
"""
|
|
104
|
+
Signal handler: Add a message to the conversation.
|
|
105
|
+
This allows clients to send followup messages while the workflow is running.
|
|
106
|
+
"""
|
|
107
|
+
async with self._lock:
|
|
108
|
+
self._state.messages.append(message)
|
|
109
|
+
self._new_message_count += 1
|
|
110
|
+
self._state.is_waiting_for_input = False
|
|
111
|
+
workflow.logger.info(
|
|
112
|
+
f"Message added to team conversation",
|
|
113
|
+
extra={
|
|
114
|
+
"role": message.role,
|
|
115
|
+
"content_preview": message.content[:100] if message.content else "",
|
|
116
|
+
"total_messages": len(self._state.messages)
|
|
117
|
+
}
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
@workflow.signal
|
|
121
|
+
async def mark_as_done(self) -> None:
|
|
122
|
+
"""
|
|
123
|
+
Signal handler: Mark the workflow as complete.
|
|
124
|
+
"""
|
|
125
|
+
async with self._lock:
|
|
126
|
+
self._state.should_complete = True
|
|
127
|
+
self._state.is_waiting_for_input = False
|
|
128
|
+
workflow.logger.info("Team workflow marked as done by user")
|
|
129
|
+
|
|
130
|
+
@workflow.signal
|
|
131
|
+
async def pause_execution(self) -> None:
|
|
132
|
+
"""
|
|
133
|
+
Signal handler: Pause the workflow execution.
|
|
134
|
+
This pauses the workflow - it will stop processing but remain active.
|
|
135
|
+
Resume can be called to continue execution.
|
|
136
|
+
"""
|
|
137
|
+
async with self._lock:
|
|
138
|
+
if not self._state.is_paused:
|
|
139
|
+
self._state.is_paused = True
|
|
140
|
+
self._state.status = "paused"
|
|
141
|
+
workflow.logger.info("Team workflow paused by user")
|
|
142
|
+
|
|
143
|
+
@workflow.signal
|
|
144
|
+
async def resume_execution(self) -> None:
|
|
145
|
+
"""
|
|
146
|
+
Signal handler: Resume a paused workflow execution.
|
|
147
|
+
This resumes the workflow from where it was paused.
|
|
148
|
+
"""
|
|
149
|
+
async with self._lock:
|
|
150
|
+
if self._state.is_paused:
|
|
151
|
+
self._state.is_paused = False
|
|
152
|
+
# Restore previous status (either running or waiting_for_input)
|
|
153
|
+
self._state.status = "waiting_for_input" if self._state.is_waiting_for_input else "running"
|
|
154
|
+
workflow.logger.info("Team workflow resumed by user")
|
|
155
|
+
|
|
156
|
+
@workflow.run
|
|
157
|
+
async def run(self, input: TeamExecutionInput) -> dict:
|
|
158
|
+
"""
|
|
159
|
+
Run the team execution workflow with HITL pattern.
|
|
160
|
+
|
|
161
|
+
This workflow implements a continuous conversation loop:
|
|
162
|
+
1. Process the initial user message
|
|
163
|
+
2. Execute team coordination and return response
|
|
164
|
+
3. Wait for user input (signals)
|
|
165
|
+
4. Process followup messages in a loop
|
|
166
|
+
5. Only complete when user explicitly marks as done
|
|
167
|
+
|
|
168
|
+
Args:
|
|
169
|
+
input: Workflow input with team execution details
|
|
170
|
+
|
|
171
|
+
Returns:
|
|
172
|
+
Team execution result dict
|
|
173
|
+
"""
|
|
174
|
+
workflow.logger.info(
|
|
175
|
+
f"Starting team execution workflow with HITL pattern",
|
|
176
|
+
extra={
|
|
177
|
+
"execution_id": input.execution_id,
|
|
178
|
+
"team_id": input.team_id,
|
|
179
|
+
"organization_id": input.organization_id,
|
|
180
|
+
}
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
# Initialize state with user's initial message
|
|
184
|
+
self._state.messages.append(ChatMessage(
|
|
185
|
+
role="user",
|
|
186
|
+
content=input.prompt,
|
|
187
|
+
timestamp=workflow.now().isoformat(),
|
|
188
|
+
))
|
|
189
|
+
self._state.status = "running"
|
|
190
|
+
self._new_message_count = 1
|
|
191
|
+
self._processed_message_count = 0
|
|
192
|
+
|
|
193
|
+
try:
|
|
194
|
+
# Step 1: Update execution status to running
|
|
195
|
+
await workflow.execute_activity(
|
|
196
|
+
update_execution_status,
|
|
197
|
+
ActivityUpdateExecutionInput(
|
|
198
|
+
execution_id=input.execution_id,
|
|
199
|
+
status="running",
|
|
200
|
+
started_at=workflow.now().isoformat(),
|
|
201
|
+
execution_metadata={
|
|
202
|
+
"workflow_started": True,
|
|
203
|
+
"hitl_enabled": True,
|
|
204
|
+
},
|
|
205
|
+
),
|
|
206
|
+
start_to_close_timeout=timedelta(seconds=30),
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
# Step 2: Get team agents once at the beginning
|
|
210
|
+
workflow.logger.info(
|
|
211
|
+
f"[WORKFLOW] About to call get_team_agents",
|
|
212
|
+
extra={
|
|
213
|
+
"team_id": input.team_id,
|
|
214
|
+
"organization_id": input.organization_id,
|
|
215
|
+
}
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
team_agents = await workflow.execute_activity(
|
|
219
|
+
get_team_agents,
|
|
220
|
+
ActivityGetTeamAgentsInput(
|
|
221
|
+
team_id=input.team_id,
|
|
222
|
+
organization_id=input.organization_id,
|
|
223
|
+
),
|
|
224
|
+
start_to_close_timeout=timedelta(seconds=30),
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
workflow.logger.info(
|
|
228
|
+
f"[WORKFLOW] get_team_agents returned",
|
|
229
|
+
extra={
|
|
230
|
+
"result": team_agents,
|
|
231
|
+
"agents_count": len(team_agents.get("agents", [])) if team_agents else 0,
|
|
232
|
+
}
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
if not team_agents.get("agents"):
|
|
236
|
+
workflow.logger.error(
|
|
237
|
+
f"[WORKFLOW] NO AGENTS RETURNED!",
|
|
238
|
+
extra={
|
|
239
|
+
"team_agents": team_agents,
|
|
240
|
+
"team_id": input.team_id,
|
|
241
|
+
"organization_id": input.organization_id,
|
|
242
|
+
}
|
|
243
|
+
)
|
|
244
|
+
raise ValueError("No agents found in team")
|
|
245
|
+
|
|
246
|
+
# HITL Conversation Loop - Continue until user marks as done
|
|
247
|
+
conversation_turn = 0
|
|
248
|
+
while not self._state.should_complete:
|
|
249
|
+
# Check if workflow is paused - wait until resumed
|
|
250
|
+
if self._state.is_paused:
|
|
251
|
+
workflow.logger.info("Team workflow is paused, waiting for resume signal")
|
|
252
|
+
await workflow.wait_condition(
|
|
253
|
+
lambda: not self._state.is_paused or self._state.should_complete,
|
|
254
|
+
timeout=timedelta(hours=24)
|
|
255
|
+
)
|
|
256
|
+
if self._state.should_complete:
|
|
257
|
+
break
|
|
258
|
+
workflow.logger.info("Team workflow resumed, continuing execution")
|
|
259
|
+
|
|
260
|
+
conversation_turn += 1
|
|
261
|
+
workflow.logger.info(
|
|
262
|
+
f"Starting team conversation turn {conversation_turn}",
|
|
263
|
+
extra={"turn": conversation_turn, "message_count": len(self._state.messages)}
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
# Get the latest user message
|
|
267
|
+
latest_message = self._state.messages[-1] if self._state.messages else None
|
|
268
|
+
latest_prompt = latest_message.content if latest_message and latest_message.role == "user" else input.prompt
|
|
269
|
+
|
|
270
|
+
# Step 3: Execute team coordination
|
|
271
|
+
team_result = await workflow.execute_activity(
|
|
272
|
+
execute_team_coordination,
|
|
273
|
+
ActivityExecuteTeamInput(
|
|
274
|
+
execution_id=input.execution_id,
|
|
275
|
+
team_id=input.team_id,
|
|
276
|
+
organization_id=input.organization_id,
|
|
277
|
+
prompt=latest_prompt,
|
|
278
|
+
system_prompt=input.system_prompt,
|
|
279
|
+
agents=team_agents["agents"],
|
|
280
|
+
team_config=input.team_config,
|
|
281
|
+
mcp_servers=input.mcp_servers, # Pass MCP servers
|
|
282
|
+
session_id=input.execution_id, # Use execution_id as session_id for 1:1 mapping
|
|
283
|
+
user_id=input.user_metadata.get("user_id") if input.user_metadata else None, # Extract user_id from JWT
|
|
284
|
+
),
|
|
285
|
+
start_to_close_timeout=timedelta(minutes=30), # Teams can take longer
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
# Update state with team response
|
|
289
|
+
if team_result.get("response"):
|
|
290
|
+
async with self._lock:
|
|
291
|
+
self._state.messages.append(ChatMessage(
|
|
292
|
+
role="assistant",
|
|
293
|
+
content=team_result["response"],
|
|
294
|
+
timestamp=workflow.now().isoformat(),
|
|
295
|
+
))
|
|
296
|
+
self._state.current_response = team_result["response"]
|
|
297
|
+
self._processed_message_count += 1
|
|
298
|
+
|
|
299
|
+
# Update usage and metadata (accumulate across turns)
|
|
300
|
+
if team_result.get("usage"):
|
|
301
|
+
current_usage = self._state.usage
|
|
302
|
+
new_usage = team_result.get("usage", {})
|
|
303
|
+
self._state.usage = {
|
|
304
|
+
"input_tokens": current_usage.get("input_tokens", 0) + new_usage.get("input_tokens", 0),
|
|
305
|
+
"output_tokens": current_usage.get("output_tokens", 0) + new_usage.get("output_tokens", 0),
|
|
306
|
+
"total_tokens": current_usage.get("total_tokens", 0) + new_usage.get("total_tokens", 0),
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
# Update metadata
|
|
310
|
+
self._state.metadata.update({
|
|
311
|
+
"agent_count": len(team_agents["agents"]),
|
|
312
|
+
"coordination_type": team_result.get("coordination_type"),
|
|
313
|
+
"conversation_turns": conversation_turn,
|
|
314
|
+
})
|
|
315
|
+
|
|
316
|
+
# Check if team execution failed
|
|
317
|
+
if not team_result.get("success"):
|
|
318
|
+
self._state.status = "failed"
|
|
319
|
+
self._state.error_message = team_result.get("error")
|
|
320
|
+
break
|
|
321
|
+
|
|
322
|
+
# Update execution status to waiting_for_input
|
|
323
|
+
self._state.status = "waiting_for_input"
|
|
324
|
+
self._state.is_waiting_for_input = True
|
|
325
|
+
|
|
326
|
+
# Update database to reflect waiting state
|
|
327
|
+
await workflow.execute_activity(
|
|
328
|
+
update_execution_status,
|
|
329
|
+
ActivityUpdateExecutionInput(
|
|
330
|
+
execution_id=input.execution_id,
|
|
331
|
+
status="waiting_for_input",
|
|
332
|
+
response=self._state.current_response,
|
|
333
|
+
usage=self._state.usage,
|
|
334
|
+
execution_metadata={
|
|
335
|
+
**self._state.metadata,
|
|
336
|
+
"conversation_turns": conversation_turn,
|
|
337
|
+
"waiting_for_user": True,
|
|
338
|
+
},
|
|
339
|
+
),
|
|
340
|
+
start_to_close_timeout=timedelta(seconds=30),
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
workflow.logger.info(
|
|
344
|
+
f"Waiting for user input after team turn {conversation_turn}",
|
|
345
|
+
extra={"turn": conversation_turn}
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
# Wait for either new message, mark as done, or pause signal
|
|
349
|
+
await workflow.wait_condition(
|
|
350
|
+
lambda: self._new_message_count > self._processed_message_count or self._state.should_complete or self._state.is_paused,
|
|
351
|
+
timeout=timedelta(hours=24)
|
|
352
|
+
)
|
|
353
|
+
|
|
354
|
+
if self._state.should_complete:
|
|
355
|
+
workflow.logger.info("User marked team workflow as done")
|
|
356
|
+
break
|
|
357
|
+
|
|
358
|
+
# If paused while waiting, loop back to check pause condition at top of while loop
|
|
359
|
+
if self._state.is_paused:
|
|
360
|
+
workflow.logger.info("Team workflow paused while waiting for input")
|
|
361
|
+
continue
|
|
362
|
+
|
|
363
|
+
# Continue loop to process new message
|
|
364
|
+
self._state.status = "running"
|
|
365
|
+
|
|
366
|
+
# Conversation complete - finalize workflow
|
|
367
|
+
final_status = "failed" if self._state.status == "failed" else "completed"
|
|
368
|
+
self._state.status = final_status
|
|
369
|
+
|
|
370
|
+
await workflow.execute_activity(
|
|
371
|
+
update_execution_status,
|
|
372
|
+
ActivityUpdateExecutionInput(
|
|
373
|
+
execution_id=input.execution_id,
|
|
374
|
+
status=final_status,
|
|
375
|
+
completed_at=workflow.now().isoformat(),
|
|
376
|
+
response=self._state.current_response,
|
|
377
|
+
error_message=self._state.error_message,
|
|
378
|
+
usage=self._state.usage,
|
|
379
|
+
execution_metadata={
|
|
380
|
+
**self._state.metadata,
|
|
381
|
+
"workflow_completed": True,
|
|
382
|
+
"total_conversation_turns": conversation_turn,
|
|
383
|
+
},
|
|
384
|
+
),
|
|
385
|
+
start_to_close_timeout=timedelta(seconds=30),
|
|
386
|
+
)
|
|
387
|
+
|
|
388
|
+
workflow.logger.info(
|
|
389
|
+
f"Team execution workflow completed with HITL",
|
|
390
|
+
extra={
|
|
391
|
+
"execution_id": input.execution_id,
|
|
392
|
+
"status": final_status,
|
|
393
|
+
"conversation_turns": conversation_turn,
|
|
394
|
+
}
|
|
395
|
+
)
|
|
396
|
+
|
|
397
|
+
return {
|
|
398
|
+
"success": final_status == "completed",
|
|
399
|
+
"execution_id": input.execution_id,
|
|
400
|
+
"status": final_status,
|
|
401
|
+
"response": self._state.current_response,
|
|
402
|
+
"usage": self._state.usage,
|
|
403
|
+
"conversation_turns": conversation_turn,
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
except Exception as e:
|
|
407
|
+
# Update state with error
|
|
408
|
+
self._state.status = "failed"
|
|
409
|
+
self._state.error_message = str(e)
|
|
410
|
+
self._state.metadata["error_type"] = type(e).__name__
|
|
411
|
+
|
|
412
|
+
workflow.logger.error(
|
|
413
|
+
f"Team execution workflow failed",
|
|
414
|
+
extra={
|
|
415
|
+
"execution_id": input.execution_id,
|
|
416
|
+
"error": str(e),
|
|
417
|
+
}
|
|
418
|
+
)
|
|
419
|
+
|
|
420
|
+
# Update execution as failed
|
|
421
|
+
try:
|
|
422
|
+
await workflow.execute_activity(
|
|
423
|
+
update_execution_status,
|
|
424
|
+
ActivityUpdateExecutionInput(
|
|
425
|
+
execution_id=input.execution_id,
|
|
426
|
+
status="failed",
|
|
427
|
+
completed_at=workflow.now().isoformat(),
|
|
428
|
+
error_message=f"Workflow error: {str(e)}",
|
|
429
|
+
execution_metadata={
|
|
430
|
+
"workflow_error": True,
|
|
431
|
+
"error_type": type(e).__name__,
|
|
432
|
+
},
|
|
433
|
+
),
|
|
434
|
+
start_to_close_timeout=timedelta(seconds=30),
|
|
435
|
+
)
|
|
436
|
+
except Exception as update_error:
|
|
437
|
+
workflow.logger.error(
|
|
438
|
+
f"Failed to update status after error",
|
|
439
|
+
extra={"error": str(update_error)}
|
|
440
|
+
)
|
|
441
|
+
|
|
442
|
+
raise
|