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,93 @@
1
+ from sqlalchemy import Column, String, DateTime, Text, JSON, Enum as SQLEnum, ForeignKey
2
+ from sqlalchemy.dialects.postgresql import UUID
3
+ from sqlalchemy.orm import relationship
4
+ from sqlalchemy.sql import func
5
+ from datetime import datetime
6
+ import uuid
7
+ import enum
8
+
9
+ from control_plane_api.app.database import Base
10
+
11
+
12
+ class ExecutionStatus(str, enum.Enum):
13
+ PENDING = "pending"
14
+ RUNNING = "running"
15
+ WAITING_FOR_INPUT = "waiting_for_input"
16
+ COMPLETED = "completed"
17
+ FAILED = "failed"
18
+ CANCELLED = "cancelled"
19
+
20
+
21
+ class ExecutionType(str, enum.Enum):
22
+ AGENT = "agent"
23
+ TEAM = "team"
24
+ WORKFLOW = "workflow"
25
+
26
+
27
+ class ExecutionTriggerSource(str, enum.Enum):
28
+ """How the execution was triggered"""
29
+ USER = "user" # Triggered by a user directly via UI/API
30
+ JOB_CRON = "job_cron" # Triggered by a scheduled job (cron)
31
+ JOB_WEBHOOK = "job_webhook" # Triggered by a webhook job
32
+ JOB_MANUAL = "job_manual" # Triggered manually through job trigger API
33
+ SYSTEM = "system" # Triggered by system/automation
34
+ API = "api" # Triggered directly via API
35
+ CHAT = "chat" # Triggered from chat interface
36
+
37
+
38
+ class Execution(Base):
39
+ """Model for tracking agent/team/workflow executions with user attribution"""
40
+
41
+ __tablename__ = "executions"
42
+
43
+ id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
44
+
45
+ # Organization
46
+ organization_id = Column(String, nullable=False, index=True)
47
+
48
+ # What is being executed
49
+ execution_type = Column(SQLEnum(ExecutionType, values_callable=lambda x: [e.value for e in x]), nullable=False)
50
+ entity_id = Column(String, nullable=False) # agent_id, team_id, or workflow_id
51
+ entity_name = Column(String) # Cached name for display
52
+ runner_name = Column(String, nullable=True) # Cached runner name for filtering
53
+
54
+ # User attribution - who initiated this execution
55
+ user_id = Column(String, nullable=True, index=True)
56
+ user_email = Column(String, nullable=True)
57
+ user_name = Column(String, nullable=True)
58
+ user_avatar = Column(String, nullable=True)
59
+
60
+ # Trigger source - how this execution was initiated
61
+ trigger_source = Column(
62
+ SQLEnum(ExecutionTriggerSource, values_callable=lambda x: [e.value for e in x]),
63
+ default=ExecutionTriggerSource.USER,
64
+ nullable=False,
65
+ index=True
66
+ )
67
+ trigger_metadata = Column(JSON, default={}) # Additional context about the trigger (job_id, webhook payload, etc.)
68
+
69
+ # Execution details
70
+ prompt = Column(Text, nullable=False)
71
+ system_prompt = Column(Text, nullable=True)
72
+ config = Column(JSON, default={})
73
+
74
+ # Status and results
75
+ status = Column(SQLEnum(ExecutionStatus, values_callable=lambda x: [e.value for e in x]), default=ExecutionStatus.PENDING, nullable=False)
76
+ response = Column(Text, nullable=True)
77
+ error_message = Column(Text, nullable=True)
78
+
79
+ # Metadata
80
+ usage = Column(JSON, default={}) # Token usage, cost, etc.
81
+ execution_metadata = Column(JSON, default={}) # Additional metadata
82
+
83
+ # Timestamps
84
+ created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
85
+ started_at = Column(DateTime(timezone=True), nullable=True)
86
+ completed_at = Column(DateTime(timezone=True), nullable=True)
87
+ updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now(), nullable=False)
88
+
89
+ # Relationships
90
+ participants = relationship("ExecutionParticipant", back_populates="execution", cascade="all, delete-orphan", lazy="selectin")
91
+
92
+ def __repr__(self):
93
+ return f"<Execution {self.id} ({self.execution_type}:{self.entity_id}) - {self.status} by {self.user_email}>"
@@ -0,0 +1,179 @@
1
+ from sqlalchemy import Column, String, DateTime, Text, JSON, Enum as SQLEnum, Boolean, ForeignKey, Integer
2
+ from sqlalchemy.dialects.postgresql import UUID
3
+ from sqlalchemy.orm import relationship
4
+ from sqlalchemy.sql import func
5
+ from datetime import datetime
6
+ import uuid
7
+ import enum
8
+
9
+ from control_plane_api.app.database import Base
10
+
11
+
12
+ class JobStatus(str, enum.Enum):
13
+ """Job status enumeration"""
14
+ ACTIVE = "active"
15
+ PAUSED = "paused"
16
+ FAILED = "failed"
17
+ DISABLED = "disabled"
18
+
19
+
20
+ class JobTriggerType(str, enum.Enum):
21
+ """Job trigger type enumeration"""
22
+ CRON = "cron"
23
+ WEBHOOK = "webhook"
24
+ MANUAL = "manual"
25
+
26
+
27
+ class ExecutorType(str, enum.Enum):
28
+ """Job executor routing type"""
29
+ AUTO = "auto" # First available worker queue with active workers
30
+ SPECIFIC_QUEUE = "specific_queue" # Explicit worker queue selection
31
+ ENVIRONMENT = "environment" # Route to specific environment
32
+
33
+
34
+ class PlanningMode(str, enum.Enum):
35
+ """Planning mode for job execution"""
36
+ ON_THE_FLY = "on_the_fly" # Use planner to determine execution
37
+ PREDEFINED_AGENT = "predefined_agent" # Execute specific agent
38
+ PREDEFINED_TEAM = "predefined_team" # Execute specific team
39
+ PREDEFINED_WORKFLOW = "predefined_workflow" # Execute specific workflow
40
+
41
+
42
+ class Job(Base):
43
+ """
44
+ Model for scheduled and webhook-triggered jobs.
45
+
46
+ Jobs can be triggered via:
47
+ - Cron schedule (using Temporal Schedules)
48
+ - Webhook URL (with HMAC signature verification)
49
+ - Manual API trigger
50
+
51
+ Jobs execute agents, teams, or workflows with configurable routing.
52
+ """
53
+
54
+ __tablename__ = "jobs"
55
+
56
+ id = Column(String, primary_key=True, default=lambda: f"job_{uuid.uuid4()}")
57
+
58
+ # Organization (multi-tenant isolation)
59
+ organization_id = Column(String, nullable=False, index=True)
60
+
61
+ # Job metadata
62
+ name = Column(String, nullable=False, index=True)
63
+ description = Column(Text, nullable=True)
64
+ enabled = Column(Boolean, default=True, nullable=False, index=True)
65
+ status = Column(
66
+ SQLEnum(JobStatus, values_callable=lambda x: [e.value for e in x]),
67
+ default=JobStatus.ACTIVE,
68
+ nullable=False,
69
+ index=True
70
+ )
71
+
72
+ # Trigger configuration
73
+ trigger_type = Column(
74
+ SQLEnum(JobTriggerType, values_callable=lambda x: [e.value for e in x]),
75
+ nullable=False,
76
+ index=True
77
+ )
78
+
79
+ # Cron configuration (for CRON trigger type)
80
+ cron_schedule = Column(String, nullable=True) # e.g., "0 17 * * *" (daily at 5pm)
81
+ cron_timezone = Column(String, default="UTC", nullable=True) # e.g., "America/New_York"
82
+
83
+ # Webhook configuration (for WEBHOOK trigger type)
84
+ webhook_url_path = Column(String, nullable=True, unique=True, index=True) # e.g., "/api/v1/jobs/webhook/abc123"
85
+ webhook_secret = Column(String, nullable=True) # HMAC secret for signature verification
86
+
87
+ # Temporal Schedule ID (managed by system)
88
+ temporal_schedule_id = Column(String, nullable=True, unique=True, index=True)
89
+
90
+ # Planning and execution configuration
91
+ planning_mode = Column(
92
+ SQLEnum(PlanningMode, values_callable=lambda x: [e.value for e in x]),
93
+ default=PlanningMode.PREDEFINED_AGENT,
94
+ nullable=False
95
+ )
96
+
97
+ # Entity to execute (based on planning_mode)
98
+ entity_type = Column(String, nullable=True) # "agent", "team", "workflow" (for predefined modes)
99
+ entity_id = Column(String, nullable=True) # agent_id, team_id, or workflow_id
100
+ entity_name = Column(String, nullable=True) # Cached name for display
101
+
102
+ # Prompt configuration
103
+ prompt_template = Column(Text, nullable=False) # Can include {{variables}} for dynamic params
104
+ system_prompt = Column(Text, nullable=True)
105
+
106
+ # Executor routing configuration
107
+ executor_type = Column(
108
+ SQLEnum(ExecutorType, values_callable=lambda x: [e.value for e in x]),
109
+ default=ExecutorType.AUTO,
110
+ nullable=False
111
+ )
112
+ worker_queue_name = Column(String, nullable=True) # For SPECIFIC_QUEUE executor type
113
+ environment_name = Column(String, nullable=True) # For ENVIRONMENT executor type
114
+
115
+ # Execution configuration
116
+ config = Column(JSON, default={}) # Additional execution config (timeout, retry, etc.)
117
+ execution_environment = Column(JSON, default={}) # Environment variables, secrets, etc.
118
+
119
+ # Execution tracking
120
+ last_execution_id = Column(UUID(as_uuid=True), ForeignKey("executions.id", ondelete="SET NULL"), nullable=True)
121
+ last_execution_at = Column(DateTime(timezone=True), nullable=True)
122
+ next_execution_at = Column(DateTime(timezone=True), nullable=True) # For cron jobs
123
+ total_executions = Column(Integer, default=0, nullable=False) # Total number of executions
124
+ successful_executions = Column(Integer, default=0, nullable=False) # Number of successful executions
125
+ failed_executions = Column(Integer, default=0, nullable=False) # Number of failed executions
126
+
127
+ # Execution history (last N runs)
128
+ execution_history = Column(JSON, default=list) # List of recent execution summaries
129
+
130
+ # Audit fields
131
+ created_by = Column(String, nullable=True) # User ID who created the job
132
+ updated_by = Column(String, nullable=True) # User ID who last updated the job
133
+
134
+ # Timestamps
135
+ created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
136
+ updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now(), nullable=False)
137
+ last_triggered_at = Column(DateTime(timezone=True), nullable=True) # Last time job was triggered (manual or scheduled)
138
+
139
+ # Relationships
140
+ last_execution = relationship("Execution", foreign_keys=[last_execution_id], lazy="select")
141
+
142
+ def __repr__(self):
143
+ return f"<Job {self.id} ({self.name}) - {self.trigger_type}:{self.status}>"
144
+
145
+
146
+ class JobExecution(Base):
147
+ """
148
+ Junction table linking Jobs to Executions.
149
+ Tracks which executions were triggered by which jobs.
150
+ """
151
+
152
+ __tablename__ = "job_executions"
153
+
154
+ id = Column(String, primary_key=True, default=lambda: f"jobexec_{uuid.uuid4()}")
155
+
156
+ # Foreign keys
157
+ job_id = Column(String, ForeignKey("jobs.id", ondelete="CASCADE"), nullable=False, index=True)
158
+ execution_id = Column(UUID(as_uuid=True), ForeignKey("executions.id", ondelete="CASCADE"), nullable=False, index=True)
159
+
160
+ # Organization (for efficient querying)
161
+ organization_id = Column(String, nullable=False, index=True)
162
+
163
+ # Trigger context
164
+ trigger_type = Column(String, nullable=False) # "cron", "webhook", "manual"
165
+ trigger_metadata = Column(JSON, default={}) # Additional context (webhook payload, manual trigger user, etc.)
166
+
167
+ # Execution outcome (denormalized for quick queries)
168
+ execution_status = Column(String, nullable=True) # Cached from execution.status
169
+ execution_duration_ms = Column(Integer, nullable=True) # Duration in milliseconds
170
+
171
+ # Timestamps
172
+ created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
173
+
174
+ # Relationships
175
+ job = relationship("Job", foreign_keys=[job_id], lazy="select")
176
+ execution = relationship("Execution", foreign_keys=[execution_id], lazy="select")
177
+
178
+ def __repr__(self):
179
+ return f"<JobExecution job={self.job_id} execution={self.execution_id}>"
@@ -0,0 +1,75 @@
1
+ """LLM Model database model"""
2
+ from sqlalchemy import Column, String, Boolean, Text, JSON, Integer
3
+ from datetime import datetime
4
+ from sqlalchemy import DateTime
5
+ import uuid as uuid_module
6
+
7
+ from control_plane_api.app.database import Base
8
+
9
+
10
+ class LLMModel(Base):
11
+ """
12
+ LLM Model configuration for agent execution.
13
+
14
+ Stores available LLM models that can be used by agents and teams,
15
+ including provider information, compatibility, and UI metadata.
16
+ """
17
+
18
+ __tablename__ = "llm_models"
19
+
20
+ id = Column(String, primary_key=True, default=lambda: str(uuid_module.uuid4()))
21
+
22
+ # Model identification
23
+ value = Column(String, unique=True, nullable=False, index=True)
24
+ label = Column(String, nullable=False)
25
+ provider = Column(String, nullable=False, index=True)
26
+
27
+ # UI metadata
28
+ logo = Column(String, nullable=True)
29
+ description = Column(Text, nullable=True)
30
+
31
+ # Status and flags
32
+ enabled = Column(Boolean, default=True, nullable=False, index=True)
33
+ recommended = Column(Boolean, default=False, nullable=False)
34
+
35
+ # Runtime compatibility
36
+ # Store list of compatible runtime types (e.g., ["default", "claude_code"])
37
+ compatible_runtimes = Column(JSON, default=list, nullable=False)
38
+
39
+ # Model capabilities and metadata
40
+ capabilities = Column(JSON, default=dict, nullable=False)
41
+ # Example: {"vision": true, "function_calling": true, "max_tokens": 4096}
42
+
43
+ # Pricing information (optional)
44
+ pricing = Column(JSON, default=dict, nullable=True)
45
+ # Example: {"input_cost_per_1k": 0.01, "output_cost_per_1k": 0.03}
46
+
47
+ # Display order (lower = shown first)
48
+ display_order = Column(Integer, default=1000, nullable=False)
49
+
50
+ # Metadata
51
+ created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
52
+ updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False)
53
+ created_by = Column(String, nullable=True) # User ID who created this model entry
54
+
55
+ def __repr__(self):
56
+ return f"<LLMModel(id={self.id}, value={self.value}, provider={self.provider})>"
57
+
58
+ def to_dict(self):
59
+ """Convert to dictionary for API responses"""
60
+ return {
61
+ "id": self.id,
62
+ "value": self.value,
63
+ "label": self.label,
64
+ "provider": self.provider,
65
+ "logo": self.logo,
66
+ "description": self.description,
67
+ "enabled": self.enabled,
68
+ "recommended": self.recommended,
69
+ "compatible_runtimes": self.compatible_runtimes,
70
+ "capabilities": self.capabilities,
71
+ "pricing": self.pricing,
72
+ "display_order": self.display_order,
73
+ "created_at": self.created_at.isoformat() if self.created_at else None,
74
+ "updated_at": self.updated_at.isoformat() if self.updated_at else None,
75
+ }
@@ -0,0 +1,49 @@
1
+ from sqlalchemy import Column, String, DateTime, Boolean, Index
2
+ from sqlalchemy.sql import func
3
+ from datetime import datetime
4
+ import uuid
5
+
6
+ from control_plane_api.app.database import Base
7
+
8
+
9
+ class UserPresence(Base):
10
+ """Model for tracking user presence on agents/tasks for real-time collaboration"""
11
+
12
+ __tablename__ = "user_presence"
13
+
14
+ id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4()))
15
+
16
+ # User information
17
+ user_id = Column(String, nullable=False, index=True)
18
+ user_email = Column(String, nullable=True)
19
+ user_name = Column(String, nullable=True)
20
+ user_avatar = Column(String, nullable=True)
21
+
22
+ # What the user is viewing/interacting with
23
+ agent_id = Column(String, nullable=True, index=True)
24
+ session_id = Column(String, nullable=True, index=True)
25
+ execution_id = Column(String, nullable=True, index=True)
26
+
27
+ # Presence state
28
+ is_active = Column(Boolean, default=True, nullable=False)
29
+ is_typing = Column(Boolean, default=False, nullable=False)
30
+
31
+ # Timestamps
32
+ created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
33
+ last_active_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now(), nullable=False)
34
+
35
+ # Create composite index for efficient queries
36
+ __table_args__ = (
37
+ Index('idx_presence_lookup', 'agent_id', 'is_active', 'last_active_at'),
38
+ Index('idx_presence_user', 'user_id', 'is_active'),
39
+ )
40
+
41
+ def __repr__(self):
42
+ return f"<UserPresence(user_id={self.user_id}, agent_id={self.agent_id}, is_active={self.is_active})>"
43
+
44
+ @property
45
+ def is_stale(self) -> bool:
46
+ """Check if presence is stale (no activity in last 5 minutes)"""
47
+ if not self.last_active_at:
48
+ return True
49
+ return (datetime.utcnow() - self.last_active_at).total_seconds() > 300 # 5 minutes
@@ -0,0 +1,47 @@
1
+ from sqlalchemy import Column, String, DateTime, Text, JSON, Enum, Boolean
2
+ from sqlalchemy.orm import relationship
3
+ from datetime import datetime
4
+ import enum
5
+ import uuid
6
+
7
+ from control_plane_api.app.database import Base
8
+
9
+
10
+ class ProjectStatus(str, enum.Enum):
11
+ """Project status enumeration"""
12
+
13
+ ACTIVE = "active"
14
+ ARCHIVED = "archived"
15
+ PAUSED = "paused"
16
+
17
+
18
+ class Project(Base):
19
+ """Project model for organizing agents, teams, and tasks"""
20
+
21
+ __tablename__ = "projects"
22
+
23
+ id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4()))
24
+ organization_id = Column(String, nullable=False, index=True)
25
+ name = Column(String, nullable=False, index=True)
26
+ description = Column(Text, nullable=True)
27
+ goals = Column(Text, nullable=True)
28
+ status = Column(Enum(ProjectStatus, values_callable=lambda x: [e.value for e in x]), default=ProjectStatus.ACTIVE, nullable=False)
29
+
30
+ # Environment and runner configuration
31
+ restrict_to_environment = Column(Boolean, default=False, nullable=False)
32
+ # Note: policy_ids are stored in settings JSON field, not as separate column
33
+
34
+ # Default settings for project
35
+ default_model = Column(String, nullable=True) # Default LLM model for this project
36
+ default_settings = Column(JSON, default=dict, nullable=False) # Additional project-wide settings
37
+
38
+ # Metadata
39
+ is_default = Column(Boolean, default=False, nullable=False) # Flag for the default project
40
+ created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
41
+ updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False)
42
+
43
+ # Note: Project associations are managed through junction tables (project_agents, project_teams)
44
+ # in Supabase, not through direct foreign keys in the SQLAlchemy models
45
+
46
+ def __repr__(self):
47
+ return f"<Project(id={self.id}, name={self.name}, status={self.status})>"
@@ -0,0 +1,38 @@
1
+ from sqlalchemy import Column, String, DateTime, Text, JSON, ForeignKey
2
+ from sqlalchemy.orm import relationship
3
+ from datetime import datetime
4
+ import uuid
5
+
6
+ from control_plane_api.app.database import Base
7
+
8
+
9
+ class Session(Base):
10
+ """Session model for storing agent session information with multi-user support"""
11
+
12
+ __tablename__ = "sessions"
13
+
14
+ id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4()))
15
+ agent_id = Column(String, ForeignKey("agents.id"), nullable=False)
16
+
17
+ # Multi-user support - inspired by Agno's multi-user pattern
18
+ user_id = Column(String, nullable=True, index=True) # User who owns this session
19
+ user_email = Column(String, nullable=True)
20
+ user_name = Column(String, nullable=True)
21
+ user_avatar = Column(String, nullable=True)
22
+
23
+ # Session data
24
+ messages = Column(JSON, default=list, nullable=False)
25
+ context = Column(JSON, default=dict, nullable=False)
26
+ session_metadata = Column(JSON, default=dict, nullable=False)
27
+
28
+ # Timestamps
29
+ created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
30
+ updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False)
31
+ expires_at = Column(DateTime, nullable=True)
32
+ last_active_at = Column(DateTime, default=datetime.utcnow, nullable=False)
33
+
34
+ # Relationships
35
+ agent = relationship("Agent", back_populates="sessions")
36
+
37
+ def __repr__(self):
38
+ return f"<Session(id={self.id}, agent_id={self.agent_id}, user_id={self.user_id})>"
@@ -0,0 +1,66 @@
1
+ from sqlalchemy import Column, String, DateTime, Text, JSON, Enum, ForeignKey, UniqueConstraint, ARRAY
2
+ from sqlalchemy.dialects.postgresql import UUID as PG_UUID
3
+ from sqlalchemy.orm import relationship
4
+ from datetime import datetime
5
+ import enum
6
+ import uuid as uuid_module
7
+
8
+ from control_plane_api.app.database import Base
9
+ from control_plane_api.app.models.agent import RuntimeType
10
+
11
+
12
+ class TeamStatus(str, enum.Enum):
13
+ """Team status enumeration"""
14
+
15
+ ACTIVE = "active"
16
+ INACTIVE = "inactive"
17
+ ARCHIVED = "archived"
18
+
19
+
20
+ class Team(Base):
21
+ """Team model for storing team information"""
22
+
23
+ __tablename__ = "teams"
24
+ __table_args__ = (
25
+ UniqueConstraint('organization_id', 'name', name='uq_team_org_name'),
26
+ )
27
+
28
+ id = Column(String, primary_key=True, default=lambda: str(uuid_module.uuid4()))
29
+ organization_id = Column(String, nullable=False, index=True)
30
+ name = Column(String, nullable=False, index=True)
31
+ description = Column(Text, nullable=True)
32
+ status = Column(Enum(TeamStatus, values_callable=lambda x: [e.value for e in x]), default=TeamStatus.ACTIVE, nullable=False)
33
+
34
+ # Configuration
35
+ configuration = Column(JSON, default=dict, nullable=False)
36
+ # Use PostgreSQL UUID array - SQLAlchemy will handle the conversion
37
+ skill_ids = Column(ARRAY(PG_UUID(as_uuid=False)), default=list, nullable=False)
38
+ execution_environment = Column(JSON, default=dict, nullable=False)
39
+
40
+ # Runtime configuration
41
+ runtime = Column(
42
+ Enum(RuntimeType, values_callable=lambda x: [e.value for e in x]),
43
+ default=RuntimeType.DEFAULT,
44
+ server_default="default",
45
+ nullable=False,
46
+ index=True
47
+ ) # Runtime type for team execution (default: Agno, claude_code: Claude Code SDK)
48
+
49
+ # Metadata
50
+ created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
51
+ updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False)
52
+
53
+ # Relationships
54
+ agents = relationship("Agent", back_populates="team")
55
+ workflows = relationship("Workflow", back_populates="team", cascade="all, delete-orphan")
56
+
57
+ # Many-to-many relationship with environments
58
+ environment_associations = relationship(
59
+ "TeamEnvironment",
60
+ foreign_keys="TeamEnvironment.team_id",
61
+ cascade="all, delete-orphan",
62
+ lazy="select"
63
+ )
64
+
65
+ def __repr__(self):
66
+ return f"<Team(id={self.id}, name={self.name}, status={self.status})>"
@@ -0,0 +1,55 @@
1
+ from sqlalchemy import Column, String, DateTime, Text, JSON, ForeignKey, Enum
2
+ from sqlalchemy.orm import relationship
3
+ from datetime import datetime
4
+ import enum
5
+ import uuid
6
+
7
+ from control_plane_api.app.database import Base
8
+
9
+
10
+ class WorkflowStatus(str, enum.Enum):
11
+ """Workflow status enumeration"""
12
+
13
+ PENDING = "pending"
14
+ RUNNING = "running"
15
+ PAUSED = "paused"
16
+ COMPLETED = "completed"
17
+ FAILED = "failed"
18
+ CANCELLED = "cancelled"
19
+
20
+
21
+ class Workflow(Base):
22
+ """Workflow model for storing workflow information"""
23
+
24
+ __tablename__ = "workflows"
25
+
26
+ id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4()))
27
+ name = Column(String, nullable=False, index=True)
28
+ description = Column(Text, nullable=True)
29
+ status = Column(Enum(WorkflowStatus, values_callable=lambda x: [e.value for e in x]), default=WorkflowStatus.PENDING, nullable=False)
30
+
31
+ # Workflow definition
32
+ steps = Column(JSON, default=list, nullable=False)
33
+ current_step = Column(String, nullable=True)
34
+
35
+ # Configuration
36
+ configuration = Column(JSON, default=dict, nullable=False)
37
+
38
+ # Relationships
39
+ team_id = Column(String, ForeignKey("teams.id"), nullable=True)
40
+
41
+ # Metadata
42
+ created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
43
+ updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False)
44
+ started_at = Column(DateTime, nullable=True)
45
+ completed_at = Column(DateTime, nullable=True)
46
+
47
+ # State management
48
+ state = Column(JSON, default=dict, nullable=False)
49
+ error_message = Column(Text, nullable=True)
50
+
51
+ # Relationships
52
+ team = relationship("Team", back_populates="workflows")
53
+
54
+ def __repr__(self):
55
+ return f"<Workflow(id={self.id}, name={self.name}, status={self.status})>"