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,384 @@
1
+ """
2
+ Rate limiting middleware using token bucket algorithm.
3
+
4
+ Provides configurable rate limiting per client IP or user.
5
+ """
6
+
7
+ from fastapi import Request, Response, HTTPException, status
8
+ from starlette.middleware.base import BaseHTTPMiddleware
9
+ from starlette.types import ASGIApp
10
+ from typing import Dict, Optional, Tuple, Any
11
+ from datetime import datetime, timedelta
12
+ import time
13
+ import asyncio
14
+ import structlog
15
+ import hashlib
16
+ from control_plane_api.app.exceptions import RateLimitError
17
+
18
+ logger = structlog.get_logger()
19
+
20
+
21
+ class TokenBucket:
22
+ """
23
+ Token bucket implementation for rate limiting.
24
+
25
+ Each bucket starts with a capacity of tokens.
26
+ Tokens are consumed when requests are made.
27
+ Tokens are refilled at a constant rate.
28
+ """
29
+
30
+ def __init__(
31
+ self,
32
+ capacity: int,
33
+ refill_rate: float,
34
+ refill_period: float = 60.0,
35
+ ):
36
+ """
37
+ Initialize token bucket.
38
+
39
+ Args:
40
+ capacity: Maximum number of tokens in bucket
41
+ refill_rate: Number of tokens to add per period
42
+ refill_period: Period in seconds for refilling tokens
43
+ """
44
+ self.capacity = capacity
45
+ self.refill_rate = refill_rate
46
+ self.refill_period = refill_period
47
+ self.tokens = capacity
48
+ self.last_refill = time.time()
49
+ self.lock = asyncio.Lock()
50
+
51
+ async def consume(self, tokens: int = 1) -> Tuple[bool, Dict[str, Any]]:
52
+ """
53
+ Try to consume tokens from the bucket.
54
+
55
+ Args:
56
+ tokens: Number of tokens to consume
57
+
58
+ Returns:
59
+ Tuple of (success, info_dict)
60
+ """
61
+ async with self.lock:
62
+ now = time.time()
63
+
64
+ # Refill tokens based on time elapsed
65
+ time_elapsed = now - self.last_refill
66
+ tokens_to_add = (time_elapsed / self.refill_period) * self.refill_rate
67
+
68
+ if tokens_to_add > 0:
69
+ self.tokens = min(self.capacity, self.tokens + tokens_to_add)
70
+ self.last_refill = now
71
+
72
+ # Check if we have enough tokens
73
+ if self.tokens >= tokens:
74
+ self.tokens -= tokens
75
+ success = True
76
+ retry_after = None
77
+ else:
78
+ success = False
79
+ # Calculate when enough tokens will be available
80
+ tokens_needed = tokens - self.tokens
81
+ time_to_wait = (tokens_needed / self.refill_rate) * self.refill_period
82
+ retry_after = int(time_to_wait) + 1
83
+
84
+ info = {
85
+ "limit": self.capacity,
86
+ "remaining": int(self.tokens),
87
+ "reset": int(now + self.refill_period),
88
+ "retry_after": retry_after,
89
+ }
90
+
91
+ return success, info
92
+
93
+
94
+ class RateLimiter:
95
+ """
96
+ Rate limiter managing multiple token buckets for different clients.
97
+ """
98
+
99
+ def __init__(
100
+ self,
101
+ requests_per_minute: int = 60,
102
+ burst_size: Optional[int] = None,
103
+ cleanup_interval: int = 300,
104
+ ):
105
+ """
106
+ Initialize rate limiter.
107
+
108
+ Args:
109
+ requests_per_minute: Number of requests allowed per minute
110
+ burst_size: Maximum burst size (defaults to requests_per_minute // 4)
111
+ cleanup_interval: Interval in seconds to clean up old buckets
112
+ """
113
+ self.requests_per_minute = requests_per_minute
114
+ self.burst_size = burst_size or max(10, requests_per_minute // 4)
115
+ self.buckets: Dict[str, TokenBucket] = {}
116
+ self.last_cleanup = time.time()
117
+ self.cleanup_interval = cleanup_interval
118
+ self.lock = asyncio.Lock()
119
+
120
+ async def check_rate_limit(self, identifier: str) -> Tuple[bool, Dict[str, Any]]:
121
+ """
122
+ Check if request is within rate limit.
123
+
124
+ Args:
125
+ identifier: Client identifier (IP, user ID, etc.)
126
+
127
+ Returns:
128
+ Tuple of (allowed, headers_dict)
129
+ """
130
+ # Clean up old buckets periodically
131
+ await self._cleanup_buckets()
132
+
133
+ # Get or create bucket for this identifier
134
+ bucket = await self._get_or_create_bucket(identifier)
135
+
136
+ # Try to consume a token
137
+ allowed, info = await bucket.consume()
138
+
139
+ # Build rate limit headers
140
+ headers = {
141
+ "X-RateLimit-Limit": str(info["limit"]),
142
+ "X-RateLimit-Remaining": str(info["remaining"]),
143
+ "X-RateLimit-Reset": str(info["reset"]),
144
+ }
145
+
146
+ if not allowed and info.get("retry_after"):
147
+ headers["Retry-After"] = str(info["retry_after"])
148
+
149
+ return allowed, headers
150
+
151
+ async def _get_or_create_bucket(self, identifier: str) -> TokenBucket:
152
+ """Get existing bucket or create new one."""
153
+ async with self.lock:
154
+ if identifier not in self.buckets:
155
+ self.buckets[identifier] = TokenBucket(
156
+ capacity=self.burst_size,
157
+ refill_rate=self.requests_per_minute,
158
+ refill_period=60.0,
159
+ )
160
+ return self.buckets[identifier]
161
+
162
+ async def _cleanup_buckets(self):
163
+ """Remove old unused buckets to prevent memory leak."""
164
+ now = time.time()
165
+ if now - self.last_cleanup < self.cleanup_interval:
166
+ return
167
+
168
+ async with self.lock:
169
+ # Remove buckets that haven't been used recently
170
+ cutoff_time = now - self.cleanup_interval
171
+ to_remove = []
172
+
173
+ for identifier, bucket in self.buckets.items():
174
+ if bucket.last_refill < cutoff_time:
175
+ to_remove.append(identifier)
176
+
177
+ for identifier in to_remove:
178
+ del self.buckets[identifier]
179
+
180
+ if to_remove:
181
+ logger.info(
182
+ "rate_limiter_cleanup",
183
+ removed_count=len(to_remove),
184
+ remaining_count=len(self.buckets),
185
+ )
186
+
187
+ self.last_cleanup = now
188
+
189
+
190
+ class RateLimitMiddleware(BaseHTTPMiddleware):
191
+ """
192
+ Rate limiting middleware for FastAPI.
193
+
194
+ Limits requests per client based on IP address or authenticated user.
195
+ """
196
+
197
+ def __init__(
198
+ self,
199
+ app: ASGIApp,
200
+ requests_per_minute: int = 60,
201
+ burst_size: Optional[int] = None,
202
+ exclude_paths: Optional[list] = None,
203
+ identifier_callback: Optional[callable] = None,
204
+ ):
205
+ """
206
+ Initialize rate limit middleware.
207
+
208
+ Args:
209
+ app: FastAPI application
210
+ requests_per_minute: Default rate limit
211
+ burst_size: Maximum burst size
212
+ exclude_paths: Paths to exclude from rate limiting
213
+ identifier_callback: Custom function to get client identifier
214
+ """
215
+ super().__init__(app)
216
+ self.rate_limiter = RateLimiter(requests_per_minute, burst_size)
217
+ self.exclude_paths = exclude_paths or ["/health", "/metrics", "/docs", "/openapi.json"]
218
+ self.identifier_callback = identifier_callback
219
+
220
+ async def dispatch(self, request: Request, call_next):
221
+ """Process request with rate limiting."""
222
+
223
+ # Check if path is excluded
224
+ if self._is_excluded(request.url.path):
225
+ return await call_next(request)
226
+
227
+ # Get client identifier
228
+ identifier = await self._get_identifier(request)
229
+
230
+ # Check rate limit
231
+ allowed, headers = await self.rate_limiter.check_rate_limit(identifier)
232
+
233
+ if not allowed:
234
+ # Log rate limit exceeded
235
+ logger.warning(
236
+ "rate_limit_exceeded",
237
+ identifier=self._hash_identifier(identifier),
238
+ path=request.url.path,
239
+ method=request.method,
240
+ )
241
+
242
+ # Raise rate limit error
243
+ raise RateLimitError(
244
+ limit=self.rate_limiter.requests_per_minute,
245
+ window="minute",
246
+ retry_after=int(headers.get("Retry-After", 60)),
247
+ )
248
+
249
+ # Process request
250
+ response = await call_next(request)
251
+
252
+ # Add rate limit headers to response
253
+ for header, value in headers.items():
254
+ response.headers[header] = value
255
+
256
+ return response
257
+
258
+ def _is_excluded(self, path: str) -> bool:
259
+ """Check if path is excluded from rate limiting."""
260
+ for excluded in self.exclude_paths:
261
+ if path.startswith(excluded):
262
+ return True
263
+ return False
264
+
265
+ async def _get_identifier(self, request: Request) -> str:
266
+ """
267
+ Get client identifier for rate limiting.
268
+
269
+ Priority:
270
+ 1. Custom identifier callback
271
+ 2. Authenticated user ID
272
+ 3. Client IP address
273
+ """
274
+ # Use custom identifier callback if provided
275
+ if self.identifier_callback:
276
+ identifier = await self.identifier_callback(request)
277
+ if identifier:
278
+ return f"custom:{identifier}"
279
+
280
+ # Check for authenticated user
281
+ if hasattr(request.state, "user") and request.state.user:
282
+ user_id = getattr(request.state.user, "id", None)
283
+ if user_id:
284
+ return f"user:{user_id}"
285
+
286
+ # Fall back to IP address
287
+ client_host = request.client.host if request.client else "unknown"
288
+
289
+ # Check for proxy headers
290
+ forwarded_for = request.headers.get("X-Forwarded-For")
291
+ if forwarded_for:
292
+ # Take the first IP in the chain
293
+ client_host = forwarded_for.split(",")[0].strip()
294
+
295
+ real_ip = request.headers.get("X-Real-IP")
296
+ if real_ip:
297
+ client_host = real_ip
298
+
299
+ return f"ip:{client_host}"
300
+
301
+ def _hash_identifier(self, identifier: str) -> str:
302
+ """Hash identifier for logging (privacy)."""
303
+ return hashlib.sha256(identifier.encode()).hexdigest()[:16]
304
+
305
+
306
+ # Per-endpoint rate limiting decorator
307
+ class EndpointRateLimiter:
308
+ """
309
+ Decorator for per-endpoint rate limiting.
310
+
311
+ Usage:
312
+ rate_limiter = EndpointRateLimiter()
313
+
314
+ @app.get("/expensive-operation")
315
+ @rate_limiter.limit(requests_per_minute=10)
316
+ async def expensive_operation():
317
+ ...
318
+ """
319
+
320
+ def __init__(self):
321
+ self.limiters: Dict[str, RateLimiter] = {}
322
+
323
+ def limit(
324
+ self,
325
+ requests_per_minute: int,
326
+ burst_size: Optional[int] = None,
327
+ identifier_callback: Optional[callable] = None,
328
+ ):
329
+ """
330
+ Create rate limit decorator for endpoint.
331
+
332
+ Args:
333
+ requests_per_minute: Rate limit for this endpoint
334
+ burst_size: Burst size for this endpoint
335
+ identifier_callback: Custom identifier function
336
+ """
337
+ def decorator(func):
338
+ # Create rate limiter for this endpoint
339
+ endpoint_id = f"{func.__module__}.{func.__name__}"
340
+ self.limiters[endpoint_id] = RateLimiter(
341
+ requests_per_minute=requests_per_minute,
342
+ burst_size=burst_size,
343
+ )
344
+
345
+ async def wrapper(request: Request, *args, **kwargs):
346
+ # Get client identifier
347
+ if identifier_callback:
348
+ identifier = await identifier_callback(request)
349
+ else:
350
+ identifier = self._default_identifier(request)
351
+
352
+ # Check rate limit
353
+ limiter = self.limiters[endpoint_id]
354
+ allowed, headers = await limiter.check_rate_limit(identifier)
355
+
356
+ if not allowed:
357
+ raise RateLimitError(
358
+ limit=requests_per_minute,
359
+ window="minute",
360
+ retry_after=int(headers.get("Retry-After", 60)),
361
+ )
362
+
363
+ # Add headers to response (if we have access to it)
364
+ response = await func(request, *args, **kwargs)
365
+ if isinstance(response, Response):
366
+ for header, value in headers.items():
367
+ response.headers[header] = value
368
+
369
+ return response
370
+
371
+ return wrapper
372
+ return decorator
373
+
374
+ def _default_identifier(self, request: Request) -> str:
375
+ """Default identifier extraction."""
376
+ if hasattr(request.state, "user") and request.state.user:
377
+ return f"user:{request.state.user.id}"
378
+
379
+ client_host = request.client.host if request.client else "unknown"
380
+ return f"ip:{client_host}"
381
+
382
+
383
+ # Global rate limiter instance for decorator usage
384
+ endpoint_rate_limiter = EndpointRateLimiter()
@@ -0,0 +1,202 @@
1
+ """
2
+ Request ID middleware for request tracking and correlation.
3
+
4
+ Adds a unique request ID to each request for tracking through logs and services.
5
+ """
6
+
7
+ from fastapi import Request, Response
8
+ from starlette.middleware.base import BaseHTTPMiddleware
9
+ from starlette.types import ASGIApp
10
+ import structlog
11
+ import logging
12
+ import uuid
13
+ import contextvars
14
+ from typing import Optional
15
+
16
+ logger = structlog.get_logger()
17
+
18
+ # Context variable to store request ID
19
+ request_id_context: contextvars.ContextVar[Optional[str]] = contextvars.ContextVar(
20
+ "request_id", default=None
21
+ )
22
+
23
+
24
+ class RequestIDMiddleware(BaseHTTPMiddleware):
25
+ """
26
+ Middleware to add request ID to all requests.
27
+
28
+ - Checks for existing X-Request-ID header
29
+ - Generates new ID if not present
30
+ - Adds ID to response headers
31
+ - Makes ID available in context for logging
32
+ """
33
+
34
+ REQUEST_ID_HEADER = "X-Request-ID"
35
+
36
+ def __init__(self, app: ASGIApp, header_name: str = None):
37
+ super().__init__(app)
38
+ if header_name:
39
+ self.REQUEST_ID_HEADER = header_name
40
+
41
+ async def dispatch(self, request: Request, call_next):
42
+ """Process the request and add request ID."""
43
+
44
+ # Get or generate request ID
45
+ request_id = (
46
+ request.headers.get(self.REQUEST_ID_HEADER) or
47
+ request.headers.get(self.REQUEST_ID_HEADER.lower()) or
48
+ self._generate_request_id()
49
+ )
50
+
51
+ # Validate request ID format (basic security check)
52
+ if not self._is_valid_request_id(request_id):
53
+ request_id = self._generate_request_id()
54
+
55
+ # Store in request state for easy access
56
+ request.state.request_id = request_id
57
+
58
+ # Set context variable for logging
59
+ token = request_id_context.set(request_id)
60
+
61
+ try:
62
+ # Log request start
63
+ logger.info(
64
+ "request_started",
65
+ request_id=request_id,
66
+ method=request.method,
67
+ path=str(request.url.path),
68
+ query=str(request.url.query) if request.url.query else None,
69
+ client=request.client.host if request.client else None,
70
+ )
71
+
72
+ # Process request
73
+ response = await call_next(request)
74
+
75
+ # Add request ID to response headers
76
+ response.headers[self.REQUEST_ID_HEADER] = request_id
77
+
78
+ # Log request completion
79
+ logger.info(
80
+ "request_completed",
81
+ request_id=request_id,
82
+ status_code=response.status_code,
83
+ )
84
+
85
+ return response
86
+
87
+ except Exception as e:
88
+ # Log request failure
89
+ logger.error(
90
+ "request_failed",
91
+ request_id=request_id,
92
+ error=str(e),
93
+ error_type=type(e).__name__,
94
+ )
95
+ raise
96
+
97
+ finally:
98
+ # Reset context variable
99
+ request_id_context.reset(token)
100
+
101
+ def _generate_request_id(self) -> str:
102
+ """Generate a new request ID."""
103
+ return str(uuid.uuid4())
104
+
105
+ def _is_valid_request_id(self, request_id: str) -> bool:
106
+ """
107
+ Validate request ID format.
108
+
109
+ Accepts UUIDs and alphanumeric strings up to 128 characters.
110
+ """
111
+ if not request_id or len(request_id) > 128:
112
+ return False
113
+
114
+ # Allow UUIDs, alphanumeric, hyphens, and underscores
115
+ import re
116
+ return bool(re.match(r'^[a-zA-Z0-9\-_]+$', request_id))
117
+
118
+
119
+ def get_request_id() -> Optional[str]:
120
+ """
121
+ Get the current request ID from context.
122
+
123
+ This can be used anywhere in the application to get the current request ID.
124
+
125
+ Returns:
126
+ Request ID if in request context, None otherwise
127
+ """
128
+ return request_id_context.get()
129
+
130
+
131
+ def set_request_id(request_id: str) -> None:
132
+ """
133
+ Set the request ID in context.
134
+
135
+ This is useful for background tasks or other contexts where you want
136
+ to maintain the request ID.
137
+
138
+ Args:
139
+ request_id: Request ID to set
140
+ """
141
+ request_id_context.set(request_id)
142
+
143
+
144
+ class RequestIDLogProcessor:
145
+ """
146
+ Structlog processor to add request ID to all log entries.
147
+
148
+ Use this in your structlog configuration:
149
+
150
+ structlog.configure(
151
+ processors=[
152
+ RequestIDLogProcessor(),
153
+ structlog.processors.add_log_level,
154
+ structlog.processors.TimeStamper(fmt="iso"),
155
+ structlog.processors.JSONRenderer(),
156
+ ],
157
+ ...
158
+ )
159
+ """
160
+
161
+ def __call__(self, logger, name, event_dict):
162
+ """Add request ID to log entry if available."""
163
+ request_id = get_request_id()
164
+ if request_id:
165
+ event_dict["request_id"] = request_id
166
+ return event_dict
167
+
168
+
169
+ def setup_request_id_logging():
170
+ """
171
+ Configure structlog to include request ID in all logs.
172
+
173
+ Call this in your app initialization.
174
+ """
175
+ import structlog
176
+
177
+ structlog.configure(
178
+ processors=[
179
+ RequestIDLogProcessor(), # Add request ID
180
+ structlog.contextvars.merge_contextvars,
181
+ structlog.processors.add_log_level,
182
+ structlog.processors.TimeStamper(fmt="iso"),
183
+ structlog.processors.JSONRenderer(),
184
+ ],
185
+ wrapper_class=structlog.make_filtering_bound_logger(
186
+ logging.INFO
187
+ ),
188
+ logger_factory=structlog.PrintLoggerFactory(),
189
+ )
190
+
191
+
192
+ # For FastAPI dependency injection
193
+ async def get_request_id_from_request(request: Request) -> str:
194
+ """
195
+ FastAPI dependency to get request ID.
196
+
197
+ Usage:
198
+ @app.get("/example")
199
+ async def example(request_id: str = Depends(get_request_id_from_request)):
200
+ return {"request_id": request_id}
201
+ """
202
+ return getattr(request.state, "request_id", None) or str(uuid.uuid4())
@@ -0,0 +1,40 @@
1
+ # Database Models
2
+ from control_plane_api.app.models.project import Project, ProjectStatus
3
+ from control_plane_api.app.models.agent import Agent, AgentStatus
4
+ from control_plane_api.app.models.team import Team, TeamStatus
5
+ from control_plane_api.app.models.workflow import Workflow, WorkflowStatus
6
+ from control_plane_api.app.models.workspace import Workspace
7
+ from control_plane_api.app.models.user_profile import UserProfile
8
+ from control_plane_api.app.models.session import Session
9
+ from control_plane_api.app.models.execution import Execution, ExecutionStatus, ExecutionType, ExecutionTriggerSource
10
+ from control_plane_api.app.models.presence import UserPresence
11
+ from control_plane_api.app.models.environment import Environment, EnvironmentStatus
12
+ from control_plane_api.app.models.associations import AgentEnvironment, TeamEnvironment, ExecutionParticipant, ParticipantRole
13
+ from control_plane_api.app.models.job import Job, JobExecution, JobStatus, JobTriggerType, ExecutorType, PlanningMode
14
+ from control_plane_api.app.models.llm_model import LLMModel
15
+ from control_plane_api.app.models.orchestration import Namespace, TemporalNamespace
16
+ from control_plane_api.app.models.project_management import ProjectAgent
17
+ from control_plane_api.app.models.skill import SkillAssociation, Skill
18
+ from control_plane_api.app.models.worker import WorkerQueue
19
+ from control_plane_api.app.models.trace import Trace, Span, TraceStatus, SpanKind, SpanStatusCode
20
+
21
+ __all__ = [
22
+ "Project", "ProjectStatus",
23
+ "Agent", "AgentStatus",
24
+ "Team", "TeamStatus",
25
+ "Workflow", "WorkflowStatus",
26
+ "Workspace",
27
+ "UserProfile",
28
+ "Session",
29
+ "Execution", "ExecutionStatus", "ExecutionType", "ExecutionTriggerSource",
30
+ "UserPresence",
31
+ "Environment", "EnvironmentStatus",
32
+ "AgentEnvironment", "TeamEnvironment",
33
+ "ExecutionParticipant", "ParticipantRole",
34
+ "Job", "JobExecution", "JobStatus", "JobTriggerType", "ExecutorType", "PlanningMode",
35
+ "LLMModel",
36
+ "Namespace", "TemporalNamespace", "ProjectAgent",
37
+ "SkillAssociation", "Skill",
38
+ "WorkerQueue",
39
+ "Trace", "Span", "TraceStatus", "SpanKind", "SpanStatusCode"
40
+ ]