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,274 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Manual test script to verify message deduplication works correctly.
4
+ Tests the session_service.deduplicate_messages method.
5
+ """
6
+
7
+ import sys
8
+ sys.path.insert(0, '/Users/shaked/projects/kubiya-stack/agent-control-plane/control_plane_api')
9
+
10
+ from worker.services.session_service import SessionService
11
+ from worker.control_plane_client import ControlPlaneClient
12
+
13
+
14
+ def test_deduplicate_messages():
15
+ """Test that deduplicate_messages correctly removes duplicates."""
16
+
17
+ # Create session service
18
+ control_plane = ControlPlaneClient(base_url="http://localhost:8000", api_key="test")
19
+ session_service = SessionService(control_plane)
20
+
21
+ # Test data with duplicates
22
+ messages_with_duplicates = [
23
+ {
24
+ "role": "user",
25
+ "content": "Hello",
26
+ "message_id": "exec_123_user_1",
27
+ "timestamp": "2025-12-08T10:00:00Z"
28
+ },
29
+ {
30
+ "role": "assistant",
31
+ "content": "Hi there!",
32
+ "message_id": "exec_123_assistant_1",
33
+ "timestamp": "2025-12-08T10:00:01Z"
34
+ },
35
+ {
36
+ "role": "user",
37
+ "content": "Hello", # DUPLICATE
38
+ "message_id": "exec_123_user_1", # SAME ID
39
+ "timestamp": "2025-12-08T10:00:00Z"
40
+ },
41
+ {
42
+ "role": "user",
43
+ "content": "How are you?",
44
+ "message_id": "exec_123_user_2",
45
+ "timestamp": "2025-12-08T10:00:02Z"
46
+ },
47
+ {
48
+ "role": "assistant",
49
+ "content": "Hi there!", # DUPLICATE
50
+ "message_id": "exec_123_assistant_1", # SAME ID
51
+ "timestamp": "2025-12-08T10:00:01Z"
52
+ },
53
+ ]
54
+
55
+ print(f"📝 Original messages: {len(messages_with_duplicates)}")
56
+ print()
57
+ for i, msg in enumerate(messages_with_duplicates):
58
+ print(f" {i+1}. [{msg['role']}] {msg['content'][:30]:<30} | ID: {msg['message_id']}")
59
+
60
+ # Deduplicate
61
+ deduplicated = session_service.deduplicate_messages(messages_with_duplicates)
62
+
63
+ print(f"\n✅ Deduplicated messages: {len(deduplicated)}")
64
+ print()
65
+ for i, msg in enumerate(deduplicated):
66
+ print(f" {i+1}. [{msg['role']}] {msg['content'][:30]:<30} | ID: {msg['message_id']}")
67
+
68
+ # Verify results
69
+ print(f"\n📊 Results:")
70
+ print(f" Original count: {len(messages_with_duplicates)}")
71
+ print(f" Deduplicated count: {len(deduplicated)}")
72
+ print(f" Removed: {len(messages_with_duplicates) - len(deduplicated)}")
73
+
74
+ # Check correctness
75
+ expected_count = 3 # Only 3 unique message_ids
76
+ if len(deduplicated) == expected_count:
77
+ print(f"\n✅ TEST PASSED: Deduplication working correctly!")
78
+ return True
79
+ else:
80
+ print(f"\n❌ TEST FAILED: Expected {expected_count} messages, got {len(deduplicated)}")
81
+ return False
82
+
83
+
84
+ def test_content_based_deduplication():
85
+ """Test that messages with same content but different IDs are deduplicated."""
86
+
87
+ control_plane = ControlPlaneClient(base_url="http://localhost:8000", api_key="test")
88
+ session_service = SessionService(control_plane)
89
+
90
+ # Test data with duplicate content but different message_ids
91
+ messages = [
92
+ {
93
+ "role": "assistant",
94
+ "content": "Hello, how can I help you today?",
95
+ "message_id": "exec_123_assistant_1",
96
+ "timestamp": "2025-12-12T10:00:00Z"
97
+ },
98
+ {
99
+ "role": "assistant",
100
+ "content": "Hello, how can I help you today?", # SAME CONTENT
101
+ "message_id": "exec_123_1733990400123456", # DIFFERENT ID (timestamp-based)
102
+ "timestamp": "2025-12-12T10:00:01Z" # Close timestamp (1 second apart)
103
+ },
104
+ {
105
+ "role": "user",
106
+ "content": "Can you help me with Python?",
107
+ "message_id": "exec_123_user_1",
108
+ "timestamp": "2025-12-12T10:00:02Z"
109
+ },
110
+ {
111
+ "role": "assistant",
112
+ "content": "Sure! I'd be happy to help.",
113
+ "message_id": "exec_123_assistant_2",
114
+ "timestamp": "2025-12-12T10:00:03Z"
115
+ },
116
+ {
117
+ "role": "assistant",
118
+ "content": "Sure! I'd be happy to help.", # SAME CONTENT
119
+ "message_id": "exec_123_1733990403789012", # DIFFERENT ID
120
+ "timestamp": "2025-12-12T10:00:04Z" # Close timestamp (1 second apart)
121
+ },
122
+ ]
123
+
124
+ print(f"\n📝 Testing content-based deduplication...")
125
+ print(f" Original: {len(messages)} messages")
126
+ print()
127
+ for i, msg in enumerate(messages):
128
+ print(f" {i+1}. [{msg['role']:<9}] {msg['content'][:40]:<40} | ID: {msg['message_id'][:25]}")
129
+
130
+ # Deduplicate
131
+ deduplicated = session_service.deduplicate_messages(messages)
132
+
133
+ print(f"\n✅ Deduplicated messages: {len(deduplicated)}")
134
+ print()
135
+ for i, msg in enumerate(deduplicated):
136
+ print(f" {i+1}. [{msg['role']:<9}] {msg['content'][:40]:<40} | ID: {msg['message_id'][:25]}")
137
+
138
+ # Verify results
139
+ print(f"\n📊 Results:")
140
+ print(f" Original count: {len(messages)}")
141
+ print(f" Deduplicated count: {len(deduplicated)}")
142
+ print(f" Removed: {len(messages) - len(deduplicated)}")
143
+
144
+ # Should have 3 messages (2 duplicates removed)
145
+ expected_count = 3
146
+ if len(deduplicated) == expected_count:
147
+ print(f"\n✅ TEST PASSED: Content-based deduplication working!")
148
+ return True
149
+ else:
150
+ print(f"\n❌ TEST FAILED: Expected {expected_count} messages, got {len(deduplicated)}")
151
+ return False
152
+
153
+
154
+ def test_content_deduplication_with_distant_timestamps():
155
+ """Test that messages with same content but distant timestamps are NOT deduplicated."""
156
+
157
+ control_plane = ControlPlaneClient(base_url="http://localhost:8000", api_key="test")
158
+ session_service = SessionService(control_plane)
159
+
160
+ # Test data with duplicate content but timestamps > 5 seconds apart
161
+ messages = [
162
+ {
163
+ "role": "assistant",
164
+ "content": "Let me help you with that.",
165
+ "message_id": "exec_123_assistant_1",
166
+ "timestamp": "2025-12-12T10:00:00Z"
167
+ },
168
+ {
169
+ "role": "user",
170
+ "content": "Thanks!",
171
+ "message_id": "exec_123_user_1",
172
+ "timestamp": "2025-12-12T10:00:03Z"
173
+ },
174
+ {
175
+ "role": "assistant",
176
+ "content": "Let me help you with that.", # SAME CONTENT
177
+ "message_id": "exec_123_assistant_2",
178
+ "timestamp": "2025-12-12T10:00:10Z" # 10 seconds later (> 5 second threshold)
179
+ },
180
+ ]
181
+
182
+ print(f"\n📝 Testing content deduplication with distant timestamps...")
183
+ print(f" Original: {len(messages)} messages")
184
+ print()
185
+ for i, msg in enumerate(messages):
186
+ print(f" {i+1}. [{msg['role']:<9}] {msg['content'][:40]:<40} | Time: {msg['timestamp'][-9:]}")
187
+
188
+ # Deduplicate
189
+ deduplicated = session_service.deduplicate_messages(messages)
190
+
191
+ print(f"\n✅ Deduplicated messages: {len(deduplicated)}")
192
+ print()
193
+ for i, msg in enumerate(deduplicated):
194
+ print(f" {i+1}. [{msg['role']:<9}] {msg['content'][:40]:<40} | Time: {msg['timestamp'][-9:]}")
195
+
196
+ # Verify results - should keep all 3 messages (timestamps too far apart)
197
+ print(f"\n📊 Results:")
198
+ print(f" Original count: {len(messages)}")
199
+ print(f" Deduplicated count: {len(deduplicated)}")
200
+ print(f" Removed: {len(messages) - len(deduplicated)}")
201
+
202
+ # Should have 3 messages (no duplicates removed due to timestamp distance)
203
+ expected_count = 3
204
+ if len(deduplicated) == expected_count:
205
+ print(f"\n✅ TEST PASSED: Distant timestamps not incorrectly deduplicated!")
206
+ return True
207
+ else:
208
+ print(f"\n❌ TEST FAILED: Expected {expected_count} messages, got {len(deduplicated)}")
209
+ return False
210
+
211
+
212
+ def test_messages_without_ids():
213
+ """Test that messages without IDs are handled correctly."""
214
+
215
+ control_plane = ControlPlaneClient(base_url="http://localhost:8000", api_key="test")
216
+ session_service = SessionService(control_plane)
217
+
218
+ messages = [
219
+ {
220
+ "role": "user",
221
+ "content": "Message 1",
222
+ # NO message_id
223
+ "timestamp": "2025-12-08T10:00:00Z"
224
+ },
225
+ {
226
+ "role": "assistant",
227
+ "content": "Response 1",
228
+ "message_id": "exec_123_assistant_1",
229
+ "timestamp": "2025-12-08T10:00:01Z"
230
+ },
231
+ {
232
+ "role": "user",
233
+ "content": "Message 2",
234
+ # NO message_id
235
+ "timestamp": "2025-12-08T10:00:02Z"
236
+ },
237
+ ]
238
+
239
+ print(f"\n📝 Testing messages without IDs...")
240
+ print(f" Original: {len(messages)} messages")
241
+
242
+ deduplicated = session_service.deduplicate_messages(messages)
243
+
244
+ print(f" Deduplicated: {len(deduplicated)} messages")
245
+
246
+ # Messages without IDs should be kept (they get warning but are included)
247
+ if len(deduplicated) == len(messages):
248
+ print(f"✅ Messages without IDs handled correctly (kept with warning)")
249
+ return True
250
+ else:
251
+ print(f"❌ Messages without IDs were incorrectly filtered")
252
+ return False
253
+
254
+
255
+ if __name__ == "__main__":
256
+ print("=" * 70)
257
+ print("MESSAGE DEDUPLICATION TEST")
258
+ print("=" * 70)
259
+ print()
260
+
261
+ # Run tests
262
+ test1_passed = test_deduplicate_messages()
263
+ test2_passed = test_content_based_deduplication()
264
+ test3_passed = test_content_deduplication_with_distant_timestamps()
265
+ test4_passed = test_messages_without_ids()
266
+
267
+ print()
268
+ print("=" * 70)
269
+ if test1_passed and test2_passed and test3_passed and test4_passed:
270
+ print("✅ ALL TESTS PASSED")
271
+ sys.exit(0)
272
+ else:
273
+ print("❌ SOME TESTS FAILED")
274
+ sys.exit(1)
@@ -0,0 +1,309 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ End-to-end integration test for executor deduplication.
4
+ Tests the full flow: session_history + new_messages → deduplication → persistence
5
+ """
6
+
7
+ import sys
8
+ sys.path.insert(0, '/Users/shaked/projects/kubiya-stack/agent-control-plane/control_plane_api')
9
+
10
+ from worker.services.session_service import SessionService
11
+ from worker.control_plane_client import ControlPlaneClient
12
+
13
+
14
+ def test_executor_deduplication_flow():
15
+ """
16
+ Simulate the executor flow where messages are combined and deduplicated.
17
+ This tests the ACTUAL code path used by executors.
18
+ """
19
+ print("=" * 70)
20
+ print("EXECUTOR DEDUPLICATION - END-TO-END TEST")
21
+ print("=" * 70)
22
+ print()
23
+
24
+ # Create session service
25
+ control_plane = ControlPlaneClient(base_url="http://localhost:8000", api_key="test")
26
+ session_service = SessionService(control_plane)
27
+
28
+ print("📋 Simulating Executor Flow:")
29
+ print(" 1. Load session_history (previous turns)")
30
+ print(" 2. Add new_messages (current turn)")
31
+ print(" 3. Add tool_messages (from streaming)")
32
+ print(" 4. Combine all messages")
33
+ print(" 5. Deduplicate using session_service.deduplicate_messages()")
34
+ print()
35
+
36
+ # Simulate session_history (messages from previous turns)
37
+ session_history = [
38
+ {
39
+ "role": "user",
40
+ "content": "Hello",
41
+ "message_id": "exec_123_user_1",
42
+ "timestamp": "2025-12-12T10:00:00Z"
43
+ },
44
+ {
45
+ "role": "assistant",
46
+ "content": "Hi! How can I help you?",
47
+ "message_id": "exec_123_assistant_1",
48
+ "timestamp": "2025-12-12T10:00:01Z"
49
+ },
50
+ ]
51
+
52
+ # Simulate new_messages (current turn)
53
+ # NOTE: These might accidentally duplicate content from session_history due to bugs
54
+ new_messages = [
55
+ {
56
+ "role": "user",
57
+ "content": "Can you help with Python?",
58
+ "message_id": "exec_123_user_2",
59
+ "timestamp": "2025-12-12T10:00:05Z"
60
+ },
61
+ {
62
+ "role": "assistant",
63
+ "content": "Sure! I'd be happy to help with Python.",
64
+ "message_id": "exec_123_assistant_2",
65
+ "timestamp": "2025-12-12T10:00:06Z"
66
+ },
67
+ # DUPLICATE: Same content as assistant_2 but different message_id (timing issue)
68
+ {
69
+ "role": "assistant",
70
+ "content": "Sure! I'd be happy to help with Python.",
71
+ "message_id": "exec_123_1733990406789012", # Timestamp-based ID
72
+ "timestamp": "2025-12-12T10:00:07Z" # 1 second later
73
+ },
74
+ ]
75
+
76
+ # Simulate tool_messages (from streaming helper)
77
+ tool_messages = [
78
+ {
79
+ "role": "system",
80
+ "tool_name": "python_repl",
81
+ "tool_output": "Executed successfully",
82
+ "message_id": "exec_123_tool_python_1",
83
+ "timestamp": "2025-12-12T10:00:08Z"
84
+ },
85
+ ]
86
+
87
+ print("📊 Message Counts:")
88
+ print(f" - session_history: {len(session_history)} messages")
89
+ print(f" - new_messages: {len(new_messages)} messages")
90
+ print(f" - tool_messages: {len(tool_messages)} messages")
91
+ print()
92
+
93
+ # STEP 1: Combine messages (what executors do)
94
+ complete_session = session_history + new_messages + tool_messages
95
+ print(f"🔗 Combined messages: {len(complete_session)} total")
96
+ print()
97
+
98
+ # STEP 2: Deduplicate (using enhanced session_service method)
99
+ print("🧹 Deduplicating messages...")
100
+ original_count = len(complete_session)
101
+ deduplicated_session = session_service.deduplicate_messages(complete_session)
102
+
103
+ print()
104
+ print("📋 Deduplicated messages:")
105
+ for i, msg in enumerate(deduplicated_session):
106
+ role = msg.get("role", "unknown")
107
+ content = msg.get("content", msg.get("tool_name", ""))
108
+ msg_id = msg.get("message_id", "no_id")
109
+ timestamp = msg.get("timestamp", "")[-9:]
110
+ print(f" {i+1}. [{role:<9}] {content[:40]:<40} | {timestamp}")
111
+
112
+ print()
113
+ print("📊 Final Results:")
114
+ print(f" Original count: {original_count}")
115
+ print(f" Deduplicated count: {len(deduplicated_session)}")
116
+ print(f" Duplicates removed: {original_count - len(deduplicated_session)}")
117
+ print()
118
+
119
+ # Verify results
120
+ expected_count = 5 # user_1, assistant_1, user_2, assistant_2 (deduplicated), tool_1
121
+ if len(deduplicated_session) == expected_count:
122
+ print("✅ TEST PASSED: Executor deduplication working correctly!")
123
+ print(f" Expected {expected_count} messages, got {len(deduplicated_session)}")
124
+ return True
125
+ else:
126
+ print(f"❌ TEST FAILED: Expected {expected_count} messages, got {len(deduplicated_session)}")
127
+ return False
128
+
129
+
130
+ def test_multiple_content_duplicates():
131
+ """Test deduplication with multiple content duplicates (stress test)."""
132
+ print()
133
+ print("=" * 70)
134
+ print("STRESS TEST: Multiple Content Duplicates")
135
+ print("=" * 70)
136
+ print()
137
+
138
+ control_plane = ControlPlaneClient(base_url="http://localhost:8000", api_key="test")
139
+ session_service = SessionService(control_plane)
140
+
141
+ # Create messages with many duplicates
142
+ messages = [
143
+ # Original message
144
+ {
145
+ "role": "assistant",
146
+ "content": "Let me help you with that task.",
147
+ "message_id": "exec_456_assistant_1",
148
+ "timestamp": "2025-12-12T10:00:00Z"
149
+ },
150
+ # Duplicate 1 (different ID, 1 second later)
151
+ {
152
+ "role": "assistant",
153
+ "content": "Let me help you with that task.",
154
+ "message_id": "exec_456_1733990400123456",
155
+ "timestamp": "2025-12-12T10:00:01Z"
156
+ },
157
+ # Duplicate 2 (different ID, 2 seconds later)
158
+ {
159
+ "role": "assistant",
160
+ "content": "Let me help you with that task.",
161
+ "message_id": "exec_456_1733990400234567",
162
+ "timestamp": "2025-12-12T10:00:02Z"
163
+ },
164
+ # Different message
165
+ {
166
+ "role": "user",
167
+ "content": "Thanks!",
168
+ "message_id": "exec_456_user_1",
169
+ "timestamp": "2025-12-12T10:00:05Z"
170
+ },
171
+ # Another original
172
+ {
173
+ "role": "assistant",
174
+ "content": "You're welcome!",
175
+ "message_id": "exec_456_assistant_2",
176
+ "timestamp": "2025-12-12T10:00:06Z"
177
+ },
178
+ # Duplicate of "You're welcome!" (different ID, 1 second later)
179
+ {
180
+ "role": "assistant",
181
+ "content": "You're welcome!",
182
+ "message_id": "exec_456_1733990406345678",
183
+ "timestamp": "2025-12-12T10:00:07Z"
184
+ },
185
+ ]
186
+
187
+ print(f"📝 Testing with {len(messages)} messages (4 expected duplicates)...")
188
+ print()
189
+
190
+ deduplicated = session_service.deduplicate_messages(messages)
191
+
192
+ print(f"✅ Deduplicated to {len(deduplicated)} messages")
193
+ print()
194
+ for i, msg in enumerate(deduplicated):
195
+ content = msg.get("content", "")[:40]
196
+ msg_id = msg.get("message_id", "")[:30]
197
+ print(f" {i+1}. [{msg['role']:<9}] {content:<40} | {msg_id}")
198
+
199
+ # Should have 3 unique messages (2 assistant + 1 user)
200
+ expected = 3
201
+ duplicates_removed = len(messages) - len(deduplicated)
202
+
203
+ print()
204
+ print(f"📊 Results:")
205
+ print(f" Original: {len(messages)}")
206
+ print(f" Deduplicated: {len(deduplicated)}")
207
+ print(f" Removed: {duplicates_removed}")
208
+ print()
209
+
210
+ if len(deduplicated) == expected and duplicates_removed == 3:
211
+ print(f"✅ STRESS TEST PASSED: Correctly removed {duplicates_removed} content duplicates!")
212
+ return True
213
+ else:
214
+ print(f"❌ STRESS TEST FAILED: Expected {expected} messages, got {len(deduplicated)}")
215
+ return False
216
+
217
+
218
+ def test_mixed_duplicates():
219
+ """Test with both message_id duplicates AND content duplicates."""
220
+ print()
221
+ print("=" * 70)
222
+ print("MIXED TEST: Both ID and Content Duplicates")
223
+ print("=" * 70)
224
+ print()
225
+
226
+ control_plane = ControlPlaneClient(base_url="http://localhost:8000", api_key="test")
227
+ session_service = SessionService(control_plane)
228
+
229
+ messages = [
230
+ # Original
231
+ {
232
+ "role": "assistant",
233
+ "content": "Hello there!",
234
+ "message_id": "exec_789_assistant_1",
235
+ "timestamp": "2025-12-12T10:00:00Z"
236
+ },
237
+ # ID duplicate (same message_id)
238
+ {
239
+ "role": "assistant",
240
+ "content": "Hello there!",
241
+ "message_id": "exec_789_assistant_1", # SAME ID
242
+ "timestamp": "2025-12-12T10:00:00Z"
243
+ },
244
+ # Content duplicate (different ID)
245
+ {
246
+ "role": "assistant",
247
+ "content": "Hello there!",
248
+ "message_id": "exec_789_1733990400111111", # DIFFERENT ID
249
+ "timestamp": "2025-12-12T10:00:01Z"
250
+ },
251
+ # Unique message
252
+ {
253
+ "role": "user",
254
+ "content": "Hi!",
255
+ "message_id": "exec_789_user_1",
256
+ "timestamp": "2025-12-12T10:00:05Z"
257
+ },
258
+ ]
259
+
260
+ print(f"📝 Testing with {len(messages)} messages:")
261
+ print(" - 1 unique assistant message")
262
+ print(" - 1 ID duplicate (same message_id)")
263
+ print(" - 1 content duplicate (different message_id)")
264
+ print(" - 1 unique user message")
265
+ print()
266
+
267
+ deduplicated = session_service.deduplicate_messages(messages)
268
+
269
+ print(f"✅ Deduplicated to {len(deduplicated)} messages")
270
+ print()
271
+
272
+ # Should have 2 messages (1 assistant + 1 user)
273
+ expected = 2
274
+ duplicates_removed = len(messages) - len(deduplicated)
275
+
276
+ print(f"📊 Results:")
277
+ print(f" Original: {len(messages)}")
278
+ print(f" Deduplicated: {len(deduplicated)}")
279
+ print(f" Removed: {duplicates_removed}")
280
+ print()
281
+
282
+ if len(deduplicated) == expected and duplicates_removed == 2:
283
+ print(f"✅ MIXED TEST PASSED: Correctly handled both ID and content duplicates!")
284
+ return True
285
+ else:
286
+ print(f"❌ MIXED TEST FAILED: Expected {expected} messages, got {len(deduplicated)}")
287
+ return False
288
+
289
+
290
+ if __name__ == "__main__":
291
+ # Run all tests
292
+ test1_passed = test_executor_deduplication_flow()
293
+ test2_passed = test_multiple_content_duplicates()
294
+ test3_passed = test_mixed_duplicates()
295
+
296
+ print()
297
+ print("=" * 70)
298
+ if test1_passed and test2_passed and test3_passed:
299
+ print("✅ ALL END-TO-END TESTS PASSED")
300
+ print()
301
+ print("🎉 Executor deduplication is working correctly!")
302
+ print(" - Content-based deduplication: ✅")
303
+ print(" - ID-based deduplication: ✅")
304
+ print(" - Mixed duplicates: ✅")
305
+ print(" - Stress test: ✅")
306
+ sys.exit(0)
307
+ else:
308
+ print("❌ SOME TESTS FAILED")
309
+ sys.exit(1)