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,357 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Filesystem Skill Loader
|
|
3
|
+
|
|
4
|
+
Discovers skills from filesystem directories by scanning for skill.yaml files.
|
|
5
|
+
"""
|
|
6
|
+
import yaml
|
|
7
|
+
import importlib.util
|
|
8
|
+
import sys
|
|
9
|
+
import json
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import List, Type, Dict, Any
|
|
12
|
+
import structlog
|
|
13
|
+
|
|
14
|
+
from .base import BaseSkillLoader
|
|
15
|
+
from control_plane_api.worker.skills.registry import LoadedSkill, SkillSource
|
|
16
|
+
|
|
17
|
+
logger = structlog.get_logger()
|
|
18
|
+
|
|
19
|
+
# Try to import jsonschema for validation (optional dependency)
|
|
20
|
+
try:
|
|
21
|
+
import jsonschema
|
|
22
|
+
JSONSCHEMA_AVAILABLE = True
|
|
23
|
+
except ImportError:
|
|
24
|
+
JSONSCHEMA_AVAILABLE = False
|
|
25
|
+
logger.warning(
|
|
26
|
+
"jsonschema_not_available",
|
|
27
|
+
message="Install jsonschema package for skill.yaml validation: pip install jsonschema"
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def validate_skill_yaml(manifest: Dict[str, Any], yaml_path: Path) -> None:
|
|
32
|
+
"""
|
|
33
|
+
Issue #6 Fix: Validate skill.yaml structure against JSON schema.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
manifest: Parsed YAML manifest
|
|
37
|
+
yaml_path: Path to the YAML file for error reporting
|
|
38
|
+
|
|
39
|
+
Raises:
|
|
40
|
+
ValueError: If validation fails
|
|
41
|
+
"""
|
|
42
|
+
if not JSONSCHEMA_AVAILABLE:
|
|
43
|
+
logger.debug(
|
|
44
|
+
"skipping_skill_yaml_validation",
|
|
45
|
+
yaml_path=str(yaml_path),
|
|
46
|
+
reason="jsonschema not installed",
|
|
47
|
+
)
|
|
48
|
+
return
|
|
49
|
+
|
|
50
|
+
# Load schema
|
|
51
|
+
schema_path = Path(__file__).parent.parent / "skill_yaml_schema.json"
|
|
52
|
+
if not schema_path.exists():
|
|
53
|
+
logger.warning(
|
|
54
|
+
"skill_yaml_schema_not_found",
|
|
55
|
+
schema_path=str(schema_path),
|
|
56
|
+
yaml_path=str(yaml_path),
|
|
57
|
+
)
|
|
58
|
+
return
|
|
59
|
+
|
|
60
|
+
try:
|
|
61
|
+
with open(schema_path) as f:
|
|
62
|
+
schema = json.load(f)
|
|
63
|
+
|
|
64
|
+
# Validate
|
|
65
|
+
jsonschema.validate(instance=manifest, schema=schema)
|
|
66
|
+
|
|
67
|
+
logger.debug(
|
|
68
|
+
"skill_yaml_validation_passed",
|
|
69
|
+
yaml_path=str(yaml_path),
|
|
70
|
+
skill_name=manifest.get("metadata", {}).get("name", "unknown"),
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
except jsonschema.ValidationError as e:
|
|
74
|
+
# Build helpful error message
|
|
75
|
+
error_path = " -> ".join(str(p) for p in e.path) if e.path else "root"
|
|
76
|
+
error_msg = (
|
|
77
|
+
f"Invalid skill.yaml at {yaml_path}\n"
|
|
78
|
+
f"Location: {error_path}\n"
|
|
79
|
+
f"Error: {e.message}\n\n"
|
|
80
|
+
f"Schema requirement: {e.schema.get('description', 'No description')}\n"
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
# Add suggestions based on common errors
|
|
84
|
+
if "required" in e.message.lower():
|
|
85
|
+
error_msg += f"\nMissing required fields. Check the skill.yaml schema.\n"
|
|
86
|
+
elif "enum" in str(e.schema):
|
|
87
|
+
allowed = e.schema.get("enum", [])
|
|
88
|
+
error_msg += f"\nAllowed values: {allowed}\n"
|
|
89
|
+
|
|
90
|
+
logger.error(
|
|
91
|
+
"skill_yaml_validation_failed",
|
|
92
|
+
yaml_path=str(yaml_path),
|
|
93
|
+
error_path=error_path,
|
|
94
|
+
error_message=e.message,
|
|
95
|
+
schema_path=str(schema_path),
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
raise ValueError(error_msg)
|
|
99
|
+
|
|
100
|
+
except Exception as e:
|
|
101
|
+
logger.error(
|
|
102
|
+
"skill_yaml_validation_error",
|
|
103
|
+
yaml_path=str(yaml_path),
|
|
104
|
+
error=str(e),
|
|
105
|
+
exc_info=True,
|
|
106
|
+
)
|
|
107
|
+
raise ValueError(f"Failed to validate skill.yaml at {yaml_path}: {str(e)}")
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class FilesystemSkillLoader(BaseSkillLoader):
|
|
111
|
+
"""
|
|
112
|
+
Load skills from filesystem directories.
|
|
113
|
+
|
|
114
|
+
Scans for skill.yaml files in configured paths.
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
def __init__(self, search_paths: List[Path]):
|
|
118
|
+
self.search_paths = [Path(p) for p in search_paths]
|
|
119
|
+
self.logger = structlog.get_logger()
|
|
120
|
+
|
|
121
|
+
def discover(self) -> List[LoadedSkill]:
|
|
122
|
+
"""
|
|
123
|
+
Discover skills by scanning filesystem for skill.yaml files.
|
|
124
|
+
"""
|
|
125
|
+
skills = []
|
|
126
|
+
|
|
127
|
+
for search_path in self.search_paths:
|
|
128
|
+
if not search_path.exists():
|
|
129
|
+
self.logger.debug("search_path_not_found", path=str(search_path))
|
|
130
|
+
continue
|
|
131
|
+
|
|
132
|
+
# Find all skill.yaml files
|
|
133
|
+
for skill_yaml in search_path.rglob("skill.yaml"):
|
|
134
|
+
try:
|
|
135
|
+
skill = self._load_skill_from_yaml(skill_yaml)
|
|
136
|
+
skills.append(skill)
|
|
137
|
+
self.logger.info(
|
|
138
|
+
"skill_discovered",
|
|
139
|
+
name=skill.name,
|
|
140
|
+
path=str(skill_yaml.parent),
|
|
141
|
+
)
|
|
142
|
+
except Exception as e:
|
|
143
|
+
self.logger.error(
|
|
144
|
+
"failed_to_load_skill",
|
|
145
|
+
path=str(skill_yaml),
|
|
146
|
+
error=str(e),
|
|
147
|
+
exc_info=True,
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
return skills
|
|
151
|
+
|
|
152
|
+
def _load_skill_from_yaml(self, yaml_path: Path) -> LoadedSkill:
|
|
153
|
+
"""Load a skill from a skill.yaml file."""
|
|
154
|
+
with open(yaml_path) as f:
|
|
155
|
+
manifest = yaml.safe_load(f)
|
|
156
|
+
|
|
157
|
+
# Issue #6 Fix: Validate skill.yaml before parsing
|
|
158
|
+
validate_skill_yaml(manifest, yaml_path)
|
|
159
|
+
|
|
160
|
+
skill_dir = yaml_path.parent
|
|
161
|
+
metadata = manifest.get("metadata", {})
|
|
162
|
+
spec = manifest.get("spec", {})
|
|
163
|
+
|
|
164
|
+
# Load implementations
|
|
165
|
+
implementations = {}
|
|
166
|
+
impl_configs = spec.get("implementations", {})
|
|
167
|
+
|
|
168
|
+
if not impl_configs:
|
|
169
|
+
raise ValueError(f"No implementations defined in {yaml_path}")
|
|
170
|
+
|
|
171
|
+
# Track failed implementations for better error reporting
|
|
172
|
+
failed_implementations = {}
|
|
173
|
+
|
|
174
|
+
for runtime, impl_info in impl_configs.items():
|
|
175
|
+
try:
|
|
176
|
+
# Check if this is a builtin runtime implementation
|
|
177
|
+
if impl_info.get("builtin", False):
|
|
178
|
+
# Builtin implementations (like Claude Code SDK tools)
|
|
179
|
+
# Store metadata about builtin tools instead of Python class
|
|
180
|
+
implementations[runtime] = {
|
|
181
|
+
"builtin": True,
|
|
182
|
+
"tools": impl_info.get("tools", []),
|
|
183
|
+
}
|
|
184
|
+
self.logger.debug(
|
|
185
|
+
"builtin_implementation_registered",
|
|
186
|
+
skill=metadata["name"],
|
|
187
|
+
runtime=runtime,
|
|
188
|
+
tools=impl_info.get("tools", []),
|
|
189
|
+
)
|
|
190
|
+
continue
|
|
191
|
+
|
|
192
|
+
module_name = impl_info["module"]
|
|
193
|
+
class_name = impl_info["class"]
|
|
194
|
+
|
|
195
|
+
# Import the module
|
|
196
|
+
impl_class = self._import_class_from_skill(
|
|
197
|
+
skill_dir, module_name, class_name
|
|
198
|
+
)
|
|
199
|
+
implementations[runtime] = impl_class
|
|
200
|
+
|
|
201
|
+
self.logger.info(
|
|
202
|
+
"implementation_loaded_successfully",
|
|
203
|
+
skill=metadata["name"],
|
|
204
|
+
runtime=runtime,
|
|
205
|
+
class_name=class_name,
|
|
206
|
+
module_path=str(skill_dir / f"{module_name}.py"),
|
|
207
|
+
)
|
|
208
|
+
except FileNotFoundError as e:
|
|
209
|
+
# Issue #3 Fix: Fail loudly with actionable error
|
|
210
|
+
error_detail = str(e)
|
|
211
|
+
failed_implementations[runtime] = error_detail
|
|
212
|
+
self.logger.error(
|
|
213
|
+
"implementation_file_not_found",
|
|
214
|
+
skill=metadata["name"],
|
|
215
|
+
runtime=runtime,
|
|
216
|
+
error=error_detail,
|
|
217
|
+
module_name=module_name,
|
|
218
|
+
expected_path=str(skill_dir / f"{module_name}.py"),
|
|
219
|
+
recovery_suggestion=f"Create the file '{module_name}.py' in {skill_dir} or update the skill.yaml to reference an existing module.",
|
|
220
|
+
)
|
|
221
|
+
except AttributeError as e:
|
|
222
|
+
# Issue #3 Fix: Class not found in module
|
|
223
|
+
error_detail = str(e)
|
|
224
|
+
failed_implementations[runtime] = error_detail
|
|
225
|
+
self.logger.error(
|
|
226
|
+
"implementation_class_not_found",
|
|
227
|
+
skill=metadata["name"],
|
|
228
|
+
runtime=runtime,
|
|
229
|
+
error=error_detail,
|
|
230
|
+
class_name=class_name,
|
|
231
|
+
module_name=module_name,
|
|
232
|
+
recovery_suggestion=f"Ensure class '{class_name}' exists in '{module_name}.py' or update the skill.yaml with the correct class name.",
|
|
233
|
+
)
|
|
234
|
+
except ImportError as e:
|
|
235
|
+
# Issue #3 Fix: Module import failed
|
|
236
|
+
error_detail = str(e)
|
|
237
|
+
failed_implementations[runtime] = error_detail
|
|
238
|
+
self.logger.error(
|
|
239
|
+
"implementation_import_failed",
|
|
240
|
+
skill=metadata["name"],
|
|
241
|
+
runtime=runtime,
|
|
242
|
+
error=error_detail,
|
|
243
|
+
module_name=module_name,
|
|
244
|
+
recovery_suggestion=f"Check that '{module_name}.py' is valid Python and all dependencies are installed. Error: {error_detail}",
|
|
245
|
+
)
|
|
246
|
+
except Exception as e:
|
|
247
|
+
# Issue #3 Fix: Generic implementation failure
|
|
248
|
+
error_detail = str(e)
|
|
249
|
+
failed_implementations[runtime] = error_detail
|
|
250
|
+
self.logger.error(
|
|
251
|
+
"implementation_load_failed",
|
|
252
|
+
skill=metadata["name"],
|
|
253
|
+
runtime=runtime,
|
|
254
|
+
error=error_detail,
|
|
255
|
+
error_type=type(e).__name__,
|
|
256
|
+
recovery_suggestion="Check the implementation file for syntax errors or missing dependencies.",
|
|
257
|
+
exc_info=True,
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
# Issue #3 Fix: Fail loudly if no valid implementations
|
|
261
|
+
if not implementations:
|
|
262
|
+
error_summary = "\n".join([
|
|
263
|
+
f" - {runtime}: {error}"
|
|
264
|
+
for runtime, error in failed_implementations.items()
|
|
265
|
+
])
|
|
266
|
+
raise ValueError(
|
|
267
|
+
f"Failed to load any implementations for skill '{metadata['name']}' from {yaml_path}.\n"
|
|
268
|
+
f"All {len(impl_configs)} runtime(s) failed:\n{error_summary}\n\n"
|
|
269
|
+
f"Recovery steps:\n"
|
|
270
|
+
f"1. Check that implementation files exist in {skill_dir}\n"
|
|
271
|
+
f"2. Verify module and class names in skill.yaml match actual files\n"
|
|
272
|
+
f"3. Ensure all dependencies are installed\n"
|
|
273
|
+
f"4. Check implementation files for syntax errors"
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
return LoadedSkill(
|
|
277
|
+
name=metadata["name"],
|
|
278
|
+
version=metadata.get("version", "0.0.0"),
|
|
279
|
+
source=self._get_source_for_path(yaml_path),
|
|
280
|
+
skill_type=spec.get("type", "custom"),
|
|
281
|
+
manifest=manifest,
|
|
282
|
+
implementations=implementations,
|
|
283
|
+
metadata={
|
|
284
|
+
"path": str(skill_dir),
|
|
285
|
+
"yaml_path": str(yaml_path),
|
|
286
|
+
},
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
def _import_class_from_skill(
|
|
290
|
+
self, skill_dir: Path, module_name: str, class_name: str
|
|
291
|
+
) -> Type:
|
|
292
|
+
"""
|
|
293
|
+
Dynamically import a class from a skill's module.
|
|
294
|
+
|
|
295
|
+
Args:
|
|
296
|
+
skill_dir: Directory containing the skill
|
|
297
|
+
module_name: Module name (e.g., "implementation" or "agno_impl")
|
|
298
|
+
class_name: Class name to import
|
|
299
|
+
|
|
300
|
+
Returns:
|
|
301
|
+
The imported class
|
|
302
|
+
"""
|
|
303
|
+
# Convert module name to file path
|
|
304
|
+
module_file = skill_dir / f"{module_name}.py"
|
|
305
|
+
|
|
306
|
+
if not module_file.exists():
|
|
307
|
+
raise FileNotFoundError(f"Module file not found: {module_file}")
|
|
308
|
+
|
|
309
|
+
# Create unique module name to avoid collisions
|
|
310
|
+
unique_module_name = f"skill_{skill_dir.name}_{module_name}"
|
|
311
|
+
|
|
312
|
+
# Load the module
|
|
313
|
+
spec = importlib.util.spec_from_file_location(unique_module_name, module_file)
|
|
314
|
+
if spec is None or spec.loader is None:
|
|
315
|
+
raise ImportError(f"Could not load module spec from {module_file}")
|
|
316
|
+
|
|
317
|
+
module = importlib.util.module_from_spec(spec)
|
|
318
|
+
sys.modules[unique_module_name] = module
|
|
319
|
+
spec.loader.exec_module(module)
|
|
320
|
+
|
|
321
|
+
# Get the class
|
|
322
|
+
if not hasattr(module, class_name):
|
|
323
|
+
raise AttributeError(
|
|
324
|
+
f"Class '{class_name}' not found in module {module_file}"
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
return getattr(module, class_name)
|
|
328
|
+
|
|
329
|
+
def _get_source_for_path(self, yaml_path: Path) -> SkillSource:
|
|
330
|
+
"""Determine source type based on path."""
|
|
331
|
+
path_str = str(yaml_path.resolve())
|
|
332
|
+
|
|
333
|
+
# Check if it's in user workspace (.kubiya/skills in current dir)
|
|
334
|
+
if ".kubiya/skills" in path_str and Path.cwd() in yaml_path.parents:
|
|
335
|
+
return SkillSource.USER_WORKSPACE
|
|
336
|
+
|
|
337
|
+
# Check if it's in global user directory (~/.kubiya/skills)
|
|
338
|
+
global_skills = Path.home() / ".kubiya/skills"
|
|
339
|
+
if global_skills in yaml_path.parents:
|
|
340
|
+
return SkillSource.USER_GLOBAL
|
|
341
|
+
|
|
342
|
+
# Default to builtin
|
|
343
|
+
return SkillSource.BUILTIN
|
|
344
|
+
|
|
345
|
+
def get_source_type(self) -> SkillSource:
|
|
346
|
+
return SkillSource.USER_WORKSPACE
|
|
347
|
+
|
|
348
|
+
def load_skill(self, skill_id: str) -> LoadedSkill:
|
|
349
|
+
"""Load a specific skill by searching for its directory."""
|
|
350
|
+
for search_path in self.search_paths:
|
|
351
|
+
skill_path = search_path / skill_id / "skill.yaml"
|
|
352
|
+
if skill_path.exists():
|
|
353
|
+
return self._load_skill_from_yaml(skill_path)
|
|
354
|
+
|
|
355
|
+
raise FileNotFoundError(
|
|
356
|
+
f"Skill '{skill_id}' not found in search paths: {self.search_paths}"
|
|
357
|
+
)
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Worker-side Skill Registry
|
|
3
|
+
|
|
4
|
+
Central registry for all skills available to the worker.
|
|
5
|
+
Discovers and tracks skills from multiple sources (filesystem, API, packages).
|
|
6
|
+
"""
|
|
7
|
+
from typing import Dict, List, Optional, Type, Any, Union
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
from enum import Enum
|
|
10
|
+
import structlog
|
|
11
|
+
|
|
12
|
+
logger = structlog.get_logger()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class SkillSource(str, Enum):
|
|
16
|
+
"""Source where skill was loaded from."""
|
|
17
|
+
CONTROL_PLANE_API = "api" # Centralized skills from DB
|
|
18
|
+
USER_WORKSPACE = "workspace" # .kubiya/skills/ in project
|
|
19
|
+
USER_GLOBAL = "global" # ~/.kubiya/skills/
|
|
20
|
+
PYTHON_PACKAGE = "package" # pip installed packages
|
|
21
|
+
GIT_REPOSITORY = "git" # Git repos
|
|
22
|
+
BUILTIN = "builtin" # Built-in skills
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass
|
|
26
|
+
class LoadedSkill:
|
|
27
|
+
"""A loaded and ready-to-use skill."""
|
|
28
|
+
name: str
|
|
29
|
+
version: str
|
|
30
|
+
source: SkillSource
|
|
31
|
+
skill_type: str # e.g., "shell", "file_system", "custom"
|
|
32
|
+
manifest: Dict[str, Any] # Parsed skill.yaml or metadata
|
|
33
|
+
implementations: Dict[str, Union[Type, Dict[str, Any]]] # Runtime -> class or builtin config
|
|
34
|
+
metadata: Dict[str, Any] # Additional metadata (path, etc.)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class SkillRegistry:
|
|
38
|
+
"""
|
|
39
|
+
Central registry for all skills on worker side.
|
|
40
|
+
|
|
41
|
+
Responsibilities:
|
|
42
|
+
- Track loaded skills
|
|
43
|
+
- Resolve dependencies
|
|
44
|
+
- Handle versioning
|
|
45
|
+
- Match skills to runtimes
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
def __init__(self):
|
|
49
|
+
self._skills: Dict[str, LoadedSkill] = {}
|
|
50
|
+
self._skills_by_type: Dict[str, List[LoadedSkill]] = {}
|
|
51
|
+
self.logger = structlog.get_logger()
|
|
52
|
+
|
|
53
|
+
def register(self, skill: LoadedSkill) -> None:
|
|
54
|
+
"""Register a loaded skill."""
|
|
55
|
+
skill_key = f"{skill.name}:{skill.version}"
|
|
56
|
+
|
|
57
|
+
if skill_key in self._skills:
|
|
58
|
+
self.logger.warning(
|
|
59
|
+
"skill_already_registered",
|
|
60
|
+
name=skill.name,
|
|
61
|
+
version=skill.version,
|
|
62
|
+
existing_source=self._skills[skill_key].source,
|
|
63
|
+
new_source=skill.source,
|
|
64
|
+
)
|
|
65
|
+
# Source priority: builtin < global < workspace < git < package < api
|
|
66
|
+
if self._should_replace(self._skills[skill_key].source, skill.source):
|
|
67
|
+
self._skills[skill_key] = skill
|
|
68
|
+
self.logger.info("skill_replaced", name=skill.name)
|
|
69
|
+
else:
|
|
70
|
+
self._skills[skill_key] = skill
|
|
71
|
+
self.logger.info(
|
|
72
|
+
"skill_registered",
|
|
73
|
+
name=skill.name,
|
|
74
|
+
version=skill.version,
|
|
75
|
+
source=skill.source,
|
|
76
|
+
skill_type=skill.skill_type,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# Index by type for faster lookup
|
|
80
|
+
if skill.skill_type not in self._skills_by_type:
|
|
81
|
+
self._skills_by_type[skill.skill_type] = []
|
|
82
|
+
|
|
83
|
+
# Remove old version if exists
|
|
84
|
+
self._skills_by_type[skill.skill_type] = [
|
|
85
|
+
s for s in self._skills_by_type[skill.skill_type]
|
|
86
|
+
if s.name != skill.name
|
|
87
|
+
]
|
|
88
|
+
self._skills_by_type[skill.skill_type].append(skill)
|
|
89
|
+
|
|
90
|
+
def get(self, name: str, version: Optional[str] = None) -> Optional[LoadedSkill]:
|
|
91
|
+
"""
|
|
92
|
+
Get a skill by name and optional version.
|
|
93
|
+
|
|
94
|
+
If version not specified, returns latest version.
|
|
95
|
+
"""
|
|
96
|
+
if version:
|
|
97
|
+
return self._skills.get(f"{name}:{version}")
|
|
98
|
+
|
|
99
|
+
# Get latest version
|
|
100
|
+
matching = [s for s in self._skills.values() if s.name == name]
|
|
101
|
+
if not matching:
|
|
102
|
+
return None
|
|
103
|
+
|
|
104
|
+
# Sort by semantic version
|
|
105
|
+
try:
|
|
106
|
+
from packaging import version as pkg_version
|
|
107
|
+
return max(matching, key=lambda s: pkg_version.parse(s.version))
|
|
108
|
+
except:
|
|
109
|
+
# Fallback to simple comparison if packaging not available
|
|
110
|
+
return matching[0]
|
|
111
|
+
|
|
112
|
+
def get_by_type(self, skill_type: str) -> Optional[LoadedSkill]:
|
|
113
|
+
"""
|
|
114
|
+
Get a skill by type (e.g., "shell", "file_system").
|
|
115
|
+
|
|
116
|
+
Returns the first registered skill of that type.
|
|
117
|
+
Useful for built-in skills where type maps 1:1 to skill.
|
|
118
|
+
"""
|
|
119
|
+
skills = self._skills_by_type.get(skill_type, [])
|
|
120
|
+
return skills[0] if skills else None
|
|
121
|
+
|
|
122
|
+
def list_skills(self, source: Optional[SkillSource] = None) -> List[LoadedSkill]:
|
|
123
|
+
"""List all registered skills, optionally filtered by source."""
|
|
124
|
+
skills = list(self._skills.values())
|
|
125
|
+
if source:
|
|
126
|
+
skills = [s for s in skills if s.source == source]
|
|
127
|
+
return skills
|
|
128
|
+
|
|
129
|
+
def get_implementation_for_runtime(
|
|
130
|
+
self, skill: LoadedSkill, runtime_type: str
|
|
131
|
+
) -> Optional[Union[Type, Dict[str, Any]]]:
|
|
132
|
+
"""
|
|
133
|
+
Get the appropriate implementation class for a specific runtime.
|
|
134
|
+
|
|
135
|
+
Falls back to 'default' implementation if runtime-specific not available.
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
Either a Python class (Type) or a dict with builtin config
|
|
139
|
+
"""
|
|
140
|
+
# Check runtime-specific implementation
|
|
141
|
+
impl = skill.implementations.get(runtime_type)
|
|
142
|
+
if impl:
|
|
143
|
+
return impl
|
|
144
|
+
|
|
145
|
+
# Fall back to default implementation
|
|
146
|
+
return skill.implementations.get("default")
|
|
147
|
+
|
|
148
|
+
def resolve_dependencies(self, skill: LoadedSkill) -> List[str]:
|
|
149
|
+
"""
|
|
150
|
+
Resolve skill dependencies.
|
|
151
|
+
|
|
152
|
+
Returns list of missing dependency names.
|
|
153
|
+
"""
|
|
154
|
+
missing = []
|
|
155
|
+
dependencies = skill.manifest.get("spec", {}).get("dependencies", [])
|
|
156
|
+
|
|
157
|
+
for dep in dependencies:
|
|
158
|
+
dep_name = dep["name"]
|
|
159
|
+
dep_version = dep.get("version", ">=0.0.0")
|
|
160
|
+
|
|
161
|
+
resolved = self.get(dep_name)
|
|
162
|
+
if not resolved:
|
|
163
|
+
if not dep.get("optional", False):
|
|
164
|
+
missing.append(f"{dep_name} {dep_version}")
|
|
165
|
+
else:
|
|
166
|
+
# Check version constraint
|
|
167
|
+
try:
|
|
168
|
+
from packaging import version, specifiers
|
|
169
|
+
spec = specifiers.SpecifierSet(dep_version)
|
|
170
|
+
if not spec.contains(resolved.version):
|
|
171
|
+
missing.append(
|
|
172
|
+
f"{dep_name} {dep_version} (found {resolved.version})"
|
|
173
|
+
)
|
|
174
|
+
except:
|
|
175
|
+
# Skip version check if packaging not available
|
|
176
|
+
pass
|
|
177
|
+
|
|
178
|
+
return missing
|
|
179
|
+
|
|
180
|
+
def get_stats(self) -> Dict[str, Any]:
|
|
181
|
+
"""Get registry statistics."""
|
|
182
|
+
return {
|
|
183
|
+
"total_skills": len(self._skills),
|
|
184
|
+
"skills_by_source": {
|
|
185
|
+
source.value: len([s for s in self._skills.values() if s.source == source])
|
|
186
|
+
for source in SkillSource
|
|
187
|
+
},
|
|
188
|
+
"skills_by_type": {
|
|
189
|
+
skill_type: len(skills)
|
|
190
|
+
for skill_type, skills in self._skills_by_type.items()
|
|
191
|
+
},
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
def _should_replace(self, existing: SkillSource, new: SkillSource) -> bool:
|
|
195
|
+
"""Determine if new source should replace existing based on priority."""
|
|
196
|
+
priority = {
|
|
197
|
+
SkillSource.BUILTIN: 1,
|
|
198
|
+
SkillSource.USER_GLOBAL: 2,
|
|
199
|
+
SkillSource.USER_WORKSPACE: 3,
|
|
200
|
+
SkillSource.GIT_REPOSITORY: 4,
|
|
201
|
+
SkillSource.PYTHON_PACKAGE: 5,
|
|
202
|
+
SkillSource.CONTROL_PLANE_API: 6,
|
|
203
|
+
}
|
|
204
|
+
return priority.get(new, 0) > priority.get(existing, 0)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
# Global registry instance
|
|
208
|
+
skill_registry = SkillRegistry()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Test suite for worker files"""
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pytest configuration for worker tests.
|
|
3
|
+
Sets up sys.path to allow 'runtimes' imports.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import sys
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
# Add the worker directory to sys.path so that 'from runtimes import ...' works
|
|
10
|
+
worker_dir = Path(__file__).parent.parent
|
|
11
|
+
if str(worker_dir) not in sys.path:
|
|
12
|
+
sys.path.insert(0, str(worker_dir))
|
|
File without changes
|