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,214 @@
1
+ """
2
+ Environment Context Tools - Fetch environment and infrastructure information
3
+ """
4
+
5
+ from typing import Optional
6
+ from control_plane_api.app.lib.planning_tools.base import BasePlanningTools
7
+
8
+
9
+ class EnvironmentsContextTools(BasePlanningTools):
10
+ """
11
+ Tools for fetching environment and infrastructure context
12
+
13
+ Provides methods to:
14
+ - List available execution environments
15
+ - Get worker queue information
16
+ - Check resource availability
17
+ - Query runtime configurations
18
+ """
19
+
20
+ def __init__(self, base_url: str = "http://localhost:8000", organization_id: Optional[str] = None):
21
+ super().__init__(base_url=base_url, organization_id=organization_id)
22
+ self.name = "environment_context_tools"
23
+
24
+ async def list_environments(self, limit: int = 50) -> str:
25
+ """
26
+ List all available execution environments
27
+
28
+ Args:
29
+ limit: Maximum number of environments to return
30
+
31
+ Returns:
32
+ Formatted string with environment information including:
33
+ - Environment name and ID
34
+ - Status (active/inactive)
35
+ - Available resources
36
+ - Configuration details
37
+ """
38
+ try:
39
+ params = {"limit": limit}
40
+ if self.organization_id:
41
+ params["organization_id"] = self.organization_id
42
+
43
+ response = await self._make_request("GET", "/environments", params=params)
44
+
45
+ environments = response if isinstance(response, list) else response.get("environments", [])
46
+
47
+ return self._format_list_response(
48
+ items=environments,
49
+ title="Available Environments",
50
+ key_fields=["status", "type", "region"],
51
+ )
52
+
53
+ except Exception as e:
54
+ return f"Error listing environments: {str(e)}"
55
+
56
+ async def get_environment_details(self, environment_id: str) -> str:
57
+ """
58
+ Get detailed information about a specific environment
59
+
60
+ Args:
61
+ environment_id: ID of the environment to fetch
62
+
63
+ Returns:
64
+ Detailed environment information including:
65
+ - Full configuration
66
+ - Resource limits
67
+ - Network settings
68
+ - Security policies
69
+ """
70
+ try:
71
+ response = await self._make_request("GET", f"/environments/{environment_id}")
72
+
73
+ return self._format_detail_response(
74
+ item=response,
75
+ title=f"Environment Details: {response.get('name', 'Unknown')}",
76
+ )
77
+
78
+ except Exception as e:
79
+ return f"Error fetching environment {environment_id}: {str(e)}"
80
+
81
+ async def list_worker_queues(self, environment_id: Optional[str] = None) -> str:
82
+ """
83
+ List available worker queues for task execution
84
+
85
+ Args:
86
+ environment_id: Optional environment ID to filter queues
87
+
88
+ Returns:
89
+ List of worker queues with:
90
+ - Queue name and ID
91
+ - Active worker count
92
+ - Queue status
93
+ - Processing capacity
94
+ """
95
+ try:
96
+ if environment_id:
97
+ endpoint = f"/environments/{environment_id}/worker-queues"
98
+ else:
99
+ endpoint = "/worker-queues"
100
+
101
+ params = {}
102
+ if self.organization_id:
103
+ params["organization_id"] = self.organization_id
104
+
105
+ response = await self._make_request("GET", endpoint, params=params)
106
+
107
+ queues = response if isinstance(response, list) else response.get("queues", [])
108
+
109
+ return self._format_list_response(
110
+ items=queues,
111
+ title="Available Worker Queues",
112
+ key_fields=["status", "active_workers", "pending_tasks", "environment_id"],
113
+ )
114
+
115
+ except Exception as e:
116
+ return f"Error listing worker queues: {str(e)}"
117
+
118
+ async def get_worker_queue_details(self, queue_id: str) -> str:
119
+ """
120
+ Get detailed information about a specific worker queue
121
+
122
+ Args:
123
+ queue_id: ID of the worker queue
124
+
125
+ Returns:
126
+ Detailed queue information including:
127
+ - Worker capacity and utilization
128
+ - Queue statistics
129
+ - Configuration
130
+ """
131
+ try:
132
+ response = await self._make_request("GET", f"/worker-queues/{queue_id}")
133
+
134
+ return self._format_detail_response(
135
+ item=response,
136
+ title=f"Worker Queue Details: {response.get('name', 'Unknown')}",
137
+ )
138
+
139
+ except Exception as e:
140
+ return f"Error fetching worker queue {queue_id}: {str(e)}"
141
+
142
+ async def check_resource_availability(self, environment_id: str) -> str:
143
+ """
144
+ Check resource availability in a specific environment
145
+
146
+ Args:
147
+ environment_id: ID of the environment to check
148
+
149
+ Returns:
150
+ Resource availability status including:
151
+ - CPU/Memory availability
152
+ - Active workers
153
+ - Queue capacity
154
+ """
155
+ try:
156
+ # Get environment details
157
+ env_response = await self._make_request("GET", f"/environments/{environment_id}")
158
+
159
+ # Get worker queues for this environment
160
+ queues_response = await self._make_request(
161
+ "GET",
162
+ f"/environments/{environment_id}/worker-queues"
163
+ )
164
+
165
+ queues = queues_response if isinstance(queues_response, list) else queues_response.get("queues", [])
166
+
167
+ total_workers = sum(q.get("active_workers", 0) for q in queues)
168
+ total_pending = sum(q.get("pending_tasks", 0) for q in queues)
169
+
170
+ output = [
171
+ f"Resource Availability for Environment: {env_response.get('name', 'Unknown')}",
172
+ f" Status: {env_response.get('status', 'unknown')}",
173
+ f" Worker Queues: {len(queues)}",
174
+ f" Total Active Workers: {total_workers}",
175
+ f" Total Pending Tasks: {total_pending}",
176
+ "",
177
+ "Queue Details:",
178
+ ]
179
+
180
+ for queue in queues:
181
+ output.append(
182
+ f" - {queue.get('name')}: "
183
+ f"{queue.get('active_workers', 0)} workers, "
184
+ f"{queue.get('pending_tasks', 0)} pending"
185
+ )
186
+
187
+ return "\n".join(output)
188
+
189
+ except Exception as e:
190
+ return f"Error checking resource availability: {str(e)}"
191
+
192
+ async def list_runtime_configurations(self) -> str:
193
+ """
194
+ List available runtime configurations
195
+
196
+ Returns:
197
+ Available runtime configurations including:
198
+ - Runtime types (docker, k8s, etc.)
199
+ - Resource templates
200
+ - Default configurations
201
+ """
202
+ try:
203
+ response = await self._make_request("GET", "/runtimes")
204
+
205
+ runtimes = response if isinstance(response, list) else response.get("runtimes", [])
206
+
207
+ return self._format_list_response(
208
+ items=runtimes,
209
+ title="Available Runtime Configurations",
210
+ key_fields=["type", "version", "resource_limits"],
211
+ )
212
+
213
+ except Exception as e:
214
+ return f"Error listing runtime configurations: {str(e)}"
@@ -0,0 +1,240 @@
1
+ """
2
+ Resources Context Tools - Fetch general resource and capability information
3
+ """
4
+
5
+ from typing import Optional
6
+ from control_plane_api.app.lib.planning_tools.base import BasePlanningTools
7
+
8
+
9
+ class ResourcesContextTools(BasePlanningTools):
10
+ """
11
+ Tools for fetching general resource and capability context
12
+
13
+ Provides methods to:
14
+ - List available tools and skills
15
+ - Query project information
16
+ - Get integration capabilities
17
+ - Check system-wide resources
18
+ """
19
+
20
+ def __init__(self, base_url: str = "http://localhost:8000", organization_id: Optional[str] = None):
21
+ super().__init__(base_url=base_url, organization_id=organization_id)
22
+ self.name = "resources_context_tools"
23
+
24
+ async def list_available_skills(self) -> str:
25
+ """
26
+ List all available skills that agents can use
27
+
28
+ Returns:
29
+ Formatted list of skills including:
30
+ - Skill name and category
31
+ - Available tools within each skill
32
+ - Usage descriptions
33
+ - Cost implications
34
+ """
35
+ try:
36
+ response = await self._make_request("GET", "/skills")
37
+
38
+ skills = response if isinstance(response, list) else response.get("skills", [])
39
+
40
+ output = [f"Available Skills ({len(skills)} total):"]
41
+
42
+ for idx, skill in enumerate(skills, 1):
43
+ name = skill.get("name", "Unknown")
44
+ category = skill.get("category", "General")
45
+ tools = skill.get("tools", [])
46
+
47
+ output.append(f"\n{idx}. {name} (Category: {category})")
48
+ output.append(f" Description: {skill.get('description', 'No description')}")
49
+
50
+ if tools:
51
+ output.append(f" Tools ({len(tools)}):")
52
+ for tool in tools[:5]: # Show first 5 tools
53
+ tool_name = tool.get("name", "unknown")
54
+ output.append(f" - {tool_name}")
55
+ if len(tools) > 5:
56
+ output.append(f" ... and {len(tools) - 5} more")
57
+
58
+ return "\n".join(output)
59
+
60
+ except Exception as e:
61
+ return f"Error listing skills: {str(e)}"
62
+
63
+ async def get_skill_details(self, skill_name: str) -> str:
64
+ """
65
+ Get detailed information about a specific skill
66
+
67
+ Args:
68
+ skill_name: Name of the skill to fetch
69
+
70
+ Returns:
71
+ Detailed skill information including all available tools
72
+ """
73
+ try:
74
+ response = await self._make_request("GET", f"/skills/{skill_name}")
75
+
76
+ return self._format_detail_response(
77
+ item=response,
78
+ title=f"Skill Details: {skill_name}",
79
+ )
80
+
81
+ except Exception as e:
82
+ return f"Error fetching skill {skill_name}: {str(e)}"
83
+
84
+ async def list_projects(self, limit: int = 50) -> str:
85
+ """
86
+ List all available projects
87
+
88
+ Args:
89
+ limit: Maximum number of projects to return
90
+
91
+ Returns:
92
+ List of projects with basic information
93
+ """
94
+ try:
95
+ params = {"limit": limit}
96
+ if self.organization_id:
97
+ params["organization_id"] = self.organization_id
98
+
99
+ response = await self._make_request("GET", "/projects", params=params)
100
+
101
+ projects = response if isinstance(response, list) else response.get("projects", [])
102
+
103
+ return self._format_list_response(
104
+ items=projects,
105
+ title="Available Projects",
106
+ key_fields=["description", "status", "owner"],
107
+ )
108
+
109
+ except Exception as e:
110
+ return f"Error listing projects: {str(e)}"
111
+
112
+ async def get_project_details(self, project_id: str) -> str:
113
+ """
114
+ Get detailed information about a specific project
115
+
116
+ Args:
117
+ project_id: ID of the project
118
+
119
+ Returns:
120
+ Detailed project information
121
+ """
122
+ try:
123
+ response = await self._make_request("GET", f"/projects/{project_id}")
124
+
125
+ return self._format_detail_response(
126
+ item=response,
127
+ title=f"Project Details: {response.get('name', 'Unknown')}",
128
+ )
129
+
130
+ except Exception as e:
131
+ return f"Error fetching project {project_id}: {str(e)}"
132
+
133
+ async def list_integrations(self) -> str:
134
+ """
135
+ List all available integrations
136
+
137
+ Returns:
138
+ List of available integrations (Slack, Jira, GitHub, AWS, etc.)
139
+ """
140
+ try:
141
+ response = await self._make_request("GET", "/integrations")
142
+
143
+ integrations = response if isinstance(response, list) else response.get("integrations", [])
144
+
145
+ return self._format_list_response(
146
+ items=integrations,
147
+ title="Available Integrations",
148
+ key_fields=["type", "status", "capabilities"],
149
+ )
150
+
151
+ except Exception as e:
152
+ return f"Error listing integrations: {str(e)}"
153
+
154
+ async def get_organization_info(self) -> str:
155
+ """
156
+ Get information about the current organization
157
+
158
+ Returns:
159
+ Organization information including:
160
+ - Resource limits
161
+ - Active agents/teams count
162
+ - Usage statistics
163
+ """
164
+ try:
165
+ if not self.organization_id:
166
+ return "Organization ID not provided"
167
+
168
+ response = await self._make_request("GET", f"/organizations/{self.organization_id}")
169
+
170
+ # Get additional statistics
171
+ agents = await self._make_request("GET", "/agents", params={"organization_id": self.organization_id})
172
+ teams = await self._make_request("GET", "/teams", params={"organization_id": self.organization_id})
173
+
174
+ agent_count = len(agents) if isinstance(agents, list) else agents.get("count", 0)
175
+ team_count = len(teams) if isinstance(teams, list) else teams.get("count", 0)
176
+
177
+ output = [
178
+ f"Organization: {response.get('name', 'Unknown')}",
179
+ f" ID: {self.organization_id}",
180
+ f" Status: {response.get('status', 'unknown')}",
181
+ f" Active Agents: {agent_count}",
182
+ f" Active Teams: {team_count}",
183
+ ]
184
+
185
+ if "resource_limits" in response:
186
+ output.append("\n Resource Limits:")
187
+ for key, value in response["resource_limits"].items():
188
+ output.append(f" {key}: {value}")
189
+
190
+ return "\n".join(output)
191
+
192
+ except Exception as e:
193
+ return f"Error fetching organization info: {str(e)}"
194
+
195
+ async def search_tools_by_capability(self, capability: str) -> str:
196
+ """
197
+ Search for tools that provide a specific capability
198
+
199
+ Args:
200
+ capability: Capability to search for (e.g., "aws", "kubernetes", "database")
201
+
202
+ Returns:
203
+ List of tools/skills that provide the capability
204
+ """
205
+ try:
206
+ response = await self._make_request("GET", "/skills")
207
+ skills = response if isinstance(response, list) else response.get("skills", [])
208
+
209
+ matching_tools = []
210
+ for skill in skills:
211
+ skill_text = f"{skill.get('name', '')} {skill.get('description', '')}".lower()
212
+ tools = skill.get("tools", [])
213
+
214
+ if capability.lower() in skill_text:
215
+ matching_tools.append({
216
+ "name": skill.get("name"),
217
+ "category": skill.get("category"),
218
+ "description": skill.get("description"),
219
+ "tool_count": len(tools),
220
+ })
221
+ else:
222
+ # Check individual tools
223
+ for tool in tools:
224
+ tool_text = f"{tool.get('name', '')} {tool.get('description', '')}".lower()
225
+ if capability.lower() in tool_text:
226
+ matching_tools.append({
227
+ "name": f"{skill.get('name')}/{tool.get('name')}",
228
+ "category": skill.get("category"),
229
+ "description": tool.get("description"),
230
+ "tool_count": 1,
231
+ })
232
+
233
+ return self._format_list_response(
234
+ items=matching_tools,
235
+ title=f"Tools/Skills with '{capability}' capability",
236
+ key_fields=["category", "description"],
237
+ )
238
+
239
+ except Exception as e:
240
+ return f"Error searching tools: {str(e)}"
@@ -0,0 +1,198 @@
1
+ """
2
+ Team Context Tools - Fetch team information for task planning
3
+ """
4
+
5
+ from typing import Optional
6
+ from control_plane_api.app.lib.planning_tools.base import BasePlanningTools
7
+
8
+
9
+ class TeamsContextTools(BasePlanningTools):
10
+ """
11
+ Tools for fetching team context and composition
12
+
13
+ Provides methods to:
14
+ - List all available teams
15
+ - Get detailed team information
16
+ - Query team member capabilities
17
+ - Check team availability
18
+ """
19
+
20
+ def __init__(self, base_url: str = "http://localhost:8000", organization_id: Optional[str] = None):
21
+ super().__init__(base_url=base_url, organization_id=organization_id)
22
+ self.name = "team_context_tools"
23
+
24
+ async def list_teams(self, limit: int = 50) -> str:
25
+ """
26
+ List all available teams with their basic information
27
+
28
+ Args:
29
+ limit: Maximum number of teams to return
30
+
31
+ Returns:
32
+ Formatted string with team information including:
33
+ - Team name and ID
34
+ - Number of agents
35
+ - Team description
36
+ - Agent composition
37
+ """
38
+ try:
39
+ params = {"limit": limit}
40
+ if self.organization_id:
41
+ params["organization_id"] = self.organization_id
42
+
43
+ response = await self._make_request("GET", "/teams", params=params)
44
+
45
+ teams = response if isinstance(response, list) else response.get("teams", [])
46
+
47
+ return self._format_list_response(
48
+ items=teams,
49
+ title="Available Teams",
50
+ key_fields=["description", "agent_count", "status"],
51
+ )
52
+
53
+ except Exception as e:
54
+ return f"Error listing teams: {str(e)}"
55
+
56
+ async def get_team_details(self, team_id: str) -> str:
57
+ """
58
+ Get detailed information about a specific team
59
+
60
+ Args:
61
+ team_id: ID of the team to fetch
62
+
63
+ Returns:
64
+ Detailed team information including:
65
+ - Full team configuration
66
+ - List of all team members with their roles
67
+ - Team capabilities (aggregate of member capabilities)
68
+ - Coordination strategy
69
+ """
70
+ try:
71
+ response = await self._make_request("GET", f"/teams/{team_id}")
72
+
73
+ team_name = response.get("name", "Unknown")
74
+ agents = response.get("agents", [])
75
+
76
+ output = [
77
+ f"Team Details: {team_name}",
78
+ f" ID: {response.get('id')}",
79
+ f" Description: {response.get('description', 'No description')}",
80
+ f" Agent Count: {len(agents)}",
81
+ "",
82
+ "Team Members:",
83
+ ]
84
+
85
+ for idx, agent in enumerate(agents, 1):
86
+ output.append(f" {idx}. {agent.get('name', 'Unnamed')} (ID: {agent.get('id')})")
87
+ if "model_id" in agent:
88
+ output.append(f" Model: {agent['model_id']}")
89
+ if "description" in agent:
90
+ output.append(f" Capabilities: {agent['description'][:100]}")
91
+
92
+ return "\n".join(output)
93
+
94
+ except Exception as e:
95
+ return f"Error fetching team {team_id}: {str(e)}"
96
+
97
+ async def get_team_members(self, team_id: str) -> str:
98
+ """
99
+ Get list of agents in a specific team
100
+
101
+ Args:
102
+ team_id: ID of the team
103
+
104
+ Returns:
105
+ List of team members with their capabilities
106
+ """
107
+ try:
108
+ response = await self._make_request("GET", f"/teams/{team_id}")
109
+
110
+ agents = response.get("agents", [])
111
+
112
+ if not agents:
113
+ return f"Team {team_id} has no members"
114
+
115
+ return self._format_list_response(
116
+ items=agents,
117
+ title=f"Team Members ({len(agents)} total)",
118
+ key_fields=["model_id", "description", "runner_name"],
119
+ )
120
+
121
+ except Exception as e:
122
+ return f"Error fetching team members: {str(e)}"
123
+
124
+ async def search_teams_by_capability(self, capability: str) -> str:
125
+ """
126
+ Search for teams that have agents with a specific capability
127
+
128
+ Args:
129
+ capability: Capability to search for (e.g., "devops", "security", "data")
130
+
131
+ Returns:
132
+ List of teams with members having the capability
133
+ """
134
+ try:
135
+ params = {}
136
+ if self.organization_id:
137
+ params["organization_id"] = self.organization_id
138
+
139
+ response = await self._make_request("GET", "/teams", params=params)
140
+ teams = response if isinstance(response, list) else response.get("teams", [])
141
+
142
+ matching_teams = []
143
+ for team in teams:
144
+ team_text = f"{team.get('name', '')} {team.get('description', '')}".lower()
145
+ agents = team.get("agents", [])
146
+ agent_text = " ".join([a.get("description", "") for a in agents]).lower()
147
+
148
+ if capability.lower() in team_text or capability.lower() in agent_text:
149
+ matching_teams.append(team)
150
+
151
+ return self._format_list_response(
152
+ items=matching_teams,
153
+ title=f"Teams with '{capability}' capability",
154
+ key_fields=["description", "agent_count"],
155
+ )
156
+
157
+ except Exception as e:
158
+ return f"Error searching teams: {str(e)}"
159
+
160
+ async def get_team_execution_history(self, team_id: str, limit: int = 10) -> str:
161
+ """
162
+ Get recent execution history for a team
163
+
164
+ Args:
165
+ team_id: ID of the team
166
+ limit: Number of recent executions to fetch
167
+
168
+ Returns:
169
+ Recent execution history with success rates
170
+ """
171
+ try:
172
+ params = {"limit": limit, "entity_id": team_id, "execution_type": "team"}
173
+ response = await self._make_request("GET", "/executions", params=params)
174
+
175
+ executions = response if isinstance(response, list) else response.get("executions", [])
176
+
177
+ if not executions:
178
+ return f"No execution history found for team {team_id}"
179
+
180
+ completed = sum(1 for e in executions if e.get("status") == "completed")
181
+ total = len(executions)
182
+ success_rate = (completed / total * 100) if total > 0 else 0
183
+
184
+ output = [
185
+ f"Execution History for Team (Last {total} runs):",
186
+ f"Success Rate: {success_rate:.1f}% ({completed}/{total})",
187
+ "\nRecent Executions:",
188
+ ]
189
+
190
+ for idx, execution in enumerate(executions[:5], 1):
191
+ status = execution.get("status", "unknown")
192
+ prompt = execution.get("prompt", "No description")[:50]
193
+ output.append(f"{idx}. Status: {status} | Task: {prompt}...")
194
+
195
+ return "\n".join(output)
196
+
197
+ except Exception as e:
198
+ return f"Error fetching execution history: {str(e)}"