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.
- control_plane_api/README.md +266 -0
- control_plane_api/__init__.py +0 -0
- control_plane_api/__version__.py +1 -0
- control_plane_api/alembic/README +1 -0
- control_plane_api/alembic/env.py +98 -0
- control_plane_api/alembic/script.py.mako +28 -0
- control_plane_api/alembic/versions/1382bec74309_initial_migration_with_all_models.py +251 -0
- control_plane_api/alembic/versions/1f54bc2a37e3_add_analytics_tables.py +162 -0
- control_plane_api/alembic/versions/2e4cb136dc10_rename_toolset_ids_to_skill_ids_in_teams.py +30 -0
- control_plane_api/alembic/versions/31cd69a644ce_add_skill_templates_table.py +28 -0
- control_plane_api/alembic/versions/89e127caa47d_add_jobs_and_job_executions_tables.py +161 -0
- control_plane_api/alembic/versions/add_llm_models_table.py +51 -0
- control_plane_api/alembic/versions/b0e10697f212_add_runtime_column_to_teams_simple.py +42 -0
- control_plane_api/alembic/versions/ce43b24b63bf_add_execution_trigger_source_and_fix_.py +155 -0
- control_plane_api/alembic/versions/d4eaf16e3f8d_rename_toolsets_to_skills.py +84 -0
- control_plane_api/alembic/versions/efa2dc427da1_rename_metadata_to_custom_metadata.py +32 -0
- control_plane_api/alembic/versions/f973b431d1ce_add_workflow_executor_to_skill_types.py +44 -0
- control_plane_api/alembic.ini +148 -0
- control_plane_api/api/index.py +12 -0
- control_plane_api/app/__init__.py +11 -0
- control_plane_api/app/activities/__init__.py +20 -0
- control_plane_api/app/activities/agent_activities.py +379 -0
- control_plane_api/app/activities/team_activities.py +410 -0
- control_plane_api/app/activities/temporal_cloud_activities.py +577 -0
- control_plane_api/app/config/__init__.py +35 -0
- control_plane_api/app/config/api_config.py +354 -0
- control_plane_api/app/config/model_pricing.py +318 -0
- control_plane_api/app/config.py +95 -0
- control_plane_api/app/database.py +135 -0
- control_plane_api/app/exceptions.py +408 -0
- control_plane_api/app/lib/__init__.py +11 -0
- control_plane_api/app/lib/job_executor.py +312 -0
- control_plane_api/app/lib/kubiya_client.py +235 -0
- control_plane_api/app/lib/litellm_pricing.py +166 -0
- control_plane_api/app/lib/planning_tools/__init__.py +22 -0
- control_plane_api/app/lib/planning_tools/agents.py +155 -0
- control_plane_api/app/lib/planning_tools/base.py +189 -0
- control_plane_api/app/lib/planning_tools/environments.py +214 -0
- control_plane_api/app/lib/planning_tools/resources.py +240 -0
- control_plane_api/app/lib/planning_tools/teams.py +198 -0
- control_plane_api/app/lib/policy_enforcer_client.py +939 -0
- control_plane_api/app/lib/redis_client.py +436 -0
- control_plane_api/app/lib/supabase.py +71 -0
- control_plane_api/app/lib/temporal_client.py +138 -0
- control_plane_api/app/lib/validation/__init__.py +20 -0
- control_plane_api/app/lib/validation/runtime_validation.py +287 -0
- control_plane_api/app/main.py +128 -0
- control_plane_api/app/middleware/__init__.py +8 -0
- control_plane_api/app/middleware/auth.py +513 -0
- control_plane_api/app/middleware/exception_handler.py +267 -0
- control_plane_api/app/middleware/rate_limiting.py +384 -0
- control_plane_api/app/middleware/request_id.py +202 -0
- control_plane_api/app/models/__init__.py +27 -0
- control_plane_api/app/models/agent.py +79 -0
- control_plane_api/app/models/analytics.py +206 -0
- control_plane_api/app/models/associations.py +81 -0
- control_plane_api/app/models/environment.py +63 -0
- control_plane_api/app/models/execution.py +93 -0
- control_plane_api/app/models/job.py +179 -0
- control_plane_api/app/models/llm_model.py +75 -0
- control_plane_api/app/models/presence.py +49 -0
- control_plane_api/app/models/project.py +47 -0
- control_plane_api/app/models/session.py +38 -0
- control_plane_api/app/models/team.py +66 -0
- control_plane_api/app/models/workflow.py +55 -0
- control_plane_api/app/policies/README.md +121 -0
- control_plane_api/app/policies/approved_users.rego +62 -0
- control_plane_api/app/policies/business_hours.rego +51 -0
- control_plane_api/app/policies/rate_limiting.rego +100 -0
- control_plane_api/app/policies/tool_restrictions.rego +86 -0
- control_plane_api/app/routers/__init__.py +4 -0
- control_plane_api/app/routers/agents.py +364 -0
- control_plane_api/app/routers/agents_v2.py +1260 -0
- control_plane_api/app/routers/analytics.py +1014 -0
- control_plane_api/app/routers/context_manager.py +562 -0
- control_plane_api/app/routers/environment_context.py +270 -0
- control_plane_api/app/routers/environments.py +715 -0
- control_plane_api/app/routers/execution_environment.py +517 -0
- control_plane_api/app/routers/executions.py +1911 -0
- control_plane_api/app/routers/health.py +92 -0
- control_plane_api/app/routers/health_v2.py +326 -0
- control_plane_api/app/routers/integrations.py +274 -0
- control_plane_api/app/routers/jobs.py +1344 -0
- control_plane_api/app/routers/models.py +82 -0
- control_plane_api/app/routers/models_v2.py +361 -0
- control_plane_api/app/routers/policies.py +639 -0
- control_plane_api/app/routers/presence.py +234 -0
- control_plane_api/app/routers/projects.py +902 -0
- control_plane_api/app/routers/runners.py +379 -0
- control_plane_api/app/routers/runtimes.py +172 -0
- control_plane_api/app/routers/secrets.py +155 -0
- control_plane_api/app/routers/skills.py +1001 -0
- control_plane_api/app/routers/skills_definitions.py +140 -0
- control_plane_api/app/routers/task_planning.py +1256 -0
- control_plane_api/app/routers/task_queues.py +654 -0
- control_plane_api/app/routers/team_context.py +270 -0
- control_plane_api/app/routers/teams.py +1400 -0
- control_plane_api/app/routers/worker_queues.py +1545 -0
- control_plane_api/app/routers/workers.py +935 -0
- control_plane_api/app/routers/workflows.py +204 -0
- control_plane_api/app/runtimes/__init__.py +6 -0
- control_plane_api/app/runtimes/validation.py +344 -0
- control_plane_api/app/schemas/job_schemas.py +295 -0
- control_plane_api/app/services/__init__.py +1 -0
- control_plane_api/app/services/agno_service.py +619 -0
- control_plane_api/app/services/litellm_service.py +190 -0
- control_plane_api/app/services/policy_service.py +525 -0
- control_plane_api/app/services/temporal_cloud_provisioning.py +150 -0
- control_plane_api/app/skills/__init__.py +44 -0
- control_plane_api/app/skills/base.py +229 -0
- control_plane_api/app/skills/business_intelligence.py +189 -0
- control_plane_api/app/skills/data_visualization.py +154 -0
- control_plane_api/app/skills/docker.py +104 -0
- control_plane_api/app/skills/file_generation.py +94 -0
- control_plane_api/app/skills/file_system.py +110 -0
- control_plane_api/app/skills/python.py +92 -0
- control_plane_api/app/skills/registry.py +65 -0
- control_plane_api/app/skills/shell.py +102 -0
- control_plane_api/app/skills/workflow_executor.py +469 -0
- control_plane_api/app/utils/workflow_executor.py +354 -0
- control_plane_api/app/workflows/__init__.py +11 -0
- control_plane_api/app/workflows/agent_execution.py +507 -0
- control_plane_api/app/workflows/agent_execution_with_skills.py +222 -0
- control_plane_api/app/workflows/namespace_provisioning.py +326 -0
- control_plane_api/app/workflows/team_execution.py +399 -0
- control_plane_api/scripts/seed_models.py +239 -0
- control_plane_api/worker/__init__.py +0 -0
- control_plane_api/worker/activities/__init__.py +0 -0
- control_plane_api/worker/activities/agent_activities.py +1241 -0
- control_plane_api/worker/activities/approval_activities.py +234 -0
- control_plane_api/worker/activities/runtime_activities.py +388 -0
- control_plane_api/worker/activities/skill_activities.py +267 -0
- control_plane_api/worker/activities/team_activities.py +1217 -0
- control_plane_api/worker/config/__init__.py +31 -0
- control_plane_api/worker/config/worker_config.py +275 -0
- control_plane_api/worker/control_plane_client.py +529 -0
- control_plane_api/worker/examples/analytics_integration_example.py +362 -0
- control_plane_api/worker/models/__init__.py +1 -0
- control_plane_api/worker/models/inputs.py +89 -0
- control_plane_api/worker/runtimes/__init__.py +31 -0
- control_plane_api/worker/runtimes/base.py +789 -0
- control_plane_api/worker/runtimes/claude_code_runtime.py +1443 -0
- control_plane_api/worker/runtimes/default_runtime.py +617 -0
- control_plane_api/worker/runtimes/factory.py +173 -0
- control_plane_api/worker/runtimes/validation.py +93 -0
- control_plane_api/worker/services/__init__.py +1 -0
- control_plane_api/worker/services/agent_executor.py +422 -0
- control_plane_api/worker/services/agent_executor_v2.py +383 -0
- control_plane_api/worker/services/analytics_collector.py +457 -0
- control_plane_api/worker/services/analytics_service.py +464 -0
- control_plane_api/worker/services/approval_tools.py +310 -0
- control_plane_api/worker/services/approval_tools_agno.py +207 -0
- control_plane_api/worker/services/cancellation_manager.py +177 -0
- control_plane_api/worker/services/data_visualization.py +827 -0
- control_plane_api/worker/services/jira_tools.py +257 -0
- control_plane_api/worker/services/runtime_analytics.py +328 -0
- control_plane_api/worker/services/session_service.py +194 -0
- control_plane_api/worker/services/skill_factory.py +175 -0
- control_plane_api/worker/services/team_executor.py +574 -0
- control_plane_api/worker/services/team_executor_v2.py +465 -0
- control_plane_api/worker/services/workflow_executor_tools.py +1418 -0
- control_plane_api/worker/tests/__init__.py +1 -0
- control_plane_api/worker/tests/e2e/__init__.py +0 -0
- control_plane_api/worker/tests/e2e/test_execution_flow.py +571 -0
- control_plane_api/worker/tests/integration/__init__.py +0 -0
- control_plane_api/worker/tests/integration/test_control_plane_integration.py +308 -0
- control_plane_api/worker/tests/unit/__init__.py +0 -0
- control_plane_api/worker/tests/unit/test_control_plane_client.py +401 -0
- control_plane_api/worker/utils/__init__.py +1 -0
- control_plane_api/worker/utils/chunk_batcher.py +305 -0
- control_plane_api/worker/utils/retry_utils.py +60 -0
- control_plane_api/worker/utils/streaming_utils.py +373 -0
- control_plane_api/worker/worker.py +753 -0
- control_plane_api/worker/workflows/__init__.py +0 -0
- control_plane_api/worker/workflows/agent_execution.py +589 -0
- control_plane_api/worker/workflows/team_execution.py +429 -0
- kubiya_control_plane_api-0.3.4.dist-info/METADATA +229 -0
- kubiya_control_plane_api-0.3.4.dist-info/RECORD +182 -0
- kubiya_control_plane_api-0.3.4.dist-info/entry_points.txt +2 -0
- kubiya_control_plane_api-0.3.4.dist-info/top_level.txt +1 -0
- kubiya_control_plane_api-0.1.0.dist-info/METADATA +0 -66
- kubiya_control_plane_api-0.1.0.dist-info/RECORD +0 -5
- kubiya_control_plane_api-0.1.0.dist-info/top_level.txt +0 -1
- {kubiya_control_plane_api-0.1.0.dist-info/licenses → control_plane_api}/LICENSE +0 -0
- {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)}"
|