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,529 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Control Plane Client - Clean API for worker to communicate with Control Plane.
|
|
3
|
+
|
|
4
|
+
This centralizes all HTTP communication between worker and Control Plane,
|
|
5
|
+
providing a clean interface for:
|
|
6
|
+
- Event streaming (real-time UI updates)
|
|
7
|
+
- Session persistence (history storage)
|
|
8
|
+
- Metadata caching (execution types)
|
|
9
|
+
- Skill resolution
|
|
10
|
+
|
|
11
|
+
Usage:
|
|
12
|
+
from control_plane_client import get_control_plane_client
|
|
13
|
+
|
|
14
|
+
client = get_control_plane_client()
|
|
15
|
+
client.publish_event(execution_id, "message_chunk", {...})
|
|
16
|
+
client.persist_session(execution_id, session_id, user_id, messages)
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import os
|
|
20
|
+
import httpx
|
|
21
|
+
from datetime import datetime, timezone
|
|
22
|
+
from typing import Optional, Dict, List, Any
|
|
23
|
+
import structlog
|
|
24
|
+
|
|
25
|
+
logger = structlog.get_logger()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ControlPlaneClient:
|
|
29
|
+
"""Client for communicating with the Control Plane API from workers."""
|
|
30
|
+
|
|
31
|
+
def __init__(self, base_url: str, api_key: str):
|
|
32
|
+
"""
|
|
33
|
+
Initialize Control Plane client.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
base_url: Control Plane URL (e.g., http://localhost:8000)
|
|
37
|
+
api_key: Kubiya API key for authentication
|
|
38
|
+
"""
|
|
39
|
+
self.base_url = base_url.rstrip("/")
|
|
40
|
+
self.api_key = api_key
|
|
41
|
+
self.headers = {"Authorization": f"UserKey {api_key}"}
|
|
42
|
+
|
|
43
|
+
# Use BOTH sync and async clients for different use cases
|
|
44
|
+
# Sync client for backwards compatibility with non-async code
|
|
45
|
+
self._client = httpx.Client(
|
|
46
|
+
timeout=httpx.Timeout(30.0, connect=5.0, read=30.0, write=10.0),
|
|
47
|
+
limits=httpx.Limits(max_connections=10, max_keepalive_connections=5),
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
# Async client for streaming/real-time operations
|
|
51
|
+
# Longer read timeout to handle streaming scenarios
|
|
52
|
+
self._async_client = httpx.AsyncClient(
|
|
53
|
+
timeout=httpx.Timeout(60.0, connect=5.0, read=60.0, write=10.0),
|
|
54
|
+
limits=httpx.Limits(max_connections=20, max_keepalive_connections=10),
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
def __del__(self):
|
|
58
|
+
"""Close the HTTP clients on cleanup."""
|
|
59
|
+
try:
|
|
60
|
+
self._client.close()
|
|
61
|
+
except:
|
|
62
|
+
pass
|
|
63
|
+
# Async client cleanup happens via context manager or explicit close
|
|
64
|
+
|
|
65
|
+
async def aclose(self):
|
|
66
|
+
"""Async cleanup for async client."""
|
|
67
|
+
try:
|
|
68
|
+
await self._async_client.aclose()
|
|
69
|
+
except:
|
|
70
|
+
pass
|
|
71
|
+
|
|
72
|
+
def publish_event(
|
|
73
|
+
self,
|
|
74
|
+
execution_id: str,
|
|
75
|
+
event_type: str,
|
|
76
|
+
data: Dict[str, Any],
|
|
77
|
+
) -> bool:
|
|
78
|
+
"""
|
|
79
|
+
Publish a streaming event for real-time UI updates (SYNC version).
|
|
80
|
+
|
|
81
|
+
NOTE: This is the BLOCKING version. For real-time streaming,
|
|
82
|
+
use publish_event_async() instead to avoid blocking the event loop.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
execution_id: Execution ID
|
|
86
|
+
event_type: Event type (message_chunk, tool_started, etc.)
|
|
87
|
+
data: Event payload
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
True if successful, False otherwise
|
|
91
|
+
"""
|
|
92
|
+
try:
|
|
93
|
+
url = f"{self.base_url}/api/v1/executions/{execution_id}/events"
|
|
94
|
+
payload = {
|
|
95
|
+
"event_type": event_type,
|
|
96
|
+
"data": data,
|
|
97
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
response = self._client.post(url, json=payload, headers=self.headers)
|
|
101
|
+
|
|
102
|
+
if response.status_code not in (200, 202):
|
|
103
|
+
logger.warning(
|
|
104
|
+
"event_publish_failed",
|
|
105
|
+
status=response.status_code,
|
|
106
|
+
execution_id=execution_id[:8],
|
|
107
|
+
event_type=event_type,
|
|
108
|
+
)
|
|
109
|
+
return False
|
|
110
|
+
|
|
111
|
+
return True
|
|
112
|
+
|
|
113
|
+
except Exception as e:
|
|
114
|
+
logger.warning(
|
|
115
|
+
"event_publish_error",
|
|
116
|
+
error=str(e),
|
|
117
|
+
execution_id=execution_id[:8],
|
|
118
|
+
event_type=event_type,
|
|
119
|
+
)
|
|
120
|
+
return False
|
|
121
|
+
|
|
122
|
+
async def publish_event_async(
|
|
123
|
+
self,
|
|
124
|
+
execution_id: str,
|
|
125
|
+
event_type: str,
|
|
126
|
+
data: Dict[str, Any],
|
|
127
|
+
) -> bool:
|
|
128
|
+
"""
|
|
129
|
+
Publish a streaming event for real-time UI updates (ASYNC version).
|
|
130
|
+
|
|
131
|
+
This is NON-BLOCKING and should be used for streaming to avoid
|
|
132
|
+
blocking the event loop while waiting for HTTP responses.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
execution_id: Execution ID
|
|
136
|
+
event_type: Event type (message_chunk, tool_started, etc.)
|
|
137
|
+
data: Event payload
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
True if successful, False otherwise
|
|
141
|
+
"""
|
|
142
|
+
try:
|
|
143
|
+
url = f"{self.base_url}/api/v1/executions/{execution_id}/events"
|
|
144
|
+
payload = {
|
|
145
|
+
"event_type": event_type,
|
|
146
|
+
"data": data,
|
|
147
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
response = await self._async_client.post(url, json=payload, headers=self.headers)
|
|
151
|
+
|
|
152
|
+
if response.status_code not in (200, 202):
|
|
153
|
+
logger.warning(
|
|
154
|
+
"event_publish_failed",
|
|
155
|
+
status=response.status_code,
|
|
156
|
+
execution_id=execution_id[:8],
|
|
157
|
+
event_type=event_type,
|
|
158
|
+
)
|
|
159
|
+
return False
|
|
160
|
+
|
|
161
|
+
return True
|
|
162
|
+
|
|
163
|
+
except Exception as e:
|
|
164
|
+
logger.warning(
|
|
165
|
+
"event_publish_error",
|
|
166
|
+
error=str(e),
|
|
167
|
+
execution_id=execution_id[:8],
|
|
168
|
+
event_type=event_type,
|
|
169
|
+
)
|
|
170
|
+
return False
|
|
171
|
+
|
|
172
|
+
def cache_metadata(
|
|
173
|
+
self,
|
|
174
|
+
execution_id: str,
|
|
175
|
+
execution_type: str,
|
|
176
|
+
) -> bool:
|
|
177
|
+
"""
|
|
178
|
+
Cache execution metadata in Redis for fast SSE lookups.
|
|
179
|
+
|
|
180
|
+
This eliminates the need for database queries on every SSE connection.
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
execution_id: Execution ID
|
|
184
|
+
execution_type: "AGENT" or "TEAM"
|
|
185
|
+
|
|
186
|
+
Returns:
|
|
187
|
+
True if successful, False otherwise
|
|
188
|
+
"""
|
|
189
|
+
return self.publish_event(
|
|
190
|
+
execution_id=execution_id,
|
|
191
|
+
event_type="metadata",
|
|
192
|
+
data={"execution_type": execution_type},
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
def get_session(
|
|
196
|
+
self,
|
|
197
|
+
execution_id: str,
|
|
198
|
+
session_id: Optional[str] = None,
|
|
199
|
+
) -> Optional[Dict[str, Any]]:
|
|
200
|
+
"""
|
|
201
|
+
Retrieve session history from Control Plane database.
|
|
202
|
+
|
|
203
|
+
This loads conversation history so workers can restore context
|
|
204
|
+
across multiple execution turns.
|
|
205
|
+
|
|
206
|
+
Args:
|
|
207
|
+
execution_id: Execution ID
|
|
208
|
+
session_id: Session ID (defaults to execution_id if not provided)
|
|
209
|
+
|
|
210
|
+
Returns:
|
|
211
|
+
Dict with session data including messages, or None if not found
|
|
212
|
+
"""
|
|
213
|
+
try:
|
|
214
|
+
session_id = session_id or execution_id
|
|
215
|
+
url = f"{self.base_url}/api/v1/executions/{execution_id}/session"
|
|
216
|
+
|
|
217
|
+
response = self._client.get(url, headers=self.headers)
|
|
218
|
+
|
|
219
|
+
if response.status_code == 200:
|
|
220
|
+
session_data = response.json()
|
|
221
|
+
logger.info(
|
|
222
|
+
"session_loaded",
|
|
223
|
+
execution_id=execution_id[:8],
|
|
224
|
+
message_count=len(session_data.get("messages", [])),
|
|
225
|
+
)
|
|
226
|
+
return session_data
|
|
227
|
+
elif response.status_code == 404:
|
|
228
|
+
logger.info(
|
|
229
|
+
"session_not_found",
|
|
230
|
+
execution_id=execution_id[:8],
|
|
231
|
+
)
|
|
232
|
+
return None
|
|
233
|
+
else:
|
|
234
|
+
logger.warning(
|
|
235
|
+
"session_load_failed",
|
|
236
|
+
status=response.status_code,
|
|
237
|
+
execution_id=execution_id[:8],
|
|
238
|
+
)
|
|
239
|
+
return None
|
|
240
|
+
|
|
241
|
+
except Exception as e:
|
|
242
|
+
logger.warning(
|
|
243
|
+
"session_load_error",
|
|
244
|
+
error=str(e),
|
|
245
|
+
execution_id=execution_id[:8],
|
|
246
|
+
)
|
|
247
|
+
return None
|
|
248
|
+
|
|
249
|
+
def persist_session(
|
|
250
|
+
self,
|
|
251
|
+
execution_id: str,
|
|
252
|
+
session_id: str,
|
|
253
|
+
user_id: Optional[str],
|
|
254
|
+
messages: List[Dict[str, Any]],
|
|
255
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
256
|
+
) -> bool:
|
|
257
|
+
"""
|
|
258
|
+
Persist session history to Control Plane database.
|
|
259
|
+
|
|
260
|
+
This ensures history is available even when worker is offline.
|
|
261
|
+
|
|
262
|
+
Args:
|
|
263
|
+
execution_id: Execution ID
|
|
264
|
+
session_id: Session ID
|
|
265
|
+
user_id: User ID
|
|
266
|
+
messages: List of session messages
|
|
267
|
+
metadata: Optional metadata
|
|
268
|
+
|
|
269
|
+
Returns:
|
|
270
|
+
True if successful, False otherwise
|
|
271
|
+
"""
|
|
272
|
+
try:
|
|
273
|
+
url = f"{self.base_url}/api/v1/executions/{execution_id}/session"
|
|
274
|
+
payload = {
|
|
275
|
+
"session_id": session_id,
|
|
276
|
+
"user_id": user_id,
|
|
277
|
+
"messages": messages,
|
|
278
|
+
"metadata": metadata or {},
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
response = self._client.post(url, json=payload, headers=self.headers)
|
|
282
|
+
|
|
283
|
+
if response.status_code in (200, 201):
|
|
284
|
+
logger.info(
|
|
285
|
+
"session_persisted",
|
|
286
|
+
execution_id=execution_id[:8],
|
|
287
|
+
message_count=len(messages),
|
|
288
|
+
)
|
|
289
|
+
return True
|
|
290
|
+
else:
|
|
291
|
+
logger.warning(
|
|
292
|
+
"session_persistence_failed",
|
|
293
|
+
status=response.status_code,
|
|
294
|
+
execution_id=execution_id[:8],
|
|
295
|
+
)
|
|
296
|
+
return False
|
|
297
|
+
|
|
298
|
+
except Exception as e:
|
|
299
|
+
logger.warning(
|
|
300
|
+
"session_persistence_error",
|
|
301
|
+
error=str(e),
|
|
302
|
+
execution_id=execution_id[:8],
|
|
303
|
+
)
|
|
304
|
+
return False
|
|
305
|
+
|
|
306
|
+
def get_skills(
|
|
307
|
+
self,
|
|
308
|
+
agent_id: str,
|
|
309
|
+
) -> List[Dict[str, Any]]:
|
|
310
|
+
"""
|
|
311
|
+
Fetch resolved skills for an agent from Control Plane.
|
|
312
|
+
|
|
313
|
+
This endpoint returns skills merged from all layers:
|
|
314
|
+
- All agent environments (many-to-many)
|
|
315
|
+
- Team skills (if agent has team)
|
|
316
|
+
- All team environments (many-to-many)
|
|
317
|
+
- Agent's own skills
|
|
318
|
+
|
|
319
|
+
Args:
|
|
320
|
+
agent_id: Agent ID
|
|
321
|
+
|
|
322
|
+
Returns:
|
|
323
|
+
List of skill configurations with source and inheritance info
|
|
324
|
+
"""
|
|
325
|
+
try:
|
|
326
|
+
url = f"{self.base_url}/api/v1/skills/associations/agents/{agent_id}/skills/resolved"
|
|
327
|
+
response = self._client.get(url, headers=self.headers)
|
|
328
|
+
|
|
329
|
+
if response.status_code == 200:
|
|
330
|
+
skills = response.json()
|
|
331
|
+
logger.info(
|
|
332
|
+
"skills_fetched",
|
|
333
|
+
agent_id=agent_id[:8],
|
|
334
|
+
skill_count=len(skills),
|
|
335
|
+
)
|
|
336
|
+
return skills
|
|
337
|
+
else:
|
|
338
|
+
logger.warning(
|
|
339
|
+
"skills_fetch_failed",
|
|
340
|
+
status=response.status_code,
|
|
341
|
+
agent_id=agent_id[:8],
|
|
342
|
+
)
|
|
343
|
+
return []
|
|
344
|
+
|
|
345
|
+
except Exception as e:
|
|
346
|
+
logger.warning(
|
|
347
|
+
"skills_fetch_error",
|
|
348
|
+
error=str(e),
|
|
349
|
+
agent_id=agent_id[:8],
|
|
350
|
+
)
|
|
351
|
+
return []
|
|
352
|
+
|
|
353
|
+
def get_team_skills(
|
|
354
|
+
self,
|
|
355
|
+
team_id: str,
|
|
356
|
+
) -> List[Dict[str, Any]]:
|
|
357
|
+
"""
|
|
358
|
+
Fetch resolved skills for a team from Control Plane.
|
|
359
|
+
|
|
360
|
+
This endpoint returns skills merged from all layers:
|
|
361
|
+
- All team environments (many-to-many)
|
|
362
|
+
- Team's own skills
|
|
363
|
+
|
|
364
|
+
Args:
|
|
365
|
+
team_id: Team ID
|
|
366
|
+
|
|
367
|
+
Returns:
|
|
368
|
+
List of skill configurations with source and inheritance info
|
|
369
|
+
"""
|
|
370
|
+
try:
|
|
371
|
+
url = f"{self.base_url}/api/v1/skills/associations/teams/{team_id}/skills/resolved"
|
|
372
|
+
response = self._client.get(url, headers=self.headers)
|
|
373
|
+
|
|
374
|
+
if response.status_code == 200:
|
|
375
|
+
skills = response.json()
|
|
376
|
+
logger.info(
|
|
377
|
+
"team_skills_fetched",
|
|
378
|
+
team_id=team_id[:8],
|
|
379
|
+
skill_count=len(skills),
|
|
380
|
+
)
|
|
381
|
+
return skills
|
|
382
|
+
else:
|
|
383
|
+
logger.warning(
|
|
384
|
+
"team_skills_fetch_failed",
|
|
385
|
+
status=response.status_code,
|
|
386
|
+
team_id=team_id[:8],
|
|
387
|
+
)
|
|
388
|
+
return []
|
|
389
|
+
|
|
390
|
+
except Exception as e:
|
|
391
|
+
logger.warning(
|
|
392
|
+
"team_skills_fetch_error",
|
|
393
|
+
error=str(e),
|
|
394
|
+
team_id=team_id[:8],
|
|
395
|
+
)
|
|
396
|
+
return []
|
|
397
|
+
|
|
398
|
+
def get_agent_execution_environment(
|
|
399
|
+
self,
|
|
400
|
+
agent_id: str,
|
|
401
|
+
) -> Dict[str, str]:
|
|
402
|
+
"""
|
|
403
|
+
Fetch resolved execution environment for an agent from Control Plane.
|
|
404
|
+
|
|
405
|
+
This endpoint returns a fully resolved environment variable dict with:
|
|
406
|
+
- Custom env vars from agent configuration
|
|
407
|
+
- Secret values (resolved from Kubiya vault)
|
|
408
|
+
- Integration tokens (resolved and mapped to env var names like GH_TOKEN, JIRA_TOKEN)
|
|
409
|
+
|
|
410
|
+
Args:
|
|
411
|
+
agent_id: Agent ID
|
|
412
|
+
|
|
413
|
+
Returns:
|
|
414
|
+
Dict of environment variables ready to inject into agent execution
|
|
415
|
+
"""
|
|
416
|
+
try:
|
|
417
|
+
url = f"{self.base_url}/api/v1/execution-environment/agents/{agent_id}/resolved"
|
|
418
|
+
response = self._client.get(url, headers=self.headers)
|
|
419
|
+
|
|
420
|
+
if response.status_code == 200:
|
|
421
|
+
env_vars = response.json()
|
|
422
|
+
logger.info(
|
|
423
|
+
"agent_execution_environment_fetched",
|
|
424
|
+
agent_id=agent_id[:8],
|
|
425
|
+
env_var_count=len(env_vars),
|
|
426
|
+
env_var_keys=list(env_vars.keys()),
|
|
427
|
+
)
|
|
428
|
+
return env_vars
|
|
429
|
+
else:
|
|
430
|
+
logger.warning(
|
|
431
|
+
"agent_execution_environment_fetch_failed",
|
|
432
|
+
status=response.status_code,
|
|
433
|
+
agent_id=agent_id[:8],
|
|
434
|
+
)
|
|
435
|
+
return {}
|
|
436
|
+
|
|
437
|
+
except Exception as e:
|
|
438
|
+
logger.warning(
|
|
439
|
+
"agent_execution_environment_fetch_error",
|
|
440
|
+
error=str(e),
|
|
441
|
+
agent_id=agent_id[:8],
|
|
442
|
+
)
|
|
443
|
+
return {}
|
|
444
|
+
|
|
445
|
+
def get_team_execution_environment(
|
|
446
|
+
self,
|
|
447
|
+
team_id: str,
|
|
448
|
+
) -> Dict[str, str]:
|
|
449
|
+
"""
|
|
450
|
+
Fetch resolved execution environment for a team from Control Plane.
|
|
451
|
+
|
|
452
|
+
This endpoint returns a fully resolved environment variable dict with:
|
|
453
|
+
- Custom env vars from team configuration
|
|
454
|
+
- Secret values (resolved from Kubiya vault)
|
|
455
|
+
- Integration tokens (resolved and mapped to env var names like GH_TOKEN, JIRA_TOKEN)
|
|
456
|
+
|
|
457
|
+
Args:
|
|
458
|
+
team_id: Team ID
|
|
459
|
+
|
|
460
|
+
Returns:
|
|
461
|
+
Dict of environment variables ready to inject into team execution
|
|
462
|
+
"""
|
|
463
|
+
try:
|
|
464
|
+
url = f"{self.base_url}/api/v1/execution-environment/teams/{team_id}/resolved"
|
|
465
|
+
response = self._client.get(url, headers=self.headers)
|
|
466
|
+
|
|
467
|
+
if response.status_code == 200:
|
|
468
|
+
env_vars = response.json()
|
|
469
|
+
logger.info(
|
|
470
|
+
"team_execution_environment_fetched",
|
|
471
|
+
team_id=team_id[:8],
|
|
472
|
+
env_var_count=len(env_vars),
|
|
473
|
+
env_var_keys=list(env_vars.keys()),
|
|
474
|
+
)
|
|
475
|
+
return env_vars
|
|
476
|
+
else:
|
|
477
|
+
logger.warning(
|
|
478
|
+
"team_execution_environment_fetch_failed",
|
|
479
|
+
status=response.status_code,
|
|
480
|
+
team_id=team_id[:8],
|
|
481
|
+
)
|
|
482
|
+
return {}
|
|
483
|
+
|
|
484
|
+
except Exception as e:
|
|
485
|
+
logger.warning(
|
|
486
|
+
"team_execution_environment_fetch_error",
|
|
487
|
+
error=str(e),
|
|
488
|
+
team_id=team_id[:8],
|
|
489
|
+
)
|
|
490
|
+
return {}
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
# Singleton instance
|
|
494
|
+
_control_plane_client: Optional[ControlPlaneClient] = None
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
def get_control_plane_client() -> ControlPlaneClient:
|
|
498
|
+
"""
|
|
499
|
+
Get or create the Control Plane client singleton.
|
|
500
|
+
|
|
501
|
+
Reads configuration from environment variables:
|
|
502
|
+
- CONTROL_PLANE_URL: Control Plane URL
|
|
503
|
+
- KUBIYA_API_KEY: API key for authentication
|
|
504
|
+
|
|
505
|
+
Returns:
|
|
506
|
+
ControlPlaneClient instance
|
|
507
|
+
|
|
508
|
+
Raises:
|
|
509
|
+
ValueError: If required environment variables are not set
|
|
510
|
+
"""
|
|
511
|
+
global _control_plane_client
|
|
512
|
+
|
|
513
|
+
if _control_plane_client is None:
|
|
514
|
+
base_url = os.environ.get("CONTROL_PLANE_URL")
|
|
515
|
+
api_key = os.environ.get("KUBIYA_API_KEY")
|
|
516
|
+
|
|
517
|
+
if not base_url:
|
|
518
|
+
raise ValueError("CONTROL_PLANE_URL environment variable not set")
|
|
519
|
+
if not api_key:
|
|
520
|
+
raise ValueError("KUBIYA_API_KEY environment variable not set")
|
|
521
|
+
|
|
522
|
+
_control_plane_client = ControlPlaneClient(base_url=base_url, api_key=api_key)
|
|
523
|
+
|
|
524
|
+
logger.info(
|
|
525
|
+
"control_plane_client_initialized",
|
|
526
|
+
base_url=base_url,
|
|
527
|
+
)
|
|
528
|
+
|
|
529
|
+
return _control_plane_client
|