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,273 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Entity name-to-UUID resolution helpers for task planning.
|
|
3
|
+
|
|
4
|
+
This module provides functions to resolve entity names (agents, teams, worker queues,
|
|
5
|
+
environments) to their actual UUIDs. This is needed because the LLM planning workflow
|
|
6
|
+
often returns entity names in the entity_id field instead of UUIDs, which causes
|
|
7
|
+
PostgreSQL errors when trying to execute plans.
|
|
8
|
+
"""
|
|
9
|
+
from typing import Optional
|
|
10
|
+
from sqlalchemy.orm import Session
|
|
11
|
+
from uuid import UUID
|
|
12
|
+
import structlog
|
|
13
|
+
|
|
14
|
+
from control_plane_api.app.models.agent import Agent
|
|
15
|
+
from control_plane_api.app.models.team import Team
|
|
16
|
+
from control_plane_api.app.models.worker import WorkerQueue
|
|
17
|
+
from control_plane_api.app.models.environment import Environment
|
|
18
|
+
|
|
19
|
+
logger = structlog.get_logger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
async def resolve_agent_name_to_uuid(
|
|
23
|
+
name_or_id: str,
|
|
24
|
+
organization_id: str,
|
|
25
|
+
db: Session
|
|
26
|
+
) -> Optional[str]:
|
|
27
|
+
"""
|
|
28
|
+
Resolve agent name or ID to UUID.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
name_or_id: Agent name or UUID string
|
|
32
|
+
organization_id: Organization ID
|
|
33
|
+
db: Database session
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
UUID string if found, None otherwise
|
|
37
|
+
"""
|
|
38
|
+
# First check if it's already a valid UUID
|
|
39
|
+
try:
|
|
40
|
+
uuid_obj = UUID(name_or_id)
|
|
41
|
+
# It's a valid UUID, verify it exists
|
|
42
|
+
agent = db.query(Agent).filter(
|
|
43
|
+
Agent.id == str(uuid_obj),
|
|
44
|
+
Agent.organization_id == organization_id
|
|
45
|
+
).first()
|
|
46
|
+
|
|
47
|
+
if agent:
|
|
48
|
+
logger.info("agent_uuid_valid", agent_id=str(uuid_obj))
|
|
49
|
+
return str(uuid_obj)
|
|
50
|
+
except (ValueError, AttributeError):
|
|
51
|
+
# Not a UUID, treat as name
|
|
52
|
+
pass
|
|
53
|
+
|
|
54
|
+
# Look up by name
|
|
55
|
+
agent = db.query(Agent).filter(
|
|
56
|
+
Agent.name == name_or_id,
|
|
57
|
+
Agent.organization_id == organization_id
|
|
58
|
+
).first()
|
|
59
|
+
|
|
60
|
+
if agent:
|
|
61
|
+
logger.info("agent_resolved", name=name_or_id, uuid=str(agent.id))
|
|
62
|
+
return str(agent.id)
|
|
63
|
+
|
|
64
|
+
logger.warning("agent_not_found", name_or_id=name_or_id)
|
|
65
|
+
return None
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
async def resolve_team_name_to_uuid(
|
|
69
|
+
name_or_id: str,
|
|
70
|
+
organization_id: str,
|
|
71
|
+
db: Session
|
|
72
|
+
) -> Optional[str]:
|
|
73
|
+
"""
|
|
74
|
+
Resolve team name or ID to UUID.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
name_or_id: Team name or UUID string
|
|
78
|
+
organization_id: Organization ID
|
|
79
|
+
db: Database session
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
UUID string if found, None otherwise
|
|
83
|
+
"""
|
|
84
|
+
# First check if it's already a valid UUID
|
|
85
|
+
try:
|
|
86
|
+
uuid_obj = UUID(name_or_id)
|
|
87
|
+
# It's a valid UUID, verify it exists
|
|
88
|
+
team = db.query(Team).filter(
|
|
89
|
+
Team.id == str(uuid_obj),
|
|
90
|
+
Team.organization_id == organization_id
|
|
91
|
+
).first()
|
|
92
|
+
|
|
93
|
+
if team:
|
|
94
|
+
logger.info("team_uuid_valid", team_id=str(uuid_obj))
|
|
95
|
+
return str(uuid_obj)
|
|
96
|
+
except (ValueError, AttributeError):
|
|
97
|
+
# Not a UUID, treat as name
|
|
98
|
+
pass
|
|
99
|
+
|
|
100
|
+
# Look up by name
|
|
101
|
+
team = db.query(Team).filter(
|
|
102
|
+
Team.name == name_or_id,
|
|
103
|
+
Team.organization_id == organization_id
|
|
104
|
+
).first()
|
|
105
|
+
|
|
106
|
+
if team:
|
|
107
|
+
logger.info("team_resolved", name=name_or_id, uuid=str(team.id))
|
|
108
|
+
return str(team.id)
|
|
109
|
+
|
|
110
|
+
logger.warning("team_not_found", name_or_id=name_or_id)
|
|
111
|
+
return None
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
async def resolve_worker_queue_name_to_uuid(
|
|
115
|
+
name_or_id: str,
|
|
116
|
+
organization_id: str,
|
|
117
|
+
db: Session
|
|
118
|
+
) -> Optional[str]:
|
|
119
|
+
"""
|
|
120
|
+
Resolve worker queue name or ID to UUID.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
name_or_id: Worker queue name or UUID string
|
|
124
|
+
organization_id: Organization ID
|
|
125
|
+
db: Database session
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
UUID string if found, None otherwise
|
|
129
|
+
"""
|
|
130
|
+
# First check if it's already a valid UUID
|
|
131
|
+
try:
|
|
132
|
+
uuid_obj = UUID(name_or_id)
|
|
133
|
+
wq = db.query(WorkerQueue).filter(
|
|
134
|
+
WorkerQueue.id == str(uuid_obj),
|
|
135
|
+
WorkerQueue.organization_id == organization_id,
|
|
136
|
+
WorkerQueue.ephemeral == False, # Exclude ephemeral queues
|
|
137
|
+
~WorkerQueue.name.startswith('local-exec') # Exclude local-exec queues
|
|
138
|
+
).first()
|
|
139
|
+
|
|
140
|
+
if wq:
|
|
141
|
+
logger.info("worker_queue_uuid_valid", wq_id=str(uuid_obj))
|
|
142
|
+
return str(uuid_obj)
|
|
143
|
+
except (ValueError, AttributeError):
|
|
144
|
+
pass
|
|
145
|
+
|
|
146
|
+
# Look up by name
|
|
147
|
+
wq = db.query(WorkerQueue).filter(
|
|
148
|
+
WorkerQueue.name == name_or_id,
|
|
149
|
+
WorkerQueue.organization_id == organization_id,
|
|
150
|
+
WorkerQueue.ephemeral == False, # Exclude ephemeral queues
|
|
151
|
+
~WorkerQueue.name.startswith('local-exec') # Exclude local-exec queues
|
|
152
|
+
).first()
|
|
153
|
+
|
|
154
|
+
if wq:
|
|
155
|
+
logger.info("worker_queue_resolved", name=name_or_id, uuid=str(wq.id))
|
|
156
|
+
return str(wq.id)
|
|
157
|
+
|
|
158
|
+
logger.warning("worker_queue_not_found", name_or_id=name_or_id)
|
|
159
|
+
return None
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
async def resolve_environment_name_to_uuid(
|
|
163
|
+
name_or_id: str,
|
|
164
|
+
organization_id: str,
|
|
165
|
+
db: Session
|
|
166
|
+
) -> Optional[str]:
|
|
167
|
+
"""
|
|
168
|
+
Resolve environment name or ID to UUID.
|
|
169
|
+
|
|
170
|
+
Args:
|
|
171
|
+
name_or_id: Environment name or UUID string
|
|
172
|
+
organization_id: Organization ID
|
|
173
|
+
db: Database session
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
UUID string if found, None otherwise
|
|
177
|
+
"""
|
|
178
|
+
# First check if it's already a valid UUID
|
|
179
|
+
try:
|
|
180
|
+
uuid_obj = UUID(name_or_id)
|
|
181
|
+
env = db.query(Environment).filter(
|
|
182
|
+
Environment.id == str(uuid_obj),
|
|
183
|
+
Environment.organization_id == organization_id
|
|
184
|
+
).first()
|
|
185
|
+
|
|
186
|
+
if env:
|
|
187
|
+
logger.info("environment_uuid_valid", env_id=str(uuid_obj))
|
|
188
|
+
return str(uuid_obj)
|
|
189
|
+
except (ValueError, AttributeError):
|
|
190
|
+
pass
|
|
191
|
+
|
|
192
|
+
# Look up by name
|
|
193
|
+
env = db.query(Environment).filter(
|
|
194
|
+
Environment.name == name_or_id,
|
|
195
|
+
Environment.organization_id == organization_id
|
|
196
|
+
).first()
|
|
197
|
+
|
|
198
|
+
if env:
|
|
199
|
+
logger.info("environment_resolved", name=name_or_id, uuid=str(env.id))
|
|
200
|
+
return str(env.id)
|
|
201
|
+
|
|
202
|
+
logger.warning("environment_not_found", name_or_id=name_or_id)
|
|
203
|
+
return None
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
async def resolve_plan_entities(
|
|
207
|
+
plan_response,
|
|
208
|
+
organization_id: str,
|
|
209
|
+
db: Session
|
|
210
|
+
) -> None:
|
|
211
|
+
"""
|
|
212
|
+
Resolve all entity names in a plan response to UUIDs (in-place).
|
|
213
|
+
|
|
214
|
+
This modifies the plan_response object to replace entity names with UUIDs
|
|
215
|
+
in the recommended_execution section.
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
plan_response: TaskPlanResponse object
|
|
219
|
+
organization_id: Organization ID
|
|
220
|
+
db: Database session
|
|
221
|
+
"""
|
|
222
|
+
if not plan_response.recommended_execution:
|
|
223
|
+
logger.info("no_recommended_execution_to_resolve")
|
|
224
|
+
return
|
|
225
|
+
|
|
226
|
+
rec_exec = plan_response.recommended_execution
|
|
227
|
+
|
|
228
|
+
# Resolve main entity (agent or team)
|
|
229
|
+
if rec_exec.entity_id:
|
|
230
|
+
entity_type = rec_exec.entity_type
|
|
231
|
+
name_or_id = rec_exec.entity_id
|
|
232
|
+
|
|
233
|
+
if entity_type == "agent":
|
|
234
|
+
uuid = await resolve_agent_name_to_uuid(name_or_id, organization_id, db)
|
|
235
|
+
else: # team
|
|
236
|
+
uuid = await resolve_team_name_to_uuid(name_or_id, organization_id, db)
|
|
237
|
+
|
|
238
|
+
if uuid:
|
|
239
|
+
rec_exec.entity_id = uuid
|
|
240
|
+
logger.info("entity_resolved",
|
|
241
|
+
entity_type=entity_type,
|
|
242
|
+
original=name_or_id,
|
|
243
|
+
resolved=uuid)
|
|
244
|
+
else:
|
|
245
|
+
error_msg = f"{entity_type.capitalize()} '{name_or_id}' not found in organization"
|
|
246
|
+
logger.error("entity_resolution_failed",
|
|
247
|
+
entity_type=entity_type,
|
|
248
|
+
name_or_id=name_or_id,
|
|
249
|
+
error=error_msg)
|
|
250
|
+
# Raise error instead of failing silently
|
|
251
|
+
raise ValueError(f"Entity resolution failed: {error_msg}. Please ensure the {entity_type} exists in your organization.")
|
|
252
|
+
|
|
253
|
+
# Resolve worker queue
|
|
254
|
+
if rec_exec.recommended_worker_queue_id:
|
|
255
|
+
wq_uuid = await resolve_worker_queue_name_to_uuid(
|
|
256
|
+
rec_exec.recommended_worker_queue_id,
|
|
257
|
+
organization_id,
|
|
258
|
+
db
|
|
259
|
+
)
|
|
260
|
+
if wq_uuid:
|
|
261
|
+
rec_exec.recommended_worker_queue_id = wq_uuid
|
|
262
|
+
logger.info("worker_queue_resolved", resolved=wq_uuid)
|
|
263
|
+
|
|
264
|
+
# Resolve environment
|
|
265
|
+
if rec_exec.recommended_environment_id:
|
|
266
|
+
env_uuid = await resolve_environment_name_to_uuid(
|
|
267
|
+
rec_exec.recommended_environment_id,
|
|
268
|
+
organization_id,
|
|
269
|
+
db
|
|
270
|
+
)
|
|
271
|
+
if env_uuid:
|
|
272
|
+
rec_exec.recommended_environment_id = env_uuid
|
|
273
|
+
logger.info("environment_resolved", resolved=env_uuid)
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Task Planning Helper Functions
|
|
3
|
+
"""
|
|
4
|
+
from typing import List, Dict, Optional
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from sqlalchemy.orm import Session
|
|
8
|
+
import structlog
|
|
9
|
+
import os
|
|
10
|
+
|
|
11
|
+
logger = structlog.get_logger()
|
|
12
|
+
|
|
13
|
+
# Debug configuration - set via environment variable
|
|
14
|
+
DEBUG_SAVE_PROMPTS = os.getenv("DEBUG_SAVE_PLANNING_PROMPTS", "false").lower() in ("true", "1", "yes")
|
|
15
|
+
DEBUG_PROMPTS_DIR = os.getenv("DEBUG_PROMPTS_DIR", "./debug_prompts")
|
|
16
|
+
|
|
17
|
+
# Refinement mode instructions template
|
|
18
|
+
REFINEMENT_INSTRUCTIONS = """
|
|
19
|
+
## REFINEMENT MODE - Critical Instructions
|
|
20
|
+
|
|
21
|
+
You are refining an existing plan (Iteration #{iteration}). Follow these rules:
|
|
22
|
+
|
|
23
|
+
1. **PRESERVE Unchanged Portions**:
|
|
24
|
+
- Keep ALL parts of the previous plan that the user didn't ask to change
|
|
25
|
+
- Maintain the same agent/team assignments unless specifically requested to change
|
|
26
|
+
- Preserve task IDs, dependencies, and structure where possible
|
|
27
|
+
|
|
28
|
+
2. **ONLY Modify What Was Requested**:
|
|
29
|
+
- Read the user feedback carefully
|
|
30
|
+
- Change ONLY the specific aspects mentioned in the feedback
|
|
31
|
+
- Don't over-optimize or change things that work
|
|
32
|
+
|
|
33
|
+
3. **EXPLAIN Changes**:
|
|
34
|
+
- In the summary, briefly mention what changed from the previous iteration
|
|
35
|
+
- Reference why changes were made based on user feedback
|
|
36
|
+
- Keep the reasoning from the previous plan if still valid
|
|
37
|
+
|
|
38
|
+
4. **Incremental Updates**:
|
|
39
|
+
- If user says "change task 3 to use a different agent", only update task 3
|
|
40
|
+
- If user says "add a testing step", add it without changing other tasks
|
|
41
|
+
- Think minimal, surgical changes - not full replanning
|
|
42
|
+
|
|
43
|
+
5. **Context Awareness**:
|
|
44
|
+
- Use the conversation history above to understand the full context
|
|
45
|
+
- Reference previous decisions and build upon them
|
|
46
|
+
- Maintain consistency with earlier iterations
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def make_json_serializable(obj):
|
|
51
|
+
"""
|
|
52
|
+
Recursively convert datetime objects to ISO format strings for JSON serialization
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
obj: Object to make JSON serializable (dict, list, or primitive)
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
JSON-serializable version of the object
|
|
59
|
+
"""
|
|
60
|
+
if isinstance(obj, datetime):
|
|
61
|
+
return obj.isoformat()
|
|
62
|
+
elif isinstance(obj, dict):
|
|
63
|
+
return {key: make_json_serializable(value) for key, value in obj.items()}
|
|
64
|
+
elif isinstance(obj, list):
|
|
65
|
+
return [make_json_serializable(item) for item in obj]
|
|
66
|
+
else:
|
|
67
|
+
return obj
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def save_planning_prompt_debug(prompt: str, iteration: int = 1, task_desc: str = "") -> Optional[str]:
|
|
71
|
+
"""
|
|
72
|
+
Save planning prompt to file for debugging if DEBUG_SAVE_PLANNING_PROMPTS is enabled
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
prompt: The planning prompt to save
|
|
76
|
+
iteration: Planning iteration number
|
|
77
|
+
task_desc: Short task description for filename
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
Path to saved file or None if debug is disabled
|
|
81
|
+
"""
|
|
82
|
+
if not DEBUG_SAVE_PROMPTS:
|
|
83
|
+
return None
|
|
84
|
+
|
|
85
|
+
try:
|
|
86
|
+
# Create debug directory if it doesn't exist
|
|
87
|
+
debug_dir = Path(DEBUG_PROMPTS_DIR)
|
|
88
|
+
debug_dir.mkdir(parents=True, exist_ok=True)
|
|
89
|
+
|
|
90
|
+
# Generate filename with timestamp
|
|
91
|
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S_%f")[:-3] # Include milliseconds
|
|
92
|
+
|
|
93
|
+
# Sanitize task description for filename
|
|
94
|
+
task_slug = "".join(c if c.isalnum() or c in ("-", "_") else "_" for c in task_desc[:50])
|
|
95
|
+
if not task_slug:
|
|
96
|
+
task_slug = "unknown_task"
|
|
97
|
+
|
|
98
|
+
filename = f"planning_prompt_{timestamp}_iter{iteration}_{task_slug}.txt"
|
|
99
|
+
filepath = debug_dir / filename
|
|
100
|
+
|
|
101
|
+
# Write prompt to file
|
|
102
|
+
with open(filepath, "w", encoding="utf-8") as f:
|
|
103
|
+
f.write(f"# Planning Prompt Debug\n")
|
|
104
|
+
f.write(f"# Generated: {datetime.now().isoformat()}\n")
|
|
105
|
+
f.write(f"# Iteration: {iteration}\n")
|
|
106
|
+
f.write(f"# Task: {task_desc}\n")
|
|
107
|
+
f.write(f"# {'=' * 80}\n\n")
|
|
108
|
+
f.write(prompt)
|
|
109
|
+
|
|
110
|
+
logger.info(
|
|
111
|
+
"saved_planning_prompt_debug",
|
|
112
|
+
filepath=str(filepath),
|
|
113
|
+
prompt_length=len(prompt),
|
|
114
|
+
iteration=iteration
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
return str(filepath)
|
|
118
|
+
|
|
119
|
+
except Exception as e:
|
|
120
|
+
logger.error("failed_to_save_planning_prompt_debug", error=str(e))
|
|
121
|
+
return None
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _extract_organization_id_from_token(api_token: Optional[str]) -> Optional[str]:
|
|
125
|
+
"""
|
|
126
|
+
Extract organization ID from JWT token
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
api_token: JWT token string
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
Organization ID if found, None otherwise
|
|
133
|
+
"""
|
|
134
|
+
if not api_token:
|
|
135
|
+
return None
|
|
136
|
+
|
|
137
|
+
try:
|
|
138
|
+
import jwt
|
|
139
|
+
# Decode without verification to get organization
|
|
140
|
+
decoded = jwt.decode(api_token, options={"verify_signature": False})
|
|
141
|
+
org_id = decoded.get("organization") or decoded.get("org") or decoded.get("org_id")
|
|
142
|
+
|
|
143
|
+
if org_id:
|
|
144
|
+
logger.debug("extracted_org_from_token", organization_id=org_id)
|
|
145
|
+
|
|
146
|
+
return org_id
|
|
147
|
+
except Exception as e:
|
|
148
|
+
logger.warning("failed_to_decode_token", error=str(e))
|
|
149
|
+
return None
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _get_organization_id_fallback(agents: List, teams: List) -> Optional[str]:
|
|
153
|
+
"""
|
|
154
|
+
Get organization ID from agents or teams as fallback
|
|
155
|
+
|
|
156
|
+
Args:
|
|
157
|
+
agents: List of agent objects
|
|
158
|
+
teams: List of team objects
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
Organization ID if found, None otherwise
|
|
162
|
+
"""
|
|
163
|
+
if agents and len(agents) > 0:
|
|
164
|
+
return getattr(agents[0], "organization_id", None)
|
|
165
|
+
elif teams and len(teams) > 0:
|
|
166
|
+
return getattr(teams[0], "organization_id", None)
|
|
167
|
+
return None
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
async def _discover_agents(db: Session, organization_id: Optional[str], limit: int = 50) -> List[dict]:
|
|
171
|
+
"""
|
|
172
|
+
Discover available agents from database
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
db: Database session
|
|
176
|
+
organization_id: Organization ID for filtering
|
|
177
|
+
limit: Maximum number of agents to discover
|
|
178
|
+
|
|
179
|
+
Returns:
|
|
180
|
+
List of discovered agents as dicts
|
|
181
|
+
"""
|
|
182
|
+
try:
|
|
183
|
+
from control_plane_api.app.lib.planning_tools import AgentsContextTools
|
|
184
|
+
agents_tools = AgentsContextTools(db=db, organization_id=organization_id)
|
|
185
|
+
discovered_agents = await agents_tools.list_agents(limit=limit)
|
|
186
|
+
logger.info("discovered_agents_before_planning", count=len(discovered_agents))
|
|
187
|
+
return discovered_agents
|
|
188
|
+
except Exception as e:
|
|
189
|
+
logger.error("failed_to_discover_agents", error=str(e))
|
|
190
|
+
return []
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
async def _discover_teams(db: Session, organization_id: Optional[str], limit: int = 50) -> List[dict]:
|
|
194
|
+
"""
|
|
195
|
+
Discover available teams from database
|
|
196
|
+
|
|
197
|
+
Args:
|
|
198
|
+
db: Database session
|
|
199
|
+
organization_id: Organization ID for filtering
|
|
200
|
+
limit: Maximum number of teams to discover
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
List of discovered teams as dicts
|
|
204
|
+
"""
|
|
205
|
+
try:
|
|
206
|
+
from control_plane_api.app.lib.planning_tools import TeamsContextTools
|
|
207
|
+
teams_tools = TeamsContextTools(db=db, organization_id=organization_id)
|
|
208
|
+
discovered_teams = await teams_tools.list_teams(limit=limit)
|
|
209
|
+
logger.info("discovered_teams_before_planning", count=len(discovered_teams))
|
|
210
|
+
return discovered_teams
|
|
211
|
+
except Exception as e:
|
|
212
|
+
logger.error("failed_to_discover_teams", error=str(e))
|
|
213
|
+
return []
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def _prepare_resources_for_planning(
|
|
217
|
+
request_agents: Optional[List],
|
|
218
|
+
request_teams: Optional[List],
|
|
219
|
+
discovered_agents: List[dict],
|
|
220
|
+
discovered_teams: List[dict]
|
|
221
|
+
) -> tuple[List[dict], List[dict]]:
|
|
222
|
+
"""
|
|
223
|
+
Prepare agents and teams for planning by converting to JSON-serializable format
|
|
224
|
+
|
|
225
|
+
Args:
|
|
226
|
+
request_agents: Agents provided in request
|
|
227
|
+
request_teams: Teams provided in request
|
|
228
|
+
discovered_agents: Agents discovered from database
|
|
229
|
+
discovered_teams: Teams discovered from database
|
|
230
|
+
|
|
231
|
+
Returns:
|
|
232
|
+
Tuple of (agents_to_use, teams_to_use) as JSON-serializable dicts
|
|
233
|
+
"""
|
|
234
|
+
# Prepare agents
|
|
235
|
+
agents_to_use = []
|
|
236
|
+
if request_agents:
|
|
237
|
+
agents_to_use = [a.model_dump() for a in request_agents]
|
|
238
|
+
elif discovered_agents:
|
|
239
|
+
agents_to_use = discovered_agents
|
|
240
|
+
|
|
241
|
+
# Prepare teams
|
|
242
|
+
teams_to_use = []
|
|
243
|
+
if request_teams:
|
|
244
|
+
teams_to_use = [t.model_dump() for t in request_teams]
|
|
245
|
+
elif discovered_teams:
|
|
246
|
+
teams_to_use = discovered_teams
|
|
247
|
+
|
|
248
|
+
# Make JSON serializable
|
|
249
|
+
agents_to_use = make_json_serializable(agents_to_use)
|
|
250
|
+
teams_to_use = make_json_serializable(teams_to_use)
|
|
251
|
+
|
|
252
|
+
# Log agent data for debugging
|
|
253
|
+
if agents_to_use:
|
|
254
|
+
logger.info("agent_data_for_planner",
|
|
255
|
+
agent_count=len(agents_to_use),
|
|
256
|
+
first_agent_id=agents_to_use[0].get('id') if agents_to_use else None,
|
|
257
|
+
first_agent_name=agents_to_use[0].get('name') if agents_to_use else None,
|
|
258
|
+
has_skills=len(agents_to_use[0].get('skills', [])) if agents_to_use else 0,
|
|
259
|
+
has_exec_env=bool(agents_to_use[0].get('execution_environment')) if agents_to_use else False)
|
|
260
|
+
|
|
261
|
+
return agents_to_use, teams_to_use
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def _infer_agent_specialty(name: str, description: Optional[str]) -> str:
|
|
265
|
+
"""
|
|
266
|
+
Infer agent specialty from name and description for better context.
|
|
267
|
+
"""
|
|
268
|
+
name_lower = name.lower()
|
|
269
|
+
desc_lower = (description or "").lower()
|
|
270
|
+
|
|
271
|
+
# Check for specific specialties
|
|
272
|
+
if "devops" in name_lower or "devops" in desc_lower:
|
|
273
|
+
return "Infrastructure, deployments, cloud operations, monitoring"
|
|
274
|
+
elif "security" in name_lower or "ciso" in name_lower or "security" in desc_lower:
|
|
275
|
+
return "Security audits, compliance, vulnerability scanning, IAM"
|
|
276
|
+
elif "data" in name_lower or "analytics" in desc_lower:
|
|
277
|
+
return "Data analysis, ETL, reporting, database operations"
|
|
278
|
+
elif "backend" in name_lower or "api" in desc_lower:
|
|
279
|
+
return "API development, backend services, database integration"
|
|
280
|
+
elif "frontend" in name_lower or "ui" in desc_lower:
|
|
281
|
+
return "UI development, React/Vue/Angular, responsive design"
|
|
282
|
+
elif "full" in name_lower or "fullstack" in name_lower:
|
|
283
|
+
return "End-to-end development, frontend + backend + infrastructure"
|
|
284
|
+
elif "test" in name_lower or "qa" in desc_lower:
|
|
285
|
+
return "Testing, quality assurance, test automation"
|
|
286
|
+
else:
|
|
287
|
+
return "General automation, scripting, API integration, cloud operations"
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
def format_sse_message(event: str, data: dict) -> str:
|
|
291
|
+
"""Format data as Server-Sent Event message"""
|
|
292
|
+
import json
|
|
293
|
+
return f"event: {event}\ndata: {json.dumps(data)}\n\n"
|