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,150 @@
1
+ """
2
+ Temporal Cloud Provisioning Service
3
+
4
+ This service handles provisioning Temporal Cloud namespaces via the
5
+ Vercel serverless function (api/provision-namespace.go).
6
+ """
7
+
8
+ import os
9
+ import httpx
10
+ import structlog
11
+ from typing import Dict, Any
12
+
13
+ logger = structlog.get_logger()
14
+
15
+
16
+ class TemporalCloudProvisioningService:
17
+ """Service for provisioning Temporal Cloud namespaces"""
18
+
19
+ def __init__(self):
20
+ # Get the Vercel function URL from environment
21
+ self.proxy_url = os.getenv("TEMPORAL_CLOUD_PROXY_URL")
22
+ if not self.proxy_url:
23
+ # If not set, use the Vercel production URL
24
+ self.proxy_url = "https://temporal-cloud-proxy.vercel.app/api/provision-namespace"
25
+
26
+ # Get the admin token for authorization (will be checked when actually used)
27
+ self.admin_token = os.getenv("TEMPORAL_CLOUD_ADMIN_TOKEN")
28
+
29
+ async def provision_namespace_for_organization(
30
+ self,
31
+ organization_id: str,
32
+ organization_name: str,
33
+ region: str = "aws-us-east-1",
34
+ retention_days: int = 7,
35
+ ) -> Dict[str, Any]:
36
+ """
37
+ Provision a Temporal Cloud namespace for an organization.
38
+
39
+ Args:
40
+ organization_id: The organization ID
41
+ organization_name: The organization name
42
+ region: The Temporal Cloud region (default: aws-us-east-1)
43
+ retention_days: Number of days to retain workflow history (default: 7)
44
+
45
+ Returns:
46
+ Dictionary with:
47
+ - success: Boolean indicating if provisioning succeeded
48
+ - namespace_name: The provisioned namespace name
49
+ - namespace: Dictionary with namespace details
50
+ - already_exists: Boolean indicating if namespace already existed
51
+ - error: Error message if failed
52
+ - timeout: Boolean indicating if operation timed out
53
+ """
54
+ logger.info(
55
+ "provisioning_temporal_namespace",
56
+ organization_id=organization_id,
57
+ organization_name=organization_name,
58
+ region=region,
59
+ )
60
+
61
+ # Check if token is available
62
+ if not self.admin_token:
63
+ error_msg = "TEMPORAL_CLOUD_ADMIN_TOKEN environment variable is required but not set"
64
+ logger.error("temporal_cloud_token_missing", organization_id=organization_id)
65
+ return {
66
+ "success": False,
67
+ "error": error_msg,
68
+ "timeout": False,
69
+ }
70
+
71
+ try:
72
+ # Prepare the request
73
+ payload = {
74
+ "organization_id": organization_id,
75
+ "organization_name": organization_name,
76
+ "region": region,
77
+ "retention_days": retention_days,
78
+ }
79
+
80
+ headers = {
81
+ "Authorization": f"Bearer {self.admin_token}",
82
+ "Content-Type": "application/json",
83
+ }
84
+
85
+ # Call the Vercel function
86
+ async with httpx.AsyncClient(timeout=60.0) as client:
87
+ response = await client.post(
88
+ self.proxy_url,
89
+ json=payload,
90
+ headers=headers,
91
+ )
92
+
93
+ # Check response status
94
+ if response.status_code == 200:
95
+ result = response.json()
96
+ logger.info(
97
+ "namespace_provisioned_successfully",
98
+ organization_id=organization_id,
99
+ namespace_name=result.get("namespace_name"),
100
+ already_exists=result.get("already_exists", False),
101
+ )
102
+ return result
103
+ else:
104
+ error_msg = response.text
105
+ logger.error(
106
+ "namespace_provisioning_failed",
107
+ organization_id=organization_id,
108
+ status_code=response.status_code,
109
+ error=error_msg,
110
+ )
111
+ return {
112
+ "success": False,
113
+ "error": f"HTTP {response.status_code}: {error_msg}",
114
+ "timeout": False,
115
+ }
116
+
117
+ except httpx.TimeoutException as e:
118
+ logger.warning(
119
+ "namespace_provisioning_timeout",
120
+ organization_id=organization_id,
121
+ error=str(e),
122
+ )
123
+ return {
124
+ "success": False,
125
+ "error": "Provisioning timed out",
126
+ "timeout": True,
127
+ }
128
+ except Exception as e:
129
+ logger.error(
130
+ "namespace_provisioning_exception",
131
+ organization_id=organization_id,
132
+ error=str(e),
133
+ )
134
+ return {
135
+ "success": False,
136
+ "error": str(e),
137
+ "timeout": False,
138
+ }
139
+
140
+
141
+ # Global instance
142
+ _provisioning_service = None
143
+
144
+
145
+ def get_provisioning_service() -> TemporalCloudProvisioningService:
146
+ """Get the provisioning service singleton"""
147
+ global _provisioning_service
148
+ if _provisioning_service is None:
149
+ _provisioning_service = TemporalCloudProvisioningService()
150
+ return _provisioning_service
@@ -0,0 +1,432 @@
1
+ """
2
+ Context Graph Skill - Memory and Knowledge Graph Tools
3
+
4
+ Provides agents with persistent memory capabilities using async job polling.
5
+ All operations are automatically scoped to environment-specific datasets.
6
+ """
7
+
8
+ from typing import Optional, Dict, Any
9
+ import httpx
10
+ import logging
11
+ import asyncio
12
+ import time
13
+ from agno.tools import Toolkit
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+ # Configuration
18
+ MAX_RETRIES = 3
19
+ INITIAL_RETRY_DELAY = 1.0
20
+ MAX_RETRY_DELAY = 10.0
21
+ POLL_INTERVAL = 2.0 # seconds between job status checks
22
+ MAX_POLL_TIME = 300.0 # 5 minutes max wait
23
+
24
+
25
+ def _is_retryable_error(response: httpx.Response | None, exception: Exception | None) -> bool:
26
+ """Determine if error is retryable (network/5xx) or permanent (4xx)."""
27
+ if exception and isinstance(exception, (httpx.ConnectError, httpx.TimeoutException, httpx.NetworkError)):
28
+ return True
29
+ if response and response.status_code >= 500:
30
+ return True
31
+ return False
32
+
33
+
34
+ async def _retry_with_backoff(func, max_retries: int = MAX_RETRIES):
35
+ """Retry with exponential backoff for transient failures only."""
36
+ last_exception = None
37
+ delay = INITIAL_RETRY_DELAY
38
+
39
+ for attempt in range(max_retries):
40
+ try:
41
+ result = await func()
42
+ if isinstance(result, httpx.Response) and not _is_retryable_error(result, None):
43
+ return result
44
+ return result
45
+ except Exception as e:
46
+ last_exception = e
47
+ if not _is_retryable_error(None, e):
48
+ logger.error(f"Non-retryable error", extra={"error": str(e)})
49
+ raise
50
+ if attempt < max_retries - 1:
51
+ logger.warning(f"Retry {attempt + 1}/{max_retries} in {delay}s", extra={"error": str(e)})
52
+ await asyncio.sleep(delay)
53
+ delay = min(delay * 2, MAX_RETRY_DELAY)
54
+ else:
55
+ logger.error(f"All retries failed", extra={"error": str(e)})
56
+
57
+ if last_exception:
58
+ raise last_exception
59
+
60
+
61
+ class ContextGraphSkill(Toolkit):
62
+ """
63
+ Context graph skill with async job polling and progress indicators.
64
+
65
+ Tools:
66
+ - store_memory: Store information (can wait for completion or return immediately)
67
+ - poll_job_status: Poll job status until complete
68
+ - recall_memory: Search stored memories
69
+ - semantic_search: Search knowledge graph
70
+ """
71
+
72
+ def __init__(
73
+ self,
74
+ graph_api_url: str,
75
+ api_key: str,
76
+ organization_id: str,
77
+ dataset_name: str,
78
+ auto_create_dataset: bool = True,
79
+ ):
80
+ super().__init__(name="context-graph-memory")
81
+
82
+ self.graph_api_url = graph_api_url.rstrip('/')
83
+ self.api_key = api_key
84
+ self.organization_id = organization_id
85
+ self.dataset_name = dataset_name
86
+ self.auto_create_dataset = auto_create_dataset
87
+ self._dataset_id = None
88
+
89
+ self.register(self.store_memory)
90
+ self.register(self.poll_job_status)
91
+ self.register(self.recall_memory)
92
+ self.register(self.semantic_search)
93
+
94
+ logger.info("Initialized ContextGraphSkill", extra={"dataset_name": dataset_name})
95
+
96
+ async def _get_or_create_dataset(self) -> str:
97
+ """Get or create dataset ID (cached)."""
98
+ if self._dataset_id:
99
+ return self._dataset_id
100
+
101
+ headers = {
102
+ "Authorization": f"Bearer {self.api_key}",
103
+ "X-Organization-ID": self.organization_id,
104
+ }
105
+
106
+ async with httpx.AsyncClient(timeout=60.0) as client:
107
+ response = await client.get(
108
+ f"{self.graph_api_url}/api/v1/graph/datasets",
109
+ headers=headers,
110
+ )
111
+
112
+ if response.status_code == 200:
113
+ response_data = response.json()
114
+ # Handle both list response (old API) and dict response (new API)
115
+ if isinstance(response_data, dict):
116
+ datasets = response_data.get("datasets", [])
117
+ else:
118
+ datasets = response_data
119
+
120
+ for ds in datasets:
121
+ if ds.get("name") == self.dataset_name:
122
+ self._dataset_id = ds["id"]
123
+ return self._dataset_id
124
+
125
+ if self.auto_create_dataset:
126
+ create_response = await client.post(
127
+ f"{self.graph_api_url}/api/v1/graph/datasets",
128
+ headers=headers,
129
+ json={
130
+ "name": self.dataset_name,
131
+ "description": f"Auto-created for: {self.dataset_name}",
132
+ "scope": "org",
133
+ },
134
+ )
135
+
136
+ if create_response.status_code in [200, 201]:
137
+ self._dataset_id = create_response.json()["id"]
138
+ return self._dataset_id
139
+ else:
140
+ raise Exception(f"Failed to create dataset: {create_response.status_code}")
141
+ else:
142
+ raise Exception(f"Dataset '{self.dataset_name}' not found")
143
+
144
+ async def _poll_job_status(
145
+ self,
146
+ job_id: str,
147
+ dataset_id: str,
148
+ headers: Dict[str, str],
149
+ ) -> tuple[Dict[str, Any], str]:
150
+ """
151
+ Poll job status until complete, building progress log.
152
+
153
+ Returns (final_job_status, progress_log) or raises exception on timeout/failure.
154
+ """
155
+ start_time = time.time()
156
+ last_progress = -1
157
+ progress_log = []
158
+ poll_count = 0
159
+
160
+ progress_log.append(f"⏳ Submitting memory job...")
161
+ logger.info("Starting job polling", extra={"job_id": job_id})
162
+
163
+ async with httpx.AsyncClient(timeout=30.0) as client:
164
+ while True:
165
+ elapsed = time.time() - start_time
166
+ poll_count += 1
167
+
168
+ if elapsed > MAX_POLL_TIME:
169
+ progress_log.append(f"✗ Timeout after {MAX_POLL_TIME}s ({poll_count} polls)")
170
+ raise Exception(f"Job timed out after {MAX_POLL_TIME}s")
171
+
172
+ # Poll job status
173
+ try:
174
+ response = await client.get(
175
+ f"{self.graph_api_url}/api/v1/graph/jobs/{job_id}",
176
+ headers=headers,
177
+ )
178
+
179
+ if response.status_code != 200:
180
+ # Fallback to memory/status endpoint
181
+ response = await client.get(
182
+ f"{self.graph_api_url}/api/v1/graph/memory/status/{job_id}",
183
+ headers=headers,
184
+ params={"dataset_id": dataset_id},
185
+ )
186
+
187
+ if response.status_code == 200:
188
+ job_status = response.json()
189
+ status = job_status.get("status", "unknown")
190
+ progress = job_status.get("progress", 0)
191
+
192
+ # Log progress changes
193
+ if progress != last_progress and progress > 0:
194
+ progress_log.append(f" ⚙️ Progress: {progress}%")
195
+ last_progress = progress
196
+ elif poll_count == 1:
197
+ progress_log.append(f" 🔄 Job submitted, polling for completion...")
198
+
199
+ # Check completion
200
+ if status == "completed":
201
+ progress_log.append(f"✅ Complete in {elapsed:.1f}s ({poll_count} polls)")
202
+ logger.info("Job completed", extra={"job_id": job_id, "elapsed": elapsed})
203
+ return job_status, "\n".join(progress_log)
204
+ elif status == "failed":
205
+ error = job_status.get("error", "Unknown error")
206
+ progress_log.append(f"✗ Job failed: {error}")
207
+ raise Exception(f"Job failed: {error}")
208
+
209
+ # Log occasional status updates
210
+ if poll_count % 5 == 0:
211
+ progress_log.append(f" ⏱️ Still processing... ({elapsed:.0f}s elapsed)")
212
+
213
+ except httpx.HTTPError as e:
214
+ logger.warning("Poll error", extra={"error": str(e), "poll_count": poll_count})
215
+ if poll_count == 1:
216
+ progress_log.append(f" ⚠️ Connection issue, retrying...")
217
+
218
+ # Wait before next poll
219
+ await asyncio.sleep(POLL_INTERVAL)
220
+
221
+ async def store_memory(
222
+ self,
223
+ context: str,
224
+ wait: bool = True,
225
+ metadata: Optional[Dict[str, Any]] = None,
226
+ ) -> str:
227
+ """
228
+ Store context in persistent memory.
229
+
230
+ Args:
231
+ context: Information to remember
232
+ wait: If True, wait for job to complete. If False, return immediately with job_id
233
+ metadata: Optional metadata
234
+
235
+ Returns:
236
+ If wait=True: Success message with memory ID (only after job completes)
237
+ If wait=False: Job ID for manual polling with poll_job_status tool
238
+ """
239
+ try:
240
+ dataset_id = await self._get_or_create_dataset()
241
+
242
+ headers = {
243
+ "Authorization": f"Bearer {self.api_key}",
244
+ "X-Organization-ID": self.organization_id,
245
+ }
246
+
247
+ context_dict = {"content": context}
248
+ if metadata:
249
+ context_dict["metadata"] = metadata
250
+
251
+ # Submit job (async mode - returns immediately)
252
+ async def _do_store():
253
+ async with httpx.AsyncClient(timeout=30.0) as client:
254
+ return await client.post(
255
+ f"{self.graph_api_url}/api/v1/graph/memory/store",
256
+ headers=headers,
257
+ json={
258
+ "context": context_dict,
259
+ "dataset_id": dataset_id,
260
+ "metadata": metadata,
261
+ "sync": False, # Async mode
262
+ },
263
+ )
264
+
265
+ response = await _retry_with_backoff(_do_store)
266
+
267
+ if response.status_code != 200:
268
+ error_msg = f"Failed to submit memory job: HTTP {response.status_code}"
269
+ logger.error(error_msg, extra={"response": response.text[:500]})
270
+ return f"✗ Error: {error_msg}"
271
+
272
+ result = response.json()
273
+ job_id = result.get("job_id")
274
+ memory_id = result.get("memory_id", "unknown")
275
+
276
+ if not job_id:
277
+ # Sync response (shouldn't happen with sync=False, but handle it)
278
+ return f"✓ Memory stored. Memory ID: {memory_id}"
279
+
280
+ # If wait=False, return job_id immediately for manual polling
281
+ if not wait:
282
+ return f"⏳ Memory storage job submitted.\n\nJob ID: {job_id}\nMemory ID: {memory_id}\n\n" \
283
+ f"Use poll_job_status(job_id=\"{job_id}\") to check progress."
284
+
285
+ # If wait=True, poll until complete (backend waits for Cognee pipeline)
286
+ try:
287
+ final_status, progress_log = await self._poll_job_status(job_id, dataset_id, headers)
288
+
289
+ logger.info("Memory stored and indexed", extra={"memory_id": memory_id, "job_id": job_id})
290
+
291
+ # Return detailed progress log with final status
292
+ return f"{progress_log}\n\n✅ Memory stored and indexed successfully!\nMemory ID: {memory_id}\n\n" \
293
+ f"The memory is now searchable and can be recalled."
294
+ except Exception as poll_error:
295
+ logger.error("Polling failed", extra={"error": str(poll_error), "job_id": job_id})
296
+ return f"✗ Job submitted (ID: {job_id[:8]}...) but polling failed: {str(poll_error)}\n" \
297
+ f"Use poll_job_status(job_id=\"{job_id}\") to check manually."
298
+
299
+ except Exception as e:
300
+ error_msg = f"Failed to store memory: {str(e)}"
301
+ logger.error(error_msg, extra={"error_type": type(e).__name__})
302
+ return f"✗ Error: {error_msg}"
303
+
304
+ async def poll_job_status(
305
+ self,
306
+ job_id: str,
307
+ ) -> str:
308
+ """
309
+ Poll a memory storage job until complete.
310
+
311
+ Use this after calling store_memory with wait=False to monitor job progress.
312
+
313
+ Args:
314
+ job_id: Job ID returned from store_memory
315
+
316
+ Returns:
317
+ Progress log and final status
318
+ """
319
+ try:
320
+ dataset_id = await self._get_or_create_dataset()
321
+
322
+ headers = {
323
+ "Authorization": f"Bearer {self.api_key}",
324
+ "X-Organization-ID": self.organization_id,
325
+ }
326
+
327
+ final_status, progress_log = await self._poll_job_status(job_id, dataset_id, headers)
328
+ memory_id = final_status.get("job_metadata", {}).get("memory_id", "unknown")
329
+
330
+ logger.info("Job polling complete", extra={"memory_id": memory_id, "job_id": job_id})
331
+
332
+ return f"{progress_log}\n\n✅ Job completed successfully!\nMemory ID: {memory_id}"
333
+
334
+ except Exception as e:
335
+ error_msg = f"Failed to poll job status: {str(e)}"
336
+ logger.error(error_msg, extra={"error_type": type(e).__name__, "job_id": job_id})
337
+ return f"✗ Error: {error_msg}"
338
+
339
+ async def recall_memory(
340
+ self,
341
+ query: str,
342
+ limit: int = 5,
343
+ ) -> str:
344
+ """Recall memories using semantic search."""
345
+ try:
346
+ dataset_id = await self._get_or_create_dataset()
347
+
348
+ headers = {
349
+ "Authorization": f"Bearer {self.api_key}",
350
+ "X-Organization-ID": self.organization_id,
351
+ }
352
+
353
+ async def _do_recall():
354
+ # Increased timeout to 300s for large datasets/slow Cognee queries
355
+ async with httpx.AsyncClient(timeout=300.0) as client:
356
+ return await client.post(
357
+ f"{self.graph_api_url}/api/v1/graph/memory/recall",
358
+ headers=headers,
359
+ json={"query": query, "dataset_id": dataset_id, "limit": limit},
360
+ )
361
+
362
+ response = await _retry_with_backoff(_do_recall)
363
+
364
+ if response.status_code == 200:
365
+ response_data = response.json()
366
+
367
+ # Handle both response formats
368
+ if isinstance(response_data, dict) and 'memories' in response_data:
369
+ results = response_data['memories']
370
+ elif isinstance(response_data, list):
371
+ results = response_data
372
+ else:
373
+ results = []
374
+
375
+ if not results:
376
+ return f"No memories found for: '{query}'"
377
+
378
+ formatted = f"Found {len(results)} memories:\n\n"
379
+ for i, item in enumerate(results, 1):
380
+ content = item.get('content', item.get('text', 'N/A'))
381
+ formatted += f"{i}. {content}\n"
382
+ if item.get('metadata'):
383
+ formatted += f" Metadata: {item['metadata']}\n"
384
+ if item.get('similarity_score'):
385
+ formatted += f" Relevance: {item['similarity_score']:.2f}\n"
386
+ formatted += "\n"
387
+
388
+ return formatted
389
+ else:
390
+ return f"✗ Error: HTTP {response.status_code}"
391
+
392
+ except Exception as e:
393
+ return f"✗ Error: {str(e)}"
394
+
395
+ async def semantic_search(self, query: str, limit: int = 10) -> str:
396
+ """Perform semantic search across knowledge graph."""
397
+ try:
398
+ dataset_id = await self._get_or_create_dataset()
399
+
400
+ headers = {
401
+ "Authorization": f"Bearer {self.api_key}",
402
+ "X-Organization-ID": self.organization_id,
403
+ }
404
+
405
+ async with httpx.AsyncClient(timeout=120.0) as client:
406
+ response = await client.post(
407
+ f"{self.graph_api_url}/api/v1/graph/nodes/search/semantic",
408
+ headers=headers,
409
+ json={"query": query, "filters": {"dataset_ids": [dataset_id]}, "limit": limit},
410
+ )
411
+
412
+ if response.status_code == 200:
413
+ results = response.json()
414
+ if not results:
415
+ return f"No results for: '{query}'"
416
+
417
+ formatted = f"Search results for '{query}':\n\n"
418
+ for i, item in enumerate(results, 1):
419
+ content = item.get('content', item.get('text', 'N/A'))
420
+ formatted += f"{i}. {content}\n"
421
+ if item.get('similarity_score'):
422
+ formatted += f" Relevance: {item['similarity_score']:.2f}\n"
423
+ if item.get('metadata'):
424
+ formatted += f" Metadata: {item['metadata']}\n"
425
+ formatted += "\n"
426
+
427
+ return formatted
428
+ else:
429
+ return f"✗ Error: HTTP {response.status_code}"
430
+
431
+ except Exception as e:
432
+ return f"✗ Error: {str(e)}"