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,401 @@
|
|
|
1
|
+
"""Unit tests for ControlPlaneClient"""
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
import httpx
|
|
5
|
+
from unittest.mock import Mock, patch, MagicMock
|
|
6
|
+
from datetime import datetime, timezone
|
|
7
|
+
|
|
8
|
+
# Import the module under test
|
|
9
|
+
import sys
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
|
|
12
|
+
|
|
13
|
+
from control_plane_api.worker.control_plane_client import ControlPlaneClient, get_control_plane_client
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class TestControlPlaneClient:
|
|
17
|
+
"""Test suite for ControlPlaneClient class"""
|
|
18
|
+
|
|
19
|
+
@pytest.fixture
|
|
20
|
+
def client(self):
|
|
21
|
+
"""Create a ControlPlaneClient instance for testing"""
|
|
22
|
+
return ControlPlaneClient(
|
|
23
|
+
base_url="http://localhost:8000",
|
|
24
|
+
api_key="test_api_key_123"
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
@pytest.fixture
|
|
28
|
+
def mock_httpx_client(self):
|
|
29
|
+
"""Mock httpx.Client for testing"""
|
|
30
|
+
with patch('control_plane_client.httpx.Client') as mock_client_class:
|
|
31
|
+
mock_client = MagicMock()
|
|
32
|
+
mock_client_class.return_value = mock_client
|
|
33
|
+
yield mock_client
|
|
34
|
+
|
|
35
|
+
def test_client_initialization(self, client):
|
|
36
|
+
"""Test that client initializes correctly with proper configuration"""
|
|
37
|
+
assert client.base_url == "http://localhost:8000"
|
|
38
|
+
assert client.api_key == "test_api_key_123"
|
|
39
|
+
assert client.headers == {"Authorization": "UserKey test_api_key_123"}
|
|
40
|
+
|
|
41
|
+
def test_client_strips_trailing_slash(self):
|
|
42
|
+
"""Test that trailing slashes are removed from base_url"""
|
|
43
|
+
client = ControlPlaneClient(
|
|
44
|
+
base_url="http://localhost:8000/",
|
|
45
|
+
api_key="test_key"
|
|
46
|
+
)
|
|
47
|
+
assert client.base_url == "http://localhost:8000"
|
|
48
|
+
|
|
49
|
+
def test_publish_event_success(self, client):
|
|
50
|
+
"""Test successful event publishing"""
|
|
51
|
+
# Mock the HTTP client
|
|
52
|
+
mock_response = Mock()
|
|
53
|
+
mock_response.status_code = 200
|
|
54
|
+
client._client.post = Mock(return_value=mock_response)
|
|
55
|
+
|
|
56
|
+
# Call publish_event
|
|
57
|
+
result = client.publish_event(
|
|
58
|
+
execution_id="exec_123",
|
|
59
|
+
event_type="message_chunk",
|
|
60
|
+
data={"content": "test", "role": "assistant"}
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
# Verify success
|
|
64
|
+
assert result is True
|
|
65
|
+
|
|
66
|
+
# Verify the HTTP call was made correctly
|
|
67
|
+
client._client.post.assert_called_once()
|
|
68
|
+
call_args = client._client.post.call_args
|
|
69
|
+
|
|
70
|
+
# Check URL
|
|
71
|
+
assert call_args[0][0] == "http://localhost:8000/api/v1/executions/exec_123/events"
|
|
72
|
+
|
|
73
|
+
# Check headers
|
|
74
|
+
assert call_args[1]["headers"] == {"Authorization": "UserKey test_api_key_123"}
|
|
75
|
+
|
|
76
|
+
# Check payload
|
|
77
|
+
payload = call_args[1]["json"]
|
|
78
|
+
assert payload["event_type"] == "message_chunk"
|
|
79
|
+
assert payload["data"]["content"] == "test"
|
|
80
|
+
assert payload["data"]["role"] == "assistant"
|
|
81
|
+
assert "timestamp" in payload
|
|
82
|
+
|
|
83
|
+
def test_publish_event_with_202_status(self, client):
|
|
84
|
+
"""Test that 202 status is also considered success"""
|
|
85
|
+
mock_response = Mock()
|
|
86
|
+
mock_response.status_code = 202
|
|
87
|
+
client._client.post = Mock(return_value=mock_response)
|
|
88
|
+
|
|
89
|
+
result = client.publish_event(
|
|
90
|
+
execution_id="exec_123",
|
|
91
|
+
event_type="tool_started",
|
|
92
|
+
data={"tool_name": "test_tool"}
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
assert result is True
|
|
96
|
+
|
|
97
|
+
def test_publish_event_failure(self, client):
|
|
98
|
+
"""Test event publishing with non-success status code"""
|
|
99
|
+
mock_response = Mock()
|
|
100
|
+
mock_response.status_code = 500
|
|
101
|
+
client._client.post = Mock(return_value=mock_response)
|
|
102
|
+
|
|
103
|
+
result = client.publish_event(
|
|
104
|
+
execution_id="exec_123",
|
|
105
|
+
event_type="message_chunk",
|
|
106
|
+
data={"content": "test"}
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
assert result is False
|
|
110
|
+
|
|
111
|
+
def test_publish_event_exception(self, client):
|
|
112
|
+
"""Test event publishing when HTTP request raises exception"""
|
|
113
|
+
client._client.post = Mock(side_effect=httpx.ConnectError("Connection failed"))
|
|
114
|
+
|
|
115
|
+
result = client.publish_event(
|
|
116
|
+
execution_id="exec_123",
|
|
117
|
+
event_type="message_chunk",
|
|
118
|
+
data={"content": "test"}
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
assert result is False
|
|
122
|
+
|
|
123
|
+
def test_cache_metadata_success(self, client):
|
|
124
|
+
"""Test successful metadata caching"""
|
|
125
|
+
# Mock publish_event since cache_metadata calls it
|
|
126
|
+
client.publish_event = Mock(return_value=True)
|
|
127
|
+
|
|
128
|
+
result = client.cache_metadata(
|
|
129
|
+
execution_id="exec_123",
|
|
130
|
+
execution_type="AGENT"
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
assert result is True
|
|
134
|
+
client.publish_event.assert_called_once_with(
|
|
135
|
+
execution_id="exec_123",
|
|
136
|
+
event_type="metadata",
|
|
137
|
+
data={"execution_type": "AGENT"}
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
def test_cache_metadata_failure(self, client):
|
|
141
|
+
"""Test metadata caching failure"""
|
|
142
|
+
client.publish_event = Mock(return_value=False)
|
|
143
|
+
|
|
144
|
+
result = client.cache_metadata(
|
|
145
|
+
execution_id="exec_123",
|
|
146
|
+
execution_type="TEAM"
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
assert result is False
|
|
150
|
+
|
|
151
|
+
def test_persist_session_success(self, client):
|
|
152
|
+
"""Test successful session persistence"""
|
|
153
|
+
mock_response = Mock()
|
|
154
|
+
mock_response.status_code = 200
|
|
155
|
+
client._client.post = Mock(return_value=mock_response)
|
|
156
|
+
|
|
157
|
+
messages = [
|
|
158
|
+
{"role": "user", "content": "Hello"},
|
|
159
|
+
{"role": "assistant", "content": "Hi there!"}
|
|
160
|
+
]
|
|
161
|
+
|
|
162
|
+
result = client.persist_session(
|
|
163
|
+
execution_id="exec_123",
|
|
164
|
+
session_id="session_456",
|
|
165
|
+
user_id="user_789",
|
|
166
|
+
messages=messages,
|
|
167
|
+
metadata={"key": "value"}
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
assert result is True
|
|
171
|
+
|
|
172
|
+
# Verify HTTP call
|
|
173
|
+
client._client.post.assert_called_once()
|
|
174
|
+
call_args = client._client.post.call_args
|
|
175
|
+
|
|
176
|
+
# Check URL
|
|
177
|
+
assert call_args[0][0] == "http://localhost:8000/api/v1/executions/exec_123/session"
|
|
178
|
+
|
|
179
|
+
# Check payload
|
|
180
|
+
payload = call_args[1]["json"]
|
|
181
|
+
assert payload["session_id"] == "session_456"
|
|
182
|
+
assert payload["user_id"] == "user_789"
|
|
183
|
+
assert payload["messages"] == messages
|
|
184
|
+
assert payload["metadata"] == {"key": "value"}
|
|
185
|
+
|
|
186
|
+
def test_persist_session_with_201_status(self, client):
|
|
187
|
+
"""Test that 201 status is also considered success"""
|
|
188
|
+
mock_response = Mock()
|
|
189
|
+
mock_response.status_code = 201
|
|
190
|
+
client._client.post = Mock(return_value=mock_response)
|
|
191
|
+
|
|
192
|
+
result = client.persist_session(
|
|
193
|
+
execution_id="exec_123",
|
|
194
|
+
session_id="session_456",
|
|
195
|
+
user_id="user_789",
|
|
196
|
+
messages=[],
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
assert result is True
|
|
200
|
+
|
|
201
|
+
def test_persist_session_without_metadata(self, client):
|
|
202
|
+
"""Test session persistence without metadata"""
|
|
203
|
+
mock_response = Mock()
|
|
204
|
+
mock_response.status_code = 200
|
|
205
|
+
client._client.post = Mock(return_value=mock_response)
|
|
206
|
+
|
|
207
|
+
result = client.persist_session(
|
|
208
|
+
execution_id="exec_123",
|
|
209
|
+
session_id="session_456",
|
|
210
|
+
user_id="user_789",
|
|
211
|
+
messages=[]
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
assert result is True
|
|
215
|
+
|
|
216
|
+
# Verify metadata defaults to empty dict
|
|
217
|
+
payload = client._client.post.call_args[1]["json"]
|
|
218
|
+
assert payload["metadata"] == {}
|
|
219
|
+
|
|
220
|
+
def test_persist_session_failure(self, client):
|
|
221
|
+
"""Test session persistence with non-success status code"""
|
|
222
|
+
mock_response = Mock()
|
|
223
|
+
mock_response.status_code = 500
|
|
224
|
+
client._client.post = Mock(return_value=mock_response)
|
|
225
|
+
|
|
226
|
+
result = client.persist_session(
|
|
227
|
+
execution_id="exec_123",
|
|
228
|
+
session_id="session_456",
|
|
229
|
+
user_id="user_789",
|
|
230
|
+
messages=[]
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
assert result is False
|
|
234
|
+
|
|
235
|
+
def test_persist_session_exception(self, client):
|
|
236
|
+
"""Test session persistence when HTTP request raises exception"""
|
|
237
|
+
client._client.post = Mock(side_effect=httpx.TimeoutException("Timeout"))
|
|
238
|
+
|
|
239
|
+
result = client.persist_session(
|
|
240
|
+
execution_id="exec_123",
|
|
241
|
+
session_id="session_456",
|
|
242
|
+
user_id="user_789",
|
|
243
|
+
messages=[]
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
assert result is False
|
|
247
|
+
|
|
248
|
+
def test_get_skills_success(self, client):
|
|
249
|
+
"""Test successful skill fetching"""
|
|
250
|
+
mock_skills = [
|
|
251
|
+
{"type": "file_system", "name": "File Tools", "enabled": True},
|
|
252
|
+
{"type": "shell", "name": "Shell Tools", "enabled": True}
|
|
253
|
+
]
|
|
254
|
+
|
|
255
|
+
mock_response = Mock()
|
|
256
|
+
mock_response.status_code = 200
|
|
257
|
+
mock_response.json = Mock(return_value=mock_skills)
|
|
258
|
+
client._client.get = Mock(return_value=mock_response)
|
|
259
|
+
|
|
260
|
+
result = client.get_skills(agent_id="agent_123")
|
|
261
|
+
|
|
262
|
+
assert result == mock_skills
|
|
263
|
+
|
|
264
|
+
# Verify HTTP call
|
|
265
|
+
client._client.get.assert_called_once()
|
|
266
|
+
call_args = client._client.get.call_args
|
|
267
|
+
|
|
268
|
+
# Check URL
|
|
269
|
+
expected_url = "http://localhost:8000/api/v1/skills/associations/agents/agent_123/skills/resolved"
|
|
270
|
+
assert call_args[0][0] == expected_url
|
|
271
|
+
|
|
272
|
+
# Check headers
|
|
273
|
+
assert call_args[1]["headers"] == {"Authorization": "UserKey test_api_key_123"}
|
|
274
|
+
|
|
275
|
+
def test_get_skills_not_found(self, client):
|
|
276
|
+
"""Test skill fetching when agent not found"""
|
|
277
|
+
mock_response = Mock()
|
|
278
|
+
mock_response.status_code = 404
|
|
279
|
+
client._client.get = Mock(return_value=mock_response)
|
|
280
|
+
|
|
281
|
+
result = client.get_skills(agent_id="agent_123")
|
|
282
|
+
|
|
283
|
+
assert result == []
|
|
284
|
+
|
|
285
|
+
def test_get_skills_server_error(self, client):
|
|
286
|
+
"""Test skill fetching with server error"""
|
|
287
|
+
mock_response = Mock()
|
|
288
|
+
mock_response.status_code = 500
|
|
289
|
+
client._client.get = Mock(return_value=mock_response)
|
|
290
|
+
|
|
291
|
+
result = client.get_skills(agent_id="agent_123")
|
|
292
|
+
|
|
293
|
+
assert result == []
|
|
294
|
+
|
|
295
|
+
def test_get_skills_exception(self, client):
|
|
296
|
+
"""Test skill fetching when HTTP request raises exception"""
|
|
297
|
+
client._client.get = Mock(side_effect=httpx.ConnectError("Connection failed"))
|
|
298
|
+
|
|
299
|
+
result = client.get_skills(agent_id="agent_123")
|
|
300
|
+
|
|
301
|
+
assert result == []
|
|
302
|
+
|
|
303
|
+
def test_client_cleanup(self, client):
|
|
304
|
+
"""Test that client properly closes HTTP connection on cleanup"""
|
|
305
|
+
mock_close = Mock()
|
|
306
|
+
client._client.close = mock_close
|
|
307
|
+
|
|
308
|
+
# Trigger cleanup
|
|
309
|
+
del client
|
|
310
|
+
|
|
311
|
+
# Verify close was called (would happen in __del__)
|
|
312
|
+
# Note: This is hard to test reliably due to Python GC behavior
|
|
313
|
+
# In real usage, the close would be called by __del__
|
|
314
|
+
|
|
315
|
+
def test_client_cleanup_with_exception(self):
|
|
316
|
+
"""Test that client cleanup handles exceptions gracefully"""
|
|
317
|
+
client = ControlPlaneClient(
|
|
318
|
+
base_url="http://localhost:8000",
|
|
319
|
+
api_key="test_key"
|
|
320
|
+
)
|
|
321
|
+
client._client.close = Mock(side_effect=Exception("Close failed"))
|
|
322
|
+
|
|
323
|
+
# Should not raise exception
|
|
324
|
+
try:
|
|
325
|
+
del client
|
|
326
|
+
except Exception as e:
|
|
327
|
+
pytest.fail(f"Client cleanup should not raise exception: {e}")
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
class TestGetControlPlaneClient:
|
|
331
|
+
"""Test suite for get_control_plane_client singleton function"""
|
|
332
|
+
|
|
333
|
+
def teardown_method(self):
|
|
334
|
+
"""Reset singleton after each test"""
|
|
335
|
+
import control_plane_client
|
|
336
|
+
control_plane_client._control_plane_client = None
|
|
337
|
+
|
|
338
|
+
@patch.dict('os.environ', {
|
|
339
|
+
'CONTROL_PLANE_URL': 'http://localhost:8000',
|
|
340
|
+
'KUBIYA_API_KEY': 'test_key_123'
|
|
341
|
+
})
|
|
342
|
+
def test_singleton_creation(self):
|
|
343
|
+
"""Test that singleton is created with environment variables"""
|
|
344
|
+
client = get_control_plane_client()
|
|
345
|
+
|
|
346
|
+
assert client is not None
|
|
347
|
+
assert client.base_url == "http://localhost:8000"
|
|
348
|
+
assert client.api_key == "test_key_123"
|
|
349
|
+
|
|
350
|
+
@patch.dict('os.environ', {
|
|
351
|
+
'CONTROL_PLANE_URL': 'http://localhost:8000',
|
|
352
|
+
'KUBIYA_API_KEY': 'test_key_123'
|
|
353
|
+
})
|
|
354
|
+
def test_singleton_reuse(self):
|
|
355
|
+
"""Test that singleton returns same instance on multiple calls"""
|
|
356
|
+
client1 = get_control_plane_client()
|
|
357
|
+
client2 = get_control_plane_client()
|
|
358
|
+
|
|
359
|
+
assert client1 is client2
|
|
360
|
+
|
|
361
|
+
@patch.dict('os.environ', {}, clear=True)
|
|
362
|
+
def test_missing_control_plane_url(self):
|
|
363
|
+
"""Test that missing CONTROL_PLANE_URL raises ValueError"""
|
|
364
|
+
with pytest.raises(ValueError, match="CONTROL_PLANE_URL environment variable not set"):
|
|
365
|
+
get_control_plane_client()
|
|
366
|
+
|
|
367
|
+
@patch.dict('os.environ', {'CONTROL_PLANE_URL': 'http://localhost:8000'}, clear=True)
|
|
368
|
+
def test_missing_api_key(self):
|
|
369
|
+
"""Test that missing KUBIYA_API_KEY raises ValueError"""
|
|
370
|
+
with pytest.raises(ValueError, match="KUBIYA_API_KEY environment variable not set"):
|
|
371
|
+
get_control_plane_client()
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
class TestConnectionPooling:
|
|
375
|
+
"""Test suite for HTTP connection pooling configuration"""
|
|
376
|
+
|
|
377
|
+
def test_client_uses_connection_pooling(self):
|
|
378
|
+
"""Test that client is configured with connection pooling"""
|
|
379
|
+
with patch('control_plane_client.httpx.Client') as mock_client_class:
|
|
380
|
+
client = ControlPlaneClient(
|
|
381
|
+
base_url="http://localhost:8000",
|
|
382
|
+
api_key="test_key"
|
|
383
|
+
)
|
|
384
|
+
|
|
385
|
+
# Verify httpx.Client was called with correct parameters
|
|
386
|
+
mock_client_class.assert_called_once()
|
|
387
|
+
call_kwargs = mock_client_class.call_args[1]
|
|
388
|
+
|
|
389
|
+
# Check timeout configuration
|
|
390
|
+
assert 'timeout' in call_kwargs
|
|
391
|
+
timeout = call_kwargs['timeout']
|
|
392
|
+
assert isinstance(timeout, httpx.Timeout)
|
|
393
|
+
|
|
394
|
+
# Check limits configuration
|
|
395
|
+
assert 'limits' in call_kwargs
|
|
396
|
+
limits = call_kwargs['limits']
|
|
397
|
+
assert isinstance(limits, httpx.Limits)
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
if __name__ == "__main__":
|
|
401
|
+
pytest.main([__file__, "-v"])
|