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,486 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SQLAlchemy utility functions for database operations.
|
|
3
|
+
|
|
4
|
+
Provides helper functions and patterns to migrate from Supabase SDK to SQLAlchemy ORM.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import structlog
|
|
8
|
+
from typing import Any, Optional, Callable, Type, TypeVar, List, Dict
|
|
9
|
+
from sqlalchemy.orm import Session, Query
|
|
10
|
+
from sqlalchemy.exc import SQLAlchemyError, IntegrityError, NoResultFound
|
|
11
|
+
from sqlalchemy import func, select
|
|
12
|
+
from control_plane_api.app.database import Base
|
|
13
|
+
|
|
14
|
+
logger = structlog.get_logger()
|
|
15
|
+
|
|
16
|
+
T = TypeVar('T', bound=Base)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def safe_execute_query(
|
|
20
|
+
db: Session,
|
|
21
|
+
query_func: Callable[[], Query],
|
|
22
|
+
operation_name: str,
|
|
23
|
+
fallback_query_func: Optional[Callable[[], Query]] = None,
|
|
24
|
+
**context
|
|
25
|
+
) -> Any:
|
|
26
|
+
"""
|
|
27
|
+
Execute a SQLAlchemy query with defensive error handling.
|
|
28
|
+
|
|
29
|
+
This wrapper handles common database errors gracefully,
|
|
30
|
+
providing fallback queries when the primary query fails.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
db: SQLAlchemy database session
|
|
34
|
+
query_func: Function that returns the query to execute
|
|
35
|
+
operation_name: Name of the operation for logging
|
|
36
|
+
fallback_query_func: Optional fallback query if primary fails
|
|
37
|
+
**context: Additional context for logging
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
Query result or None if both queries fail
|
|
41
|
+
|
|
42
|
+
Example:
|
|
43
|
+
```python
|
|
44
|
+
result = safe_execute_query(
|
|
45
|
+
db=db,
|
|
46
|
+
query_func=lambda: db.query(Execution)
|
|
47
|
+
.options(joinedload(Execution.execution_participants))
|
|
48
|
+
.filter(Execution.organization_id == org_id),
|
|
49
|
+
fallback_query_func=lambda: db.query(Execution)
|
|
50
|
+
.filter(Execution.organization_id == org_id),
|
|
51
|
+
operation_name="list_executions",
|
|
52
|
+
org_id=org_id,
|
|
53
|
+
)
|
|
54
|
+
```
|
|
55
|
+
"""
|
|
56
|
+
try:
|
|
57
|
+
# Try primary query
|
|
58
|
+
query = query_func()
|
|
59
|
+
result = query.all() if hasattr(query, 'all') else list(query)
|
|
60
|
+
return result
|
|
61
|
+
|
|
62
|
+
except SQLAlchemyError as primary_error:
|
|
63
|
+
error_str = str(primary_error)
|
|
64
|
+
|
|
65
|
+
logger.warning(
|
|
66
|
+
f"{operation_name}_query_error",
|
|
67
|
+
error=error_str[:200], # Truncate long errors
|
|
68
|
+
error_type=type(primary_error).__name__,
|
|
69
|
+
**context
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
# Try fallback query if provided
|
|
73
|
+
if fallback_query_func:
|
|
74
|
+
try:
|
|
75
|
+
fallback_query = fallback_query_func()
|
|
76
|
+
result = fallback_query.all() if hasattr(fallback_query, 'all') else list(fallback_query)
|
|
77
|
+
logger.debug(
|
|
78
|
+
f"{operation_name}_fallback_succeeded",
|
|
79
|
+
**context
|
|
80
|
+
)
|
|
81
|
+
return result
|
|
82
|
+
|
|
83
|
+
except Exception as fallback_error:
|
|
84
|
+
logger.error(
|
|
85
|
+
f"{operation_name}_fallback_query_failed",
|
|
86
|
+
error=str(fallback_error),
|
|
87
|
+
**context
|
|
88
|
+
)
|
|
89
|
+
raise fallback_error
|
|
90
|
+
else:
|
|
91
|
+
# No fallback provided, re-raise original error
|
|
92
|
+
raise primary_error
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def model_to_dict(model: Base, exclude: Optional[List[str]] = None) -> Dict[str, Any]:
|
|
96
|
+
"""
|
|
97
|
+
Convert a SQLAlchemy model instance to a dictionary.
|
|
98
|
+
|
|
99
|
+
This mimics the Supabase response format where records are returned as dicts.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
model: SQLAlchemy model instance
|
|
103
|
+
exclude: List of field names to exclude
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
Dictionary representation of the model
|
|
107
|
+
"""
|
|
108
|
+
if model is None:
|
|
109
|
+
return None
|
|
110
|
+
|
|
111
|
+
exclude = exclude or []
|
|
112
|
+
result = {}
|
|
113
|
+
|
|
114
|
+
# Get all columns
|
|
115
|
+
for column in model.__table__.columns:
|
|
116
|
+
if column.name not in exclude:
|
|
117
|
+
value = getattr(model, column.name)
|
|
118
|
+
# Convert UUID to string for JSON serialization
|
|
119
|
+
if hasattr(value, '__str__') and column.name.endswith('_id') or column.name == 'id':
|
|
120
|
+
result[column.name] = str(value) if value else None
|
|
121
|
+
else:
|
|
122
|
+
result[column.name] = value
|
|
123
|
+
|
|
124
|
+
return result
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def models_to_dict_list(models: List[Base], exclude: Optional[List[str]] = None) -> List[Dict[str, Any]]:
|
|
128
|
+
"""
|
|
129
|
+
Convert a list of SQLAlchemy model instances to a list of dictionaries.
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
models: List of SQLAlchemy model instances
|
|
133
|
+
exclude: List of field names to exclude
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
List of dictionary representations
|
|
137
|
+
"""
|
|
138
|
+
return [model_to_dict(model, exclude) for model in models if model is not None]
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def get_or_create(
|
|
142
|
+
db: Session,
|
|
143
|
+
model: Type[T],
|
|
144
|
+
defaults: Optional[Dict[str, Any]] = None,
|
|
145
|
+
**kwargs
|
|
146
|
+
) -> tuple[T, bool]:
|
|
147
|
+
"""
|
|
148
|
+
Get an existing instance or create a new one.
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
db: Database session
|
|
152
|
+
model: Model class
|
|
153
|
+
defaults: Default values for creation
|
|
154
|
+
**kwargs: Filter parameters
|
|
155
|
+
|
|
156
|
+
Returns:
|
|
157
|
+
Tuple of (instance, created) where created is True if new instance was created
|
|
158
|
+
"""
|
|
159
|
+
instance = db.query(model).filter_by(**kwargs).first()
|
|
160
|
+
|
|
161
|
+
if instance:
|
|
162
|
+
return instance, False
|
|
163
|
+
else:
|
|
164
|
+
params = dict(kwargs)
|
|
165
|
+
if defaults:
|
|
166
|
+
params.update(defaults)
|
|
167
|
+
instance = model(**params)
|
|
168
|
+
try:
|
|
169
|
+
db.add(instance)
|
|
170
|
+
db.commit()
|
|
171
|
+
db.refresh(instance)
|
|
172
|
+
return instance, True
|
|
173
|
+
except IntegrityError:
|
|
174
|
+
db.rollback()
|
|
175
|
+
# Another thread might have created it
|
|
176
|
+
instance = db.query(model).filter_by(**kwargs).first()
|
|
177
|
+
if instance:
|
|
178
|
+
return instance, False
|
|
179
|
+
raise
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def bulk_insert(
|
|
183
|
+
db: Session,
|
|
184
|
+
model: Type[T],
|
|
185
|
+
records: List[Dict[str, Any]],
|
|
186
|
+
return_instances: bool = False
|
|
187
|
+
) -> Optional[List[T]]:
|
|
188
|
+
"""
|
|
189
|
+
Bulk insert records into the database.
|
|
190
|
+
|
|
191
|
+
Args:
|
|
192
|
+
db: Database session
|
|
193
|
+
model: Model class
|
|
194
|
+
records: List of dictionaries with record data
|
|
195
|
+
return_instances: If True, return created instances
|
|
196
|
+
|
|
197
|
+
Returns:
|
|
198
|
+
List of created instances if return_instances is True, else None
|
|
199
|
+
"""
|
|
200
|
+
instances = [model(**record) for record in records]
|
|
201
|
+
|
|
202
|
+
try:
|
|
203
|
+
db.bulk_save_objects(instances, return_defaults=return_instances)
|
|
204
|
+
db.commit()
|
|
205
|
+
|
|
206
|
+
if return_instances:
|
|
207
|
+
return instances
|
|
208
|
+
return None
|
|
209
|
+
except SQLAlchemyError as e:
|
|
210
|
+
db.rollback()
|
|
211
|
+
logger.error(
|
|
212
|
+
"bulk_insert_failed",
|
|
213
|
+
model=model.__tablename__,
|
|
214
|
+
count=len(records),
|
|
215
|
+
error=str(e)
|
|
216
|
+
)
|
|
217
|
+
raise
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def paginate(
|
|
221
|
+
query: Query,
|
|
222
|
+
skip: int = 0,
|
|
223
|
+
limit: int = 100
|
|
224
|
+
) -> tuple[List[Any], int]:
|
|
225
|
+
"""
|
|
226
|
+
Paginate a query and return results with total count.
|
|
227
|
+
|
|
228
|
+
Args:
|
|
229
|
+
query: SQLAlchemy query object
|
|
230
|
+
skip: Number of records to skip (offset)
|
|
231
|
+
limit: Maximum number of records to return
|
|
232
|
+
|
|
233
|
+
Returns:
|
|
234
|
+
Tuple of (results, total_count)
|
|
235
|
+
"""
|
|
236
|
+
total = query.count()
|
|
237
|
+
results = query.offset(skip).limit(limit).all()
|
|
238
|
+
return results, total
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def sanitize_jsonb_field(value: Any, field_name: str, default: dict = None) -> dict:
|
|
242
|
+
"""
|
|
243
|
+
Sanitize a JSONB field value to ensure it's a valid dict.
|
|
244
|
+
|
|
245
|
+
Args:
|
|
246
|
+
value: The value to sanitize
|
|
247
|
+
field_name: Name of the field for logging
|
|
248
|
+
default: Default value if sanitization fails
|
|
249
|
+
|
|
250
|
+
Returns:
|
|
251
|
+
Valid dict or default
|
|
252
|
+
"""
|
|
253
|
+
if default is None:
|
|
254
|
+
default = {}
|
|
255
|
+
|
|
256
|
+
if value is None:
|
|
257
|
+
return default
|
|
258
|
+
|
|
259
|
+
if isinstance(value, dict):
|
|
260
|
+
return value
|
|
261
|
+
|
|
262
|
+
logger.debug(
|
|
263
|
+
"invalid_jsonb_field_sanitized",
|
|
264
|
+
field_name=field_name,
|
|
265
|
+
type=type(value).__name__
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
return default
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def set_organization_context(db: Session, org_id: str):
|
|
272
|
+
"""
|
|
273
|
+
Set organization context for RLS-like behavior in SQLAlchemy.
|
|
274
|
+
|
|
275
|
+
This sets a PostgreSQL session variable that can be used by database
|
|
276
|
+
triggers or application-level filtering.
|
|
277
|
+
|
|
278
|
+
Args:
|
|
279
|
+
db: Database session
|
|
280
|
+
org_id: Organization UUID
|
|
281
|
+
"""
|
|
282
|
+
try:
|
|
283
|
+
db.execute(
|
|
284
|
+
"SELECT set_config('app.current_org_id', :org_id, false)",
|
|
285
|
+
{"org_id": org_id}
|
|
286
|
+
)
|
|
287
|
+
except SQLAlchemyError as e:
|
|
288
|
+
logger.warning(
|
|
289
|
+
"set_organization_context_failed",
|
|
290
|
+
org_id=org_id,
|
|
291
|
+
error=str(e)
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def execute_with_org_context(db: Session, org_id: str, query_func: Callable):
|
|
296
|
+
"""
|
|
297
|
+
Execute a query with organization context for RLS-like behavior.
|
|
298
|
+
|
|
299
|
+
Sets the app.current_org_id config parameter that can be used
|
|
300
|
+
by database policies or application logic.
|
|
301
|
+
|
|
302
|
+
Args:
|
|
303
|
+
db: Database session
|
|
304
|
+
org_id: Organization UUID
|
|
305
|
+
query_func: Function that performs the database operation
|
|
306
|
+
|
|
307
|
+
Returns:
|
|
308
|
+
Query result
|
|
309
|
+
"""
|
|
310
|
+
set_organization_context(db, org_id)
|
|
311
|
+
result = query_func()
|
|
312
|
+
return result
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def upsert_record(
|
|
316
|
+
db: Session,
|
|
317
|
+
model: Type[T],
|
|
318
|
+
lookup_fields: Dict[str, Any],
|
|
319
|
+
update_fields: Dict[str, Any],
|
|
320
|
+
create_fields: Optional[Dict[str, Any]] = None
|
|
321
|
+
) -> T:
|
|
322
|
+
"""
|
|
323
|
+
Upsert (insert or update) a record.
|
|
324
|
+
|
|
325
|
+
Args:
|
|
326
|
+
db: Database session
|
|
327
|
+
model: The SQLAlchemy model class
|
|
328
|
+
lookup_fields: Fields to use for finding existing record
|
|
329
|
+
update_fields: Fields to update if record exists
|
|
330
|
+
create_fields: Additional fields only for create (optional)
|
|
331
|
+
|
|
332
|
+
Returns:
|
|
333
|
+
The upserted model instance
|
|
334
|
+
"""
|
|
335
|
+
# Build filter from lookup fields
|
|
336
|
+
filters = [getattr(model, k) == v for k, v in lookup_fields.items()]
|
|
337
|
+
existing = db.query(model).filter(*filters).first()
|
|
338
|
+
|
|
339
|
+
if existing:
|
|
340
|
+
# Update existing record
|
|
341
|
+
for key, value in update_fields.items():
|
|
342
|
+
setattr(existing, key, value)
|
|
343
|
+
db.commit()
|
|
344
|
+
db.refresh(existing)
|
|
345
|
+
return existing
|
|
346
|
+
else:
|
|
347
|
+
# Create new record
|
|
348
|
+
all_fields = {**lookup_fields, **update_fields}
|
|
349
|
+
if create_fields:
|
|
350
|
+
all_fields.update(create_fields)
|
|
351
|
+
new_record = model(**all_fields)
|
|
352
|
+
db.add(new_record)
|
|
353
|
+
db.commit()
|
|
354
|
+
db.refresh(new_record)
|
|
355
|
+
return new_record
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
def get_by_id(
|
|
359
|
+
db: Session,
|
|
360
|
+
model: Type[T],
|
|
361
|
+
id_value: Any,
|
|
362
|
+
org_id: Optional[str] = None
|
|
363
|
+
) -> Optional[T]:
|
|
364
|
+
"""
|
|
365
|
+
Get a model instance by ID with optional organization filtering.
|
|
366
|
+
|
|
367
|
+
Args:
|
|
368
|
+
db: Database session
|
|
369
|
+
model: The SQLAlchemy model class
|
|
370
|
+
id_value: The ID value to look up
|
|
371
|
+
org_id: Optional organization ID for multi-tenant filtering
|
|
372
|
+
|
|
373
|
+
Returns:
|
|
374
|
+
Model instance or None if not found
|
|
375
|
+
"""
|
|
376
|
+
query = db.query(model).filter(model.id == id_value)
|
|
377
|
+
|
|
378
|
+
if org_id and hasattr(model, 'organization_id'):
|
|
379
|
+
query = query.filter(model.organization_id == org_id)
|
|
380
|
+
|
|
381
|
+
return query.first()
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
def delete_by_id(
|
|
385
|
+
db: Session,
|
|
386
|
+
model: Type[T],
|
|
387
|
+
id_value: Any,
|
|
388
|
+
org_id: Optional[str] = None
|
|
389
|
+
) -> bool:
|
|
390
|
+
"""
|
|
391
|
+
Delete a model instance by ID with optional organization filtering.
|
|
392
|
+
|
|
393
|
+
Args:
|
|
394
|
+
db: Database session
|
|
395
|
+
model: The SQLAlchemy model class
|
|
396
|
+
id_value: The ID value to delete
|
|
397
|
+
org_id: Optional organization ID for multi-tenant filtering
|
|
398
|
+
|
|
399
|
+
Returns:
|
|
400
|
+
True if record was deleted, False if not found
|
|
401
|
+
"""
|
|
402
|
+
query = db.query(model).filter(model.id == id_value)
|
|
403
|
+
|
|
404
|
+
if org_id and hasattr(model, 'organization_id'):
|
|
405
|
+
query = query.filter(model.organization_id == org_id)
|
|
406
|
+
|
|
407
|
+
result = query.delete(synchronize_session=False)
|
|
408
|
+
db.commit()
|
|
409
|
+
return result > 0
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
def update_by_id(
|
|
413
|
+
db: Session,
|
|
414
|
+
model: Type[T],
|
|
415
|
+
id_value: Any,
|
|
416
|
+
update_data: Dict[str, Any],
|
|
417
|
+
org_id: Optional[str] = None
|
|
418
|
+
) -> Optional[T]:
|
|
419
|
+
"""
|
|
420
|
+
Update a model instance by ID with optional organization filtering.
|
|
421
|
+
|
|
422
|
+
Args:
|
|
423
|
+
db: Database session
|
|
424
|
+
model: The SQLAlchemy model class
|
|
425
|
+
id_value: The ID value to update
|
|
426
|
+
update_data: Dictionary of fields to update
|
|
427
|
+
org_id: Optional organization ID for multi-tenant filtering
|
|
428
|
+
|
|
429
|
+
Returns:
|
|
430
|
+
Updated model instance or None if not found
|
|
431
|
+
"""
|
|
432
|
+
query = db.query(model).filter(model.id == id_value)
|
|
433
|
+
|
|
434
|
+
if org_id and hasattr(model, 'organization_id'):
|
|
435
|
+
query = query.filter(model.organization_id == org_id)
|
|
436
|
+
|
|
437
|
+
instance = query.first()
|
|
438
|
+
if not instance:
|
|
439
|
+
return None
|
|
440
|
+
|
|
441
|
+
for key, value in update_data.items():
|
|
442
|
+
if hasattr(instance, key):
|
|
443
|
+
setattr(instance, key, value)
|
|
444
|
+
|
|
445
|
+
db.commit()
|
|
446
|
+
db.refresh(instance)
|
|
447
|
+
return instance
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
def increment_field(
|
|
451
|
+
db: Session,
|
|
452
|
+
model: Type[T],
|
|
453
|
+
id_value: Any,
|
|
454
|
+
field_name: str,
|
|
455
|
+
increment_by: int = 1,
|
|
456
|
+
org_id: Optional[str] = None
|
|
457
|
+
) -> Optional[int]:
|
|
458
|
+
"""
|
|
459
|
+
Atomically increment a numeric field.
|
|
460
|
+
|
|
461
|
+
Args:
|
|
462
|
+
db: Database session
|
|
463
|
+
model: The SQLAlchemy model class
|
|
464
|
+
id_value: The ID value to update
|
|
465
|
+
field_name: Name of the field to increment
|
|
466
|
+
increment_by: Amount to increment by (default 1)
|
|
467
|
+
org_id: Optional organization ID for multi-tenant filtering
|
|
468
|
+
|
|
469
|
+
Returns:
|
|
470
|
+
New value after increment, or None if record not found
|
|
471
|
+
"""
|
|
472
|
+
query = db.query(model).filter(model.id == id_value)
|
|
473
|
+
|
|
474
|
+
if org_id and hasattr(model, 'organization_id'):
|
|
475
|
+
query = query.filter(model.organization_id == org_id)
|
|
476
|
+
|
|
477
|
+
instance = query.first()
|
|
478
|
+
if not instance:
|
|
479
|
+
return None
|
|
480
|
+
|
|
481
|
+
current_value = getattr(instance, field_name, 0) or 0
|
|
482
|
+
new_value = current_value + increment_by
|
|
483
|
+
setattr(instance, field_name, new_value)
|
|
484
|
+
db.commit()
|
|
485
|
+
|
|
486
|
+
return new_value
|