kubiya-control-plane-api 0.1.0__py3-none-any.whl → 0.3.4__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.

Potentially problematic release.


This version of kubiya-control-plane-api might be problematic. Click here for more details.

Files changed (185) hide show
  1. control_plane_api/README.md +266 -0
  2. control_plane_api/__init__.py +0 -0
  3. control_plane_api/__version__.py +1 -0
  4. control_plane_api/alembic/README +1 -0
  5. control_plane_api/alembic/env.py +98 -0
  6. control_plane_api/alembic/script.py.mako +28 -0
  7. control_plane_api/alembic/versions/1382bec74309_initial_migration_with_all_models.py +251 -0
  8. control_plane_api/alembic/versions/1f54bc2a37e3_add_analytics_tables.py +162 -0
  9. control_plane_api/alembic/versions/2e4cb136dc10_rename_toolset_ids_to_skill_ids_in_teams.py +30 -0
  10. control_plane_api/alembic/versions/31cd69a644ce_add_skill_templates_table.py +28 -0
  11. control_plane_api/alembic/versions/89e127caa47d_add_jobs_and_job_executions_tables.py +161 -0
  12. control_plane_api/alembic/versions/add_llm_models_table.py +51 -0
  13. control_plane_api/alembic/versions/b0e10697f212_add_runtime_column_to_teams_simple.py +42 -0
  14. control_plane_api/alembic/versions/ce43b24b63bf_add_execution_trigger_source_and_fix_.py +155 -0
  15. control_plane_api/alembic/versions/d4eaf16e3f8d_rename_toolsets_to_skills.py +84 -0
  16. control_plane_api/alembic/versions/efa2dc427da1_rename_metadata_to_custom_metadata.py +32 -0
  17. control_plane_api/alembic/versions/f973b431d1ce_add_workflow_executor_to_skill_types.py +44 -0
  18. control_plane_api/alembic.ini +148 -0
  19. control_plane_api/api/index.py +12 -0
  20. control_plane_api/app/__init__.py +11 -0
  21. control_plane_api/app/activities/__init__.py +20 -0
  22. control_plane_api/app/activities/agent_activities.py +379 -0
  23. control_plane_api/app/activities/team_activities.py +410 -0
  24. control_plane_api/app/activities/temporal_cloud_activities.py +577 -0
  25. control_plane_api/app/config/__init__.py +35 -0
  26. control_plane_api/app/config/api_config.py +354 -0
  27. control_plane_api/app/config/model_pricing.py +318 -0
  28. control_plane_api/app/config.py +95 -0
  29. control_plane_api/app/database.py +135 -0
  30. control_plane_api/app/exceptions.py +408 -0
  31. control_plane_api/app/lib/__init__.py +11 -0
  32. control_plane_api/app/lib/job_executor.py +312 -0
  33. control_plane_api/app/lib/kubiya_client.py +235 -0
  34. control_plane_api/app/lib/litellm_pricing.py +166 -0
  35. control_plane_api/app/lib/planning_tools/__init__.py +22 -0
  36. control_plane_api/app/lib/planning_tools/agents.py +155 -0
  37. control_plane_api/app/lib/planning_tools/base.py +189 -0
  38. control_plane_api/app/lib/planning_tools/environments.py +214 -0
  39. control_plane_api/app/lib/planning_tools/resources.py +240 -0
  40. control_plane_api/app/lib/planning_tools/teams.py +198 -0
  41. control_plane_api/app/lib/policy_enforcer_client.py +939 -0
  42. control_plane_api/app/lib/redis_client.py +436 -0
  43. control_plane_api/app/lib/supabase.py +71 -0
  44. control_plane_api/app/lib/temporal_client.py +138 -0
  45. control_plane_api/app/lib/validation/__init__.py +20 -0
  46. control_plane_api/app/lib/validation/runtime_validation.py +287 -0
  47. control_plane_api/app/main.py +128 -0
  48. control_plane_api/app/middleware/__init__.py +8 -0
  49. control_plane_api/app/middleware/auth.py +513 -0
  50. control_plane_api/app/middleware/exception_handler.py +267 -0
  51. control_plane_api/app/middleware/rate_limiting.py +384 -0
  52. control_plane_api/app/middleware/request_id.py +202 -0
  53. control_plane_api/app/models/__init__.py +27 -0
  54. control_plane_api/app/models/agent.py +79 -0
  55. control_plane_api/app/models/analytics.py +206 -0
  56. control_plane_api/app/models/associations.py +81 -0
  57. control_plane_api/app/models/environment.py +63 -0
  58. control_plane_api/app/models/execution.py +93 -0
  59. control_plane_api/app/models/job.py +179 -0
  60. control_plane_api/app/models/llm_model.py +75 -0
  61. control_plane_api/app/models/presence.py +49 -0
  62. control_plane_api/app/models/project.py +47 -0
  63. control_plane_api/app/models/session.py +38 -0
  64. control_plane_api/app/models/team.py +66 -0
  65. control_plane_api/app/models/workflow.py +55 -0
  66. control_plane_api/app/policies/README.md +121 -0
  67. control_plane_api/app/policies/approved_users.rego +62 -0
  68. control_plane_api/app/policies/business_hours.rego +51 -0
  69. control_plane_api/app/policies/rate_limiting.rego +100 -0
  70. control_plane_api/app/policies/tool_restrictions.rego +86 -0
  71. control_plane_api/app/routers/__init__.py +4 -0
  72. control_plane_api/app/routers/agents.py +364 -0
  73. control_plane_api/app/routers/agents_v2.py +1260 -0
  74. control_plane_api/app/routers/analytics.py +1014 -0
  75. control_plane_api/app/routers/context_manager.py +562 -0
  76. control_plane_api/app/routers/environment_context.py +270 -0
  77. control_plane_api/app/routers/environments.py +715 -0
  78. control_plane_api/app/routers/execution_environment.py +517 -0
  79. control_plane_api/app/routers/executions.py +1911 -0
  80. control_plane_api/app/routers/health.py +92 -0
  81. control_plane_api/app/routers/health_v2.py +326 -0
  82. control_plane_api/app/routers/integrations.py +274 -0
  83. control_plane_api/app/routers/jobs.py +1344 -0
  84. control_plane_api/app/routers/models.py +82 -0
  85. control_plane_api/app/routers/models_v2.py +361 -0
  86. control_plane_api/app/routers/policies.py +639 -0
  87. control_plane_api/app/routers/presence.py +234 -0
  88. control_plane_api/app/routers/projects.py +902 -0
  89. control_plane_api/app/routers/runners.py +379 -0
  90. control_plane_api/app/routers/runtimes.py +172 -0
  91. control_plane_api/app/routers/secrets.py +155 -0
  92. control_plane_api/app/routers/skills.py +1001 -0
  93. control_plane_api/app/routers/skills_definitions.py +140 -0
  94. control_plane_api/app/routers/task_planning.py +1256 -0
  95. control_plane_api/app/routers/task_queues.py +654 -0
  96. control_plane_api/app/routers/team_context.py +270 -0
  97. control_plane_api/app/routers/teams.py +1400 -0
  98. control_plane_api/app/routers/worker_queues.py +1545 -0
  99. control_plane_api/app/routers/workers.py +935 -0
  100. control_plane_api/app/routers/workflows.py +204 -0
  101. control_plane_api/app/runtimes/__init__.py +6 -0
  102. control_plane_api/app/runtimes/validation.py +344 -0
  103. control_plane_api/app/schemas/job_schemas.py +295 -0
  104. control_plane_api/app/services/__init__.py +1 -0
  105. control_plane_api/app/services/agno_service.py +619 -0
  106. control_plane_api/app/services/litellm_service.py +190 -0
  107. control_plane_api/app/services/policy_service.py +525 -0
  108. control_plane_api/app/services/temporal_cloud_provisioning.py +150 -0
  109. control_plane_api/app/skills/__init__.py +44 -0
  110. control_plane_api/app/skills/base.py +229 -0
  111. control_plane_api/app/skills/business_intelligence.py +189 -0
  112. control_plane_api/app/skills/data_visualization.py +154 -0
  113. control_plane_api/app/skills/docker.py +104 -0
  114. control_plane_api/app/skills/file_generation.py +94 -0
  115. control_plane_api/app/skills/file_system.py +110 -0
  116. control_plane_api/app/skills/python.py +92 -0
  117. control_plane_api/app/skills/registry.py +65 -0
  118. control_plane_api/app/skills/shell.py +102 -0
  119. control_plane_api/app/skills/workflow_executor.py +469 -0
  120. control_plane_api/app/utils/workflow_executor.py +354 -0
  121. control_plane_api/app/workflows/__init__.py +11 -0
  122. control_plane_api/app/workflows/agent_execution.py +507 -0
  123. control_plane_api/app/workflows/agent_execution_with_skills.py +222 -0
  124. control_plane_api/app/workflows/namespace_provisioning.py +326 -0
  125. control_plane_api/app/workflows/team_execution.py +399 -0
  126. control_plane_api/scripts/seed_models.py +239 -0
  127. control_plane_api/worker/__init__.py +0 -0
  128. control_plane_api/worker/activities/__init__.py +0 -0
  129. control_plane_api/worker/activities/agent_activities.py +1241 -0
  130. control_plane_api/worker/activities/approval_activities.py +234 -0
  131. control_plane_api/worker/activities/runtime_activities.py +388 -0
  132. control_plane_api/worker/activities/skill_activities.py +267 -0
  133. control_plane_api/worker/activities/team_activities.py +1217 -0
  134. control_plane_api/worker/config/__init__.py +31 -0
  135. control_plane_api/worker/config/worker_config.py +275 -0
  136. control_plane_api/worker/control_plane_client.py +529 -0
  137. control_plane_api/worker/examples/analytics_integration_example.py +362 -0
  138. control_plane_api/worker/models/__init__.py +1 -0
  139. control_plane_api/worker/models/inputs.py +89 -0
  140. control_plane_api/worker/runtimes/__init__.py +31 -0
  141. control_plane_api/worker/runtimes/base.py +789 -0
  142. control_plane_api/worker/runtimes/claude_code_runtime.py +1443 -0
  143. control_plane_api/worker/runtimes/default_runtime.py +617 -0
  144. control_plane_api/worker/runtimes/factory.py +173 -0
  145. control_plane_api/worker/runtimes/validation.py +93 -0
  146. control_plane_api/worker/services/__init__.py +1 -0
  147. control_plane_api/worker/services/agent_executor.py +422 -0
  148. control_plane_api/worker/services/agent_executor_v2.py +383 -0
  149. control_plane_api/worker/services/analytics_collector.py +457 -0
  150. control_plane_api/worker/services/analytics_service.py +464 -0
  151. control_plane_api/worker/services/approval_tools.py +310 -0
  152. control_plane_api/worker/services/approval_tools_agno.py +207 -0
  153. control_plane_api/worker/services/cancellation_manager.py +177 -0
  154. control_plane_api/worker/services/data_visualization.py +827 -0
  155. control_plane_api/worker/services/jira_tools.py +257 -0
  156. control_plane_api/worker/services/runtime_analytics.py +328 -0
  157. control_plane_api/worker/services/session_service.py +194 -0
  158. control_plane_api/worker/services/skill_factory.py +175 -0
  159. control_plane_api/worker/services/team_executor.py +574 -0
  160. control_plane_api/worker/services/team_executor_v2.py +465 -0
  161. control_plane_api/worker/services/workflow_executor_tools.py +1418 -0
  162. control_plane_api/worker/tests/__init__.py +1 -0
  163. control_plane_api/worker/tests/e2e/__init__.py +0 -0
  164. control_plane_api/worker/tests/e2e/test_execution_flow.py +571 -0
  165. control_plane_api/worker/tests/integration/__init__.py +0 -0
  166. control_plane_api/worker/tests/integration/test_control_plane_integration.py +308 -0
  167. control_plane_api/worker/tests/unit/__init__.py +0 -0
  168. control_plane_api/worker/tests/unit/test_control_plane_client.py +401 -0
  169. control_plane_api/worker/utils/__init__.py +1 -0
  170. control_plane_api/worker/utils/chunk_batcher.py +305 -0
  171. control_plane_api/worker/utils/retry_utils.py +60 -0
  172. control_plane_api/worker/utils/streaming_utils.py +373 -0
  173. control_plane_api/worker/worker.py +753 -0
  174. control_plane_api/worker/workflows/__init__.py +0 -0
  175. control_plane_api/worker/workflows/agent_execution.py +589 -0
  176. control_plane_api/worker/workflows/team_execution.py +429 -0
  177. kubiya_control_plane_api-0.3.4.dist-info/METADATA +229 -0
  178. kubiya_control_plane_api-0.3.4.dist-info/RECORD +182 -0
  179. kubiya_control_plane_api-0.3.4.dist-info/entry_points.txt +2 -0
  180. kubiya_control_plane_api-0.3.4.dist-info/top_level.txt +1 -0
  181. kubiya_control_plane_api-0.1.0.dist-info/METADATA +0 -66
  182. kubiya_control_plane_api-0.1.0.dist-info/RECORD +0 -5
  183. kubiya_control_plane_api-0.1.0.dist-info/top_level.txt +0 -1
  184. {kubiya_control_plane_api-0.1.0.dist-info/licenses → control_plane_api}/LICENSE +0 -0
  185. {kubiya_control_plane_api-0.1.0.dist-info → kubiya_control_plane_api-0.3.4.dist-info}/WHEEL +0 -0
