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,523 @@
1
+ """End-to-end tests for context graph node templates in system prompts."""
2
+ import pytest
3
+ import os
4
+ from unittest.mock import patch, MagicMock
5
+ import httpx
6
+ from control_plane_api.app.lib.templating.types import TemplateContext
7
+ from control_plane_api.app.lib.templating.resolver import resolve_templates
8
+ from control_plane_api.app.lib.templating.compiler import TemplateCompiler
9
+ from control_plane_api.app.lib.templating.engine import get_default_engine
10
+
11
+
12
+ class TestGraphTemplateE2EFlow:
13
+ """End-to-end tests for full template resolution flow with graph nodes."""
14
+
15
+ @patch('httpx.Client')
16
+ def test_e2e_system_prompt_with_graph_node_api_fetch(self, mock_client_cls):
17
+ """Test complete flow: system prompt → template parsing → API fetch → compilation."""
18
+ # Mock API response for a service node
19
+ service_node = {
20
+ "id": "service-prod-api",
21
+ "labels": ["Service", "Production"],
22
+ "properties": {
23
+ "name": "Production API",
24
+ "version": "2.1.0",
25
+ "environment": "production",
26
+ "owner_team": "platform-team"
27
+ },
28
+ "relationships": {
29
+ "incoming": [
30
+ {"type": "DEPENDS_ON", "from": "service-frontend", "properties": {}}
31
+ ],
32
+ "outgoing": [
33
+ {"type": "USES", "to": "database-prod", "properties": {"connection": "primary"}}
34
+ ]
35
+ }
36
+ }
37
+
38
+ mock_response = MagicMock()
39
+ mock_response.json.return_value = service_node
40
+
41
+ mock_client = MagicMock()
42
+ mock_client.get.return_value = mock_response
43
+ mock_client.__enter__ = MagicMock(return_value=mock_client)
44
+ mock_client.__exit__ = MagicMock(return_value=False)
45
+ mock_client_cls.return_value = mock_client
46
+
47
+ # System prompt with graph node reference
48
+ system_prompt = """
49
+ You are managing the following service:
50
+
51
+ {{.graph.service-prod-api}}
52
+
53
+ Please ensure all operations follow production best practices.
54
+ """
55
+
56
+ # Build template context (simulating agent executor)
57
+ context = TemplateContext(
58
+ variables={},
59
+ secrets={},
60
+ env_vars={},
61
+ graph_api_base="https://graph.kubiya.ai",
62
+ graph_api_key="test-api-key",
63
+ graph_org_id="test-org-123"
64
+ )
65
+
66
+ # Resolve templates
67
+ compiled_prompt = resolve_templates(
68
+ system_prompt,
69
+ context,
70
+ strict=False,
71
+ skip_on_error=False
72
+ )
73
+
74
+ # Verify compilation succeeded
75
+ assert compiled_prompt != system_prompt
76
+ assert "service-prod-api" in compiled_prompt
77
+ assert "Production API" in compiled_prompt
78
+ assert "version" in compiled_prompt
79
+ assert "2.1.0" in compiled_prompt
80
+ assert "relationships" in compiled_prompt
81
+
82
+ # Verify API was called correctly
83
+ mock_client.get.assert_called_once()
84
+ call_args = mock_client.get.call_args
85
+ assert "/api/v1/graph/nodes/service-prod-api" in call_args[0][0]
86
+
87
+ headers = call_args[1]["headers"]
88
+ assert headers["Authorization"] == "UserKey test-api-key"
89
+ assert headers["X-Organization-ID"] == "test-org-123"
90
+
91
+ @patch('httpx.Client')
92
+ def test_e2e_mixed_template_variables(self, mock_client_cls):
93
+ """Test system prompt with mixed variable types: simple, secret, env, graph."""
94
+ # Mock graph node
95
+ user_node = {
96
+ "id": "user-john",
97
+ "properties": {"name": "John Doe", "email": "john@example.com"}
98
+ }
99
+
100
+ mock_response = MagicMock()
101
+ mock_response.json.return_value = user_node
102
+
103
+ mock_client = MagicMock()
104
+ mock_client.get.return_value = mock_response
105
+ mock_client.__enter__ = MagicMock(return_value=mock_client)
106
+ mock_client.__exit__ = MagicMock(return_value=False)
107
+ mock_client_cls.return_value = mock_client
108
+
109
+ system_prompt = """
110
+ Agent: {{agent_name}}
111
+ API Key: {{.secret.api_key}}
112
+ Environment: {{.env.ENVIRONMENT}}
113
+ User Context: {{.graph.user-john}}
114
+ """
115
+
116
+ context = TemplateContext(
117
+ variables={"agent_name": "TestAgent"},
118
+ secrets={"api_key": "secret-key-123"},
119
+ env_vars={"ENVIRONMENT": "staging"},
120
+ graph_api_base="https://graph.kubiya.ai",
121
+ graph_api_key="test-key"
122
+ )
123
+
124
+ compiled = resolve_templates(system_prompt, context, strict=False)
125
+
126
+ # All variables should be resolved
127
+ assert "TestAgent" in compiled
128
+ assert "secret-key-123" in compiled
129
+ assert "staging" in compiled
130
+ assert "john@example.com" in compiled
131
+
132
+ @patch('httpx.Client')
133
+ def test_e2e_multiple_graph_nodes_caching(self, mock_client_cls):
134
+ """Test that multiple references to same node only fetch once (caching)."""
135
+ node_data = {"id": "repo-1", "name": "Backend Repo"}
136
+
137
+ mock_response = MagicMock()
138
+ mock_response.json.return_value = node_data
139
+
140
+ mock_client = MagicMock()
141
+ mock_client.get.return_value = mock_response
142
+ mock_client.__enter__ = MagicMock(return_value=mock_client)
143
+ mock_client.__exit__ = MagicMock(return_value=False)
144
+ mock_client_cls.return_value = mock_client
145
+
146
+ system_prompt = """
147
+ Primary repo: {{.graph.repo-1}}
148
+ Also check: {{.graph.repo-1}}
149
+ And again: {{.graph.repo-1}}
150
+ """
151
+
152
+ context = TemplateContext(
153
+ graph_api_base="https://graph.kubiya.ai",
154
+ graph_api_key="test-key"
155
+ )
156
+
157
+ compiled = resolve_templates(system_prompt, context)
158
+
159
+ # Should appear 3 times in output
160
+ assert compiled.count("Backend Repo") == 3
161
+
162
+ # But API should only be called ONCE due to caching
163
+ assert mock_client.get.call_count == 1
164
+
165
+ @patch('httpx.Client')
166
+ def test_e2e_node_not_found_404(self, mock_client_cls):
167
+ """Test handling of non-existent nodes (404)."""
168
+ mock_response = MagicMock()
169
+ mock_response.status_code = 404
170
+
171
+ mock_client = MagicMock()
172
+ mock_client.get.side_effect = httpx.HTTPStatusError(
173
+ "Not Found",
174
+ request=MagicMock(),
175
+ response=mock_response
176
+ )
177
+ mock_client.__enter__ = MagicMock(return_value=mock_client)
178
+ mock_client.__exit__ = MagicMock(return_value=False)
179
+ mock_client_cls.return_value = mock_client
180
+
181
+ system_prompt = "Service: {{.graph.nonexistent-service}}"
182
+
183
+ context = TemplateContext(
184
+ graph_api_base="https://graph.kubiya.ai",
185
+ graph_api_key="test-key"
186
+ )
187
+
188
+ # Should fail with descriptive error
189
+ compiler = TemplateCompiler()
190
+ result = compiler.compile(system_prompt, context)
191
+
192
+ assert not result.success
193
+ assert "nonexistent-service" in result.error
194
+ assert "not found" in result.error.lower()
195
+
196
+ @patch('httpx.Client')
197
+ def test_e2e_pre_populated_graph_nodes(self, mock_client_cls):
198
+ """Test using pre-populated graph nodes (no API call needed)."""
199
+ system_prompt = "User: {{.graph.user-alice}}"
200
+
201
+ # Pre-populate node in context (e.g., from a previous fetch)
202
+ context = TemplateContext(
203
+ graph_nodes={
204
+ "user-alice": {
205
+ "id": "user-alice",
206
+ "name": "Alice Smith",
207
+ "role": "admin"
208
+ }
209
+ }
210
+ )
211
+
212
+ compiled = resolve_templates(system_prompt, context)
213
+
214
+ # Should compile successfully
215
+ assert "Alice Smith" in compiled
216
+ assert "admin" in compiled
217
+
218
+ # No API call should be made
219
+ mock_client_cls.assert_not_called()
220
+
221
+ def test_e2e_graph_node_without_api_config_fails(self):
222
+ """Test that graph nodes fail gracefully without API configuration."""
223
+ system_prompt = "Service: {{.graph.service-1}}"
224
+
225
+ # No API configuration
226
+ context = TemplateContext(
227
+ variables={},
228
+ secrets={},
229
+ env_vars={}
230
+ )
231
+
232
+ compiler = TemplateCompiler()
233
+ result = compiler.compile(system_prompt, context)
234
+
235
+ assert not result.success
236
+ assert "service-1" in result.error
237
+ assert "not configured" in result.error.lower()
238
+
239
+ @patch('httpx.Client')
240
+ def test_e2e_complex_node_with_relationships(self, mock_client_cls):
241
+ """Test complex node with full metadata and relationships."""
242
+ complex_node = {
243
+ "id": "kubernetes-cluster-prod",
244
+ "labels": ["Cluster", "Kubernetes", "Production"],
245
+ "properties": {
246
+ "name": "Production K8s Cluster",
247
+ "version": "1.28.3",
248
+ "region": "us-east-1",
249
+ "node_count": 15,
250
+ "created_at": "2023-01-15T10:30:00Z"
251
+ },
252
+ "relationships": {
253
+ "incoming": [
254
+ {
255
+ "type": "DEPLOYED_TO",
256
+ "from": "service-api",
257
+ "properties": {"namespace": "production"}
258
+ },
259
+ {
260
+ "type": "DEPLOYED_TO",
261
+ "from": "service-worker",
262
+ "properties": {"namespace": "production"}
263
+ }
264
+ ],
265
+ "outgoing": [
266
+ {
267
+ "type": "MONITORS",
268
+ "to": "prometheus-prod",
269
+ "properties": {"scrape_interval": "30s"}
270
+ },
271
+ {
272
+ "type": "LOGS_TO",
273
+ "to": "elasticsearch-prod",
274
+ "properties": {}
275
+ }
276
+ ]
277
+ },
278
+ "metadata": {
279
+ "last_updated": "2024-12-01T15:45:00Z",
280
+ "integration": "aws",
281
+ "sync_status": "active"
282
+ }
283
+ }
284
+
285
+ mock_response = MagicMock()
286
+ mock_response.json.return_value = complex_node
287
+
288
+ mock_client = MagicMock()
289
+ mock_client.get.return_value = mock_response
290
+ mock_client.__enter__ = MagicMock(return_value=mock_client)
291
+ mock_client.__exit__ = MagicMock(return_value=False)
292
+ mock_client_cls.return_value = mock_client
293
+
294
+ system_prompt = """
295
+ You are managing:
296
+ {{.graph.kubernetes-cluster-prod}}
297
+
298
+ Ensure all operations are production-safe.
299
+ """
300
+
301
+ context = TemplateContext(
302
+ graph_api_base="https://graph.kubiya.ai",
303
+ graph_api_key="test-key"
304
+ )
305
+
306
+ compiled = resolve_templates(system_prompt, context)
307
+
308
+ # Verify all complex data is present
309
+ assert "Production K8s Cluster" in compiled
310
+ assert "1.28.3" in compiled
311
+ assert "us-east-1" in compiled
312
+ assert "DEPLOYED_TO" in compiled
313
+ assert "service-api" in compiled
314
+ assert "MONITORS" in compiled
315
+ assert "prometheus-prod" in compiled
316
+ assert "elasticsearch-prod" in compiled
317
+ assert "metadata" in compiled
318
+
319
+ @patch('httpx.Client')
320
+ def test_e2e_multiple_different_nodes(self, mock_client_cls):
321
+ """Test system prompt with multiple different graph nodes."""
322
+ # Mock responses for different nodes
323
+ def mock_get(url, **kwargs):
324
+ response = MagicMock()
325
+ if "service-api" in url:
326
+ response.json.return_value = {
327
+ "id": "service-api",
328
+ "properties": {"name": "API Service", "port": 8080}
329
+ }
330
+ elif "database-prod" in url:
331
+ response.json.return_value = {
332
+ "id": "database-prod",
333
+ "properties": {"name": "Prod Database", "type": "PostgreSQL"}
334
+ }
335
+ elif "user-admin" in url:
336
+ response.json.return_value = {
337
+ "id": "user-admin",
338
+ "properties": {"name": "Admin User", "email": "admin@example.com"}
339
+ }
340
+ return response
341
+
342
+ mock_client = MagicMock()
343
+ mock_client.get.side_effect = mock_get
344
+ mock_client.__enter__ = MagicMock(return_value=mock_client)
345
+ mock_client.__exit__ = MagicMock(return_value=False)
346
+ mock_client_cls.return_value = mock_client
347
+
348
+ system_prompt = """
349
+ Service: {{.graph.service-api}}
350
+ Database: {{.graph.database-prod}}
351
+ Admin: {{.graph.user-admin}}
352
+ """
353
+
354
+ context = TemplateContext(
355
+ graph_api_base="https://graph.kubiya.ai",
356
+ graph_api_key="test-key"
357
+ )
358
+
359
+ compiled = resolve_templates(system_prompt, context)
360
+
361
+ # All three nodes should be present
362
+ assert "API Service" in compiled
363
+ assert "8080" in compiled
364
+ assert "Prod Database" in compiled
365
+ assert "PostgreSQL" in compiled
366
+ assert "Admin User" in compiled
367
+ assert "admin@example.com" in compiled
368
+
369
+ # Should make 3 API calls (one per unique node)
370
+ assert mock_client.get.call_count == 3
371
+
372
+ @patch('httpx.Client')
373
+ def test_e2e_graceful_degradation_with_skip_on_error(self, mock_client_cls):
374
+ """Test graceful degradation when skip_on_error=True."""
375
+ mock_response = MagicMock()
376
+ mock_response.status_code = 500
377
+
378
+ mock_client = MagicMock()
379
+ mock_client.get.side_effect = httpx.HTTPStatusError(
380
+ "Internal Server Error",
381
+ request=MagicMock(),
382
+ response=mock_response
383
+ )
384
+ mock_client.__enter__ = MagicMock(return_value=mock_client)
385
+ mock_client.__exit__ = MagicMock(return_value=False)
386
+ mock_client_cls.return_value = mock_client
387
+
388
+ system_prompt = "Service: {{.graph.service-1}} with {{user}} access"
389
+
390
+ context = TemplateContext(
391
+ variables={"user": "john"},
392
+ graph_api_base="https://graph.kubiya.ai",
393
+ graph_api_key="test-key"
394
+ )
395
+
396
+ # With skip_on_error=True, should return original template on error
397
+ compiled = resolve_templates(
398
+ system_prompt,
399
+ context,
400
+ strict=False,
401
+ skip_on_error=True
402
+ )
403
+
404
+ # Should return original (graceful degradation)
405
+ assert compiled == system_prompt
406
+
407
+ @patch.dict(os.environ, {
408
+ "CONTEXT_GRAPH_API_BASE": "https://test-graph.example.com",
409
+ "KUBIYA_API_KEY": "env-api-key-456",
410
+ "KUBIYA_ORG_ID": "env-org-789"
411
+ })
412
+ @patch('httpx.Client')
413
+ def test_e2e_uses_environment_variables(self, mock_client_cls):
414
+ """Test that executor uses environment variables for graph API config."""
415
+ node_data = {"id": "test-node", "name": "Test"}
416
+
417
+ mock_response = MagicMock()
418
+ mock_response.json.return_value = node_data
419
+
420
+ mock_client = MagicMock()
421
+ mock_client.get.return_value = mock_response
422
+ mock_client.__enter__ = MagicMock(return_value=mock_client)
423
+ mock_client.__exit__ = MagicMock(return_value=False)
424
+ mock_client_cls.return_value = mock_client
425
+
426
+ system_prompt = "Node: {{.graph.test-node}}"
427
+
428
+ # Simulate what agent executor does
429
+ context = TemplateContext(
430
+ graph_api_base=os.environ.get("CONTEXT_GRAPH_API_BASE", "https://graph.kubiya.ai"),
431
+ graph_api_key=os.environ.get("KUBIYA_API_KEY"),
432
+ graph_org_id=os.environ.get("KUBIYA_ORG_ID")
433
+ )
434
+
435
+ compiled = resolve_templates(system_prompt, context)
436
+
437
+ # Verify it used environment variables
438
+ call_args = mock_client.get.call_args
439
+ assert "https://test-graph.example.com" in call_args[0][0]
440
+
441
+ headers = call_args[1]["headers"]
442
+ assert headers["Authorization"] == "UserKey env-api-key-456"
443
+ assert headers["X-Organization-ID"] == "env-org-789"
444
+
445
+
446
+ class TestGraphTemplateValidation:
447
+ """E2E tests for template validation with graph nodes."""
448
+
449
+ def test_e2e_validation_syntax_check(self):
450
+ """Test validation catches invalid graph node syntax."""
451
+ from control_plane_api.app.lib.templating.validator import TemplateValidator
452
+
453
+ validator = TemplateValidator()
454
+
455
+ # Valid syntax
456
+ valid_templates = [
457
+ "{{.graph.node-1}}",
458
+ "{{.graph.service.prod}}",
459
+ "{{.graph.user_123}}",
460
+ "{{.graph.a.b.c-d_e}}"
461
+ ]
462
+
463
+ for template in valid_templates:
464
+ context = TemplateContext(
465
+ graph_api_base="https://graph.kubiya.ai",
466
+ graph_api_key="test-key"
467
+ )
468
+ result = validator.validate(template, context)
469
+ assert result.valid, f"Template should be valid: {template}"
470
+
471
+ def test_e2e_validation_missing_api_config(self):
472
+ """Test validation detects missing API configuration."""
473
+ from control_plane_api.app.lib.templating.validator import TemplateValidator
474
+
475
+ validator = TemplateValidator()
476
+ template = "{{.graph.node-1}}"
477
+
478
+ # Missing API config
479
+ context = TemplateContext()
480
+
481
+ result = validator.validate(template, context)
482
+ assert not result.valid
483
+ assert len(result.errors) == 1
484
+ assert result.errors[0].code == "MISSING_GRAPH_NODE"
485
+ assert "node-1" in result.missing_graph_nodes
486
+
487
+
488
+ class TestGraphTemplateWithRealResolver:
489
+ """E2E tests using the actual resolve_templates function."""
490
+
491
+ @patch('httpx.Client')
492
+ def test_resolve_templates_integration(self, mock_client_cls):
493
+ """Test resolve_templates function with graph nodes."""
494
+ node_data = {
495
+ "id": "integration-test",
496
+ "properties": {"status": "active"}
497
+ }
498
+
499
+ mock_response = MagicMock()
500
+ mock_response.json.return_value = node_data
501
+
502
+ mock_client = MagicMock()
503
+ mock_client.get.return_value = mock_response
504
+ mock_client.__enter__ = MagicMock(return_value=mock_client)
505
+ mock_client.__exit__ = MagicMock(return_value=False)
506
+ mock_client_cls.return_value = mock_client
507
+
508
+ template = "Status: {{.graph.integration-test}}"
509
+
510
+ context = TemplateContext(
511
+ graph_api_base="https://graph.kubiya.ai",
512
+ graph_api_key="test-key"
513
+ )
514
+
515
+ # Use the actual resolver
516
+ result = resolve_templates(template, context)
517
+
518
+ assert "active" in result
519
+ assert node_data["id"] in result
520
+
521
+
522
+ if __name__ == "__main__":
523
+ pytest.main([__file__, "-v", "-s"])