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,213 @@
1
+ """
2
+ Planning Agent Streaming Hook
3
+
4
+ Captures agent execution events (tool calls, reasoning) during task planning
5
+ and publishes them for real-time UI streaming.
6
+ """
7
+
8
+ from typing import Callable, Any, Optional
9
+ import time
10
+ import uuid
11
+ import structlog
12
+
13
+ logger = structlog.get_logger()
14
+
15
+
16
+ class PlanningStreamingHook:
17
+ """
18
+ Hook to capture and stream agent execution events during planning.
19
+
20
+ Similar to worker execution streaming but optimized for planning agents.
21
+ Captures:
22
+ - Tool executions (start/complete)
23
+ - Reasoning/thinking content
24
+ - Agent state transitions
25
+ """
26
+
27
+ def __init__(self, event_publisher: Callable):
28
+ """
29
+ Initialize streaming hook.
30
+
31
+ Args:
32
+ event_publisher: Async function to publish events (yield SSE messages)
33
+ """
34
+ self.event_publisher = event_publisher
35
+ self.active_tools = {} # tool_id -> tool info
36
+ self.tool_count = 0
37
+ self.thinking_buffer = [] # Buffer for thinking content
38
+ self.last_thinking_time = 0 # Debounce thinking events
39
+
40
+ def create_tool_hook(self) -> Callable:
41
+ """
42
+ Create tool execution hook for Agno agent.
43
+
44
+ This hook captures tool calls before execution and publishes events
45
+ for real-time streaming to the UI.
46
+
47
+ Returns:
48
+ Tool hook function compatible with Agno agent
49
+ """
50
+ def tool_hook(
51
+ name: str = None,
52
+ function_name: str = None,
53
+ function: Callable = None,
54
+ arguments: dict = None,
55
+ **kwargs,
56
+ ) -> Any:
57
+ """Hook called by Agno when tools are executed"""
58
+ tool_name = name or function_name or "unknown_tool"
59
+ tool_args = arguments or {}
60
+
61
+ # Generate unique tool ID
62
+ self.tool_count += 1
63
+ tool_id = f"{tool_name}_{self.tool_count}_{uuid.uuid4().hex[:8]}"
64
+
65
+ logger.info(
66
+ "planning_tool_started",
67
+ tool_name=tool_name,
68
+ tool_id=tool_id,
69
+ args_keys=list(tool_args.keys()) if isinstance(tool_args, dict) else None
70
+ )
71
+
72
+ # Track tool start time
73
+ start_time = time.time()
74
+ self.active_tools[tool_id] = {
75
+ "name": tool_name,
76
+ "start_time": start_time,
77
+ }
78
+
79
+ # Publish tool_started event
80
+ try:
81
+ logger.info("publishing_tool_started", tool_name=tool_name, tool_id=tool_id)
82
+ self.event_publisher({
83
+ "event": "tool_call",
84
+ "data": {
85
+ "tool_name": tool_name,
86
+ "tool_id": tool_id,
87
+ "tool_arguments": tool_args,
88
+ "timestamp": start_time,
89
+ }
90
+ })
91
+ logger.info("tool_started_event_published", tool_name=tool_name)
92
+ except Exception as e:
93
+ logger.error("failed_to_publish_tool_started", error=str(e))
94
+
95
+ # Execute the tool
96
+ result = None
97
+ error = None
98
+ status = "success"
99
+
100
+ try:
101
+ if function and callable(function):
102
+ result = function(**tool_args) if tool_args else function()
103
+ else:
104
+ raise ValueError(f"Function not callable: {function}")
105
+ except Exception as e:
106
+ error = e
107
+ status = "failed"
108
+ result = str(e)
109
+ logger.error(
110
+ "planning_tool_failed",
111
+ tool_name=tool_name,
112
+ tool_id=tool_id,
113
+ error=str(e)
114
+ )
115
+
116
+ # Calculate duration
117
+ end_time = time.time()
118
+ duration = end_time - start_time
119
+
120
+ # Format result for streaming with smart truncation
121
+ result_str = str(result) if result else ""
122
+ MAX_RESULT_SIZE = 5000 # Increased from 1000 to preserve more context
123
+
124
+ if len(result_str) > MAX_RESULT_SIZE:
125
+ # Smart truncation: keep start and end, truncate middle
126
+ start_chunk = result_str[:2500]
127
+ end_chunk = result_str[-2500:]
128
+ result_str = f"{start_chunk}\n... ({len(result_str)} chars total, middle section truncated) ...\n{end_chunk}"
129
+
130
+ # Publish tool_completed event
131
+ try:
132
+ self.event_publisher({
133
+ "event": "tool_result",
134
+ "data": {
135
+ "tool_name": tool_name,
136
+ "tool_id": tool_id,
137
+ "status": status,
138
+ "result": result_str,
139
+ "duration": duration,
140
+ "timestamp": end_time,
141
+ }
142
+ })
143
+ except Exception as e:
144
+ logger.error("failed_to_publish_tool_completed", error=str(e))
145
+
146
+ # Clean up tracking
147
+ self.active_tools.pop(tool_id, None)
148
+
149
+ logger.info(
150
+ "planning_tool_completed",
151
+ tool_name=tool_name,
152
+ tool_id=tool_id,
153
+ status=status,
154
+ duration=f"{duration:.2f}s"
155
+ )
156
+
157
+ # Return result or raise error
158
+ if error:
159
+ raise error
160
+ return result
161
+
162
+ return tool_hook
163
+
164
+ def create_thinking_hook(self) -> Callable:
165
+ """
166
+ Create thinking/reasoning hook for capturing LLM thoughts.
167
+
168
+ This hook captures LLM reasoning content as it's generated
169
+ and streams it in real-time to the UI with debouncing.
170
+
171
+ Returns:
172
+ Thinking hook function compatible with Agno agent
173
+ """
174
+ def thinking_hook(content: str, **kwargs):
175
+ """Hook called when LLM generates reasoning content"""
176
+ if not content or len(content.strip()) == 0:
177
+ return
178
+
179
+ # Debounce: Only publish every 200ms to avoid flooding
180
+ current_time = time.time()
181
+ if current_time - self.last_thinking_time < 0.2:
182
+ self.thinking_buffer.append(content)
183
+ return
184
+
185
+ # Flush buffer if we have content
186
+ if self.thinking_buffer:
187
+ content = " ".join(self.thinking_buffer) + " " + content
188
+ self.thinking_buffer = []
189
+
190
+ self.last_thinking_time = current_time
191
+
192
+ # Truncate very long thinking to avoid overwhelming the UI
193
+ MAX_THINKING_LENGTH = 500
194
+ if len(content) > MAX_THINKING_LENGTH:
195
+ content = content[:MAX_THINKING_LENGTH] + "..."
196
+
197
+ try:
198
+ logger.debug("publishing_thinking", length=len(content))
199
+ self.event_publisher({
200
+ "event": "thinking",
201
+ "data": {
202
+ "content": content,
203
+ "timestamp": current_time,
204
+ }
205
+ })
206
+ except Exception as e:
207
+ logger.error("failed_to_publish_thinking", error=str(e))
208
+
209
+ return thinking_hook
210
+
211
+ def get_active_tools_count(self) -> int:
212
+ """Get count of currently executing tools"""
213
+ return len(self.active_tools)
@@ -0,0 +1,424 @@
1
+ """
2
+ Task Planning Workflow Factory - Creates and configures planning workflows
3
+
4
+ This module provides factory functions to create Agno workflows for task planning:
5
+ - create_planning_workflow(): Standard 2-step workflow
6
+ - create_fast_workflow(): Single-step workflow for --local mode
7
+
8
+ The workflow orchestrates agents through defined steps with:
9
+ - Pre-fetched data caching
10
+ - Model configuration (Sonnet for reliability)
11
+ - Validation hooks
12
+ """
13
+
14
+ from typing import Optional, Dict, Any
15
+ import os
16
+ import structlog
17
+
18
+ from sqlalchemy.orm import Session
19
+ from agno.workflow import Workflow
20
+ from agno.models.litellm import LiteLLM
21
+
22
+ from .cache import get_cached_prefetch, set_cached_prefetch
23
+ from .agents import (
24
+ create_analysis_and_selection_agent,
25
+ create_plan_generation_agent,
26
+ create_fast_selection_agent,
27
+ )
28
+ from .hooks import validate_prefetch_context
29
+
30
+ logger = structlog.get_logger()
31
+
32
+
33
+ # ============================================================================
34
+ # Model Configuration
35
+ # ============================================================================
36
+
37
+ def get_litellm_config() -> tuple[str, str]:
38
+ """
39
+ Get LiteLLM API configuration from environment.
40
+
41
+ Returns:
42
+ Tuple of (api_url, api_key)
43
+
44
+ Raises:
45
+ ValueError: If LITELLM_API_KEY is not set
46
+ """
47
+ api_url = (
48
+ os.getenv("LITELLM_API_URL") or
49
+ os.getenv("LITELLM_API_BASE") or
50
+ "https://llm-proxy.kubiya.ai"
51
+ ).strip()
52
+
53
+ api_key = os.getenv("LITELLM_API_KEY", "").strip()
54
+ if not api_key:
55
+ raise ValueError("LITELLM_API_KEY environment variable not set")
56
+
57
+ return api_url, api_key
58
+
59
+
60
+ def create_model(
61
+ model_id: str,
62
+ api_url: str,
63
+ api_key: str,
64
+ timeout: int = 60
65
+ ) -> LiteLLM:
66
+ """
67
+ Create a LiteLLM model instance.
68
+
69
+ Args:
70
+ model_id: Model identifier (e.g., "kubiya/claude-sonnet-4")
71
+ api_url: LiteLLM API base URL
72
+ api_key: LiteLLM API key
73
+ timeout: Request timeout in seconds
74
+
75
+ Returns:
76
+ Configured LiteLLM instance
77
+ """
78
+ return LiteLLM(
79
+ id=f"openai/{model_id}",
80
+ api_base=api_url,
81
+ api_key=api_key,
82
+ request_params={"timeout": timeout}
83
+ )
84
+
85
+
86
+ # ============================================================================
87
+ # Pre-fetch Resources
88
+ # ============================================================================
89
+
90
+ def prefetch_resources(
91
+ db: Session,
92
+ organization_id: str,
93
+ api_token: str,
94
+ outer_context: Optional[Dict[str, Any]] = None
95
+ ) -> Dict[str, Any]:
96
+ """
97
+ Pre-fetch organization resources with caching.
98
+
99
+ Fetches top 20 of each resource type (agents, teams, environments, queues)
100
+ and caches for 5 minutes to avoid repeated DB calls.
101
+
102
+ Args:
103
+ db: Database session
104
+ organization_id: Organization ID
105
+ api_token: API token for authentication
106
+ outer_context: Optional pre-provided context (skips fetch)
107
+
108
+ Returns:
109
+ Dict with pre-fetched resources
110
+ """
111
+ # Check cache first
112
+ cached = get_cached_prefetch(organization_id)
113
+ if cached and not outer_context:
114
+ logger.info("using_cached_prefetch", organization_id=organization_id[:8])
115
+ return cached.copy()
116
+
117
+ # Validate any provided outer_context
118
+ if outer_context:
119
+ outer_context = validate_prefetch_context(outer_context)
120
+ # If outer_context has data, use it (don't override with DB fetch)
121
+ if outer_context.get("agents") or outer_context.get("teams"):
122
+ logger.info(
123
+ "using_provided_context",
124
+ agents=len(outer_context.get("agents", [])),
125
+ teams=len(outer_context.get("teams", []))
126
+ )
127
+ return outer_context
128
+
129
+ # Fetch fresh data from database
130
+ logger.info("pre_fetching_top_resources", organization_id=organization_id[:8])
131
+
132
+ from control_plane_api.app.lib.planning_tools.planning_service import PlanningService
133
+
134
+ planning_service = PlanningService(db, organization_id, api_token)
135
+ result = {}
136
+
137
+ try:
138
+ # Execute fetches SEQUENTIALLY to avoid SQLAlchemy session issues
139
+ agents_data = planning_service.list_agents(limit=20, status=None)
140
+ teams_data = planning_service.list_teams(limit=20, status=None)
141
+ # Don't filter by status - environments may have status="ready" or "active"
142
+ envs_data = planning_service.list_environments(status=None, limit=20)
143
+ queues_data = planning_service.list_worker_queues(limit=20)
144
+
145
+ result["agents"] = agents_data[:20]
146
+ result["teams"] = teams_data[:20]
147
+ result["environments"] = envs_data[:20]
148
+
149
+ # Sort worker queues by active_workers DESC
150
+ sorted_queues = sorted(
151
+ queues_data[:20],
152
+ key=lambda q: q.get("active_workers", 0),
153
+ reverse=True
154
+ )
155
+ result["worker_queues"] = sorted_queues
156
+
157
+ result["pre_fetch_note"] = "Top 20 resources. Use tools for more specific searches."
158
+
159
+ logger.info(
160
+ "pre_fetch_completed",
161
+ agents_count=len(result["agents"]),
162
+ teams_count=len(result["teams"]),
163
+ envs_count=len(result["environments"]),
164
+ queues_count=len(result["worker_queues"])
165
+ )
166
+
167
+ # Cache for subsequent requests
168
+ set_cached_prefetch(organization_id, result)
169
+
170
+ except Exception as e:
171
+ import traceback
172
+ logger.warning(
173
+ "pre_fetch_failed",
174
+ error=str(e),
175
+ traceback=traceback.format_exc()
176
+ )
177
+ # Return empty context - agent will use tools instead
178
+ result = {}
179
+
180
+ return result
181
+
182
+
183
+ # ============================================================================
184
+ # Main Workflow Factory
185
+ # ============================================================================
186
+
187
+ def create_planning_workflow(
188
+ db: Session,
189
+ organization_id: str,
190
+ api_token: str,
191
+ quick_mode: bool = False,
192
+ outer_context: Optional[Dict[str, Any]] = None
193
+ ) -> Workflow:
194
+ """
195
+ Create the task planning workflow.
196
+
197
+ Creates a 2-step workflow:
198
+ - Step 1: Analysis & Resource Selection (tool calling + structured output)
199
+ - Step 2: Plan Generation (structured output only)
200
+
201
+ Args:
202
+ db: Database session
203
+ organization_id: Organization ID (required)
204
+ api_token: API token (required)
205
+ quick_mode: Deprecated, kept for compatibility
206
+ outer_context: Optional pre-fetched context
207
+
208
+ Returns:
209
+ Configured Workflow instance
210
+
211
+ Raises:
212
+ ValueError: If required parameters are missing
213
+ """
214
+ if not organization_id:
215
+ raise ValueError("organization_id is required")
216
+ if not api_token:
217
+ raise ValueError("api_token is required")
218
+
219
+ # Get LiteLLM configuration
220
+ api_url, api_key = get_litellm_config()
221
+
222
+ # Get model IDs from environment
223
+ step1_model_id = os.getenv("STEP1_MODEL", "kubiya/claude-sonnet-4").strip()
224
+ step2_model_id = os.getenv("STEP2_MODEL", "kubiya/claude-sonnet-4").strip()
225
+
226
+ logger.info(
227
+ "model_configuration",
228
+ step1_model=step1_model_id,
229
+ step2_model=step2_model_id,
230
+ message="Using Sonnet for both steps (reliable structured output)"
231
+ )
232
+
233
+ # Create model instances
234
+ step1_model = create_model(step1_model_id, api_url, api_key, timeout=60)
235
+ step2_model = create_model(step2_model_id, api_url, api_key, timeout=60)
236
+
237
+ # Pre-fetch resources with caching
238
+ outer_context = prefetch_resources(db, organization_id, api_token, outer_context)
239
+
240
+ # Create planning toolkit for Step 1 tools
241
+ from control_plane_api.app.lib.planning_tools.agno_toolkit import PlanningToolkit
242
+ planning_toolkit = PlanningToolkit(db, organization_id, api_token)
243
+
244
+ # Create agents
245
+ step1_agent = create_analysis_and_selection_agent(
246
+ model=step1_model,
247
+ planning_toolkit=planning_toolkit,
248
+ outer_context=outer_context
249
+ )
250
+
251
+ step2_agent = create_plan_generation_agent(step2_model)
252
+
253
+ # Create 2-step workflow
254
+ workflow = Workflow(
255
+ name="Task Planning Workflow",
256
+ steps=[step1_agent, step2_agent],
257
+ description="2-step task planning: (1) Analysis & Selection, (2) Plan Generation"
258
+ )
259
+
260
+ # Store references for validation and runtime access
261
+ workflow._planning_toolkit = planning_toolkit
262
+ workflow._outer_context = outer_context
263
+ workflow._organization_id = organization_id
264
+
265
+ logger.info(
266
+ "planning_workflow_created",
267
+ steps=2,
268
+ pre_fetched_data=bool(outer_context)
269
+ )
270
+
271
+ return workflow
272
+
273
+
274
+ # ============================================================================
275
+ # Fast Workflow Factory (--local mode)
276
+ # ============================================================================
277
+
278
+ def create_fast_planning_workflow(
279
+ db: Session,
280
+ organization_id: str,
281
+ api_token: str,
282
+ outer_context: Dict[str, Any]
283
+ ) -> Workflow:
284
+ """
285
+ Create a fast single-step workflow for --local mode.
286
+
287
+ Uses pre-fetched data only (no API calls) for maximum speed.
288
+
289
+ Args:
290
+ db: Database session
291
+ organization_id: Organization ID
292
+ api_token: API token
293
+ outer_context: Pre-fetched context (required for fast mode)
294
+
295
+ Returns:
296
+ Configured single-step Workflow
297
+
298
+ Raises:
299
+ ValueError: If outer_context is empty
300
+ """
301
+ if not outer_context or (not outer_context.get("agents") and not outer_context.get("teams")):
302
+ raise ValueError("outer_context with agents or teams is required for fast mode")
303
+
304
+ # Get LiteLLM configuration
305
+ api_url, api_key = get_litellm_config()
306
+
307
+ # Use faster model for single-step
308
+ model_id = os.getenv("FAST_MODEL", "kubiya/claude-sonnet-4").strip()
309
+ model = create_model(model_id, api_url, api_key, timeout=30)
310
+
311
+ # Validate context
312
+ outer_context = validate_prefetch_context(outer_context)
313
+
314
+ # Create fast selection agent
315
+ fast_agent = create_fast_selection_agent(model, outer_context)
316
+
317
+ # Create single-step workflow
318
+ workflow = Workflow(
319
+ name="Fast Selection Workflow",
320
+ steps=[fast_agent],
321
+ description="Single-step fast selection for --local mode"
322
+ )
323
+
324
+ workflow._outer_context = outer_context
325
+ workflow._organization_id = organization_id
326
+
327
+ logger.info(
328
+ "fast_workflow_created",
329
+ agents_count=len(outer_context.get("agents", [])),
330
+ teams_count=len(outer_context.get("teams", []))
331
+ )
332
+
333
+ return workflow
334
+
335
+
336
+ # ============================================================================
337
+ # Workflow Configuration Helpers
338
+ # ============================================================================
339
+
340
+ class WorkflowConfig:
341
+ """Configuration for task planning workflows."""
342
+
343
+ def __init__(
344
+ self,
345
+ step1_model: str = "kubiya/claude-sonnet-4",
346
+ step2_model: str = "kubiya/claude-sonnet-4",
347
+ step1_timeout: int = 60,
348
+ step2_timeout: int = 60,
349
+ cache_ttl: int = 300,
350
+ prefer_runtime: str = "claude_code"
351
+ ):
352
+ self.step1_model = step1_model
353
+ self.step2_model = step2_model
354
+ self.step1_timeout = step1_timeout
355
+ self.step2_timeout = step2_timeout
356
+ self.cache_ttl = cache_ttl
357
+ self.prefer_runtime = prefer_runtime
358
+
359
+ @classmethod
360
+ def from_env(cls) -> 'WorkflowConfig':
361
+ """Create config from environment variables."""
362
+ return cls(
363
+ step1_model=os.getenv("STEP1_MODEL", "kubiya/claude-sonnet-4"),
364
+ step2_model=os.getenv("STEP2_MODEL", "kubiya/claude-sonnet-4"),
365
+ step1_timeout=int(os.getenv("STEP1_TIMEOUT", "60")),
366
+ step2_timeout=int(os.getenv("STEP2_TIMEOUT", "60")),
367
+ cache_ttl=int(os.getenv("PREFETCH_CACHE_TTL", "300")),
368
+ prefer_runtime=os.getenv("PREFER_RUNTIME", "claude_code")
369
+ )
370
+
371
+
372
+ def create_workflow_with_config(
373
+ db: Session,
374
+ organization_id: str,
375
+ api_token: str,
376
+ config: WorkflowConfig,
377
+ outer_context: Optional[Dict[str, Any]] = None
378
+ ) -> Workflow:
379
+ """
380
+ Create workflow with explicit configuration.
381
+
382
+ Args:
383
+ db: Database session
384
+ organization_id: Organization ID
385
+ api_token: API token
386
+ config: Workflow configuration
387
+ outer_context: Optional pre-fetched context
388
+
389
+ Returns:
390
+ Configured Workflow
391
+ """
392
+ api_url, api_key = get_litellm_config()
393
+
394
+ step1_model = create_model(config.step1_model, api_url, api_key, config.step1_timeout)
395
+ step2_model = create_model(config.step2_model, api_url, api_key, config.step2_timeout)
396
+
397
+ # Pre-fetch with config
398
+ outer_context = prefetch_resources(db, organization_id, api_token, outer_context)
399
+
400
+ # Add preferred runtime to context
401
+ if outer_context and not outer_context.get("preferred_runtime"):
402
+ outer_context["preferred_runtime"] = config.prefer_runtime
403
+
404
+ from control_plane_api.app.lib.planning_tools.agno_toolkit import PlanningToolkit
405
+ planning_toolkit = PlanningToolkit(db, organization_id, api_token)
406
+
407
+ step1_agent = create_analysis_and_selection_agent(
408
+ model=step1_model,
409
+ planning_toolkit=planning_toolkit,
410
+ outer_context=outer_context
411
+ )
412
+ step2_agent = create_plan_generation_agent(step2_model)
413
+
414
+ workflow = Workflow(
415
+ name="Task Planning Workflow",
416
+ steps=[step1_agent, step2_agent],
417
+ description="Configured 2-step task planning workflow"
418
+ )
419
+
420
+ workflow._planning_toolkit = planning_toolkit
421
+ workflow._outer_context = outer_context
422
+ workflow._config = config
423
+
424
+ return workflow