@@ -0,0 +1,162 @@
1
+ """add comprehensive analytics tables for execution tracking
2
+
3
+ Revision ID: 1f54bc2a37e3
4
+ Revises: add_llm_models_table
5
+ Create Date: 2025-01-08 14:00:00.000000
6
+
7
+ This migration adds production-grade analytics tables to track:
8
+ - Per-turn LLM metrics (tokens, duration, cost)
9
+ - Tool execution details (success/failure, timing)
10
+ - Task completion tracking
11
+ """
12
+ from alembic import op
13
+ import sqlalchemy as sa
14
+ from sqlalchemy.dialects import postgresql
15
+
16
+ # revision identifiers, used by Alembic.
17
+ revision = '1f54bc2a37e3'
18
+ down_revision = 'add_llm_models_table'
19
+ branch_labels = None
20
+ depends_on = None
21
+
22
+
23
+ def upgrade():
24
+ # Create execution_turns table
25
+ op.create_table(
26
+ 'execution_turns',
27
+ sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False),
28
+ sa.Column('organization_id', sa.String(), nullable=False),
29
+ sa.Column('execution_id', postgresql.UUID(as_uuid=True), nullable=False),
30
+ sa.Column('turn_number', sa.Integer(), nullable=False),
31
+ sa.Column('turn_id', sa.String(), nullable=True),
32
+ sa.Column('model', sa.String(), nullable=False),
33
+ sa.Column('model_provider', sa.String(), nullable=True),
34
+ sa.Column('started_at', sa.DateTime(timezone=True), nullable=False, server_default=sa.text('now()')),
35
+ sa.Column('completed_at', sa.DateTime(timezone=True), nullable=True),
36
+ sa.Column('duration_ms', sa.Integer(), nullable=True),
37
+ sa.Column('input_tokens', sa.Integer(), nullable=True, server_default='0'),
38
+ sa.Column('output_tokens', sa.Integer(), nullable=True, server_default='0'),
39
+ sa.Column('cache_read_tokens', sa.Integer(), nullable=True, server_default='0'),
40
+ sa.Column('cache_creation_tokens', sa.Integer(), nullable=True, server_default='0'),
41
+ sa.Column('total_tokens', sa.Integer(), nullable=True, server_default='0'),
42
+ sa.Column('input_cost', sa.Float(), nullable=True, server_default='0.0'),
43
+ sa.Column('output_cost', sa.Float(), nullable=True, server_default='0.0'),
44
+ sa.Column('cache_read_cost', sa.Float(), nullable=True, server_default='0.0'),
45
+ sa.Column('cache_creation_cost', sa.Float(), nullable=True, server_default='0.0'),
46
+ sa.Column('total_cost', sa.Float(), nullable=True, server_default='0.0'),
47
+ sa.Column('finish_reason', sa.String(), nullable=True),
48
+ sa.Column('response_preview', sa.Text(), nullable=True),
49
+ sa.Column('tools_called_count', sa.Integer(), nullable=False, server_default='0'),
50
+ sa.Column('tools_called_names', postgresql.JSON(astext_type=sa.Text()), nullable=False, server_default='[]'),
51
+ sa.Column('error_message', sa.Text(), nullable=True),
52
+ sa.Column('metrics', postgresql.JSON(astext_type=sa.Text()), nullable=False, server_default='{}'),
53
+ sa.Column('runtime_minutes', sa.Float(), nullable=True, server_default='0.0'),
54
+ sa.Column('model_weight', sa.Float(), nullable=True, server_default='1.0'),
55
+ sa.Column('tool_calls_weight', sa.Float(), nullable=True, server_default='1.0'),
56
+ sa.Column('aem_value', sa.Float(), nullable=True, server_default='0.0'),
57
+ sa.Column('aem_cost', sa.Float(), nullable=True, server_default='0.0'),
58
+ sa.Column('created_at', sa.DateTime(timezone=True), nullable=False, server_default=sa.text('now()')),
59
+ sa.PrimaryKeyConstraint('id'),
60
+ sa.ForeignKeyConstraint(['execution_id'], ['executions.id'], ondelete='CASCADE')
61
+ )
62
+
63
+ # Create indexes for execution_turns
64
+ op.create_index('ix_execution_turns_organization_id', 'execution_turns', ['organization_id'])
65
+ op.create_index('ix_execution_turns_execution_id', 'execution_turns', ['execution_id'])
66
+ op.create_index('ix_execution_turns_org_execution', 'execution_turns', ['organization_id', 'execution_id'])
67
+ op.create_index('ix_execution_turns_org_model', 'execution_turns', ['organization_id', 'model'])
68
+ op.create_index('ix_execution_turns_org_created', 'execution_turns', ['organization_id', 'created_at'])
69
+ op.create_index('ix_execution_turns_org_cost', 'execution_turns', ['organization_id', 'total_cost'])
70
+
71
+ # Create execution_tool_calls table
72
+ op.create_table(
73
+ 'execution_tool_calls',
74
+ sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False),
75
+ sa.Column('organization_id', sa.String(), nullable=False),
76
+ sa.Column('execution_id', postgresql.UUID(as_uuid=True), nullable=False),
77
+ sa.Column('turn_id', postgresql.UUID(as_uuid=True), nullable=True),
78
+ sa.Column('tool_name', sa.String(), nullable=False),
79
+ sa.Column('tool_use_id', sa.String(), nullable=True),
80
+ sa.Column('started_at', sa.DateTime(timezone=True), nullable=False, server_default=sa.text('now()')),
81
+ sa.Column('completed_at', sa.DateTime(timezone=True), nullable=True),
82
+ sa.Column('duration_ms', sa.Integer(), nullable=True),
83
+ sa.Column('tool_input', postgresql.JSON(astext_type=sa.Text()), nullable=True),
84
+ sa.Column('tool_output', sa.Text(), nullable=True),
85
+ sa.Column('tool_output_size', sa.Integer(), nullable=True),
86
+ sa.Column('success', sa.Boolean(), nullable=False, server_default='true'),
87
+ sa.Column('error_message', sa.Text(), nullable=True),
88
+ sa.Column('error_type', sa.String(), nullable=True),
89
+ sa.Column('metadata', postgresql.JSON(astext_type=sa.Text()), nullable=False, server_default='{}'),
90
+ sa.Column('created_at', sa.DateTime(timezone=True), nullable=False, server_default=sa.text('now()')),
91
+ sa.PrimaryKeyConstraint('id'),
92
+ sa.ForeignKeyConstraint(['execution_id'], ['executions.id'], ondelete='CASCADE'),
93
+ sa.ForeignKeyConstraint(['turn_id'], ['execution_turns.id'], ondelete='CASCADE')
94
+ )
95
+
96
+ # Create indexes for execution_tool_calls
97
+ op.create_index('ix_execution_tool_calls_organization_id', 'execution_tool_calls', ['organization_id'])
98
+ op.create_index('ix_execution_tool_calls_execution_id', 'execution_tool_calls', ['execution_id'])
99
+ op.create_index('ix_execution_tool_calls_turn_id', 'execution_tool_calls', ['turn_id'])
100
+ op.create_index('ix_execution_tool_calls_tool_name', 'execution_tool_calls', ['tool_name'])
101
+ op.create_index('ix_execution_tool_calls_org_execution', 'execution_tool_calls', ['organization_id', 'execution_id'])
102
+ op.create_index('ix_execution_tool_calls_org_tool', 'execution_tool_calls', ['organization_id', 'tool_name'])
103
+ op.create_index('ix_execution_tool_calls_org_success', 'execution_tool_calls', ['organization_id', 'success'])
104
+ op.create_index('ix_execution_tool_calls_org_created', 'execution_tool_calls', ['organization_id', 'created_at'])
105
+
106
+ # Create execution_tasks table
107
+ op.create_table(
108
+ 'execution_tasks',
109
+ sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False),
110
+ sa.Column('organization_id', sa.String(), nullable=False),
111
+ sa.Column('execution_id', postgresql.UUID(as_uuid=True), nullable=False),
112
+ sa.Column('task_number', sa.Integer(), nullable=True),
113
+ sa.Column('task_id', sa.String(), nullable=True),
114
+ sa.Column('task_description', sa.Text(), nullable=False),
115
+ sa.Column('task_type', sa.String(), nullable=True),
116
+ sa.Column('status', sa.String(), nullable=False, server_default='pending'),
117
+ sa.Column('started_at', sa.DateTime(timezone=True), nullable=True),
118
+ sa.Column('completed_at', sa.DateTime(timezone=True), nullable=True),
119
+ sa.Column('duration_ms', sa.Integer(), nullable=True),
120
+ sa.Column('result', sa.Text(), nullable=True),
121
+ sa.Column('error_message', sa.Text(), nullable=True),
122
+ sa.Column('metadata', postgresql.JSON(astext_type=sa.Text()), nullable=False, server_default='{}'),
123
+ sa.Column('created_at', sa.DateTime(timezone=True), nullable=False, server_default=sa.text('now()')),
124
+ sa.Column('updated_at', sa.DateTime(timezone=True), nullable=False, server_default=sa.text('now()')),
125
+ sa.PrimaryKeyConstraint('id'),
126
+ sa.ForeignKeyConstraint(['execution_id'], ['executions.id'], ondelete='CASCADE')
127
+ )
128
+
129
+ # Create indexes for execution_tasks
130
+ op.create_index('ix_execution_tasks_organization_id', 'execution_tasks', ['organization_id'])
131
+ op.create_index('ix_execution_tasks_execution_id', 'execution_tasks', ['execution_id'])
132
+ op.create_index('ix_execution_tasks_org_execution', 'execution_tasks', ['organization_id', 'execution_id'])
133
+ op.create_index('ix_execution_tasks_org_status', 'execution_tasks', ['organization_id', 'status'])
134
+
135
+
136
+ def downgrade():
137
+ # Drop execution_tasks table and indexes
138
+ op.drop_index('ix_execution_tasks_org_status', table_name='execution_tasks')
139
+ op.drop_index('ix_execution_tasks_org_execution', table_name='execution_tasks')
140
+ op.drop_index('ix_execution_tasks_execution_id', table_name='execution_tasks')
141
+ op.drop_index('ix_execution_tasks_organization_id', table_name='execution_tasks')
142
+ op.drop_table('execution_tasks')
143
+
144
+ # Drop execution_tool_calls table and indexes
145
+ op.drop_index('ix_execution_tool_calls_org_created', table_name='execution_tool_calls')
146
+ op.drop_index('ix_execution_tool_calls_org_success', table_name='execution_tool_calls')
147
+ op.drop_index('ix_execution_tool_calls_org_tool', table_name='execution_tool_calls')
148
+ op.drop_index('ix_execution_tool_calls_org_execution', table_name='execution_tool_calls')
149
+ op.drop_index('ix_execution_tool_calls_tool_name', table_name='execution_tool_calls')
150
+ op.drop_index('ix_execution_tool_calls_turn_id', table_name='execution_tool_calls')
151
+ op.drop_index('ix_execution_tool_calls_execution_id', table_name='execution_tool_calls')
152
+ op.drop_index('ix_execution_tool_calls_organization_id', table_name='execution_tool_calls')
153
+ op.drop_table('execution_tool_calls')
154
+
155
+ # Drop execution_turns table and indexes
156
+ op.drop_index('ix_execution_turns_org_cost', table_name='execution_turns')
157
+ op.drop_index('ix_execution_turns_org_created', table_name='execution_turns')
158
+ op.drop_index('ix_execution_turns_org_model', table_name='execution_turns')
159
+ op.drop_index('ix_execution_turns_org_execution', table_name='execution_turns')
160
+ op.drop_index('ix_execution_turns_execution_id', table_name='execution_turns')
161
+ op.drop_index('ix_execution_turns_organization_id', table_name='execution_turns')
162
+ op.drop_table('execution_turns')
@@ -0,0 +1,30 @@
1
+ """rename_toolset_ids_to_skill_ids_in_teams
2
+
3
+ Revision ID: 2e4cb136dc10
4
+ Revises: d4eaf16e3f8d
5
+ Create Date: 2025-11-06 21:31:20.981166
6
+
7
+ """
8
+ from typing import Sequence, Union
9
+
10
+ from alembic import op
11
+ import sqlalchemy as sa
12
+
13
+
14
+ # revision identifiers, used by Alembic.
15
+ revision: str = '2e4cb136dc10'
16
+ down_revision: Union[str, Sequence[str], None] = 'd4eaf16e3f8d'
17
+ branch_labels: Union[str, Sequence[str], None] = None
18
+ depends_on: Union[str, Sequence[str], None] = None
19
+
20
+
21
+ def upgrade() -> None:
22
+ """Rename toolset_ids to skill_ids in teams table."""
23
+ # Rename toolset_ids column to skill_ids
24
+ op.alter_column('teams', 'toolset_ids', new_column_name='skill_ids')
25
+
26
+
27
+ def downgrade() -> None:
28
+ """Rename skill_ids back to toolset_ids in teams table."""
29
+ # Reverse the column rename
30
+ op.alter_column('teams', 'skill_ids', new_column_name='toolset_ids')
@@ -0,0 +1,28 @@
1
+ """add_skill_templates_table
2
+
3
+ Revision ID: 31cd69a644ce
4
+ Revises: f973b431d1ce
5
+ Create Date: 2025-11-08 10:35:32.991694
6
+
7
+ """
8
+ from typing import Sequence, Union
9
+
10
+ from alembic import op
11
+ import sqlalchemy as sa
12
+
13
+
14
+ # revision identifiers, used by Alembic.
15
+ revision: str = '31cd69a644ce'
16
+ down_revision: Union[str, Sequence[str], None] = 'f973b431d1ce'
17
+ branch_labels: Union[str, Sequence[str], None] = None
18
+ depends_on: Union[str, Sequence[str], None] = None
19
+
20
+
21
+ def upgrade() -> None:
22
+ """Upgrade schema."""
23
+ pass
24
+
25
+
26
+ def downgrade() -> None:
27
+ """Downgrade schema."""
28
+ pass
@@ -0,0 +1,161 @@
1
+ """add_jobs_and_job_executions_tables
2
+
3
+ Revision ID: 89e127caa47d
4
+ Revises: b0e10697f212
5
+ Create Date: 2025-11-06 20:11:04.561446
6
+
7
+ """
8
+ from typing import Sequence, Union
9
+
10
+ from alembic import op
11
+ import sqlalchemy as sa
12
+
13
+
14
+ # revision identifiers, used by Alembic.
15
+ revision: str = '89e127caa47d'
16
+ down_revision: Union[str, Sequence[str], None] = 'b0e10697f212'
17
+ branch_labels: Union[str, Sequence[str], None] = None
18
+ depends_on: Union[str, Sequence[str], None] = None
19
+
20
+
21
+ def upgrade() -> None:
22
+ """Upgrade schema."""
23
+ # Create jobs table
24
+ op.create_table(
25
+ 'jobs',
26
+ sa.Column('id', sa.String(255), primary_key=True),
27
+ sa.Column('organization_id', sa.String(255), nullable=False),
28
+ sa.Column('name', sa.String(255), nullable=False),
29
+ sa.Column('description', sa.Text(), nullable=True),
30
+ sa.Column('enabled', sa.Boolean(), nullable=False, server_default='true'),
31
+ sa.Column('status', sa.String(50), nullable=False, server_default='active'),
32
+ sa.Column('trigger_type', sa.String(50), nullable=False),
33
+ sa.Column('cron_schedule', sa.String(255), nullable=True),
34
+ sa.Column('cron_timezone', sa.String(100), server_default='UTC'),
35
+ sa.Column('webhook_url_path', sa.String(500), nullable=True),
36
+ sa.Column('webhook_secret', sa.String(500), nullable=True),
37
+ sa.Column('temporal_schedule_id', sa.String(255), nullable=True),
38
+ sa.Column('planning_mode', sa.String(50), nullable=False, server_default='predefined_agent'),
39
+ sa.Column('entity_type', sa.String(50), nullable=True),
40
+ sa.Column('entity_id', sa.String(255), nullable=True),
41
+ sa.Column('entity_name', sa.String(255), nullable=True),
42
+ sa.Column('prompt_template', sa.Text(), nullable=False),
43
+ sa.Column('system_prompt', sa.Text(), nullable=True),
44
+ sa.Column('executor_type', sa.String(50), nullable=False, server_default='auto'),
45
+ sa.Column('worker_queue_name', sa.String(255), nullable=True),
46
+ sa.Column('environment_name', sa.String(255), nullable=True),
47
+ sa.Column('config', sa.JSON(), server_default='{}'),
48
+ sa.Column('execution_environment', sa.JSON(), server_default='{}'),
49
+ sa.Column('last_execution_id', sa.String(255), nullable=True),
50
+ sa.Column('last_execution_at', sa.DateTime(timezone=True), nullable=True),
51
+ sa.Column('next_execution_at', sa.DateTime(timezone=True), nullable=True),
52
+ sa.Column('total_executions', sa.Integer(), server_default='0'),
53
+ sa.Column('successful_executions', sa.Integer(), server_default='0'),
54
+ sa.Column('failed_executions', sa.Integer(), server_default='0'),
55
+ sa.Column('execution_history', sa.JSON(), server_default='[]'),
56
+ sa.Column('created_by', sa.String(255), nullable=True),
57
+ sa.Column('updated_by', sa.String(255), nullable=True),
58
+ sa.Column('created_at', sa.DateTime(timezone=True), nullable=False, server_default=sa.text('now()')),
59
+ sa.Column('updated_at', sa.DateTime(timezone=True), nullable=False, server_default=sa.text('now()')),
60
+ sa.Column('last_triggered_at', sa.DateTime(timezone=True), nullable=True),
61
+ )
62
+
63
+ # Note: Skipping foreign key constraint for last_execution_id due to type mismatch
64
+ # The executions.id is stored as UUID in the database but defined as String in models
65
+ # We can add this constraint later after resolving the type discrepancy
66
+
67
+ # Add unique constraints
68
+ op.create_unique_constraint('uq_jobs_webhook_url_path', 'jobs', ['webhook_url_path'])
69
+ op.create_unique_constraint('uq_jobs_temporal_schedule_id', 'jobs', ['temporal_schedule_id'])
70
+
71
+ # Create indexes for jobs table
72
+ op.create_index('idx_jobs_organization_id', 'jobs', ['organization_id'])
73
+ op.create_index('idx_jobs_name', 'jobs', ['organization_id', 'name'])
74
+ op.create_index('idx_jobs_enabled', 'jobs', ['enabled'])
75
+ op.create_index('idx_jobs_status', 'jobs', ['status'])
76
+ op.create_index('idx_jobs_trigger_type', 'jobs', ['trigger_type'])
77
+ op.create_index('idx_jobs_webhook_url_path', 'jobs', ['webhook_url_path'])
78
+ op.create_index('idx_jobs_temporal_schedule_id', 'jobs', ['temporal_schedule_id'])
79
+ op.create_index('idx_jobs_created_at', 'jobs', ['created_at'])
80
+ op.create_index('idx_jobs_next_execution_at', 'jobs', ['next_execution_at'])
81
+
82
+ # Create job_executions table
83
+ op.create_table(
84
+ 'job_executions',
85
+ sa.Column('id', sa.String(255), primary_key=True),
86
+ sa.Column('job_id', sa.String(255), nullable=False),
87
+ sa.Column('execution_id', sa.String(255), nullable=False),
88
+ sa.Column('organization_id', sa.String(255), nullable=False),
89
+ sa.Column('trigger_type', sa.String(50), nullable=False),
90
+ sa.Column('trigger_metadata', sa.JSON(), server_default='{}'),
91
+ sa.Column('execution_status', sa.String(50), nullable=True),
92
+ sa.Column('execution_duration_ms', sa.Integer(), nullable=True),
93
+ sa.Column('created_at', sa.DateTime(timezone=True), nullable=False, server_default=sa.text('now()')),
94
+ )
95
+
96
+ # Add foreign key constraints for job_executions
97
+ op.create_foreign_key(
98
+ 'fk_job_executions_job',
99
+ 'job_executions', 'jobs',
100
+ ['job_id'], ['id'],
101
+ ondelete='CASCADE'
102
+ )
103
+ # Note: Skipping foreign key for execution_id due to type mismatch with executions.id
104
+
105
+ # Create indexes for job_executions table
106
+ op.create_index('idx_job_executions_job_id', 'job_executions', ['job_id'])
107
+ op.create_index('idx_job_executions_execution_id', 'job_executions', ['execution_id'])
108
+ op.create_index('idx_job_executions_organization_id', 'job_executions', ['organization_id'])
109
+ op.create_index('idx_job_executions_created_at', 'job_executions', ['created_at'])
110
+ op.create_index('idx_job_executions_trigger_type', 'job_executions', ['trigger_type'])
111
+ op.create_index('idx_job_executions_execution_status', 'job_executions', ['execution_status'])
112
+
113
+ # Create trigger function for updated_at
114
+ op.execute("""
115
+ CREATE OR REPLACE FUNCTION update_updated_at_column()
116
+ RETURNS TRIGGER AS $$
117
+ BEGIN
118
+ NEW.updated_at = NOW();
119
+ RETURN NEW;
120
+ END;
121
+ $$ LANGUAGE plpgsql;
122
+ """)
123
+
124
+ # Create trigger for jobs table
125
+ op.execute("""
126
+ DROP TRIGGER IF EXISTS update_jobs_updated_at ON jobs;
127
+ CREATE TRIGGER update_jobs_updated_at
128
+ BEFORE UPDATE ON jobs
129
+ FOR EACH ROW
130
+ EXECUTE FUNCTION update_updated_at_column();
131
+ """)
132
+
133
+
134
+ def downgrade() -> None:
135
+ """Downgrade schema."""
136
+ # Drop triggers
137
+ op.execute("DROP TRIGGER IF EXISTS update_jobs_updated_at ON jobs;")
138
+ op.execute("DROP FUNCTION IF EXISTS update_updated_at_column CASCADE;")
139
+
140
+ # Drop indexes for job_executions
141
+ op.drop_index('idx_job_executions_execution_status', 'job_executions')
142
+ op.drop_index('idx_job_executions_trigger_type', 'job_executions')
143
+ op.drop_index('idx_job_executions_created_at', 'job_executions')
144
+ op.drop_index('idx_job_executions_organization_id', 'job_executions')
145
+ op.drop_index('idx_job_executions_execution_id', 'job_executions')
146
+ op.drop_index('idx_job_executions_job_id', 'job_executions')
147
+
148
+ # Drop indexes for jobs
149
+ op.drop_index('idx_jobs_next_execution_at', 'jobs')
150
+ op.drop_index('idx_jobs_created_at', 'jobs')
151
+ op.drop_index('idx_jobs_temporal_schedule_id', 'jobs')
152
+ op.drop_index('idx_jobs_webhook_url_path', 'jobs')
153
+ op.drop_index('idx_jobs_trigger_type', 'jobs')
154
+ op.drop_index('idx_jobs_status', 'jobs')
155
+ op.drop_index('idx_jobs_enabled', 'jobs')
156
+ op.drop_index('idx_jobs_name', 'jobs')
157
+ op.drop_index('idx_jobs_organization_id', 'jobs')
158
+
159
+ # Drop tables
160
+ op.drop_table('job_executions')
161
+ op.drop_table('jobs')
@@ -0,0 +1,51 @@
1
+ """add llm_models table
2
+
3
+ Revision ID: add_llm_models_table
4
+ Revises: f973b431d1ce
5
+ Create Date: 2025-01-08 13:25:00.000000
6
+
7
+ """
8
+ from alembic import op
9
+ import sqlalchemy as sa
10
+ from sqlalchemy.dialects import postgresql
11
+
12
+ # revision identifiers, used by Alembic.
13
+ revision = 'add_llm_models_table'
14
+ down_revision = 'f973b431d1ce'
15
+ branch_labels = None
16
+ depends_on = None
17
+
18
+
19
+ def upgrade():
20
+ # Create llm_models table
21
+ op.create_table(
22
+ 'llm_models',
23
+ sa.Column('id', sa.String(), nullable=False),
24
+ sa.Column('value', sa.String(), nullable=False),
25
+ sa.Column('label', sa.String(), nullable=False),
26
+ sa.Column('provider', sa.String(), nullable=False),
27
+ sa.Column('logo', sa.String(), nullable=True),
28
+ sa.Column('description', sa.Text(), nullable=True),
29
+ sa.Column('enabled', sa.Boolean(), nullable=False, server_default='true'),
30
+ sa.Column('recommended', sa.Boolean(), nullable=False, server_default='false'),
31
+ sa.Column('compatible_runtimes', postgresql.JSON(astext_type=sa.Text()), nullable=False, server_default='[]'),
32
+ sa.Column('capabilities', postgresql.JSON(astext_type=sa.Text()), nullable=False, server_default='{}'),
33
+ sa.Column('pricing', postgresql.JSON(astext_type=sa.Text()), nullable=True),
34
+ sa.Column('display_order', sa.Integer(), nullable=False, server_default='1000'),
35
+ sa.Column('created_at', sa.DateTime(), nullable=False, server_default=sa.text('now()')),
36
+ sa.Column('updated_at', sa.DateTime(), nullable=False, server_default=sa.text('now()')),
37
+ sa.Column('created_by', sa.String(), nullable=True),
38
+ sa.PrimaryKeyConstraint('id')
39
+ )
40
+
41
+ # Create indexes
42
+ op.create_index(op.f('ix_llm_models_value'), 'llm_models', ['value'], unique=True)
43
+ op.create_index(op.f('ix_llm_models_provider'), 'llm_models', ['provider'], unique=False)
44
+ op.create_index(op.f('ix_llm_models_enabled'), 'llm_models', ['enabled'], unique=False)
45
+
46
+
47
+ def downgrade():
48
+ op.drop_index(op.f('ix_llm_models_enabled'), table_name='llm_models')
49
+ op.drop_index(op.f('ix_llm_models_provider'), table_name='llm_models')
50
+ op.drop_index(op.f('ix_llm_models_value'), table_name='llm_models')
51
+ op.drop_table('llm_models')
@@ -0,0 +1,42 @@
1
+ """add_runtime_column_to_teams_simple
2
+
3
+ Revision ID: b0e10697f212
4
+ Revises: 1382bec74309
5
+ Create Date: 2025-11-06 11:48:06.515460
6
+
7
+ """
8
+ from typing import Sequence, Union
9
+
10
+ from alembic import op
11
+ import sqlalchemy as sa
12
+
13
+
14
+ # revision identifiers, used by Alembic.
15
+ revision: str = 'b0e10697f212'
16
+ down_revision: Union[str, Sequence[str], None] = '1382bec74309'
17
+ branch_labels: Union[str, Sequence[str], None] = None
18
+ depends_on: Union[str, Sequence[str], None] = None
19
+
20
+
21
+ def upgrade() -> None:
22
+ """Upgrade schema: Add runtime column to teams table."""
23
+ # Create the RuntimeType enum if it doesn't exist
24
+ op.execute("CREATE TYPE runtimetype AS ENUM ('default', 'claude_code')")
25
+
26
+ # Add runtime column to teams table
27
+ op.add_column('teams', sa.Column('runtime', sa.Enum('default', 'claude_code', name='runtimetype'), server_default='default', nullable=False))
28
+
29
+ # Create index on runtime column
30
+ op.create_index(op.f('ix_teams_runtime'), 'teams', ['runtime'], unique=False)
31
+
32
+
33
+ def downgrade() -> None:
34
+ """Downgrade schema: Remove runtime column from teams table."""
35
+ # Drop index
36
+ op.drop_index(op.f('ix_teams_runtime'), table_name='teams')
37
+
38
+ # Drop column
39
+ op.drop_column('teams', 'runtime')
40
+
41
+ # Drop enum type
42
+ op.execute("DROP TYPE runtimetype")
@@ -0,0 +1,155 @@
1
+ """add_execution_trigger_source_and_fix_types
2
+
3
+ Revision ID: ce43b24b63bf
4
+ Revises: 89e127caa47d
5
+ Create Date: 2025-11-06 20:15:09.289858
6
+
7
+ """
8
+ from typing import Sequence, Union
9
+
10
+ from alembic import op
11
+ import sqlalchemy as sa
12
+
13
+
14
+ # revision identifiers, used by Alembic.
15
+ revision: str = 'ce43b24b63bf'
16
+ down_revision: Union[str, Sequence[str], None] = '89e127caa47d'
17
+ branch_labels: Union[str, Sequence[str], None] = None
18
+ depends_on: Union[str, Sequence[str], None] = None
19
+
20
+
21
+ def upgrade() -> None:
22
+ """Upgrade schema."""
23
+ # 1. Add trigger_source enum type
24
+ op.execute("""
25
+ CREATE TYPE executiontriggersource AS ENUM (
26
+ 'user', 'job_cron', 'job_webhook', 'job_manual', 'system', 'api', 'chat'
27
+ )
28
+ """)
29
+
30
+ # 2. Add trigger_source and trigger_metadata columns to executions table
31
+ op.add_column('executions',
32
+ sa.Column('trigger_source', sa.Enum(
33
+ 'user', 'job_cron', 'job_webhook', 'job_manual', 'system', 'api', 'chat',
34
+ name='executiontriggersource'
35
+ ), nullable=False, server_default='user')
36
+ )
37
+ op.add_column('executions',
38
+ sa.Column('trigger_metadata', sa.JSON(), server_default='{}', nullable=True)
39
+ )
40
+
41
+ # 3. Create index on trigger_source
42
+ op.create_index('ix_executions_trigger_source', 'executions', ['trigger_source'])
43
+
44
+ # 4. Fix jobs table column types (total_executions, successful_executions, failed_executions from JSON to INTEGER)
45
+ # First, convert any existing data
46
+ op.execute("UPDATE jobs SET total_executions = COALESCE((total_executions::text)::integer, 0) WHERE total_executions IS NOT NULL")
47
+ op.execute("UPDATE jobs SET successful_executions = COALESCE((successful_executions::text)::integer, 0) WHERE successful_executions IS NOT NULL")
48
+ op.execute("UPDATE jobs SET failed_executions = COALESCE((failed_executions::text)::integer, 0) WHERE failed_executions IS NOT NULL")
49
+
50
+ # Then alter column types
51
+ op.alter_column('jobs', 'total_executions',
52
+ type_=sa.Integer(),
53
+ existing_type=sa.JSON(),
54
+ server_default='0',
55
+ nullable=False
56
+ )
57
+ op.alter_column('jobs', 'successful_executions',
58
+ type_=sa.Integer(),
59
+ existing_type=sa.JSON(),
60
+ server_default='0',
61
+ nullable=False
62
+ )
63
+ op.alter_column('jobs', 'failed_executions',
64
+ type_=sa.Integer(),
65
+ existing_type=sa.JSON(),
66
+ server_default='0',
67
+ nullable=False
68
+ )
69
+
70
+ # 5. Fix job_executions.execution_duration_ms from JSON to INTEGER
71
+ op.execute("UPDATE job_executions SET execution_duration_ms = COALESCE((execution_duration_ms::text)::integer, NULL) WHERE execution_duration_ms IS NOT NULL")
72
+ op.alter_column('job_executions', 'execution_duration_ms',
73
+ type_=sa.Integer(),
74
+ existing_type=sa.JSON(),
75
+ nullable=True
76
+ )
77
+
78
+ # 6. Update last_execution_id in jobs to be UUID type instead of VARCHAR
79
+ # First drop the existing column (safe since we're still setting up)
80
+ op.drop_column('jobs', 'last_execution_id')
81
+
82
+ # Add it back with correct UUID type
83
+ op.add_column('jobs',
84
+ sa.Column('last_execution_id', sa.dialects.postgresql.UUID(as_uuid=True), nullable=True)
85
+ )
86
+
87
+ # 7. Update execution_id in job_executions to be UUID type
88
+ op.drop_column('job_executions', 'execution_id')
89
+ op.add_column('job_executions',
90
+ sa.Column('execution_id', sa.dialects.postgresql.UUID(as_uuid=True), nullable=False)
91
+ )
92
+ op.create_index('idx_job_executions_execution_id', 'job_executions', ['execution_id'])
93
+
94
+ # 8. Now add the foreign key constraints with proper types
95
+ op.create_foreign_key(
96
+ 'fk_jobs_last_execution',
97
+ 'jobs', 'executions',
98
+ ['last_execution_id'], ['id'],
99
+ ondelete='SET NULL'
100
+ )
101
+
102
+ op.create_foreign_key(
103
+ 'fk_job_executions_execution',
104
+ 'job_executions', 'executions',
105
+ ['execution_id'], ['id'],
106
+ ondelete='CASCADE'
107
+ )
108
+
109
+
110
+ def downgrade() -> None:
111
+ """Downgrade schema."""
112
+ # Drop foreign keys
113
+ op.drop_constraint('fk_job_executions_execution', 'job_executions', type_='foreignkey')
114
+ op.drop_constraint('fk_jobs_last_execution', 'jobs', type_='foreignkey')
115
+
116
+ # Revert execution_id in job_executions back to VARCHAR
117
+ op.drop_index('idx_job_executions_execution_id', 'job_executions')
118
+ op.drop_column('job_executions', 'execution_id')
119
+ op.add_column('job_executions',
120
+ sa.Column('execution_id', sa.String(255), nullable=False)
121
+ )
122
+
123
+ # Revert last_execution_id in jobs back to VARCHAR
124
+ op.drop_column('jobs', 'last_execution_id')
125
+ op.add_column('jobs',
126
+ sa.Column('last_execution_id', sa.String(255), nullable=True)
127
+ )
128
+
129
+ # Revert execution_duration_ms back to JSON
130
+ op.alter_column('job_executions', 'execution_duration_ms',
131
+ type_=sa.JSON(),
132
+ existing_type=sa.Integer()
133
+ )
134
+
135
+ # Revert job execution counters back to JSON
136
+ op.alter_column('jobs', 'failed_executions',
137
+ type_=sa.JSON(),
138
+ existing_type=sa.Integer()
139
+ )
140
+ op.alter_column('jobs', 'successful_executions',
141
+ type_=sa.JSON(),
142
+ existing_type=sa.Integer()
143
+ )
144
+ op.alter_column('jobs', 'total_executions',
145
+ type_=sa.JSON(),
146
+ existing_type=sa.Integer()
147
+ )
148
+
149
+ # Drop trigger_source index and columns
150
+ op.drop_index('ix_executions_trigger_source', 'executions')
151
+ op.drop_column('executions', 'trigger_metadata')
152
+ op.drop_column('executions', 'trigger_source')
153
+
154
+ # Drop enum type
155
+ op.execute("DROP TYPE executiontriggersource")