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.
Files changed (479) hide show
  1. control_plane_api/LICENSE +676 -0
  2. control_plane_api/README.md +350 -0
  3. control_plane_api/__init__.py +4 -0
  4. control_plane_api/__version__.py +8 -0
  5. control_plane_api/alembic/README +1 -0
  6. control_plane_api/alembic/env.py +121 -0
  7. control_plane_api/alembic/script.py.mako +28 -0
  8. control_plane_api/alembic/versions/2613c65c3dbe_initial_database_setup.py +32 -0
  9. control_plane_api/alembic/versions/2df520d4927d_merge_heads.py +28 -0
  10. control_plane_api/alembic/versions/43abf98d6a01_add_paused_status_to_executions.py +73 -0
  11. control_plane_api/alembic/versions/6289854264cb_merge_multiple_heads.py +28 -0
  12. control_plane_api/alembic/versions/6a4d4dc3d8dc_generate_execution_transitions.py +50 -0
  13. control_plane_api/alembic/versions/87d11cf0a783_add_disconnected_status_to_worker_.py +44 -0
  14. control_plane_api/alembic/versions/add_ephemeral_queue_support.py +85 -0
  15. control_plane_api/alembic/versions/add_model_type_to_llm_models.py +31 -0
  16. control_plane_api/alembic/versions/add_plan_executions_table.py +114 -0
  17. control_plane_api/alembic/versions/add_trace_span_tables.py +154 -0
  18. control_plane_api/alembic/versions/add_user_info_to_traces.py +36 -0
  19. control_plane_api/alembic/versions/adjusting_foreign_keys.py +32 -0
  20. control_plane_api/alembic/versions/b4983d976db2_initial_tables.py +1128 -0
  21. control_plane_api/alembic/versions/d181a3b40e71_rename_custom_metadata_to_metadata_in_.py +50 -0
  22. control_plane_api/alembic/versions/df9117888e82_add_missing_columns.py +82 -0
  23. control_plane_api/alembic/versions/f25de6ad895a_missing_migrations.py +34 -0
  24. control_plane_api/alembic/versions/f71305fb69b9_fix_ephemeral_queue_deletion_foreign_key.py +54 -0
  25. control_plane_api/alembic/versions/mark_local_exec_queues_as_ephemeral.py +68 -0
  26. control_plane_api/alembic.ini +148 -0
  27. control_plane_api/api/index.py +12 -0
  28. control_plane_api/app/__init__.py +11 -0
  29. control_plane_api/app/activities/__init__.py +20 -0
  30. control_plane_api/app/activities/agent_activities.py +384 -0
  31. control_plane_api/app/activities/plan_generation_activities.py +499 -0
  32. control_plane_api/app/activities/team_activities.py +424 -0
  33. control_plane_api/app/activities/temporal_cloud_activities.py +588 -0
  34. control_plane_api/app/config/__init__.py +35 -0
  35. control_plane_api/app/config/api_config.py +469 -0
  36. control_plane_api/app/config/config_loader.py +224 -0
  37. control_plane_api/app/config/model_pricing.py +323 -0
  38. control_plane_api/app/config/storage_config.py +159 -0
  39. control_plane_api/app/config.py +115 -0
  40. control_plane_api/app/controllers/__init__.py +0 -0
  41. control_plane_api/app/controllers/execution_environment_controller.py +1315 -0
  42. control_plane_api/app/database.py +135 -0
  43. control_plane_api/app/exceptions.py +408 -0
  44. control_plane_api/app/lib/__init__.py +11 -0
  45. control_plane_api/app/lib/environment.py +65 -0
  46. control_plane_api/app/lib/event_bus/__init__.py +17 -0
  47. control_plane_api/app/lib/event_bus/base.py +136 -0
  48. control_plane_api/app/lib/event_bus/manager.py +335 -0
  49. control_plane_api/app/lib/event_bus/providers/__init__.py +6 -0
  50. control_plane_api/app/lib/event_bus/providers/http_provider.py +166 -0
  51. control_plane_api/app/lib/event_bus/providers/nats_provider.py +324 -0
  52. control_plane_api/app/lib/event_bus/providers/redis_provider.py +233 -0
  53. control_plane_api/app/lib/event_bus/providers/websocket_provider.py +497 -0
  54. control_plane_api/app/lib/job_executor.py +330 -0
  55. control_plane_api/app/lib/kubiya_client.py +293 -0
  56. control_plane_api/app/lib/litellm_pricing.py +166 -0
  57. control_plane_api/app/lib/mcp_validation.py +163 -0
  58. control_plane_api/app/lib/nats/__init__.py +13 -0
  59. control_plane_api/app/lib/nats/credentials_manager.py +288 -0
  60. control_plane_api/app/lib/nats/listener.py +374 -0
  61. control_plane_api/app/lib/planning_prompt_builder.py +153 -0
  62. control_plane_api/app/lib/planning_tools/__init__.py +41 -0
  63. control_plane_api/app/lib/planning_tools/agents.py +409 -0
  64. control_plane_api/app/lib/planning_tools/agno_toolkit.py +836 -0
  65. control_plane_api/app/lib/planning_tools/base.py +119 -0
  66. control_plane_api/app/lib/planning_tools/cognitive_memory_tools.py +403 -0
  67. control_plane_api/app/lib/planning_tools/context_graph_tools.py +545 -0
  68. control_plane_api/app/lib/planning_tools/environments.py +218 -0
  69. control_plane_api/app/lib/planning_tools/knowledge.py +204 -0
  70. control_plane_api/app/lib/planning_tools/models.py +93 -0
  71. control_plane_api/app/lib/planning_tools/planning_service.py +646 -0
  72. control_plane_api/app/lib/planning_tools/resources.py +242 -0
  73. control_plane_api/app/lib/planning_tools/teams.py +334 -0
  74. control_plane_api/app/lib/policy_enforcer_client.py +1016 -0
  75. control_plane_api/app/lib/redis_client.py +803 -0
  76. control_plane_api/app/lib/sqlalchemy_utils.py +486 -0
  77. control_plane_api/app/lib/state_transition_tools/__init__.py +7 -0
  78. control_plane_api/app/lib/state_transition_tools/execution_context.py +388 -0
  79. control_plane_api/app/lib/storage/__init__.py +20 -0
  80. control_plane_api/app/lib/storage/base_provider.py +274 -0
  81. control_plane_api/app/lib/storage/provider_factory.py +157 -0
  82. control_plane_api/app/lib/storage/vercel_blob_provider.py +468 -0
  83. control_plane_api/app/lib/supabase.py +71 -0
  84. control_plane_api/app/lib/supabase_utils.py +138 -0
  85. control_plane_api/app/lib/task_planning/__init__.py +138 -0
  86. control_plane_api/app/lib/task_planning/agent_factory.py +308 -0
  87. control_plane_api/app/lib/task_planning/agents.py +389 -0
  88. control_plane_api/app/lib/task_planning/cache.py +218 -0
  89. control_plane_api/app/lib/task_planning/entity_resolver.py +273 -0
  90. control_plane_api/app/lib/task_planning/helpers.py +293 -0
  91. control_plane_api/app/lib/task_planning/hooks.py +474 -0
  92. control_plane_api/app/lib/task_planning/models.py +503 -0
  93. control_plane_api/app/lib/task_planning/plan_validator.py +166 -0
  94. control_plane_api/app/lib/task_planning/planning_workflow.py +2911 -0
  95. control_plane_api/app/lib/task_planning/runner.py +656 -0
  96. control_plane_api/app/lib/task_planning/streaming_hook.py +213 -0
  97. control_plane_api/app/lib/task_planning/workflow.py +424 -0
  98. control_plane_api/app/lib/templating/__init__.py +88 -0
  99. control_plane_api/app/lib/templating/compiler.py +278 -0
  100. control_plane_api/app/lib/templating/engine.py +178 -0
  101. control_plane_api/app/lib/templating/parsers/__init__.py +29 -0
  102. control_plane_api/app/lib/templating/parsers/base.py +96 -0
  103. control_plane_api/app/lib/templating/parsers/env.py +85 -0
  104. control_plane_api/app/lib/templating/parsers/graph.py +112 -0
  105. control_plane_api/app/lib/templating/parsers/secret.py +87 -0
  106. control_plane_api/app/lib/templating/parsers/simple.py +81 -0
  107. control_plane_api/app/lib/templating/resolver.py +366 -0
  108. control_plane_api/app/lib/templating/types.py +214 -0
  109. control_plane_api/app/lib/templating/validator.py +201 -0
  110. control_plane_api/app/lib/temporal_client.py +232 -0
  111. control_plane_api/app/lib/temporal_credentials_cache.py +178 -0
  112. control_plane_api/app/lib/temporal_credentials_service.py +203 -0
  113. control_plane_api/app/lib/validation/__init__.py +24 -0
  114. control_plane_api/app/lib/validation/runtime_validation.py +388 -0
  115. control_plane_api/app/main.py +531 -0
  116. control_plane_api/app/middleware/__init__.py +10 -0
  117. control_plane_api/app/middleware/auth.py +645 -0
  118. control_plane_api/app/middleware/exception_handler.py +267 -0
  119. control_plane_api/app/middleware/prometheus_middleware.py +173 -0
  120. control_plane_api/app/middleware/rate_limiting.py +384 -0
  121. control_plane_api/app/middleware/request_id.py +202 -0
  122. control_plane_api/app/models/__init__.py +40 -0
  123. control_plane_api/app/models/agent.py +90 -0
  124. control_plane_api/app/models/analytics.py +206 -0
  125. control_plane_api/app/models/associations.py +107 -0
  126. control_plane_api/app/models/auth_user.py +73 -0
  127. control_plane_api/app/models/context.py +161 -0
  128. control_plane_api/app/models/custom_integration.py +99 -0
  129. control_plane_api/app/models/environment.py +64 -0
  130. control_plane_api/app/models/execution.py +125 -0
  131. control_plane_api/app/models/execution_transition.py +50 -0
  132. control_plane_api/app/models/job.py +159 -0
  133. control_plane_api/app/models/llm_model.py +78 -0
  134. control_plane_api/app/models/orchestration.py +66 -0
  135. control_plane_api/app/models/plan_execution.py +102 -0
  136. control_plane_api/app/models/presence.py +49 -0
  137. control_plane_api/app/models/project.py +61 -0
  138. control_plane_api/app/models/project_management.py +85 -0
  139. control_plane_api/app/models/session.py +29 -0
  140. control_plane_api/app/models/skill.py +155 -0
  141. control_plane_api/app/models/system_tables.py +43 -0
  142. control_plane_api/app/models/task_planning.py +372 -0
  143. control_plane_api/app/models/team.py +86 -0
  144. control_plane_api/app/models/trace.py +257 -0
  145. control_plane_api/app/models/user_profile.py +54 -0
  146. control_plane_api/app/models/worker.py +221 -0
  147. control_plane_api/app/models/workflow.py +161 -0
  148. control_plane_api/app/models/workspace.py +50 -0
  149. control_plane_api/app/observability/__init__.py +177 -0
  150. control_plane_api/app/observability/context_logging.py +475 -0
  151. control_plane_api/app/observability/decorators.py +337 -0
  152. control_plane_api/app/observability/local_span_processor.py +702 -0
  153. control_plane_api/app/observability/metrics.py +303 -0
  154. control_plane_api/app/observability/middleware.py +246 -0
  155. control_plane_api/app/observability/optional.py +115 -0
  156. control_plane_api/app/observability/tracing.py +382 -0
  157. control_plane_api/app/policies/README.md +149 -0
  158. control_plane_api/app/policies/approved_users.rego +62 -0
  159. control_plane_api/app/policies/business_hours.rego +51 -0
  160. control_plane_api/app/policies/rate_limiting.rego +100 -0
  161. control_plane_api/app/policies/tool_enforcement/README.md +336 -0
  162. control_plane_api/app/policies/tool_enforcement/bash_command_validation.rego +71 -0
  163. control_plane_api/app/policies/tool_enforcement/business_hours_enforcement.rego +82 -0
  164. control_plane_api/app/policies/tool_enforcement/mcp_tool_allowlist.rego +58 -0
  165. control_plane_api/app/policies/tool_enforcement/production_safeguards.rego +80 -0
  166. control_plane_api/app/policies/tool_enforcement/role_based_tool_access.rego +44 -0
  167. control_plane_api/app/policies/tool_restrictions.rego +86 -0
  168. control_plane_api/app/routers/__init__.py +4 -0
  169. control_plane_api/app/routers/agents.py +382 -0
  170. control_plane_api/app/routers/agents_v2.py +1598 -0
  171. control_plane_api/app/routers/analytics.py +1310 -0
  172. control_plane_api/app/routers/auth.py +59 -0
  173. control_plane_api/app/routers/client_config.py +57 -0
  174. control_plane_api/app/routers/context_graph.py +561 -0
  175. control_plane_api/app/routers/context_manager.py +577 -0
  176. control_plane_api/app/routers/custom_integrations.py +490 -0
  177. control_plane_api/app/routers/enforcer.py +132 -0
  178. control_plane_api/app/routers/environment_context.py +252 -0
  179. control_plane_api/app/routers/environments.py +761 -0
  180. control_plane_api/app/routers/execution_environment.py +847 -0
  181. control_plane_api/app/routers/executions/__init__.py +28 -0
  182. control_plane_api/app/routers/executions/router.py +286 -0
  183. control_plane_api/app/routers/executions/services/__init__.py +22 -0
  184. control_plane_api/app/routers/executions/services/demo_worker_health.py +156 -0
  185. control_plane_api/app/routers/executions/services/status_service.py +420 -0
  186. control_plane_api/app/routers/executions/services/test_worker_health.py +480 -0
  187. control_plane_api/app/routers/executions/services/worker_health.py +514 -0
  188. control_plane_api/app/routers/executions/streaming/__init__.py +22 -0
  189. control_plane_api/app/routers/executions/streaming/deduplication.py +352 -0
  190. control_plane_api/app/routers/executions/streaming/event_buffer.py +353 -0
  191. control_plane_api/app/routers/executions/streaming/event_formatter.py +964 -0
  192. control_plane_api/app/routers/executions/streaming/history_loader.py +588 -0
  193. control_plane_api/app/routers/executions/streaming/live_source.py +693 -0
  194. control_plane_api/app/routers/executions/streaming/streamer.py +849 -0
  195. control_plane_api/app/routers/executions.py +4888 -0
  196. control_plane_api/app/routers/health.py +165 -0
  197. control_plane_api/app/routers/health_v2.py +394 -0
  198. control_plane_api/app/routers/integration_templates.py +496 -0
  199. control_plane_api/app/routers/integrations.py +287 -0
  200. control_plane_api/app/routers/jobs.py +1809 -0
  201. control_plane_api/app/routers/metrics.py +517 -0
  202. control_plane_api/app/routers/models.py +82 -0
  203. control_plane_api/app/routers/models_v2.py +628 -0
  204. control_plane_api/app/routers/plan_executions.py +1481 -0
  205. control_plane_api/app/routers/plan_generation_async.py +304 -0
  206. control_plane_api/app/routers/policies.py +669 -0
  207. control_plane_api/app/routers/presence.py +234 -0
  208. control_plane_api/app/routers/projects.py +987 -0
  209. control_plane_api/app/routers/runners.py +379 -0
  210. control_plane_api/app/routers/runtimes.py +172 -0
  211. control_plane_api/app/routers/secrets.py +171 -0
  212. control_plane_api/app/routers/skills.py +1010 -0
  213. control_plane_api/app/routers/skills_definitions.py +140 -0
  214. control_plane_api/app/routers/storage.py +456 -0
  215. control_plane_api/app/routers/task_planning.py +611 -0
  216. control_plane_api/app/routers/task_queues.py +650 -0
  217. control_plane_api/app/routers/team_context.py +274 -0
  218. control_plane_api/app/routers/teams.py +1747 -0
  219. control_plane_api/app/routers/templates.py +248 -0
  220. control_plane_api/app/routers/traces.py +571 -0
  221. control_plane_api/app/routers/websocket_client.py +479 -0
  222. control_plane_api/app/routers/websocket_executions_status.py +437 -0
  223. control_plane_api/app/routers/websocket_gateway.py +323 -0
  224. control_plane_api/app/routers/websocket_traces.py +576 -0
  225. control_plane_api/app/routers/worker_queues.py +2555 -0
  226. control_plane_api/app/routers/worker_websocket.py +419 -0
  227. control_plane_api/app/routers/workers.py +1004 -0
  228. control_plane_api/app/routers/workflows.py +204 -0
  229. control_plane_api/app/runtimes/__init__.py +6 -0
  230. control_plane_api/app/runtimes/validation.py +344 -0
  231. control_plane_api/app/schemas/__init__.py +1 -0
  232. control_plane_api/app/schemas/job_schemas.py +302 -0
  233. control_plane_api/app/schemas/mcp_schemas.py +311 -0
  234. control_plane_api/app/schemas/template_schemas.py +133 -0
  235. control_plane_api/app/schemas/trace_schemas.py +168 -0
  236. control_plane_api/app/schemas/worker_queue_observability_schemas.py +165 -0
  237. control_plane_api/app/services/__init__.py +1 -0
  238. control_plane_api/app/services/agno_planning_strategy.py +233 -0
  239. control_plane_api/app/services/agno_service.py +838 -0
  240. control_plane_api/app/services/claude_code_planning_service.py +203 -0
  241. control_plane_api/app/services/context_graph_client.py +224 -0
  242. control_plane_api/app/services/custom_integration_service.py +415 -0
  243. control_plane_api/app/services/integration_resolution_service.py +345 -0
  244. control_plane_api/app/services/litellm_service.py +394 -0
  245. control_plane_api/app/services/plan_generator.py +79 -0
  246. control_plane_api/app/services/planning_strategy.py +66 -0
  247. control_plane_api/app/services/planning_strategy_factory.py +118 -0
  248. control_plane_api/app/services/policy_service.py +615 -0
  249. control_plane_api/app/services/state_transition_service.py +755 -0
  250. control_plane_api/app/services/storage_service.py +593 -0
  251. control_plane_api/app/services/temporal_cloud_provisioning.py +150 -0
  252. control_plane_api/app/services/toolsets/context_graph_skill.py +432 -0
  253. control_plane_api/app/services/trace_retention.py +354 -0
  254. control_plane_api/app/services/worker_queue_metrics_service.py +190 -0
  255. control_plane_api/app/services/workflow_cancellation_manager.py +135 -0
  256. control_plane_api/app/services/workflow_operations_service.py +611 -0
  257. control_plane_api/app/skills/__init__.py +100 -0
  258. control_plane_api/app/skills/base.py +239 -0
  259. control_plane_api/app/skills/builtin/__init__.py +37 -0
  260. control_plane_api/app/skills/builtin/agent_communication/__init__.py +8 -0
  261. control_plane_api/app/skills/builtin/agent_communication/skill.py +246 -0
  262. control_plane_api/app/skills/builtin/code_ingestion/__init__.py +4 -0
  263. control_plane_api/app/skills/builtin/code_ingestion/skill.py +267 -0
  264. control_plane_api/app/skills/builtin/cognitive_memory/__init__.py +4 -0
  265. control_plane_api/app/skills/builtin/cognitive_memory/skill.py +174 -0
  266. control_plane_api/app/skills/builtin/contextual_awareness/__init__.py +4 -0
  267. control_plane_api/app/skills/builtin/contextual_awareness/skill.py +387 -0
  268. control_plane_api/app/skills/builtin/data_visualization/__init__.py +4 -0
  269. control_plane_api/app/skills/builtin/data_visualization/skill.py +154 -0
  270. control_plane_api/app/skills/builtin/docker/__init__.py +4 -0
  271. control_plane_api/app/skills/builtin/docker/skill.py +104 -0
  272. control_plane_api/app/skills/builtin/file_generation/__init__.py +4 -0
  273. control_plane_api/app/skills/builtin/file_generation/skill.py +94 -0
  274. control_plane_api/app/skills/builtin/file_system/__init__.py +4 -0
  275. control_plane_api/app/skills/builtin/file_system/skill.py +110 -0
  276. control_plane_api/app/skills/builtin/knowledge_api/__init__.py +5 -0
  277. control_plane_api/app/skills/builtin/knowledge_api/skill.py +124 -0
  278. control_plane_api/app/skills/builtin/python/__init__.py +4 -0
  279. control_plane_api/app/skills/builtin/python/skill.py +92 -0
  280. control_plane_api/app/skills/builtin/remote_filesystem/__init__.py +5 -0
  281. control_plane_api/app/skills/builtin/remote_filesystem/skill.py +170 -0
  282. control_plane_api/app/skills/builtin/shell/__init__.py +4 -0
  283. control_plane_api/app/skills/builtin/shell/skill.py +161 -0
  284. control_plane_api/app/skills/builtin/slack/__init__.py +3 -0
  285. control_plane_api/app/skills/builtin/slack/skill.py +302 -0
  286. control_plane_api/app/skills/builtin/workflow_executor/__init__.py +4 -0
  287. control_plane_api/app/skills/builtin/workflow_executor/skill.py +469 -0
  288. control_plane_api/app/skills/business_intelligence.py +189 -0
  289. control_plane_api/app/skills/config.py +63 -0
  290. control_plane_api/app/skills/loaders/__init__.py +14 -0
  291. control_plane_api/app/skills/loaders/base.py +73 -0
  292. control_plane_api/app/skills/loaders/filesystem_loader.py +199 -0
  293. control_plane_api/app/skills/registry.py +125 -0
  294. control_plane_api/app/utils/helpers.py +12 -0
  295. control_plane_api/app/utils/workflow_executor.py +354 -0
  296. control_plane_api/app/workflows/__init__.py +11 -0
  297. control_plane_api/app/workflows/agent_execution.py +520 -0
  298. control_plane_api/app/workflows/agent_execution_with_skills.py +223 -0
  299. control_plane_api/app/workflows/namespace_provisioning.py +326 -0
  300. control_plane_api/app/workflows/plan_generation.py +254 -0
  301. control_plane_api/app/workflows/team_execution.py +442 -0
  302. control_plane_api/scripts/seed_models.py +240 -0
  303. control_plane_api/scripts/validate_existing_tool_names.py +492 -0
  304. control_plane_api/shared/__init__.py +8 -0
  305. control_plane_api/shared/version.py +17 -0
  306. control_plane_api/test_deduplication.py +274 -0
  307. control_plane_api/test_executor_deduplication_e2e.py +309 -0
  308. control_plane_api/test_job_execution_e2e.py +283 -0
  309. control_plane_api/test_real_integration.py +193 -0
  310. control_plane_api/version.py +38 -0
  311. control_plane_api/worker/__init__.py +0 -0
  312. control_plane_api/worker/activities/__init__.py +0 -0
  313. control_plane_api/worker/activities/agent_activities.py +1585 -0
  314. control_plane_api/worker/activities/approval_activities.py +234 -0
  315. control_plane_api/worker/activities/job_activities.py +199 -0
  316. control_plane_api/worker/activities/runtime_activities.py +1167 -0
  317. control_plane_api/worker/activities/skill_activities.py +282 -0
  318. control_plane_api/worker/activities/team_activities.py +479 -0
  319. control_plane_api/worker/agent_runtime_server.py +370 -0
  320. control_plane_api/worker/binary_manager.py +333 -0
  321. control_plane_api/worker/config/__init__.py +31 -0
  322. control_plane_api/worker/config/worker_config.py +273 -0
  323. control_plane_api/worker/control_plane_client.py +1491 -0
  324. control_plane_api/worker/examples/analytics_integration_example.py +362 -0
  325. control_plane_api/worker/health_monitor.py +159 -0
  326. control_plane_api/worker/metrics.py +237 -0
  327. control_plane_api/worker/models/__init__.py +1 -0
  328. control_plane_api/worker/models/error_events.py +105 -0
  329. control_plane_api/worker/models/inputs.py +89 -0
  330. control_plane_api/worker/runtimes/__init__.py +35 -0
  331. control_plane_api/worker/runtimes/agent_runtime/runtime.py +485 -0
  332. control_plane_api/worker/runtimes/agno/__init__.py +34 -0
  333. control_plane_api/worker/runtimes/agno/config.py +248 -0
  334. control_plane_api/worker/runtimes/agno/hooks.py +385 -0
  335. control_plane_api/worker/runtimes/agno/mcp_builder.py +195 -0
  336. control_plane_api/worker/runtimes/agno/runtime.py +1063 -0
  337. control_plane_api/worker/runtimes/agno/utils.py +163 -0
  338. control_plane_api/worker/runtimes/base.py +979 -0
  339. control_plane_api/worker/runtimes/claude_code/__init__.py +38 -0
  340. control_plane_api/worker/runtimes/claude_code/cleanup.py +184 -0
  341. control_plane_api/worker/runtimes/claude_code/client_pool.py +529 -0
  342. control_plane_api/worker/runtimes/claude_code/config.py +829 -0
  343. control_plane_api/worker/runtimes/claude_code/hooks.py +482 -0
  344. control_plane_api/worker/runtimes/claude_code/litellm_proxy.py +1702 -0
  345. control_plane_api/worker/runtimes/claude_code/mcp_builder.py +467 -0
  346. control_plane_api/worker/runtimes/claude_code/mcp_discovery.py +558 -0
  347. control_plane_api/worker/runtimes/claude_code/runtime.py +1546 -0
  348. control_plane_api/worker/runtimes/claude_code/tool_mapper.py +403 -0
  349. control_plane_api/worker/runtimes/claude_code/utils.py +149 -0
  350. control_plane_api/worker/runtimes/factory.py +173 -0
  351. control_plane_api/worker/runtimes/model_utils.py +107 -0
  352. control_plane_api/worker/runtimes/validation.py +93 -0
  353. control_plane_api/worker/services/__init__.py +1 -0
  354. control_plane_api/worker/services/agent_communication_tools.py +908 -0
  355. control_plane_api/worker/services/agent_executor.py +485 -0
  356. control_plane_api/worker/services/agent_executor_v2.py +793 -0
  357. control_plane_api/worker/services/analytics_collector.py +457 -0
  358. control_plane_api/worker/services/analytics_service.py +464 -0
  359. control_plane_api/worker/services/approval_tools.py +310 -0
  360. control_plane_api/worker/services/approval_tools_agno.py +207 -0
  361. control_plane_api/worker/services/cancellation_manager.py +177 -0
  362. control_plane_api/worker/services/code_ingestion_tools.py +465 -0
  363. control_plane_api/worker/services/contextual_awareness_tools.py +405 -0
  364. control_plane_api/worker/services/data_visualization.py +834 -0
  365. control_plane_api/worker/services/event_publisher.py +531 -0
  366. control_plane_api/worker/services/jira_tools.py +257 -0
  367. control_plane_api/worker/services/remote_filesystem_tools.py +498 -0
  368. control_plane_api/worker/services/runtime_analytics.py +328 -0
  369. control_plane_api/worker/services/session_service.py +365 -0
  370. control_plane_api/worker/services/skill_context_enhancement.py +181 -0
  371. control_plane_api/worker/services/skill_factory.py +471 -0
  372. control_plane_api/worker/services/system_prompt_enhancement.py +410 -0
  373. control_plane_api/worker/services/team_executor.py +715 -0
  374. control_plane_api/worker/services/team_executor_v2.py +1866 -0
  375. control_plane_api/worker/services/tool_enforcement.py +254 -0
  376. control_plane_api/worker/services/workflow_executor/__init__.py +52 -0
  377. control_plane_api/worker/services/workflow_executor/event_processor.py +287 -0
  378. control_plane_api/worker/services/workflow_executor/event_publisher.py +210 -0
  379. control_plane_api/worker/services/workflow_executor/executors/__init__.py +15 -0
  380. control_plane_api/worker/services/workflow_executor/executors/base.py +270 -0
  381. control_plane_api/worker/services/workflow_executor/executors/json_executor.py +50 -0
  382. control_plane_api/worker/services/workflow_executor/executors/python_executor.py +50 -0
  383. control_plane_api/worker/services/workflow_executor/models.py +142 -0
  384. control_plane_api/worker/services/workflow_executor_tools.py +1748 -0
  385. control_plane_api/worker/skills/__init__.py +12 -0
  386. control_plane_api/worker/skills/builtin/context_graph_search/README.md +213 -0
  387. control_plane_api/worker/skills/builtin/context_graph_search/__init__.py +5 -0
  388. control_plane_api/worker/skills/builtin/context_graph_search/agno_impl.py +808 -0
  389. control_plane_api/worker/skills/builtin/context_graph_search/skill.yaml +67 -0
  390. control_plane_api/worker/skills/builtin/contextual_awareness/__init__.py +4 -0
  391. control_plane_api/worker/skills/builtin/contextual_awareness/agno_impl.py +62 -0
  392. control_plane_api/worker/skills/builtin/data_visualization/agno_impl.py +18 -0
  393. control_plane_api/worker/skills/builtin/data_visualization/skill.yaml +84 -0
  394. control_plane_api/worker/skills/builtin/docker/agno_impl.py +65 -0
  395. control_plane_api/worker/skills/builtin/docker/skill.yaml +60 -0
  396. control_plane_api/worker/skills/builtin/file_generation/agno_impl.py +47 -0
  397. control_plane_api/worker/skills/builtin/file_generation/skill.yaml +64 -0
  398. control_plane_api/worker/skills/builtin/file_system/agno_impl.py +32 -0
  399. control_plane_api/worker/skills/builtin/file_system/skill.yaml +54 -0
  400. control_plane_api/worker/skills/builtin/knowledge_api/__init__.py +4 -0
  401. control_plane_api/worker/skills/builtin/knowledge_api/agno_impl.py +50 -0
  402. control_plane_api/worker/skills/builtin/knowledge_api/skill.yaml +66 -0
  403. control_plane_api/worker/skills/builtin/python/agno_impl.py +25 -0
  404. control_plane_api/worker/skills/builtin/python/skill.yaml +60 -0
  405. control_plane_api/worker/skills/builtin/schema_fix_mixin.py +260 -0
  406. control_plane_api/worker/skills/builtin/shell/agno_impl.py +31 -0
  407. control_plane_api/worker/skills/builtin/shell/skill.yaml +60 -0
  408. control_plane_api/worker/skills/builtin/slack/__init__.py +3 -0
  409. control_plane_api/worker/skills/builtin/slack/agno_impl.py +1282 -0
  410. control_plane_api/worker/skills/builtin/slack/skill.yaml +276 -0
  411. control_plane_api/worker/skills/builtin/workflow_executor/agno_impl.py +62 -0
  412. control_plane_api/worker/skills/builtin/workflow_executor/skill.yaml +79 -0
  413. control_plane_api/worker/skills/loaders/__init__.py +5 -0
  414. control_plane_api/worker/skills/loaders/base.py +23 -0
  415. control_plane_api/worker/skills/loaders/filesystem_loader.py +357 -0
  416. control_plane_api/worker/skills/registry.py +208 -0
  417. control_plane_api/worker/tests/__init__.py +1 -0
  418. control_plane_api/worker/tests/conftest.py +12 -0
  419. control_plane_api/worker/tests/e2e/__init__.py +0 -0
  420. control_plane_api/worker/tests/e2e/test_context_graph_real_api.py +338 -0
  421. control_plane_api/worker/tests/e2e/test_context_graph_templates_e2e.py +523 -0
  422. control_plane_api/worker/tests/e2e/test_enforcement_e2e.py +344 -0
  423. control_plane_api/worker/tests/e2e/test_execution_flow.py +571 -0
  424. control_plane_api/worker/tests/e2e/test_single_execution_mode.py +656 -0
  425. control_plane_api/worker/tests/integration/__init__.py +0 -0
  426. control_plane_api/worker/tests/integration/test_builtin_skills_fixes.py +245 -0
  427. control_plane_api/worker/tests/integration/test_context_graph_search_integration.py +365 -0
  428. control_plane_api/worker/tests/integration/test_control_plane_integration.py +308 -0
  429. control_plane_api/worker/tests/integration/test_hook_enforcement_integration.py +579 -0
  430. control_plane_api/worker/tests/integration/test_scheduled_job_workflow.py +237 -0
  431. control_plane_api/worker/tests/integration/test_system_prompt_enhancement_integration.py +343 -0
  432. control_plane_api/worker/tests/unit/__init__.py +0 -0
  433. control_plane_api/worker/tests/unit/test_builtin_skill_autoload.py +396 -0
  434. control_plane_api/worker/tests/unit/test_context_graph_search.py +450 -0
  435. control_plane_api/worker/tests/unit/test_context_graph_templates.py +403 -0
  436. control_plane_api/worker/tests/unit/test_control_plane_client.py +401 -0
  437. control_plane_api/worker/tests/unit/test_control_plane_client_jobs.py +345 -0
  438. control_plane_api/worker/tests/unit/test_job_activities.py +353 -0
  439. control_plane_api/worker/tests/unit/test_skill_context_enhancement.py +321 -0
  440. control_plane_api/worker/tests/unit/test_system_prompt_enhancement.py +415 -0
  441. control_plane_api/worker/tests/unit/test_tool_enforcement.py +324 -0
  442. control_plane_api/worker/utils/__init__.py +1 -0
  443. control_plane_api/worker/utils/chunk_batcher.py +330 -0
  444. control_plane_api/worker/utils/environment.py +65 -0
  445. control_plane_api/worker/utils/error_publisher.py +260 -0
  446. control_plane_api/worker/utils/event_batcher.py +256 -0
  447. control_plane_api/worker/utils/logging_config.py +335 -0
  448. control_plane_api/worker/utils/logging_helper.py +326 -0
  449. control_plane_api/worker/utils/parameter_validator.py +120 -0
  450. control_plane_api/worker/utils/retry_utils.py +60 -0
  451. control_plane_api/worker/utils/streaming_utils.py +665 -0
  452. control_plane_api/worker/utils/tool_validation.py +332 -0
  453. control_plane_api/worker/utils/workspace_manager.py +163 -0
  454. control_plane_api/worker/websocket_client.py +393 -0
  455. control_plane_api/worker/worker.py +1297 -0
  456. control_plane_api/worker/workflows/__init__.py +0 -0
  457. control_plane_api/worker/workflows/agent_execution.py +909 -0
  458. control_plane_api/worker/workflows/scheduled_job_wrapper.py +332 -0
  459. control_plane_api/worker/workflows/team_execution.py +611 -0
  460. kubiya_control_plane_api-0.9.15.dist-info/METADATA +354 -0
  461. kubiya_control_plane_api-0.9.15.dist-info/RECORD +479 -0
  462. kubiya_control_plane_api-0.9.15.dist-info/WHEEL +5 -0
  463. kubiya_control_plane_api-0.9.15.dist-info/entry_points.txt +5 -0
  464. kubiya_control_plane_api-0.9.15.dist-info/licenses/LICENSE +676 -0
  465. kubiya_control_plane_api-0.9.15.dist-info/top_level.txt +3 -0
  466. scripts/__init__.py +1 -0
  467. scripts/migrations.py +39 -0
  468. scripts/seed_worker_queues.py +128 -0
  469. scripts/setup_agent_runtime.py +142 -0
  470. worker_internal/__init__.py +1 -0
  471. worker_internal/planner/__init__.py +1 -0
  472. worker_internal/planner/activities.py +1499 -0
  473. worker_internal/planner/agent_tools.py +197 -0
  474. worker_internal/planner/event_models.py +148 -0
  475. worker_internal/planner/event_publisher.py +67 -0
  476. worker_internal/planner/models.py +199 -0
  477. worker_internal/planner/retry_logic.py +134 -0
  478. worker_internal/planner/worker.py +300 -0
  479. worker_internal/planner/workflows.py +970 -0
