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,469 @@
1
+ """
2
+ API-specific configuration.
3
+
4
+ This module contains settings specific to the Control Plane API server.
5
+ """
6
+
7
+ from pydantic_settings import BaseSettings, SettingsConfigDict
8
+ from pydantic import Field, validator, model_validator, AliasChoices
9
+ from typing import List, Optional, Dict, Any
10
+ import secrets
11
+ import os
12
+ from control_plane_api.version import get_sdk_version
13
+
14
+
15
+ class APIConfig(BaseSettings):
16
+ """Configuration for Control Plane API server."""
17
+
18
+ # ==================== API Server Settings ====================
19
+
20
+ api_host: str = Field(
21
+ default="0.0.0.0",
22
+ description="API server host",
23
+ )
24
+
25
+ api_port: int = Field(
26
+ default=8000,
27
+ description="API server port",
28
+ )
29
+
30
+ api_workers: int = Field(
31
+ default=4,
32
+ description="Number of API worker processes",
33
+ )
34
+
35
+ api_title: str = Field(
36
+ default="Agent Control Plane API",
37
+ description="API title for documentation",
38
+ )
39
+
40
+ api_version: str = Field(
41
+ default_factory=get_sdk_version,
42
+ description="API version (dynamically read from package metadata)",
43
+ )
44
+
45
+ api_description: str = Field(
46
+ default="Multi-tenant agent orchestration with Temporal workflows",
47
+ description="API description for documentation",
48
+ )
49
+
50
+ # ==================== Environment ====================
51
+
52
+ environment: str = Field(
53
+ default="development",
54
+ description="Environment (development, staging, production)",
55
+ )
56
+
57
+ debug: bool = Field(
58
+ default=False,
59
+ description="Debug mode",
60
+ )
61
+
62
+ @validator("debug", pre=True)
63
+ def set_debug_from_env(cls, v, values):
64
+ """Set debug based on environment if not explicitly set."""
65
+ if v is None:
66
+ env = values.get("environment", "development")
67
+ return env == "development"
68
+ return v
69
+
70
+ # ==================== Database Settings ====================
71
+
72
+ database_url: Optional[str] = Field(
73
+ default=None,
74
+ description="PostgreSQL database URL",
75
+ validation_alias=AliasChoices("DATABASE_URL", "database_url"),
76
+ )
77
+
78
+ supabase_url: Optional[str] = Field(
79
+ default=None,
80
+ description="Supabase project URL",
81
+ validation_alias=AliasChoices("SUPABASE_URL", "supabase_url"),
82
+ )
83
+
84
+ supabase_service_key: Optional[str] = Field(
85
+ default=None,
86
+ description="Supabase service role key",
87
+ validation_alias=AliasChoices("SUPABASE_SERVICE_KEY", "supabase_service_key"),
88
+ )
89
+
90
+ supabase_anon_key: Optional[str] = Field(
91
+ default=None,
92
+ description="Supabase anonymous key",
93
+ validation_alias=AliasChoices("SUPABASE_ANON_KEY", "supabase_anon_key"),
94
+ )
95
+
96
+ database_pool_size: int = Field(
97
+ default=20,
98
+ description="Database connection pool size",
99
+ )
100
+
101
+ database_max_overflow: int = Field(
102
+ default=40,
103
+ description="Maximum overflow for database pool",
104
+ )
105
+
106
+ database_pool_timeout: float = Field(
107
+ default=30.0,
108
+ description="Database pool timeout in seconds",
109
+ )
110
+
111
+ @model_validator(mode='after')
112
+ def validate_database_config(self):
113
+ """Ensure we have either DATABASE_URL or Supabase configuration."""
114
+ # Always try to set database_url from Supabase env vars if not already set
115
+ if not self.database_url:
116
+ supabase_db_url = (
117
+ os.environ.get("SUPABASE_POSTGRES_URL") or
118
+ os.environ.get("SUPABASE_POSTGRES_PRISMA_URL") or
119
+ os.environ.get("SUPABASE_DB_URL")
120
+ )
121
+ if supabase_db_url:
122
+ # Fix URL format for SQLAlchemy 2.0+
123
+ if supabase_db_url.startswith("postgres://"):
124
+ supabase_db_url = supabase_db_url.replace("postgres://", "postgresql://", 1)
125
+ # Remove invalid Supabase pooler parameters that SQLAlchemy doesn't understand
126
+ supabase_db_url = supabase_db_url.replace("&supa=base-pooler.x", "")
127
+ self.database_url = supabase_db_url
128
+ elif not (self.supabase_url and self.supabase_service_key) and self.environment != "development":
129
+ raise ValueError(
130
+ "Either DATABASE_URL or Supabase configuration (SUPABASE_URL and SUPABASE_SERVICE_KEY) must be provided"
131
+ )
132
+
133
+ # Fix postgres:// to postgresql:// if needed and remove invalid params
134
+ if self.database_url:
135
+ if self.database_url.startswith("postgres://"):
136
+ self.database_url = self.database_url.replace("postgres://", "postgresql://", 1)
137
+ # Remove invalid Supabase pooler parameters from DATABASE_URL
138
+ self.database_url = self.database_url.replace("&supa=base-pooler.x", "")
139
+
140
+ return self
141
+
142
+ # ==================== Redis Settings ====================
143
+
144
+ redis_url: str = Field(
145
+ default="redis://localhost:6379/0",
146
+ description="Redis connection URL",
147
+ validation_alias=AliasChoices("REDIS_URL", "redis_url"),
148
+ )
149
+
150
+ redis_host: Optional[str] = Field(
151
+ default=None,
152
+ description="Redis host (overrides URL)",
153
+ )
154
+
155
+ redis_port: int = Field(
156
+ default=6379,
157
+ description="Redis port",
158
+ )
159
+
160
+ redis_password: Optional[str] = Field(
161
+ default=None,
162
+ description="Redis password",
163
+ )
164
+
165
+ redis_db: int = Field(
166
+ default=0,
167
+ description="Redis database number",
168
+ )
169
+
170
+ redis_pool_size: int = Field(
171
+ default=10,
172
+ description="Redis connection pool size",
173
+ )
174
+
175
+ # ==================== Temporal Settings ====================
176
+
177
+ temporal_host: str = Field(
178
+ default="localhost:7233",
179
+ description="Temporal server host:port",
180
+ )
181
+
182
+ temporal_namespace: str = Field(
183
+ default="default",
184
+ description="Temporal namespace",
185
+ )
186
+
187
+ temporal_client_cert_path: Optional[str] = Field(
188
+ default=None,
189
+ description="Path to Temporal client certificate",
190
+ )
191
+
192
+ temporal_client_key_path: Optional[str] = Field(
193
+ default=None,
194
+ description="Path to Temporal client key",
195
+ )
196
+
197
+ # ==================== Security Settings ====================
198
+
199
+ secret_key: str = Field(
200
+ default_factory=lambda: secrets.token_urlsafe(32),
201
+ description="Secret key for JWT signing",
202
+ validation_alias=AliasChoices("SECRET_KEY", "secret_key"),
203
+ )
204
+
205
+ algorithm: str = Field(
206
+ default="HS256",
207
+ description="JWT signing algorithm",
208
+ )
209
+
210
+ access_token_expire_minutes: int = Field(
211
+ default=30,
212
+ description="Access token expiration in minutes",
213
+ )
214
+
215
+ refresh_token_expire_days: int = Field(
216
+ default=30,
217
+ description="Refresh token expiration in days",
218
+ )
219
+
220
+ # ==================== CORS Settings ====================
221
+
222
+ cors_origins: List[str] = Field(
223
+ default=["*"],
224
+ description="Allowed CORS origins",
225
+ )
226
+
227
+ cors_allow_credentials: bool = Field(
228
+ default=True,
229
+ description="Allow credentials in CORS requests",
230
+ )
231
+
232
+ cors_allow_methods: List[str] = Field(
233
+ default=["*"],
234
+ description="Allowed CORS methods",
235
+ )
236
+
237
+ cors_allow_headers: List[str] = Field(
238
+ default=["*"],
239
+ description="Allowed CORS headers",
240
+ )
241
+
242
+ @validator("cors_origins", pre=True)
243
+ def validate_cors_origins(cls, v, values):
244
+ """Validate CORS origins for production."""
245
+ # Allow environment variable to override default
246
+ if v is None or (isinstance(v, list) and len(v) == 1 and v[0] == "*"):
247
+ # Check if we're in production
248
+ env = values.get("environment", "development")
249
+ if env == "production":
250
+ # In production, use specific origins unless explicitly overridden
251
+ return [
252
+ "https://agent-control-plane.vercel.app",
253
+ "https://*.vercel.app",
254
+ "http://localhost:3000",
255
+ "http://localhost:8000",
256
+ ]
257
+ return v
258
+
259
+ # ==================== External Services ====================
260
+
261
+ kubiya_api_base: str = Field(
262
+ default="https://api.kubiya.ai",
263
+ description="Kubiya API base URL",
264
+ )
265
+
266
+ kubiya_api_key: Optional[str] = Field(
267
+ default=None,
268
+ description="Kubiya API key",
269
+ validation_alias=AliasChoices("KUBIYA_API_KEY", "kubiya_api_key"),
270
+ )
271
+
272
+ litellm_api_base: str = Field(
273
+ default="https://llm-proxy.kubiya.ai",
274
+ description="LiteLLM proxy base URL",
275
+ )
276
+
277
+ litellm_api_key: Optional[str] = Field(
278
+ default=None,
279
+ description="LiteLLM API key",
280
+ validation_alias=AliasChoices("LITELLM_API_KEY", "litellm_api_key"),
281
+ )
282
+
283
+ litellm_default_model: str = Field(
284
+ default="kubiya/claude-sonnet-4",
285
+ description="Default LLM model",
286
+ )
287
+
288
+ litellm_timeout: int = Field(
289
+ default=300,
290
+ description="LiteLLM request timeout in seconds",
291
+ )
292
+
293
+ litellm_models_cache_ttl: int = Field(
294
+ default=300,
295
+ description="Cache TTL for LiteLLM models list in seconds (default 5 minutes)",
296
+ )
297
+
298
+ # ==================== Context Graph Settings ====================
299
+
300
+ context_graph_api_base: str = Field(
301
+ default="https://graph.kubiya.ai",
302
+ description="Context Graph API base URL",
303
+ validation_alias=AliasChoices("CONTEXT_GRAPH_API_BASE", "context_graph_api_base"),
304
+ )
305
+
306
+ context_graph_api_timeout: int = Field(
307
+ default=120, # Increased for semantic search operations
308
+ description="Context Graph API request timeout in seconds",
309
+ validation_alias=AliasChoices("CONTEXT_GRAPH_API_TIMEOUT", "context_graph_api_timeout"),
310
+ )
311
+
312
+ # ==================== Logging Settings ====================
313
+
314
+ log_level: str = Field(
315
+ default="INFO",
316
+ description="Logging level",
317
+ )
318
+
319
+ log_format: str = Field(
320
+ default="json",
321
+ description="Log format (json or text)",
322
+ )
323
+
324
+ # ==================== Monitoring Settings ====================
325
+
326
+ metrics_enabled: bool = Field(
327
+ default=True,
328
+ description="Enable Prometheus metrics",
329
+ )
330
+
331
+ tracing_enabled: bool = Field(
332
+ default=False,
333
+ description="Enable OpenTelemetry tracing",
334
+ )
335
+
336
+ otlp_endpoint: Optional[str] = Field(
337
+ default=None,
338
+ description="OpenTelemetry collector endpoint",
339
+ )
340
+
341
+ sentry_dsn: Optional[str] = Field(
342
+ default=None,
343
+ description="Sentry DSN for error reporting",
344
+ validation_alias=AliasChoices("SENTRY_DSN", "sentry_dsn"),
345
+ )
346
+
347
+ # ==================== Rate Limiting ====================
348
+
349
+ rate_limit_enabled: bool = Field(
350
+ default=True,
351
+ description="Enable rate limiting",
352
+ )
353
+
354
+ rate_limit_requests_per_minute: int = Field(
355
+ default=60,
356
+ description="Default requests per minute limit",
357
+ )
358
+
359
+ rate_limit_burst_size: int = Field(
360
+ default=10,
361
+ description="Burst size for rate limiting",
362
+ )
363
+
364
+ # ==================== Event Bus Settings ====================
365
+
366
+ event_bus: Optional[Dict[str, Any]] = Field(
367
+ default=None,
368
+ description="Event bus configuration for multi-provider event publishing",
369
+ )
370
+
371
+ # ==================== OpenTelemetry (OTEL) Settings ====================
372
+
373
+ OTEL_ENABLED: bool = Field(
374
+ default=True,
375
+ description="Enable OpenTelemetry distributed tracing",
376
+ )
377
+
378
+ OTEL_EXPORTER_OTLP_ENDPOINT: Optional[str] = Field(
379
+ default=None,
380
+ description="OTLP exporter endpoint (e.g., http://localhost:4317 for gRPC)",
381
+ )
382
+
383
+ OTEL_EXPORTER_OTLP_PROTOCOL: str = Field(
384
+ default="grpc",
385
+ description="OTLP exporter protocol: 'grpc' or 'http'",
386
+ )
387
+
388
+ OTEL_SERVICE_NAME: str = Field(
389
+ default="agent-control-plane",
390
+ description="Service name for telemetry",
391
+ )
392
+
393
+ OTEL_RESOURCE_ATTRIBUTES: str = Field(
394
+ default="",
395
+ description="Additional resource attributes (format: key1=value1,key2=value2)",
396
+ )
397
+
398
+ OTEL_TRACES_SAMPLER: str = Field(
399
+ default="parentbased_always_on",
400
+ description="Trace sampler: parentbased_always_on, parentbased_traceidratio, etc.",
401
+ )
402
+
403
+ OTEL_TRACES_SAMPLER_ARG: Optional[float] = Field(
404
+ default=None,
405
+ description="Sampler argument (e.g., 0.1 for 10% sampling with traceidratio)",
406
+ )
407
+
408
+ # Local Trace Storage Settings (for observability UI)
409
+ OTEL_LOCAL_STORAGE_ENABLED: bool = Field(
410
+ default=True,
411
+ description="Store traces locally in PostgreSQL for observability UI",
412
+ )
413
+
414
+ OTEL_LOCAL_STORAGE_BATCH_SIZE: int = Field(
415
+ default=100,
416
+ description="Number of spans to batch before inserting to database",
417
+ )
418
+
419
+ OTEL_LOCAL_STORAGE_FLUSH_INTERVAL: int = Field(
420
+ default=1000,
421
+ description="Max time (ms) before flushing batch",
422
+ )
423
+
424
+ OTEL_LOCAL_STORAGE_RETENTION_DAYS: int = Field(
425
+ default=30,
426
+ description="Days to retain traces",
427
+ )
428
+
429
+ model_config = SettingsConfigDict(
430
+ env_file=".env.local",
431
+ env_file_encoding="utf-8",
432
+ case_sensitive=False,
433
+ extra="ignore",
434
+ )
435
+
436
+ @classmethod
437
+ def from_yaml_and_env(cls, config_path: Optional[str] = None) -> "APIConfig":
438
+ """
439
+ Load configuration from YAML file and merge with environment variables.
440
+
441
+ Priority: Environment variables > YAML file > Defaults
442
+
443
+ Args:
444
+ config_path: Optional path to YAML config file
445
+
446
+ Returns:
447
+ APIConfig instance with merged configuration
448
+
449
+ Example YAML:
450
+ event_bus:
451
+ http:
452
+ enabled: true
453
+ base_url: ${CONTROL_PLANE_URL}
454
+ websocket:
455
+ enabled: true
456
+ redis:
457
+ enabled: true
458
+ redis_url: ${REDIS_URL}
459
+ nats:
460
+ enabled: false
461
+ """
462
+ from control_plane_api.app.config.config_loader import load_config_file
463
+
464
+ # Load YAML config (empty dict if no file found)
465
+ yaml_config = load_config_file(config_path)
466
+
467
+ # Pydantic will merge YAML config with environment variables
468
+ # Environment variables take precedence
469
+ return cls(**yaml_config)
@@ -0,0 +1,224 @@
1
+ """YAML configuration file loader with environment variable substitution."""
2
+
3
+ import os
4
+ import re
5
+ from pathlib import Path
6
+ from typing import Optional, Dict, Any
7
+ import structlog
8
+
9
+ logger = structlog.get_logger(__name__)
10
+
11
+
12
+ class ConfigFileLoader:
13
+ """Load and merge YAML configuration with environment variables."""
14
+
15
+ # Pattern to match ${VAR_NAME} or ${VAR_NAME:default}
16
+ ENV_VAR_PATTERN = re.compile(r"\$\{([^}:]+)(?::([^}]*))?\}")
17
+
18
+ @staticmethod
19
+ def _substitute_env_vars(value: Any) -> Any:
20
+ """
21
+ Recursively substitute environment variables in configuration values.
22
+
23
+ Supports:
24
+ - ${VAR_NAME} - Required variable, fails if not set
25
+ - ${VAR_NAME:default} - Optional variable with default value
26
+
27
+ Args:
28
+ value: Configuration value (str, dict, list, or other)
29
+
30
+ Returns:
31
+ Value with environment variables substituted
32
+ """
33
+ if isinstance(value, str):
34
+ # Substitute all ${VAR} occurrences in string
35
+ def replacer(match):
36
+ var_name = match.group(1)
37
+ default_value = match.group(2) # May be None
38
+
39
+ env_value = os.getenv(var_name)
40
+
41
+ if env_value is not None:
42
+ return env_value
43
+ elif default_value is not None:
44
+ return default_value
45
+ else:
46
+ # Required variable not set
47
+ raise ValueError(
48
+ f"Required environment variable not set: {var_name}"
49
+ )
50
+
51
+ return ConfigFileLoader.ENV_VAR_PATTERN.sub(replacer, value)
52
+
53
+ elif isinstance(value, dict):
54
+ # Recursively process dictionary
55
+ return {
56
+ k: ConfigFileLoader._substitute_env_vars(v)
57
+ for k, v in value.items()
58
+ }
59
+
60
+ elif isinstance(value, list):
61
+ # Recursively process list
62
+ return [ConfigFileLoader._substitute_env_vars(item) for item in value]
63
+
64
+ else:
65
+ # Return other types as-is (int, bool, None, etc.)
66
+ return value
67
+
68
+ @staticmethod
69
+ def _find_config_file(config_path: Optional[str] = None) -> Optional[Path]:
70
+ """
71
+ Find configuration file.
72
+
73
+ Search order:
74
+ 1. Explicit config_path parameter
75
+ 2. KUBIYA_CONFIG_FILE environment variable
76
+ 3. ./config.yaml (current directory)
77
+ 4. /etc/kubiya/config.yaml (system-wide)
78
+
79
+ Args:
80
+ config_path: Optional explicit path to config file
81
+
82
+ Returns:
83
+ Path to config file or None if not found
84
+ """
85
+ # 1. Explicit path
86
+ if config_path:
87
+ path = Path(config_path)
88
+ if path.exists():
89
+ return path
90
+ else:
91
+ logger.warning(
92
+ "config_file_not_found",
93
+ path=str(path),
94
+ message="Explicit config path does not exist"
95
+ )
96
+ return None
97
+
98
+ # 2. Environment variable
99
+ env_path = os.getenv("KUBIYA_CONFIG_FILE")
100
+ if env_path:
101
+ path = Path(env_path)
102
+ if path.exists():
103
+ return path
104
+ else:
105
+ logger.warning(
106
+ "config_file_not_found",
107
+ path=str(path),
108
+ env_var="KUBIYA_CONFIG_FILE",
109
+ message="Config file from env var does not exist"
110
+ )
111
+ return None
112
+
113
+ # 3. Current directory
114
+ cwd_config = Path("config.yaml")
115
+ if cwd_config.exists():
116
+ return cwd_config
117
+
118
+ # 4. System-wide config
119
+ system_config = Path("/etc/kubiya/config.yaml")
120
+ if system_config.exists():
121
+ return system_config
122
+
123
+ # No config file found
124
+ return None
125
+
126
+ @staticmethod
127
+ def load_config(config_path: Optional[str] = None) -> Dict[str, Any]:
128
+ """
129
+ Load configuration from YAML file with environment variable substitution.
130
+
131
+ Priority: Environment variables > YAML file > Defaults
132
+
133
+ Args:
134
+ config_path: Optional path to YAML config file
135
+
136
+ Returns:
137
+ Dict with configuration (empty dict if no config file found)
138
+
139
+ Raises:
140
+ ValueError: If required environment variable not set
141
+ yaml.YAMLError: If YAML syntax error
142
+ """
143
+ # Find config file
144
+ config_file = ConfigFileLoader._find_config_file(config_path)
145
+
146
+ if not config_file:
147
+ logger.info(
148
+ "no_config_file_found",
149
+ message="No config file found, using environment variables only",
150
+ checked_paths=[
151
+ "config_path parameter",
152
+ "KUBIYA_CONFIG_FILE env var",
153
+ "./config.yaml",
154
+ "/etc/kubiya/config.yaml"
155
+ ]
156
+ )
157
+ return {}
158
+
159
+ try:
160
+ # Import YAML parser
161
+ try:
162
+ import yaml
163
+ except ImportError:
164
+ raise ImportError(
165
+ "PyYAML is required for YAML config files. "
166
+ "Install it with: pip install PyYAML"
167
+ )
168
+
169
+ # Read and parse YAML
170
+ with open(config_file, "r", encoding="utf-8") as f:
171
+ raw_config = yaml.safe_load(f) or {}
172
+
173
+ # Substitute environment variables
174
+ config = ConfigFileLoader._substitute_env_vars(raw_config)
175
+
176
+ logger.info(
177
+ "config_file_loaded",
178
+ path=str(config_file),
179
+ keys=list(config.keys()) if isinstance(config, dict) else [],
180
+ )
181
+
182
+ return config
183
+
184
+ except ImportError as e:
185
+ logger.error(
186
+ "config_loader_dependency_missing",
187
+ error=str(e),
188
+ path=str(config_file)
189
+ )
190
+ raise
191
+
192
+ except ValueError as e:
193
+ # Required env var not set
194
+ logger.error(
195
+ "config_env_var_required",
196
+ error=str(e),
197
+ path=str(config_file)
198
+ )
199
+ raise
200
+
201
+ except Exception as e:
202
+ logger.error(
203
+ "config_file_load_error",
204
+ error=str(e),
205
+ path=str(config_file),
206
+ error_type=type(e).__name__
207
+ )
208
+ raise
209
+
210
+
211
+ # Convenience function
212
+ def load_config_file(config_path: Optional[str] = None) -> Dict[str, Any]:
213
+ """
214
+ Load configuration from YAML file.
215
+
216
+ Convenience wrapper around ConfigFileLoader.load_config().
217
+
218
+ Args:
219
+ config_path: Optional path to YAML config file
220
+
221
+ Returns:
222
+ Dict with configuration (empty dict if no config file found)
223
+ """
224
+ return ConfigFileLoader.load_config(config_path)