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,237 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Integration test for ScheduledJobWrapperWorkflow.
|
|
3
|
+
|
|
4
|
+
This test verifies that the workflow correctly:
|
|
5
|
+
1. Creates execution records via activities
|
|
6
|
+
2. Executes child workflows (agent/team)
|
|
7
|
+
3. Calculates duration properly (tests the fix for float.total_seconds() bug)
|
|
8
|
+
4. Updates job execution status
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import pytest
|
|
12
|
+
from unittest.mock import Mock, AsyncMock, patch
|
|
13
|
+
from datetime import timedelta
|
|
14
|
+
from temporalio.testing import WorkflowEnvironment
|
|
15
|
+
from temporalio.worker import Worker
|
|
16
|
+
from temporalio import activity
|
|
17
|
+
|
|
18
|
+
# Import the workflow and related classes
|
|
19
|
+
from control_plane_api.worker.workflows.scheduled_job_wrapper import (
|
|
20
|
+
ScheduledJobWrapperWorkflow,
|
|
21
|
+
ScheduledJobInput,
|
|
22
|
+
)
|
|
23
|
+
from control_plane_api.worker.workflows.agent_execution import (
|
|
24
|
+
AgentExecutionWorkflow,
|
|
25
|
+
)
|
|
26
|
+
from control_plane_api.worker.activities.job_activities import (
|
|
27
|
+
create_job_execution_record,
|
|
28
|
+
update_job_execution_status,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@pytest.mark.asyncio
|
|
33
|
+
async def test_scheduled_job_wrapper_workflow_duration_calculation():
|
|
34
|
+
"""
|
|
35
|
+
Test that ScheduledJobWrapperWorkflow correctly calculates duration.
|
|
36
|
+
|
|
37
|
+
This specifically tests the fix for the bug where workflow.time() returns
|
|
38
|
+
a float, not a datetime, so we can't call .total_seconds() on the difference.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
# Mock the activities
|
|
42
|
+
@activity.defn(name="create_job_execution_record")
|
|
43
|
+
async def mock_create_job_execution_record(input_data):
|
|
44
|
+
"""Mock activity for creating job execution record"""
|
|
45
|
+
# input_data can be a dict or ActivityCreateJobExecutionInput object
|
|
46
|
+
execution_id = input_data.execution_id if hasattr(input_data, 'execution_id') else input_data.get('execution_id')
|
|
47
|
+
return {
|
|
48
|
+
"execution_id": execution_id,
|
|
49
|
+
"status": "created",
|
|
50
|
+
"created_at": "2024-01-01T00:00:00Z"
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
@activity.defn(name="update_job_execution_status")
|
|
54
|
+
async def mock_update_job_execution_status(
|
|
55
|
+
job_id: str,
|
|
56
|
+
execution_id: str,
|
|
57
|
+
status: str,
|
|
58
|
+
duration_ms: int = None,
|
|
59
|
+
error_message: str = None
|
|
60
|
+
):
|
|
61
|
+
"""Mock activity for updating job execution status"""
|
|
62
|
+
# This is the key assertion - duration_ms should be an integer
|
|
63
|
+
assert isinstance(duration_ms, int), f"duration_ms should be int, got {type(duration_ms)}"
|
|
64
|
+
assert duration_ms >= 0, f"duration_ms should be non-negative, got {duration_ms}"
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
"job_id": job_id,
|
|
68
|
+
"execution_id": execution_id,
|
|
69
|
+
"status": "updated"
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
# Mock the child workflow
|
|
73
|
+
async def mock_agent_execution(input_data):
|
|
74
|
+
"""Mock AgentExecutionWorkflow.run"""
|
|
75
|
+
# Simulate some execution time
|
|
76
|
+
import asyncio
|
|
77
|
+
await asyncio.sleep(0.1) # 100ms execution
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
"status": "completed",
|
|
81
|
+
"execution_id": input_data.execution_id,
|
|
82
|
+
"response": "Test response"
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
# Create test environment
|
|
86
|
+
async with await WorkflowEnvironment.start_time_skipping() as env:
|
|
87
|
+
# Create worker with our workflow and mocked activities
|
|
88
|
+
worker = Worker(
|
|
89
|
+
env.client,
|
|
90
|
+
task_queue="test-task-queue",
|
|
91
|
+
workflows=[ScheduledJobWrapperWorkflow, AgentExecutionWorkflow],
|
|
92
|
+
activities=[
|
|
93
|
+
mock_create_job_execution_record,
|
|
94
|
+
mock_update_job_execution_status,
|
|
95
|
+
],
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
async with worker:
|
|
99
|
+
# Prepare test input
|
|
100
|
+
test_input = ScheduledJobInput(
|
|
101
|
+
execution_id="test_exec_123",
|
|
102
|
+
agent_id="test_agent_456",
|
|
103
|
+
organization_id="test_org_789",
|
|
104
|
+
prompt="Test scheduled job prompt",
|
|
105
|
+
system_prompt="You are a test agent",
|
|
106
|
+
model_id="claude-3-5-sonnet-20241022",
|
|
107
|
+
model_config={},
|
|
108
|
+
agent_config={},
|
|
109
|
+
mcp_servers={},
|
|
110
|
+
user_metadata={
|
|
111
|
+
"job_id": "test_job_123",
|
|
112
|
+
"job_name": "Test Job",
|
|
113
|
+
"trigger_type": "cron"
|
|
114
|
+
},
|
|
115
|
+
runtime_type="default"
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
# Mock the child workflow execution
|
|
119
|
+
with patch.object(
|
|
120
|
+
AgentExecutionWorkflow,
|
|
121
|
+
'run',
|
|
122
|
+
new=mock_agent_execution
|
|
123
|
+
):
|
|
124
|
+
# Execute the workflow
|
|
125
|
+
result = await env.client.execute_workflow(
|
|
126
|
+
ScheduledJobWrapperWorkflow.run,
|
|
127
|
+
test_input,
|
|
128
|
+
id=f"test-scheduled-job-{test_input.execution_id}",
|
|
129
|
+
task_queue="test-task-queue",
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
# Verify the result
|
|
133
|
+
assert result["status"] == "completed"
|
|
134
|
+
assert result["execution_id"] == "test_exec_123"
|
|
135
|
+
|
|
136
|
+
print("✅ Test passed! Duration calculation works correctly.")
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
@pytest.mark.asyncio
|
|
140
|
+
async def test_scheduled_job_wrapper_workflow_with_failure():
|
|
141
|
+
"""
|
|
142
|
+
Test that ScheduledJobWrapperWorkflow handles failures correctly
|
|
143
|
+
and still calculates duration.
|
|
144
|
+
"""
|
|
145
|
+
|
|
146
|
+
@activity.defn(name="create_job_execution_record")
|
|
147
|
+
async def mock_create_job_execution_record(input_data):
|
|
148
|
+
# input_data can be a dict or ActivityCreateJobExecutionInput object
|
|
149
|
+
execution_id = input_data.execution_id if hasattr(input_data, 'execution_id') else input_data.get('execution_id')
|
|
150
|
+
return {
|
|
151
|
+
"execution_id": execution_id,
|
|
152
|
+
"status": "created",
|
|
153
|
+
"created_at": "2024-01-01T00:00:00Z"
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
@activity.defn(name="update_job_execution_status")
|
|
157
|
+
async def mock_update_job_execution_status(
|
|
158
|
+
job_id: str,
|
|
159
|
+
execution_id: str,
|
|
160
|
+
status: str,
|
|
161
|
+
duration_ms: int = None,
|
|
162
|
+
error_message: str = None
|
|
163
|
+
):
|
|
164
|
+
# Verify we got duration even for failed execution
|
|
165
|
+
assert isinstance(duration_ms, int)
|
|
166
|
+
assert duration_ms >= 0
|
|
167
|
+
assert status == "failed"
|
|
168
|
+
assert error_message is not None
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
"job_id": job_id,
|
|
172
|
+
"execution_id": execution_id,
|
|
173
|
+
"status": "updated"
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async def mock_failing_agent_execution(input_data):
|
|
177
|
+
"""Mock failing AgentExecutionWorkflow.run"""
|
|
178
|
+
import asyncio
|
|
179
|
+
await asyncio.sleep(0.05)
|
|
180
|
+
raise Exception("Test execution failure")
|
|
181
|
+
|
|
182
|
+
async with await WorkflowEnvironment.start_time_skipping() as env:
|
|
183
|
+
worker = Worker(
|
|
184
|
+
env.client,
|
|
185
|
+
task_queue="test-task-queue-fail",
|
|
186
|
+
workflows=[ScheduledJobWrapperWorkflow, AgentExecutionWorkflow],
|
|
187
|
+
activities=[
|
|
188
|
+
mock_create_job_execution_record,
|
|
189
|
+
mock_update_job_execution_status,
|
|
190
|
+
],
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
async with worker:
|
|
194
|
+
test_input = ScheduledJobInput(
|
|
195
|
+
execution_id="test_exec_fail_456",
|
|
196
|
+
agent_id="test_agent_789",
|
|
197
|
+
organization_id="test_org_789",
|
|
198
|
+
prompt="Test failing job",
|
|
199
|
+
user_metadata={
|
|
200
|
+
"job_id": "test_job_fail_456",
|
|
201
|
+
"job_name": "Failing Test Job",
|
|
202
|
+
"trigger_type": "cron"
|
|
203
|
+
},
|
|
204
|
+
runtime_type="default"
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
with patch.object(
|
|
208
|
+
AgentExecutionWorkflow,
|
|
209
|
+
'run',
|
|
210
|
+
new=mock_failing_agent_execution
|
|
211
|
+
):
|
|
212
|
+
result = await env.client.execute_workflow(
|
|
213
|
+
ScheduledJobWrapperWorkflow.run,
|
|
214
|
+
test_input,
|
|
215
|
+
id=f"test-scheduled-job-fail-{test_input.execution_id}",
|
|
216
|
+
task_queue="test-task-queue-fail",
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
# Verify the failure was handled
|
|
220
|
+
assert result["status"] == "failed"
|
|
221
|
+
assert "error" in result
|
|
222
|
+
|
|
223
|
+
print("✅ Test passed! Duration calculation works correctly even on failure.")
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
if __name__ == "__main__":
|
|
227
|
+
import asyncio
|
|
228
|
+
|
|
229
|
+
print("Running ScheduledJobWrapperWorkflow integration tests...\n")
|
|
230
|
+
|
|
231
|
+
print("Test 1: Duration calculation with successful execution")
|
|
232
|
+
asyncio.run(test_scheduled_job_wrapper_workflow_duration_calculation())
|
|
233
|
+
|
|
234
|
+
print("\nTest 2: Duration calculation with failed execution")
|
|
235
|
+
asyncio.run(test_scheduled_job_wrapper_workflow_with_failure())
|
|
236
|
+
|
|
237
|
+
print("\n✅ All tests passed!")
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
"""Integration tests for system prompt enhancement with runtimes."""
|
|
2
|
+
import pytest
|
|
3
|
+
import os
|
|
4
|
+
from unittest.mock import patch, MagicMock
|
|
5
|
+
from control_plane_api.worker.services.system_prompt_enhancement import (
|
|
6
|
+
create_default_prompt_builder,
|
|
7
|
+
TodoListEnhancement,
|
|
8
|
+
SystemPromptBuilder,
|
|
9
|
+
)
|
|
10
|
+
from control_plane_api.worker.runtimes.agno.config import build_agno_agent_config
|
|
11
|
+
from control_plane_api.worker.runtimes.base import RuntimeExecutionContext
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class TestClaudeCodeRuntimeIntegration:
|
|
15
|
+
"""Test integration with Claude Code runtime."""
|
|
16
|
+
|
|
17
|
+
def test_claude_code_receives_enhanced_prompt(self):
|
|
18
|
+
"""Test that Claude Code runtime receives enhanced prompts with TODO instructions."""
|
|
19
|
+
from control_plane_api.worker.runtimes.claude_code.config import _prompt_builder
|
|
20
|
+
|
|
21
|
+
base_prompt = "You are a helpful coding assistant."
|
|
22
|
+
enhanced = _prompt_builder.build(base_prompt, "claude_code")
|
|
23
|
+
|
|
24
|
+
# Should have base prompt
|
|
25
|
+
assert base_prompt in enhanced
|
|
26
|
+
|
|
27
|
+
# Should have TODO list enhancement
|
|
28
|
+
assert "TODO list" in enhanced
|
|
29
|
+
assert "Task Management" in enhanced
|
|
30
|
+
assert "multi step tasks" in enhanced
|
|
31
|
+
|
|
32
|
+
# Should be longer than base
|
|
33
|
+
assert len(enhanced) > len(base_prompt)
|
|
34
|
+
|
|
35
|
+
def test_claude_code_prompt_builder_singleton(self):
|
|
36
|
+
"""Test that Claude Code runtime uses a singleton prompt builder."""
|
|
37
|
+
from control_plane_api.worker.runtimes.claude_code.config import _prompt_builder
|
|
38
|
+
|
|
39
|
+
# Should be a SystemPromptBuilder instance
|
|
40
|
+
assert isinstance(_prompt_builder, SystemPromptBuilder)
|
|
41
|
+
|
|
42
|
+
# Should have at least one enhancement (TodoList)
|
|
43
|
+
assert len(_prompt_builder._enhancements) >= 1
|
|
44
|
+
|
|
45
|
+
# First enhancement should be TodoListEnhancement
|
|
46
|
+
assert isinstance(_prompt_builder._enhancements[0], TodoListEnhancement)
|
|
47
|
+
|
|
48
|
+
def test_claude_code_runtime_with_empty_prompt(self):
|
|
49
|
+
"""Test Claude Code runtime with no base prompt."""
|
|
50
|
+
from control_plane_api.worker.runtimes.claude_code.config import _prompt_builder
|
|
51
|
+
|
|
52
|
+
enhanced = _prompt_builder.build(None, "claude_code")
|
|
53
|
+
|
|
54
|
+
# Should still have enhancement
|
|
55
|
+
assert "TODO list" in enhanced
|
|
56
|
+
assert len(enhanced) > 0
|
|
57
|
+
|
|
58
|
+
def test_claude_code_runtime_with_long_prompt(self):
|
|
59
|
+
"""Test Claude Code runtime with a long base prompt."""
|
|
60
|
+
from control_plane_api.worker.runtimes.claude_code.config import _prompt_builder
|
|
61
|
+
|
|
62
|
+
base_prompt = "You are an expert software engineer.\n" * 50
|
|
63
|
+
enhanced = _prompt_builder.build(base_prompt, "claude_code")
|
|
64
|
+
|
|
65
|
+
# Should preserve base prompt
|
|
66
|
+
assert base_prompt in enhanced
|
|
67
|
+
|
|
68
|
+
# Should add enhancement at the end
|
|
69
|
+
assert enhanced.endswith("Task Management\n" +
|
|
70
|
+
"Where suitable for multi step tasks, always create a TODO list " +
|
|
71
|
+
"to decouple the task into subtasks. This helps you track progress " +
|
|
72
|
+
"and ensures no steps are missed.")
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class TestAgnoRuntimeIntegration:
|
|
76
|
+
"""Test integration with Agno runtime."""
|
|
77
|
+
|
|
78
|
+
def test_agno_does_not_receive_todo_enhancement(self):
|
|
79
|
+
"""Test that Agno runtime does NOT receive TODO list enhancement."""
|
|
80
|
+
from control_plane_api.worker.runtimes.agno.config import _prompt_builder
|
|
81
|
+
|
|
82
|
+
base_prompt = "You are a helpful assistant."
|
|
83
|
+
enhanced = _prompt_builder.build(base_prompt, "agno")
|
|
84
|
+
|
|
85
|
+
# Should have base prompt unchanged
|
|
86
|
+
assert enhanced == base_prompt
|
|
87
|
+
|
|
88
|
+
# Should NOT have TODO list enhancement
|
|
89
|
+
assert "TODO list" not in enhanced
|
|
90
|
+
|
|
91
|
+
def test_agno_prompt_builder_singleton(self):
|
|
92
|
+
"""Test that Agno runtime uses a singleton prompt builder."""
|
|
93
|
+
from control_plane_api.worker.runtimes.agno.config import _prompt_builder
|
|
94
|
+
|
|
95
|
+
# Should be a SystemPromptBuilder instance
|
|
96
|
+
assert isinstance(_prompt_builder, SystemPromptBuilder)
|
|
97
|
+
|
|
98
|
+
# Should have at least one enhancement (TodoList)
|
|
99
|
+
assert len(_prompt_builder._enhancements) >= 1
|
|
100
|
+
|
|
101
|
+
# Even though it has TodoList enhancement, it shouldn't apply to agno
|
|
102
|
+
base = "Test prompt"
|
|
103
|
+
enhanced = _prompt_builder.build(base, "agno")
|
|
104
|
+
assert enhanced == base
|
|
105
|
+
|
|
106
|
+
@patch.dict(os.environ, {"LITELLM_API_KEY": "test-key"})
|
|
107
|
+
def test_agno_agent_config_with_enhanced_prompt(self):
|
|
108
|
+
"""Test that Agno agent config properly uses enhanced prompts."""
|
|
109
|
+
base_prompt = "You are a data analyst."
|
|
110
|
+
|
|
111
|
+
agent = build_agno_agent_config(
|
|
112
|
+
agent_id="test-agent",
|
|
113
|
+
system_prompt=base_prompt,
|
|
114
|
+
model_id="kubiya/claude-sonnet-4",
|
|
115
|
+
skills=[],
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
# Agent role should match the base prompt (no TODO enhancement)
|
|
119
|
+
assert agent.role == base_prompt
|
|
120
|
+
assert "TODO list" not in agent.role
|
|
121
|
+
|
|
122
|
+
@patch.dict(os.environ, {"LITELLM_API_KEY": "test-key"})
|
|
123
|
+
def test_agno_agent_config_with_no_prompt(self):
|
|
124
|
+
"""Test Agno agent config with no system prompt."""
|
|
125
|
+
agent = build_agno_agent_config(
|
|
126
|
+
agent_id="test-agent",
|
|
127
|
+
system_prompt=None,
|
|
128
|
+
model_id="kubiya/claude-sonnet-4",
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
# Should use default prompt
|
|
132
|
+
assert agent.role == "You are a helpful AI assistant"
|
|
133
|
+
assert "TODO list" not in agent.role
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class TestEnvironmentVariableConfiguration:
|
|
137
|
+
"""Test that environment variables control enhancements."""
|
|
138
|
+
|
|
139
|
+
@patch.dict(os.environ, {"DISABLE_SYSTEM_PROMPT_ENHANCEMENTS": "true"})
|
|
140
|
+
def test_global_disable_affects_both_runtimes(self):
|
|
141
|
+
"""Test that DISABLE_SYSTEM_PROMPT_ENHANCEMENTS disables for both runtimes."""
|
|
142
|
+
# Need to reimport to pick up env var
|
|
143
|
+
import importlib
|
|
144
|
+
from control_plane_api.worker.services import system_prompt_enhancement
|
|
145
|
+
importlib.reload(system_prompt_enhancement)
|
|
146
|
+
|
|
147
|
+
builder = system_prompt_enhancement.create_default_prompt_builder()
|
|
148
|
+
|
|
149
|
+
# Should not enhance for either runtime
|
|
150
|
+
claude_result = builder.build("Test", "claude_code")
|
|
151
|
+
assert "TODO list" not in claude_result
|
|
152
|
+
assert claude_result == "Test"
|
|
153
|
+
|
|
154
|
+
agno_result = builder.build("Test", "agno")
|
|
155
|
+
assert "TODO list" not in agno_result
|
|
156
|
+
assert agno_result == "Test"
|
|
157
|
+
|
|
158
|
+
@patch.dict(os.environ, {"ENABLE_TODO_LIST_ENHANCEMENT": "false"})
|
|
159
|
+
def test_disable_specific_enhancement(self):
|
|
160
|
+
"""Test that ENABLE_TODO_LIST_ENHANCEMENT=false disables TODO enhancement."""
|
|
161
|
+
# Need to reimport to pick up env var
|
|
162
|
+
import importlib
|
|
163
|
+
from control_plane_api.worker.services import system_prompt_enhancement
|
|
164
|
+
importlib.reload(system_prompt_enhancement)
|
|
165
|
+
|
|
166
|
+
builder = system_prompt_enhancement.create_default_prompt_builder()
|
|
167
|
+
|
|
168
|
+
# Should not have TODO enhancement
|
|
169
|
+
result = builder.build("Test", "claude_code")
|
|
170
|
+
assert "TODO list" not in result
|
|
171
|
+
assert result == "Test"
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
class TestMultipleEnhancementsLayering:
|
|
175
|
+
"""Test that multiple enhancements can be layered."""
|
|
176
|
+
|
|
177
|
+
def test_multiple_enhancements_stack(self):
|
|
178
|
+
"""Test that multiple enhancements are applied in order."""
|
|
179
|
+
from control_plane_api.worker.services.system_prompt_enhancement import (
|
|
180
|
+
SystemPromptEnhancement,
|
|
181
|
+
RuntimeType,
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
class TestEnhancement1(SystemPromptEnhancement):
|
|
185
|
+
def __init__(self):
|
|
186
|
+
super().__init__(runtime_types=[RuntimeType.CLAUDE_CODE])
|
|
187
|
+
|
|
188
|
+
@property
|
|
189
|
+
def name(self):
|
|
190
|
+
return "test_1"
|
|
191
|
+
|
|
192
|
+
def enhance(self, base_prompt):
|
|
193
|
+
return (base_prompt or "") + "\n\nEnhancement 1"
|
|
194
|
+
|
|
195
|
+
class TestEnhancement2(SystemPromptEnhancement):
|
|
196
|
+
def __init__(self):
|
|
197
|
+
super().__init__(runtime_types=[RuntimeType.CLAUDE_CODE])
|
|
198
|
+
|
|
199
|
+
@property
|
|
200
|
+
def name(self):
|
|
201
|
+
return "test_2"
|
|
202
|
+
|
|
203
|
+
def enhance(self, base_prompt):
|
|
204
|
+
return (base_prompt or "") + "\n\nEnhancement 2"
|
|
205
|
+
|
|
206
|
+
builder = SystemPromptBuilder()
|
|
207
|
+
builder.add_enhancement(TestEnhancement1())
|
|
208
|
+
builder.add_enhancement(TestEnhancement2())
|
|
209
|
+
|
|
210
|
+
result = builder.build("Base", "claude_code")
|
|
211
|
+
|
|
212
|
+
# Should have base and both enhancements in order
|
|
213
|
+
assert "Base" in result
|
|
214
|
+
assert "Enhancement 1" in result
|
|
215
|
+
assert "Enhancement 2" in result
|
|
216
|
+
|
|
217
|
+
# Enhancements should be in order
|
|
218
|
+
idx_base = result.index("Base")
|
|
219
|
+
idx_e1 = result.index("Enhancement 1")
|
|
220
|
+
idx_e2 = result.index("Enhancement 2")
|
|
221
|
+
assert idx_base < idx_e1 < idx_e2
|
|
222
|
+
|
|
223
|
+
def test_different_runtimes_get_different_enhancements(self):
|
|
224
|
+
"""Test that enhancements can be runtime-specific."""
|
|
225
|
+
from control_plane_api.worker.services.system_prompt_enhancement import (
|
|
226
|
+
SystemPromptEnhancement,
|
|
227
|
+
RuntimeType,
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
class ClaudeOnlyEnhancement(SystemPromptEnhancement):
|
|
231
|
+
def __init__(self):
|
|
232
|
+
super().__init__(runtime_types=[RuntimeType.CLAUDE_CODE])
|
|
233
|
+
|
|
234
|
+
@property
|
|
235
|
+
def name(self):
|
|
236
|
+
return "claude_only"
|
|
237
|
+
|
|
238
|
+
def enhance(self, base_prompt):
|
|
239
|
+
return (base_prompt or "") + "\nClaude Only"
|
|
240
|
+
|
|
241
|
+
class AgnoOnlyEnhancement(SystemPromptEnhancement):
|
|
242
|
+
def __init__(self):
|
|
243
|
+
super().__init__(runtime_types=[RuntimeType.AGNO])
|
|
244
|
+
|
|
245
|
+
@property
|
|
246
|
+
def name(self):
|
|
247
|
+
return "agno_only"
|
|
248
|
+
|
|
249
|
+
def enhance(self, base_prompt):
|
|
250
|
+
return (base_prompt or "") + "\nAgno Only"
|
|
251
|
+
|
|
252
|
+
builder = SystemPromptBuilder()
|
|
253
|
+
builder.add_enhancement(ClaudeOnlyEnhancement())
|
|
254
|
+
builder.add_enhancement(AgnoOnlyEnhancement())
|
|
255
|
+
|
|
256
|
+
# Claude Code should get Claude enhancement
|
|
257
|
+
claude_result = builder.build("Base", "claude_code")
|
|
258
|
+
assert "Claude Only" in claude_result
|
|
259
|
+
assert "Agno Only" not in claude_result
|
|
260
|
+
|
|
261
|
+
# Agno should get Agno enhancement
|
|
262
|
+
agno_result = builder.build("Base", "agno")
|
|
263
|
+
assert "Agno Only" in agno_result
|
|
264
|
+
assert "Claude Only" not in agno_result
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
class TestRuntimeConfigBuilders:
|
|
268
|
+
"""Test integration with actual runtime config builders."""
|
|
269
|
+
|
|
270
|
+
@patch.dict(os.environ, {"LITELLM_API_KEY": "test-key"})
|
|
271
|
+
def test_agno_config_builder_integration(self):
|
|
272
|
+
"""Test full integration with Agno config builder."""
|
|
273
|
+
agent = build_agno_agent_config(
|
|
274
|
+
agent_id="integration-test",
|
|
275
|
+
system_prompt="You are a DevOps expert.",
|
|
276
|
+
model_id="kubiya/claude-sonnet-4",
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
# Role should be the prompt without TODO enhancement
|
|
280
|
+
assert agent.role == "You are a DevOps expert."
|
|
281
|
+
assert "TODO list" not in agent.role
|
|
282
|
+
|
|
283
|
+
def test_enhancement_preserves_newlines_and_formatting(self):
|
|
284
|
+
"""Test that enhancements preserve newlines and formatting in base prompt."""
|
|
285
|
+
from control_plane_api.worker.runtimes.claude_code.config import _prompt_builder
|
|
286
|
+
|
|
287
|
+
base_prompt = """You are a helpful assistant.
|
|
288
|
+
|
|
289
|
+
Follow these rules:
|
|
290
|
+
1. Be concise
|
|
291
|
+
2. Be accurate
|
|
292
|
+
3. Be helpful"""
|
|
293
|
+
|
|
294
|
+
enhanced = _prompt_builder.build(base_prompt, "claude_code")
|
|
295
|
+
|
|
296
|
+
# Should preserve base prompt formatting
|
|
297
|
+
assert base_prompt in enhanced
|
|
298
|
+
|
|
299
|
+
# Should have enhancement appended
|
|
300
|
+
assert "TODO list" in enhanced
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
class TestEdgeCases:
|
|
304
|
+
"""Test edge cases and error handling."""
|
|
305
|
+
|
|
306
|
+
def test_enhancement_with_unicode_characters(self):
|
|
307
|
+
"""Test that enhancements work with unicode characters."""
|
|
308
|
+
from control_plane_api.worker.runtimes.claude_code.config import _prompt_builder
|
|
309
|
+
|
|
310
|
+
base_prompt = "You are a helpful assistant. 你好 🚀"
|
|
311
|
+
enhanced = _prompt_builder.build(base_prompt, "claude_code")
|
|
312
|
+
|
|
313
|
+
# Should preserve unicode
|
|
314
|
+
assert base_prompt in enhanced
|
|
315
|
+
assert "TODO list" in enhanced
|
|
316
|
+
|
|
317
|
+
def test_enhancement_with_very_long_prompt(self):
|
|
318
|
+
"""Test that enhancements work with very long prompts."""
|
|
319
|
+
from control_plane_api.worker.runtimes.claude_code.config import _prompt_builder
|
|
320
|
+
|
|
321
|
+
# Create a 10KB prompt
|
|
322
|
+
base_prompt = "You are a helpful assistant.\n" * 500
|
|
323
|
+
enhanced = _prompt_builder.build(base_prompt, "claude_code")
|
|
324
|
+
|
|
325
|
+
# Should preserve full base prompt
|
|
326
|
+
assert base_prompt in enhanced
|
|
327
|
+
|
|
328
|
+
# Should add enhancement
|
|
329
|
+
assert "TODO list" in enhanced
|
|
330
|
+
|
|
331
|
+
def test_enhancement_with_empty_string(self):
|
|
332
|
+
"""Test enhancement with empty string (not None)."""
|
|
333
|
+
from control_plane_api.worker.runtimes.claude_code.config import _prompt_builder
|
|
334
|
+
|
|
335
|
+
enhanced = _prompt_builder.build("", "claude_code")
|
|
336
|
+
|
|
337
|
+
# Should add enhancement even for empty string
|
|
338
|
+
assert "TODO list" in enhanced
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
if __name__ == "__main__":
|
|
342
|
+
# Run tests
|
|
343
|
+
pytest.main([__file__, "-v", "-s"])
|
|
File without changes
|