@@ -0,0 +1,324 @@
1
+ """NATS-based event bus provider with JetStream support."""
2
+
3
+ from typing import Dict, Any, Optional, Callable
4
+ from datetime import datetime
5
+ import json
6
+ import structlog
7
+ from pydantic import Field
8
+
9
+ from control_plane_api.app.lib.event_bus.base import EventBusProvider, EventBusConfig
10
+
11
+ logger = structlog.get_logger(__name__)
12
+
13
+
14
+ class NATSConfig(EventBusConfig):
15
+ """Configuration for NATS event provider."""
16
+
17
+ nats_url: str = Field(..., description="NATS server URL")
18
+ credentials_file: Optional[str] = Field(
19
+ default=None, description="Path to NATS credentials file (.creds)"
20
+ )
21
+ organization_id: str = Field(..., description="Organization ID for subject prefix")
22
+ worker_id: str = Field(..., description="Worker ID for subject")
23
+ jetstream_enabled: bool = Field(
24
+ default=True, description="Enable JetStream for durable delivery"
25
+ )
26
+ stream_name: str = Field(
27
+ default="EVENTS", description="JetStream stream name"
28
+ )
29
+ max_reconnect_attempts: int = Field(
30
+ default=-1, description="Max reconnection attempts (-1 = infinite)"
31
+ )
32
+ reconnect_time_wait: int = Field(
33
+ default=2, description="Initial reconnect backoff in seconds"
34
+ )
35
+ ping_interval: int = Field(
36
+ default=20, description="Ping interval in seconds"
37
+ )
38
+ max_outstanding_pings: int = Field(
39
+ default=3, description="Max outstanding pings before disconnect"
40
+ )
41
+ publish_timeout: float = Field(
42
+ default=5.0, description="Publish timeout in seconds"
43
+ )
44
+
45
+
46
+ class NATSEventProvider(EventBusProvider):
47
+ """NATS-based event publishing with JetStream (optional, high-performance)."""
48
+
49
+ def __init__(self, config: NATSConfig):
50
+ super().__init__(config)
51
+ self.config: NATSConfig = config
52
+ self.nc = None # NATS connection
53
+ self.js = None # JetStream context
54
+
55
+ async def initialize(self) -> None:
56
+ """Initialize NATS connection and JetStream."""
57
+ try:
58
+ # Import nats-py
59
+ try:
60
+ import nats
61
+ from nats.errors import TimeoutError, NoServersError
62
+ except ImportError:
63
+ raise ImportError(
64
+ "nats-py is required for NATS provider. "
65
+ "Install it with: pip install nats-py"
66
+ )
67
+
68
+ # Build connection options
69
+ connect_opts = {
70
+ "servers": [self.config.nats_url],
71
+ "max_reconnect_attempts": self.config.max_reconnect_attempts,
72
+ "reconnect_time_wait": self.config.reconnect_time_wait,
73
+ "ping_interval": self.config.ping_interval,
74
+ "max_outstanding_pings": self.config.max_outstanding_pings,
75
+ }
76
+
77
+ # Add credentials if provided
78
+ if self.config.credentials_file:
79
+ connect_opts["user_credentials"] = self.config.credentials_file
80
+
81
+ # Connect to NATS
82
+ self.nc = await nats.connect(**connect_opts)
83
+
84
+ # Initialize JetStream if enabled
85
+ if self.config.jetstream_enabled:
86
+ self.js = self.nc.jetstream()
87
+
88
+ self.logger.info(
89
+ "nats_provider_initialized",
90
+ url=self.config.nats_url,
91
+ jetstream_enabled=self.config.jetstream_enabled,
92
+ organization_id=self.config.organization_id,
93
+ worker_id=self.config.worker_id[:8],
94
+ )
95
+
96
+ except ImportError as e:
97
+ self.logger.warning(
98
+ "nats_provider_dependency_missing",
99
+ error=str(e),
100
+ message="Install nats-py to enable NATS provider"
101
+ )
102
+ raise
103
+
104
+ except Exception as e:
105
+ self.logger.error(
106
+ "nats_provider_init_failed",
107
+ error=str(e),
108
+ url=self.config.nats_url,
109
+ )
110
+ raise
111
+
112
+ async def publish_event(
113
+ self,
114
+ execution_id: str,
115
+ event_type: str,
116
+ data: Dict[str, Any],
117
+ metadata: Optional[Dict[str, Any]] = None,
118
+ ) -> bool:
119
+ """
120
+ Publish event to NATS subject with JetStream.
121
+
122
+ Subject format: events.{org_id}.{worker_id}.{execution_id}
123
+
124
+ Args:
125
+ execution_id: Execution ID
126
+ event_type: Event type
127
+ data: Event payload
128
+ metadata: Optional metadata
129
+
130
+ Returns:
131
+ True if successful
132
+ """
133
+ if not self.nc or not self.nc.is_connected:
134
+ self.logger.warning(
135
+ "nats_not_connected",
136
+ execution_id=execution_id,
137
+ event_type=event_type,
138
+ )
139
+ return False
140
+
141
+ # Build subject: events.{org_id}.{worker_id}.{execution_id}
142
+ subject = (
143
+ f"events.{self.config.organization_id}."
144
+ f"{self.config.worker_id}.{execution_id}"
145
+ )
146
+
147
+ # Build message
148
+ message = {
149
+ "event_type": event_type,
150
+ "data": data,
151
+ "execution_id": execution_id,
152
+ "worker_id": self.config.worker_id,
153
+ "organization_id": self.config.organization_id,
154
+ "timestamp": datetime.utcnow().isoformat(),
155
+ }
156
+
157
+ if metadata:
158
+ message["metadata"] = metadata
159
+
160
+ message_json = json.dumps(message).encode()
161
+
162
+ try:
163
+ if self.config.jetstream_enabled and self.js:
164
+ # Publish with JetStream (at-least-once delivery)
165
+ ack = await self.js.publish(
166
+ subject,
167
+ message_json,
168
+ timeout=self.config.publish_timeout,
169
+ )
170
+
171
+ self.logger.debug(
172
+ "nats_jetstream_event_published",
173
+ execution_id=execution_id,
174
+ event_type=event_type,
175
+ subject=subject,
176
+ stream=ack.stream if hasattr(ack, "stream") else None,
177
+ seq=ack.seq if hasattr(ack, "seq") else None,
178
+ )
179
+ else:
180
+ # Publish without JetStream (fire-and-forget)
181
+ await self.nc.publish(subject, message_json)
182
+
183
+ self.logger.debug(
184
+ "nats_event_published",
185
+ execution_id=execution_id,
186
+ event_type=event_type,
187
+ subject=subject,
188
+ )
189
+
190
+ return True
191
+
192
+ except Exception as e:
193
+ self.logger.error(
194
+ "nats_publish_failed",
195
+ error=str(e),
196
+ execution_id=execution_id,
197
+ event_type=event_type,
198
+ subject=subject,
199
+ )
200
+ return False
201
+
202
+ async def subscribe(
203
+ self, pattern: str, callback: Callable[[Dict[str, Any]], None]
204
+ ) -> None:
205
+ """
206
+ Subscribe to NATS subjects (control plane side).
207
+
208
+ Args:
209
+ pattern: Subject pattern (e.g., "events.{org_id}.>")
210
+ callback: Async callback function for messages
211
+ """
212
+ if not self.nc or not self.nc.is_connected:
213
+ raise RuntimeError("NATS not connected")
214
+
215
+ async def message_handler(msg):
216
+ """Internal message handler with ack support."""
217
+ try:
218
+ # Parse message
219
+ data = json.loads(msg.data.decode())
220
+
221
+ # Call user callback
222
+ result = callback(data)
223
+ if result and hasattr(result, "__await__"):
224
+ await result
225
+
226
+ # Acknowledge if JetStream message
227
+ if hasattr(msg, "ack"):
228
+ await msg.ack()
229
+
230
+ except Exception as e:
231
+ self.logger.error(
232
+ "nats_message_handler_error",
233
+ error=str(e),
234
+ subject=msg.subject,
235
+ )
236
+ # Negative acknowledgment for retry
237
+ if hasattr(msg, "nak"):
238
+ await msg.nak()
239
+
240
+ if self.config.jetstream_enabled and self.js:
241
+ # Subscribe with JetStream (durable consumer)
242
+ consumer_name = f"control-plane-{self.config.organization_id}"
243
+
244
+ sub = await self.js.subscribe(
245
+ pattern,
246
+ durable=consumer_name,
247
+ cb=message_handler,
248
+ manual_ack=True,
249
+ )
250
+
251
+ self.logger.info(
252
+ "nats_jetstream_subscribed",
253
+ pattern=pattern,
254
+ consumer=consumer_name,
255
+ )
256
+ else:
257
+ # Subscribe without JetStream
258
+ sub = await self.nc.subscribe(pattern, cb=message_handler)
259
+
260
+ self.logger.info(
261
+ "nats_subscribed",
262
+ pattern=pattern,
263
+ )
264
+
265
+ async def health_check(self) -> Dict[str, Any]:
266
+ """
267
+ Check NATS provider health.
268
+
269
+ Returns:
270
+ Dict with health status and connection info
271
+ """
272
+ if not self.nc:
273
+ return {
274
+ "healthy": False,
275
+ "error": "nats_not_initialized",
276
+ }
277
+
278
+ health = {
279
+ "healthy": self.nc.is_connected,
280
+ "nats_url": self.config.nats_url,
281
+ "connected": self.nc.is_connected,
282
+ "jetstream_enabled": self.config.jetstream_enabled,
283
+ "organization_id": self.config.organization_id,
284
+ "worker_id": self.config.worker_id[:8],
285
+ }
286
+
287
+ if self.nc.is_connected:
288
+ health["connected_url"] = (
289
+ self.nc.connected_url.netloc
290
+ if self.nc.connected_url
291
+ else None
292
+ )
293
+ health["max_payload"] = self.nc.max_payload
294
+
295
+ # Add stats if available
296
+ if hasattr(self.nc, "stats"):
297
+ stats = self.nc.stats
298
+ health["stats"] = {
299
+ "in_msgs": stats.get("in_msgs", 0),
300
+ "out_msgs": stats.get("out_msgs", 0),
301
+ "reconnects": stats.get("reconnects", 0),
302
+ }
303
+
304
+ return health
305
+
306
+ async def shutdown(self) -> None:
307
+ """Shutdown NATS connection gracefully."""
308
+ if self.nc:
309
+ try:
310
+ # Drain pending messages before closing
311
+ await self.nc.drain()
312
+ await self.nc.close()
313
+
314
+ self.logger.info(
315
+ "nats_provider_shutdown",
316
+ organization_id=self.config.organization_id,
317
+ worker_id=self.config.worker_id[:8],
318
+ )
319
+
320
+ except Exception as e:
321
+ self.logger.warning(
322
+ "nats_shutdown_error",
323
+ error=str(e),
324
+ )
@@ -0,0 +1,233 @@
1
+ """Redis pub/sub based event bus provider."""
2
+
3
+ from typing import Dict, Any, Optional, Callable
4
+ from datetime import datetime
5
+ import json
6
+ import structlog
7
+ from pydantic import Field
8
+
9
+ from control_plane_api.app.lib.event_bus.base import EventBusProvider, EventBusConfig
10
+ from control_plane_api.app.lib.redis_client import get_redis_client, RedisClient
11
+
12
+ logger = structlog.get_logger(__name__)
13
+
14
+
15
+ class RedisConfig(EventBusConfig):
16
+ """Configuration for Redis event provider."""
17
+
18
+ redis_url: Optional[str] = Field(
19
+ default=None,
20
+ description="Redis URL (uses REDIS_URL env var if not specified)"
21
+ )
22
+ list_max_size: int = Field(
23
+ default=1000, description="Maximum events to keep in Redis list"
24
+ )
25
+ list_ttl_seconds: int = Field(
26
+ default=3600, description="TTL for event lists in seconds"
27
+ )
28
+ channel_prefix: str = Field(
29
+ default="execution", description="Prefix for Redis pub/sub channels"
30
+ )
31
+
32
+
33
+ class RedisEventProvider(EventBusProvider):
34
+ """Redis pub/sub based event publishing."""
35
+
36
+ def __init__(self, config: RedisConfig):
37
+ super().__init__(config)
38
+ self.config: RedisConfig = config
39
+ self.redis_client: Optional[RedisClient] = None
40
+
41
+ async def initialize(self) -> None:
42
+ """Initialize Redis connection."""
43
+ # Get Redis client using existing infrastructure
44
+ self.redis_client = get_redis_client()
45
+
46
+ if not self.redis_client:
47
+ raise RuntimeError(
48
+ "Redis client not configured. Set REDIS_URL or Upstash environment variables."
49
+ )
50
+
51
+ # Test connection
52
+ try:
53
+ ping_ok = await self.redis_client.ping()
54
+ if not ping_ok:
55
+ raise RuntimeError("Redis ping failed")
56
+
57
+ self.logger.info(
58
+ "redis_provider_initialized",
59
+ list_max_size=self.config.list_max_size,
60
+ list_ttl_seconds=self.config.list_ttl_seconds,
61
+ )
62
+
63
+ except Exception as e:
64
+ self.logger.error("redis_provider_init_failed", error=str(e))
65
+ raise
66
+
67
+ async def publish_event(
68
+ self,
69
+ execution_id: str,
70
+ event_type: str,
71
+ data: Dict[str, Any],
72
+ metadata: Optional[Dict[str, Any]] = None,
73
+ ) -> bool:
74
+ """
75
+ Publish event to Redis pub/sub and store in list.
76
+
77
+ Publishes to:
78
+ 1. Redis list (execution:{id}:events) for replay - LPUSH + LTRIM
79
+ 2. Redis pub/sub channel (execution:{id}:stream) for real-time
80
+
81
+ Args:
82
+ execution_id: Execution ID
83
+ event_type: Event type
84
+ data: Event payload
85
+ metadata: Optional metadata
86
+
87
+ Returns:
88
+ True if successful
89
+ """
90
+ if not self.redis_client:
91
+ self.logger.error("redis_client_not_initialized")
92
+ return False
93
+
94
+ # Build event message
95
+ message = {
96
+ "event_type": event_type,
97
+ "data": data,
98
+ "timestamp": datetime.utcnow().isoformat(),
99
+ }
100
+
101
+ if metadata:
102
+ message["metadata"] = metadata
103
+
104
+ message_json = json.dumps(message)
105
+
106
+ # Redis keys
107
+ list_key = f"{self.config.channel_prefix}:{execution_id}:events"
108
+ channel = f"{self.config.channel_prefix}:{execution_id}:stream"
109
+
110
+ try:
111
+ # 1. Store in Redis list for replay (LPUSH + LTRIM)
112
+ lpush_ok = await self.redis_client.lpush(list_key, message_json)
113
+ if not lpush_ok:
114
+ self.logger.warning(
115
+ "redis_lpush_failed",
116
+ execution_id=execution_id,
117
+ event_type=event_type,
118
+ )
119
+ return False
120
+
121
+ # 2. Trim list to max size (keep last N events)
122
+ ltrim_ok = await self.redis_client.ltrim(
123
+ list_key, 0, self.config.list_max_size - 1
124
+ )
125
+ if not ltrim_ok:
126
+ self.logger.warning(
127
+ "redis_ltrim_failed",
128
+ execution_id=execution_id,
129
+ list_key=list_key,
130
+ )
131
+ # Non-fatal, continue
132
+
133
+ # 3. Set TTL on list
134
+ expire_ok = await self.redis_client.expire(
135
+ list_key, self.config.list_ttl_seconds
136
+ )
137
+ if not expire_ok:
138
+ self.logger.warning(
139
+ "redis_expire_failed",
140
+ execution_id=execution_id,
141
+ list_key=list_key,
142
+ )
143
+ # Non-fatal, continue
144
+
145
+ # 4. Publish to pub/sub channel for real-time delivery
146
+ publish_ok = await self.redis_client.publish(channel, message_json)
147
+ if not publish_ok:
148
+ self.logger.warning(
149
+ "redis_publish_failed",
150
+ execution_id=execution_id,
151
+ channel=channel,
152
+ )
153
+ # List storage succeeded, so return True
154
+ # Pub/sub is for real-time only, replay is available from list
155
+
156
+ self.logger.debug(
157
+ "redis_event_published",
158
+ execution_id=execution_id,
159
+ event_type=event_type,
160
+ list_stored=lpush_ok,
161
+ pubsub_published=publish_ok,
162
+ )
163
+
164
+ return True
165
+
166
+ except Exception as e:
167
+ self.logger.error(
168
+ "redis_publish_event_exception",
169
+ execution_id=execution_id,
170
+ event_type=event_type,
171
+ error=str(e),
172
+ )
173
+ return False
174
+
175
+ async def subscribe(
176
+ self, pattern: str, callback: Callable[[Dict[str, Any]], None]
177
+ ) -> None:
178
+ """
179
+ Subscribe to Redis pub/sub channels (control plane side).
180
+
181
+ Note: This requires a dedicated Redis connection for blocking subscribe.
182
+ This is typically used by the control plane to listen for events.
183
+
184
+ Args:
185
+ pattern: Channel pattern (e.g., "execution:*:stream")
186
+ callback: Async callback for messages
187
+ """
188
+ self.logger.warning(
189
+ "redis_subscribe_not_implemented",
190
+ pattern=pattern,
191
+ message="Redis subscribe requires dedicated connection and is typically "
192
+ "handled by control plane SSE streaming from Redis pub/sub"
193
+ )
194
+ raise NotImplementedError(
195
+ "Redis subscribe is handled separately by control plane SSE streaming. "
196
+ "Workers publish only."
197
+ )
198
+
199
+ async def health_check(self) -> Dict[str, Any]:
200
+ """
201
+ Check Redis provider health.
202
+
203
+ Returns:
204
+ Dict with health status
205
+ """
206
+ if not self.redis_client:
207
+ return {
208
+ "healthy": False,
209
+ "error": "redis_client_not_initialized",
210
+ }
211
+
212
+ try:
213
+ # Ping Redis
214
+ ping_ok = await self.redis_client.ping()
215
+
216
+ return {
217
+ "healthy": ping_ok,
218
+ "redis_type": "upstash" if hasattr(self.redis_client, "token") else "standard",
219
+ "list_max_size": self.config.list_max_size,
220
+ "list_ttl_seconds": self.config.list_ttl_seconds,
221
+ }
222
+
223
+ except Exception as e:
224
+ return {
225
+ "healthy": False,
226
+ "error": str(e),
227
+ }
228
+
229
+ async def shutdown(self) -> None:
230
+ """Shutdown Redis provider (connection managed globally)."""
231
+ # Redis client is managed globally by get_redis_client()
232
+ # No need to close connection here
233
+ self.logger.info("redis_provider_shutdown")