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,201 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Template validation logic.
|
|
3
|
+
|
|
4
|
+
Validates templates against a context to ensure all variables can be resolved.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import structlog
|
|
8
|
+
from typing import List
|
|
9
|
+
from control_plane_api.app.lib.templating.types import (
|
|
10
|
+
TemplateContext,
|
|
11
|
+
TemplateVariable,
|
|
12
|
+
TemplateVariableType,
|
|
13
|
+
ValidationResult,
|
|
14
|
+
ValidationError,
|
|
15
|
+
ParseResult,
|
|
16
|
+
)
|
|
17
|
+
from control_plane_api.app.lib.templating.engine import TemplateEngine, get_default_engine
|
|
18
|
+
|
|
19
|
+
logger = structlog.get_logger()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class TemplateValidator:
|
|
23
|
+
"""
|
|
24
|
+
Validates templates against a context.
|
|
25
|
+
|
|
26
|
+
Checks that all variables referenced in a template have values available
|
|
27
|
+
in the provided context.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(self, engine: TemplateEngine = None):
|
|
31
|
+
"""
|
|
32
|
+
Initialize the validator with a template engine.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
engine: Template engine to use. If None, uses default engine.
|
|
36
|
+
"""
|
|
37
|
+
self.engine = engine if engine is not None else get_default_engine()
|
|
38
|
+
|
|
39
|
+
def validate(self, template: str, context: TemplateContext) -> ValidationResult:
|
|
40
|
+
"""
|
|
41
|
+
Validate a template against a context.
|
|
42
|
+
|
|
43
|
+
Performs the following checks:
|
|
44
|
+
1. Template syntax is valid
|
|
45
|
+
2. All referenced secrets exist in context
|
|
46
|
+
3. All referenced env vars exist in context
|
|
47
|
+
4. All simple variables exist in context
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
template: Template string to validate
|
|
51
|
+
context: Template context with available values
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
ValidationResult with errors and warnings
|
|
55
|
+
"""
|
|
56
|
+
# First parse the template
|
|
57
|
+
parse_result: ParseResult = self.engine.parse(template)
|
|
58
|
+
|
|
59
|
+
# If parsing failed, return those errors
|
|
60
|
+
if not parse_result.is_valid:
|
|
61
|
+
return ValidationResult(
|
|
62
|
+
valid=False,
|
|
63
|
+
errors=parse_result.errors,
|
|
64
|
+
warnings=[],
|
|
65
|
+
variables=parse_result.variables
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
# Now validate each variable against the context
|
|
69
|
+
errors: List[ValidationError] = []
|
|
70
|
+
warnings: List[str] = []
|
|
71
|
+
|
|
72
|
+
# Validate secret variables
|
|
73
|
+
for var in parse_result.secret_variables:
|
|
74
|
+
secret_name = var.display_name
|
|
75
|
+
if secret_name not in context.secrets:
|
|
76
|
+
errors.append(ValidationError(
|
|
77
|
+
message=f"Secret '{secret_name}' not found in context",
|
|
78
|
+
variable=var,
|
|
79
|
+
position=var.start,
|
|
80
|
+
code="MISSING_SECRET"
|
|
81
|
+
))
|
|
82
|
+
logger.debug(
|
|
83
|
+
"missing_secret",
|
|
84
|
+
secret_name=secret_name,
|
|
85
|
+
position=var.start
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
# Validate environment variables
|
|
89
|
+
for var in parse_result.env_variables:
|
|
90
|
+
env_var_name = var.display_name
|
|
91
|
+
if env_var_name not in context.env_vars:
|
|
92
|
+
errors.append(ValidationError(
|
|
93
|
+
message=f"Environment variable '{env_var_name}' not found in context",
|
|
94
|
+
variable=var,
|
|
95
|
+
position=var.start,
|
|
96
|
+
code="MISSING_ENV_VAR"
|
|
97
|
+
))
|
|
98
|
+
logger.debug(
|
|
99
|
+
"missing_env_var",
|
|
100
|
+
env_var_name=env_var_name,
|
|
101
|
+
position=var.start
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
# Validate simple variables
|
|
105
|
+
for var in parse_result.simple_variables:
|
|
106
|
+
if var.name not in context.variables:
|
|
107
|
+
errors.append(ValidationError(
|
|
108
|
+
message=f"Variable '{var.name}' not found in context",
|
|
109
|
+
variable=var,
|
|
110
|
+
position=var.start,
|
|
111
|
+
code="MISSING_VARIABLE"
|
|
112
|
+
))
|
|
113
|
+
logger.debug(
|
|
114
|
+
"missing_variable",
|
|
115
|
+
variable_name=var.name,
|
|
116
|
+
position=var.start
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
# Validate graph node variables
|
|
120
|
+
for var in parse_result.graph_variables:
|
|
121
|
+
node_id = var.display_name
|
|
122
|
+
|
|
123
|
+
# Check if node is in pre-populated context
|
|
124
|
+
if context.graph_nodes and node_id in context.graph_nodes:
|
|
125
|
+
continue # Node is available
|
|
126
|
+
|
|
127
|
+
# Node not in context - check if we can fetch it
|
|
128
|
+
if not context.graph_api_base or not context.graph_api_key:
|
|
129
|
+
errors.append(ValidationError(
|
|
130
|
+
message=(
|
|
131
|
+
f"Graph node '{node_id}' not in context and "
|
|
132
|
+
f"context graph API not configured"
|
|
133
|
+
),
|
|
134
|
+
variable=var,
|
|
135
|
+
position=var.start,
|
|
136
|
+
code="MISSING_GRAPH_NODE"
|
|
137
|
+
))
|
|
138
|
+
logger.debug(
|
|
139
|
+
"missing_graph_node_config",
|
|
140
|
+
node_id=node_id,
|
|
141
|
+
position=var.start,
|
|
142
|
+
has_api_base=bool(context.graph_api_base),
|
|
143
|
+
has_api_key=bool(context.graph_api_key)
|
|
144
|
+
)
|
|
145
|
+
# If API is configured, node will be fetched on-demand during compilation
|
|
146
|
+
# We don't validate existence here to avoid unnecessary API calls
|
|
147
|
+
|
|
148
|
+
# Generate warnings for unused context values
|
|
149
|
+
# (This helps catch typos or configuration issues)
|
|
150
|
+
if not errors: # Only show warnings if validation passed
|
|
151
|
+
# Check for unused secrets
|
|
152
|
+
used_secrets = {v.display_name for v in parse_result.secret_variables}
|
|
153
|
+
unused_secrets = set(context.secrets.keys()) - used_secrets
|
|
154
|
+
if unused_secrets:
|
|
155
|
+
warnings.append(
|
|
156
|
+
f"Unused secrets in context: {', '.join(sorted(unused_secrets))}"
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
# Check for unused env vars
|
|
160
|
+
used_env_vars = {v.display_name for v in parse_result.env_variables}
|
|
161
|
+
unused_env_vars = set(context.env_vars.keys()) - used_env_vars
|
|
162
|
+
if unused_env_vars:
|
|
163
|
+
warnings.append(
|
|
164
|
+
f"Unused environment variables in context: {', '.join(sorted(unused_env_vars))}"
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
# Check for unused simple variables
|
|
168
|
+
used_variables = {v.name for v in parse_result.simple_variables}
|
|
169
|
+
unused_variables = set(context.variables.keys()) - used_variables
|
|
170
|
+
if unused_variables:
|
|
171
|
+
warnings.append(
|
|
172
|
+
f"Unused variables in context: {', '.join(sorted(unused_variables))}"
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
return ValidationResult(
|
|
176
|
+
valid=len(errors) == 0,
|
|
177
|
+
errors=errors,
|
|
178
|
+
warnings=warnings,
|
|
179
|
+
variables=parse_result.variables
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
def validate_syntax_only(self, template: str) -> ValidationResult:
|
|
183
|
+
"""
|
|
184
|
+
Validate only the syntax of a template without checking context.
|
|
185
|
+
|
|
186
|
+
Useful for validating templates before a context is available.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
template: Template string to validate
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
ValidationResult with syntax errors only
|
|
193
|
+
"""
|
|
194
|
+
parse_result: ParseResult = self.engine.parse(template)
|
|
195
|
+
|
|
196
|
+
return ValidationResult(
|
|
197
|
+
valid=parse_result.is_valid,
|
|
198
|
+
errors=parse_result.errors,
|
|
199
|
+
warnings=[],
|
|
200
|
+
variables=parse_result.variables
|
|
201
|
+
)
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
"""Temporal client for Agent Control Plane API"""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Optional
|
|
6
|
+
import structlog
|
|
7
|
+
from temporalio.client import Client, TLSConfig
|
|
8
|
+
|
|
9
|
+
logger = structlog.get_logger()
|
|
10
|
+
|
|
11
|
+
_temporal_client: Optional[Client] = None
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
async def get_temporal_client() -> Client:
|
|
15
|
+
"""
|
|
16
|
+
Get or create Temporal client singleton.
|
|
17
|
+
|
|
18
|
+
DEPRECATED: This uses shared admin credentials from env vars.
|
|
19
|
+
For org-specific credentials, use get_temporal_client_for_org() instead.
|
|
20
|
+
|
|
21
|
+
Supports mTLS authentication for Temporal Cloud.
|
|
22
|
+
This client is used by the API to submit workflows.
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
Temporal client instance
|
|
26
|
+
"""
|
|
27
|
+
global _temporal_client
|
|
28
|
+
|
|
29
|
+
if _temporal_client is not None:
|
|
30
|
+
return _temporal_client
|
|
31
|
+
|
|
32
|
+
# Get Temporal configuration with defaults matching worker_queues.py
|
|
33
|
+
temporal_host = os.environ.get("TEMPORAL_HOST", "us-east-1.aws.api.temporal.io:7233")
|
|
34
|
+
temporal_namespace = os.environ.get("TEMPORAL_NAMESPACE", "agent-control-plane.lpagu")
|
|
35
|
+
# Check for API key in multiple possible env var names
|
|
36
|
+
temporal_api_key = (
|
|
37
|
+
os.environ.get("TEMPORAL_API_KEY") or
|
|
38
|
+
os.environ.get("TEMPORAL_CLOUD_ADMIN_TOKEN")
|
|
39
|
+
)
|
|
40
|
+
# Strip whitespace and newlines from all env vars (common issue with env vars)
|
|
41
|
+
if temporal_host:
|
|
42
|
+
temporal_host = temporal_host.strip()
|
|
43
|
+
if temporal_namespace:
|
|
44
|
+
temporal_namespace = temporal_namespace.strip()
|
|
45
|
+
if temporal_api_key:
|
|
46
|
+
temporal_api_key = temporal_api_key.strip()
|
|
47
|
+
temporal_cert_path = os.environ.get("TEMPORAL_CLIENT_CERT_PATH")
|
|
48
|
+
temporal_key_path = os.environ.get("TEMPORAL_CLIENT_KEY_PATH")
|
|
49
|
+
|
|
50
|
+
try:
|
|
51
|
+
# Check if connecting to Temporal Cloud
|
|
52
|
+
is_cloud = "tmprl.cloud" in temporal_host or "api.temporal.io" in temporal_host
|
|
53
|
+
|
|
54
|
+
if is_cloud:
|
|
55
|
+
# Check authentication method: API Key or mTLS
|
|
56
|
+
if temporal_api_key:
|
|
57
|
+
# API Key authentication
|
|
58
|
+
logger.info("temporal_auth_method", method="api_key")
|
|
59
|
+
|
|
60
|
+
# Connect with TLS and API key
|
|
61
|
+
_temporal_client = await Client.connect(
|
|
62
|
+
temporal_host,
|
|
63
|
+
namespace=temporal_namespace,
|
|
64
|
+
tls=TLSConfig(), # TLS without client cert
|
|
65
|
+
rpc_metadata={"authorization": f"Bearer {temporal_api_key}"}
|
|
66
|
+
)
|
|
67
|
+
elif temporal_cert_path:
|
|
68
|
+
# mTLS authentication
|
|
69
|
+
logger.info("temporal_auth_method", method="mtls")
|
|
70
|
+
|
|
71
|
+
# Load client certificate
|
|
72
|
+
cert_path = Path(temporal_cert_path)
|
|
73
|
+
if not cert_path.exists():
|
|
74
|
+
raise FileNotFoundError(
|
|
75
|
+
f"Temporal client certificate not found at {cert_path}"
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
with open(cert_path, "rb") as f:
|
|
79
|
+
cert_content = f.read()
|
|
80
|
+
|
|
81
|
+
# Check if private key is in same file or separate
|
|
82
|
+
if b"BEGIN PRIVATE KEY" in cert_content or b"BEGIN RSA PRIVATE KEY" in cert_content:
|
|
83
|
+
# Key is in the same file
|
|
84
|
+
client_cert = cert_content
|
|
85
|
+
client_key = cert_content
|
|
86
|
+
else:
|
|
87
|
+
# Key must be in separate file
|
|
88
|
+
if not temporal_key_path:
|
|
89
|
+
raise ValueError(
|
|
90
|
+
"Private key not found in certificate file and no separate key path configured. "
|
|
91
|
+
"Please provide TEMPORAL_CLIENT_KEY_PATH environment variable."
|
|
92
|
+
)
|
|
93
|
+
key_path = Path(temporal_key_path)
|
|
94
|
+
with open(key_path, "rb") as f:
|
|
95
|
+
client_key = f.read()
|
|
96
|
+
client_cert = cert_content
|
|
97
|
+
|
|
98
|
+
# Create TLS config for mTLS
|
|
99
|
+
tls_config = TLSConfig(
|
|
100
|
+
client_cert=client_cert,
|
|
101
|
+
client_private_key=client_key,
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
# Connect to Temporal Cloud with mTLS
|
|
105
|
+
_temporal_client = await Client.connect(
|
|
106
|
+
temporal_host,
|
|
107
|
+
namespace=temporal_namespace,
|
|
108
|
+
tls=tls_config,
|
|
109
|
+
)
|
|
110
|
+
else:
|
|
111
|
+
raise ValueError(
|
|
112
|
+
"For Temporal Cloud connection, either TEMPORAL_API_KEY or TEMPORAL_CLIENT_CERT_PATH must be provided"
|
|
113
|
+
)
|
|
114
|
+
else:
|
|
115
|
+
# Local Temporal server (no authentication required)
|
|
116
|
+
_temporal_client = await Client.connect(
|
|
117
|
+
temporal_host,
|
|
118
|
+
namespace=temporal_namespace,
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
logger.info(
|
|
122
|
+
"temporal_client_connected",
|
|
123
|
+
host=temporal_host,
|
|
124
|
+
namespace=temporal_namespace,
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
return _temporal_client
|
|
128
|
+
|
|
129
|
+
except Exception as e:
|
|
130
|
+
logger.error("temporal_client_connection_failed", error=str(e))
|
|
131
|
+
raise
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
async def get_temporal_client_for_org(
|
|
135
|
+
namespace: str,
|
|
136
|
+
api_key: str,
|
|
137
|
+
host: Optional[str] = None,
|
|
138
|
+
) -> Client:
|
|
139
|
+
"""
|
|
140
|
+
Create Temporal client for specific organization.
|
|
141
|
+
|
|
142
|
+
This creates a new client instance with org-specific credentials.
|
|
143
|
+
Should be used for all workflow operations to ensure proper multi-tenant isolation.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
namespace: Temporal namespace (e.g., "kubiya-ai.lpagu")
|
|
147
|
+
api_key: Temporal API key for the namespace (empty for local Temporal)
|
|
148
|
+
host: Temporal host (optional, uses TEMPORAL_HOST env var if not provided)
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
Temporal client instance configured for the organization
|
|
152
|
+
|
|
153
|
+
Raises:
|
|
154
|
+
Exception: If connection fails
|
|
155
|
+
|
|
156
|
+
Example:
|
|
157
|
+
client = await get_temporal_client_for_org(
|
|
158
|
+
namespace="kubiya-ai.lpagu",
|
|
159
|
+
api_key="temporal-api-key-123",
|
|
160
|
+
host="us-east-1.aws.api.temporal.io:7233"
|
|
161
|
+
)
|
|
162
|
+
"""
|
|
163
|
+
if not host:
|
|
164
|
+
host = os.environ.get("TEMPORAL_HOST", "us-east-1.aws.api.temporal.io:7233")
|
|
165
|
+
|
|
166
|
+
# Strip whitespace
|
|
167
|
+
host = host.strip()
|
|
168
|
+
namespace = namespace.strip()
|
|
169
|
+
api_key = api_key.strip() if api_key else ""
|
|
170
|
+
|
|
171
|
+
try:
|
|
172
|
+
# Check if connecting to Temporal Cloud
|
|
173
|
+
is_cloud = "tmprl.cloud" in host or "api.temporal.io" in host
|
|
174
|
+
|
|
175
|
+
if is_cloud:
|
|
176
|
+
if not api_key:
|
|
177
|
+
raise ValueError("API key is required for Temporal Cloud")
|
|
178
|
+
|
|
179
|
+
logger.info(
|
|
180
|
+
"creating_temporal_client_for_org",
|
|
181
|
+
namespace=namespace,
|
|
182
|
+
host=host,
|
|
183
|
+
auth_method="api_key"
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
# Connect with TLS and API key
|
|
187
|
+
client = await Client.connect(
|
|
188
|
+
host,
|
|
189
|
+
namespace=namespace,
|
|
190
|
+
tls=TLSConfig(),
|
|
191
|
+
rpc_metadata={"authorization": f"Bearer {api_key}"}
|
|
192
|
+
)
|
|
193
|
+
else:
|
|
194
|
+
# Local Temporal server
|
|
195
|
+
logger.info(
|
|
196
|
+
"creating_temporal_client_for_org",
|
|
197
|
+
namespace=namespace,
|
|
198
|
+
host=host,
|
|
199
|
+
auth_method="none"
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
client = await Client.connect(
|
|
203
|
+
host,
|
|
204
|
+
namespace=namespace,
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
logger.info(
|
|
208
|
+
"temporal_client_created_for_org",
|
|
209
|
+
namespace=namespace,
|
|
210
|
+
host=host,
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
return client
|
|
214
|
+
|
|
215
|
+
except Exception as e:
|
|
216
|
+
logger.error(
|
|
217
|
+
"temporal_client_creation_failed",
|
|
218
|
+
error=str(e),
|
|
219
|
+
namespace=namespace,
|
|
220
|
+
host=host
|
|
221
|
+
)
|
|
222
|
+
raise
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
async def close_temporal_client() -> None:
|
|
226
|
+
"""Close the Temporal client connection"""
|
|
227
|
+
global _temporal_client
|
|
228
|
+
|
|
229
|
+
if _temporal_client is not None:
|
|
230
|
+
await _temporal_client.close()
|
|
231
|
+
_temporal_client = None
|
|
232
|
+
logger.info("temporal_client_closed")
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"""Temporal credentials caching service.
|
|
2
|
+
|
|
3
|
+
This module provides caching functionality for organization-specific Temporal
|
|
4
|
+
credentials fetched from the Kubiya API. Credentials are cached in Redis with
|
|
5
|
+
TTL calculated from the credentials expiry time.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
from typing import Optional, Dict
|
|
11
|
+
import structlog
|
|
12
|
+
|
|
13
|
+
from control_plane_api.app.lib.redis_client import get_redis_client
|
|
14
|
+
|
|
15
|
+
logger = structlog.get_logger()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get_credentials_cache_key(org_id: str) -> str:
|
|
19
|
+
"""
|
|
20
|
+
Generate cache key for organization's Temporal credentials.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
org_id: Organization ID (slug or UUID)
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
Cache key in format: temporal:credentials:{org_id}
|
|
27
|
+
"""
|
|
28
|
+
return f"temporal:credentials:{org_id}"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
async def get_cached_temporal_credentials(org_id: str) -> Optional[Dict]:
|
|
32
|
+
"""
|
|
33
|
+
Get cached Temporal credentials for organization.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
org_id: Organization ID
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
Cached credentials dict or None if not found
|
|
40
|
+
|
|
41
|
+
Example:
|
|
42
|
+
{
|
|
43
|
+
"apiKey": "...",
|
|
44
|
+
"namespace": "org-slug.lpagu",
|
|
45
|
+
"org": "org-slug",
|
|
46
|
+
"ttl": "2026-01-07T14:38:20Z"
|
|
47
|
+
}
|
|
48
|
+
"""
|
|
49
|
+
redis = get_redis_client()
|
|
50
|
+
if not redis:
|
|
51
|
+
return None
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
cache_key = get_credentials_cache_key(org_id)
|
|
55
|
+
cached_data = await redis.get(cache_key)
|
|
56
|
+
|
|
57
|
+
if cached_data:
|
|
58
|
+
logger.debug("temporal_credentials_cache_hit", org_id=org_id)
|
|
59
|
+
if isinstance(cached_data, bytes):
|
|
60
|
+
cached_data = cached_data.decode('utf-8')
|
|
61
|
+
return json.loads(cached_data)
|
|
62
|
+
|
|
63
|
+
logger.debug("temporal_credentials_cache_miss", org_id=org_id)
|
|
64
|
+
return None
|
|
65
|
+
|
|
66
|
+
except Exception as e:
|
|
67
|
+
logger.warning(
|
|
68
|
+
"temporal_credentials_cache_read_failed",
|
|
69
|
+
error=str(e),
|
|
70
|
+
org_id=org_id
|
|
71
|
+
)
|
|
72
|
+
return None
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
async def cache_temporal_credentials(org_id: str, credentials: Dict) -> None:
|
|
76
|
+
"""
|
|
77
|
+
Cache Temporal credentials for organization with TTL.
|
|
78
|
+
|
|
79
|
+
The TTL is calculated from the credentials.ttl field if present,
|
|
80
|
+
otherwise defaults to 1 hour. TTL is clamped between 60 seconds
|
|
81
|
+
and 7 days.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
org_id: Organization ID
|
|
85
|
+
credentials: Credentials dict with optional 'ttl' field
|
|
86
|
+
|
|
87
|
+
Example:
|
|
88
|
+
await cache_temporal_credentials(
|
|
89
|
+
"kubiya-ai",
|
|
90
|
+
{
|
|
91
|
+
"apiKey": "...",
|
|
92
|
+
"namespace": "kubiya-ai.lpagu",
|
|
93
|
+
"org": "kubiya-ai",
|
|
94
|
+
"ttl": "2026-01-07T14:38:20Z"
|
|
95
|
+
}
|
|
96
|
+
)
|
|
97
|
+
"""
|
|
98
|
+
redis = get_redis_client()
|
|
99
|
+
if not redis:
|
|
100
|
+
return
|
|
101
|
+
|
|
102
|
+
try:
|
|
103
|
+
cache_key = get_credentials_cache_key(org_id)
|
|
104
|
+
|
|
105
|
+
# Calculate TTL from credentials.ttl field
|
|
106
|
+
ttl_str = credentials.get("ttl")
|
|
107
|
+
if ttl_str:
|
|
108
|
+
try:
|
|
109
|
+
# Parse ISO 8601 timestamp
|
|
110
|
+
ttl_datetime = datetime.fromisoformat(ttl_str.replace('Z', '+00:00'))
|
|
111
|
+
now = datetime.now(ttl_datetime.tzinfo)
|
|
112
|
+
ttl_seconds = int((ttl_datetime - now).total_seconds())
|
|
113
|
+
|
|
114
|
+
# Ensure reasonable TTL (min 60s, max 7 days)
|
|
115
|
+
ttl_seconds = max(60, min(ttl_seconds, 7 * 24 * 3600))
|
|
116
|
+
|
|
117
|
+
logger.debug(
|
|
118
|
+
"ttl_calculated_from_expiry",
|
|
119
|
+
org_id=org_id,
|
|
120
|
+
ttl_seconds=ttl_seconds,
|
|
121
|
+
expires_at=ttl_str
|
|
122
|
+
)
|
|
123
|
+
except Exception as e:
|
|
124
|
+
logger.warning(
|
|
125
|
+
"ttl_parse_failed_using_default",
|
|
126
|
+
error=str(e),
|
|
127
|
+
ttl_str=ttl_str
|
|
128
|
+
)
|
|
129
|
+
ttl_seconds = 3600 # Default 1 hour
|
|
130
|
+
else:
|
|
131
|
+
ttl_seconds = 3600 # Default 1 hour
|
|
132
|
+
|
|
133
|
+
# Store credentials as JSON
|
|
134
|
+
await redis.set(
|
|
135
|
+
cache_key,
|
|
136
|
+
json.dumps(credentials),
|
|
137
|
+
ex=ttl_seconds
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
logger.info(
|
|
141
|
+
"temporal_credentials_cached",
|
|
142
|
+
org_id=org_id,
|
|
143
|
+
ttl_seconds=ttl_seconds,
|
|
144
|
+
namespace=credentials.get("namespace")
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
except Exception as e:
|
|
148
|
+
logger.warning(
|
|
149
|
+
"temporal_credentials_cache_write_failed",
|
|
150
|
+
error=str(e),
|
|
151
|
+
org_id=org_id
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
async def invalidate_temporal_credentials_cache(org_id: str) -> None:
|
|
156
|
+
"""
|
|
157
|
+
Invalidate cached Temporal credentials for organization.
|
|
158
|
+
|
|
159
|
+
This is useful for forcing a credential refresh, for example
|
|
160
|
+
when credentials are rotated or revoked.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
org_id: Organization ID
|
|
164
|
+
"""
|
|
165
|
+
redis = get_redis_client()
|
|
166
|
+
if not redis:
|
|
167
|
+
return
|
|
168
|
+
|
|
169
|
+
try:
|
|
170
|
+
cache_key = get_credentials_cache_key(org_id)
|
|
171
|
+
await redis.delete(cache_key)
|
|
172
|
+
logger.info("temporal_credentials_cache_invalidated", org_id=org_id)
|
|
173
|
+
except Exception as e:
|
|
174
|
+
logger.warning(
|
|
175
|
+
"temporal_credentials_cache_invalidation_failed",
|
|
176
|
+
error=str(e),
|
|
177
|
+
org_id=org_id
|
|
178
|
+
)
|