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,789 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base runtime abstraction with proper ABC, registry, and Control Plane integration.
|
|
3
|
+
|
|
4
|
+
This module provides:
|
|
5
|
+
- Abstract base class for all runtimes
|
|
6
|
+
- Runtime registry for discovery and instantiation
|
|
7
|
+
- Lifecycle hooks for extensibility
|
|
8
|
+
- Control Plane integration helpers
|
|
9
|
+
- Configuration validation framework
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from typing import AsyncIterator, Dict, Any, Optional, List, Callable, Type
|
|
13
|
+
from dataclasses import dataclass, field
|
|
14
|
+
from enum import Enum
|
|
15
|
+
from abc import ABC, abstractmethod
|
|
16
|
+
import structlog
|
|
17
|
+
|
|
18
|
+
logger = structlog.get_logger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class RuntimeType(str, Enum):
|
|
22
|
+
"""Enumeration of supported runtime types."""
|
|
23
|
+
|
|
24
|
+
DEFAULT = "default" # Agno-based runtime
|
|
25
|
+
CLAUDE_CODE = "claude_code" # Claude Code SDK runtime
|
|
26
|
+
# Easy to add more: LANGCHAIN = "langchain", CREWAI = "crewai", etc.
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class RuntimeExecutionResult:
|
|
31
|
+
"""
|
|
32
|
+
Standardized result structure from any runtime.
|
|
33
|
+
|
|
34
|
+
This ensures all runtimes return consistent data structures that can
|
|
35
|
+
be consumed by the workflow and activity layers.
|
|
36
|
+
|
|
37
|
+
Analytics Integration:
|
|
38
|
+
The `usage` field provides standardized token usage metrics that are
|
|
39
|
+
automatically extracted and submitted to the analytics system.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
response: str
|
|
43
|
+
"""The main response text from the agent."""
|
|
44
|
+
|
|
45
|
+
usage: Dict[str, Any]
|
|
46
|
+
"""
|
|
47
|
+
Token usage metrics with standardized fields:
|
|
48
|
+
- input_tokens (int): Number of input/prompt tokens
|
|
49
|
+
- output_tokens (int): Number of output/completion tokens
|
|
50
|
+
- total_tokens (int): Total tokens used
|
|
51
|
+
- cache_read_tokens (int, optional): Cached tokens read (Anthropic)
|
|
52
|
+
- cache_creation_tokens (int, optional): Tokens used for cache creation (Anthropic)
|
|
53
|
+
- prompt_tokens_details (dict, optional): Detailed breakdown from provider
|
|
54
|
+
|
|
55
|
+
Runtimes should populate this from their native usage tracking.
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
success: bool
|
|
59
|
+
"""Whether the execution succeeded."""
|
|
60
|
+
|
|
61
|
+
finish_reason: Optional[str] = None
|
|
62
|
+
"""Reason the execution finished (e.g., 'stop', 'length', 'tool_use')."""
|
|
63
|
+
|
|
64
|
+
run_id: Optional[str] = None
|
|
65
|
+
"""Unique identifier for this execution run."""
|
|
66
|
+
|
|
67
|
+
model: Optional[str] = None
|
|
68
|
+
"""Model identifier used for this execution."""
|
|
69
|
+
|
|
70
|
+
tool_execution_messages: Optional[List[Dict]] = None
|
|
71
|
+
"""
|
|
72
|
+
Tool execution messages for UI display and analytics.
|
|
73
|
+
Format: [{"tool": "Bash", "input": {...}, "output": {...}, "success": bool, "duration_ms": int}, ...]
|
|
74
|
+
|
|
75
|
+
Analytics Integration:
|
|
76
|
+
These are automatically tracked in the execution_tool_calls table.
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
tool_messages: Optional[List[Dict]] = None
|
|
80
|
+
"""
|
|
81
|
+
Detailed tool messages with execution metadata.
|
|
82
|
+
Format: [{"role": "tool", "content": "...", "tool_use_id": "..."}, ...]
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
error: Optional[str] = None
|
|
86
|
+
"""Error message if execution failed."""
|
|
87
|
+
|
|
88
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
89
|
+
"""
|
|
90
|
+
Additional runtime-specific metadata.
|
|
91
|
+
|
|
92
|
+
Can include:
|
|
93
|
+
- turn_duration_ms (int): Duration of this turn in milliseconds
|
|
94
|
+
- model_provider (str): Provider name (anthropic, openai, google, etc.)
|
|
95
|
+
- tasks (list): Task tracking data for analytics
|
|
96
|
+
- any runtime-specific metrics
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@dataclass
|
|
101
|
+
class RuntimeExecutionContext:
|
|
102
|
+
"""
|
|
103
|
+
Context passed to runtime for execution.
|
|
104
|
+
|
|
105
|
+
This contains all the information needed for an agent to execute,
|
|
106
|
+
regardless of which runtime implementation is used.
|
|
107
|
+
"""
|
|
108
|
+
|
|
109
|
+
execution_id: str
|
|
110
|
+
"""Unique identifier for this execution."""
|
|
111
|
+
|
|
112
|
+
agent_id: str
|
|
113
|
+
"""Unique identifier for the agent being executed."""
|
|
114
|
+
|
|
115
|
+
organization_id: str
|
|
116
|
+
"""Organization context for this execution."""
|
|
117
|
+
|
|
118
|
+
prompt: str
|
|
119
|
+
"""User's input prompt/message."""
|
|
120
|
+
|
|
121
|
+
system_prompt: Optional[str] = None
|
|
122
|
+
"""System-level instructions for the agent."""
|
|
123
|
+
|
|
124
|
+
conversation_history: List[Dict[str, Any]] = field(default_factory=list)
|
|
125
|
+
"""
|
|
126
|
+
Previous conversation messages.
|
|
127
|
+
Format: [{"role": "user|assistant|system", "content": "..."}, ...]
|
|
128
|
+
"""
|
|
129
|
+
|
|
130
|
+
model_id: Optional[str] = None
|
|
131
|
+
"""LiteLLM model identifier (e.g., 'gpt-4', 'claude-3-opus')."""
|
|
132
|
+
|
|
133
|
+
model_config: Optional[Dict[str, Any]] = None
|
|
134
|
+
"""Model-specific configuration (temperature, top_p, etc.)."""
|
|
135
|
+
|
|
136
|
+
agent_config: Optional[Dict[str, Any]] = None
|
|
137
|
+
"""Agent-specific configuration."""
|
|
138
|
+
|
|
139
|
+
skills: List[Any] = field(default_factory=list)
|
|
140
|
+
"""Resolved skills available to the agent."""
|
|
141
|
+
|
|
142
|
+
mcp_servers: Optional[Dict[str, Any]] = None
|
|
143
|
+
"""MCP server configurations."""
|
|
144
|
+
|
|
145
|
+
user_metadata: Optional[Dict[str, Any]] = None
|
|
146
|
+
"""User-provided metadata for this execution."""
|
|
147
|
+
|
|
148
|
+
runtime_config: Optional[Dict[str, Any]] = None
|
|
149
|
+
"""Runtime-specific configuration options."""
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@dataclass
|
|
153
|
+
class RuntimeCapabilities:
|
|
154
|
+
"""Runtime capabilities metadata."""
|
|
155
|
+
|
|
156
|
+
streaming: bool = False
|
|
157
|
+
"""Supports streaming execution."""
|
|
158
|
+
|
|
159
|
+
tools: bool = False
|
|
160
|
+
"""Supports tool calling."""
|
|
161
|
+
|
|
162
|
+
mcp: bool = False
|
|
163
|
+
"""Supports MCP servers."""
|
|
164
|
+
|
|
165
|
+
hooks: bool = False
|
|
166
|
+
"""Supports lifecycle hooks."""
|
|
167
|
+
|
|
168
|
+
cancellation: bool = False
|
|
169
|
+
"""Supports execution cancellation."""
|
|
170
|
+
|
|
171
|
+
conversation_history: bool = False
|
|
172
|
+
"""Supports multi-turn conversations."""
|
|
173
|
+
|
|
174
|
+
custom_tools: bool = False
|
|
175
|
+
"""Supports custom tool registration."""
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
class BaseRuntime(ABC):
|
|
179
|
+
"""
|
|
180
|
+
Abstract base class for all agent runtimes.
|
|
181
|
+
|
|
182
|
+
This class provides:
|
|
183
|
+
- Standard interface for all runtimes
|
|
184
|
+
- Lifecycle hooks for extensibility
|
|
185
|
+
- Control Plane integration helpers
|
|
186
|
+
- Configuration validation
|
|
187
|
+
- Error handling patterns
|
|
188
|
+
|
|
189
|
+
To create a new runtime:
|
|
190
|
+
1. Inherit from BaseRuntime
|
|
191
|
+
2. Implement abstract methods
|
|
192
|
+
3. Register via @RuntimeRegistry.register()
|
|
193
|
+
4. Override lifecycle hooks as needed
|
|
194
|
+
"""
|
|
195
|
+
|
|
196
|
+
def __init__(
|
|
197
|
+
self,
|
|
198
|
+
control_plane_client: Any,
|
|
199
|
+
cancellation_manager: Any,
|
|
200
|
+
**kwargs,
|
|
201
|
+
):
|
|
202
|
+
"""
|
|
203
|
+
Initialize the runtime.
|
|
204
|
+
|
|
205
|
+
Args:
|
|
206
|
+
control_plane_client: Client for Control Plane API
|
|
207
|
+
cancellation_manager: Manager for execution cancellation
|
|
208
|
+
**kwargs: Additional configuration options
|
|
209
|
+
"""
|
|
210
|
+
self.control_plane = control_plane_client
|
|
211
|
+
self.cancellation_manager = cancellation_manager
|
|
212
|
+
self.logger = structlog.get_logger(self.__class__.__name__)
|
|
213
|
+
self.config = kwargs
|
|
214
|
+
|
|
215
|
+
# Track active executions for cleanup
|
|
216
|
+
self._active_executions: Dict[str, Any] = {}
|
|
217
|
+
|
|
218
|
+
# ==================== Abstract Methods (Must Implement) ====================
|
|
219
|
+
|
|
220
|
+
@abstractmethod
|
|
221
|
+
async def _execute_impl(
|
|
222
|
+
self, context: RuntimeExecutionContext
|
|
223
|
+
) -> RuntimeExecutionResult:
|
|
224
|
+
"""
|
|
225
|
+
Core execution logic (non-streaming).
|
|
226
|
+
|
|
227
|
+
Implement the actual execution logic here without worrying about
|
|
228
|
+
lifecycle hooks, Control Plane integration, or error handling.
|
|
229
|
+
The base class handles those concerns.
|
|
230
|
+
|
|
231
|
+
Args:
|
|
232
|
+
context: Execution context
|
|
233
|
+
|
|
234
|
+
Returns:
|
|
235
|
+
RuntimeExecutionResult
|
|
236
|
+
"""
|
|
237
|
+
pass
|
|
238
|
+
|
|
239
|
+
@abstractmethod
|
|
240
|
+
async def _stream_execute_impl(
|
|
241
|
+
self,
|
|
242
|
+
context: RuntimeExecutionContext,
|
|
243
|
+
event_callback: Optional[Callable[[Dict], None]] = None,
|
|
244
|
+
) -> AsyncIterator[RuntimeExecutionResult]:
|
|
245
|
+
"""
|
|
246
|
+
Core streaming execution logic.
|
|
247
|
+
|
|
248
|
+
Implement the actual streaming logic here. The base class
|
|
249
|
+
handles lifecycle hooks and error handling.
|
|
250
|
+
|
|
251
|
+
Args:
|
|
252
|
+
context: Execution context
|
|
253
|
+
event_callback: Optional callback for events
|
|
254
|
+
|
|
255
|
+
Yields:
|
|
256
|
+
RuntimeExecutionResult chunks
|
|
257
|
+
"""
|
|
258
|
+
pass
|
|
259
|
+
|
|
260
|
+
@abstractmethod
|
|
261
|
+
def get_runtime_type(self) -> RuntimeType:
|
|
262
|
+
"""Return the runtime type identifier."""
|
|
263
|
+
pass
|
|
264
|
+
|
|
265
|
+
@abstractmethod
|
|
266
|
+
def get_capabilities(self) -> RuntimeCapabilities:
|
|
267
|
+
"""Return runtime capabilities."""
|
|
268
|
+
pass
|
|
269
|
+
|
|
270
|
+
# ==================== Public Interface (Don't Override) ====================
|
|
271
|
+
|
|
272
|
+
async def execute(
|
|
273
|
+
self, context: RuntimeExecutionContext
|
|
274
|
+
) -> RuntimeExecutionResult:
|
|
275
|
+
"""
|
|
276
|
+
Execute agent with full lifecycle management.
|
|
277
|
+
|
|
278
|
+
This method orchestrates the entire execution lifecycle:
|
|
279
|
+
1. Validate configuration
|
|
280
|
+
2. Call before_execute hook
|
|
281
|
+
3. Cache metadata in Control Plane
|
|
282
|
+
4. Execute via _execute_impl
|
|
283
|
+
5. Call after_execute hook
|
|
284
|
+
6. Handle errors via on_error hook
|
|
285
|
+
7. Cleanup
|
|
286
|
+
|
|
287
|
+
Args:
|
|
288
|
+
context: Execution context
|
|
289
|
+
|
|
290
|
+
Returns:
|
|
291
|
+
RuntimeExecutionResult
|
|
292
|
+
"""
|
|
293
|
+
execution_id = context.execution_id
|
|
294
|
+
|
|
295
|
+
try:
|
|
296
|
+
# 1. Validate configuration
|
|
297
|
+
self._validate_config(context)
|
|
298
|
+
|
|
299
|
+
# 2. Before execute hook
|
|
300
|
+
await self.before_execute(context)
|
|
301
|
+
|
|
302
|
+
# 3. Cache metadata in Control Plane
|
|
303
|
+
self._cache_execution_metadata(context)
|
|
304
|
+
|
|
305
|
+
# 4. Register for cancellation
|
|
306
|
+
if self.get_capabilities().cancellation:
|
|
307
|
+
self.cancellation_manager.register(
|
|
308
|
+
execution_id=execution_id,
|
|
309
|
+
instance=self,
|
|
310
|
+
instance_type=self.__class__.__name__,
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
# 5. Execute implementation
|
|
314
|
+
self.logger.info(
|
|
315
|
+
"runtime_execute_start",
|
|
316
|
+
execution_id=execution_id[:8],
|
|
317
|
+
runtime=self.get_runtime_type().value,
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
result = await self._execute_impl(context)
|
|
321
|
+
|
|
322
|
+
# 6. After execute hook
|
|
323
|
+
await self.after_execute(context, result)
|
|
324
|
+
|
|
325
|
+
self.logger.info(
|
|
326
|
+
"runtime_execute_complete",
|
|
327
|
+
execution_id=execution_id[:8],
|
|
328
|
+
success=result.success,
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
return result
|
|
332
|
+
|
|
333
|
+
except Exception as e:
|
|
334
|
+
# 7. Error hook
|
|
335
|
+
error_result = await self.on_error(context, e)
|
|
336
|
+
return error_result
|
|
337
|
+
|
|
338
|
+
finally:
|
|
339
|
+
# 8. Cleanup
|
|
340
|
+
if self.get_capabilities().cancellation:
|
|
341
|
+
self.cancellation_manager.unregister(execution_id)
|
|
342
|
+
self._active_executions.pop(execution_id, None)
|
|
343
|
+
|
|
344
|
+
async def stream_execute(
|
|
345
|
+
self,
|
|
346
|
+
context: RuntimeExecutionContext,
|
|
347
|
+
event_callback: Optional[Callable[[Dict], None]] = None,
|
|
348
|
+
) -> AsyncIterator[RuntimeExecutionResult]:
|
|
349
|
+
"""
|
|
350
|
+
Execute agent with streaming and full lifecycle management.
|
|
351
|
+
|
|
352
|
+
Args:
|
|
353
|
+
context: Execution context
|
|
354
|
+
event_callback: Optional callback for events
|
|
355
|
+
|
|
356
|
+
Yields:
|
|
357
|
+
RuntimeExecutionResult chunks
|
|
358
|
+
"""
|
|
359
|
+
execution_id = context.execution_id
|
|
360
|
+
|
|
361
|
+
try:
|
|
362
|
+
# 1. Validate configuration
|
|
363
|
+
self._validate_config(context)
|
|
364
|
+
|
|
365
|
+
# 2. Before execute hook
|
|
366
|
+
await self.before_execute(context)
|
|
367
|
+
|
|
368
|
+
# 3. Cache metadata in Control Plane
|
|
369
|
+
self._cache_execution_metadata(context)
|
|
370
|
+
|
|
371
|
+
# 4. Register for cancellation
|
|
372
|
+
if self.get_capabilities().cancellation:
|
|
373
|
+
self.cancellation_manager.register(
|
|
374
|
+
execution_id=execution_id,
|
|
375
|
+
instance=self,
|
|
376
|
+
instance_type=self.__class__.__name__,
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
# 5. Stream implementation
|
|
380
|
+
self.logger.info(
|
|
381
|
+
"runtime_stream_start",
|
|
382
|
+
execution_id=execution_id[:8],
|
|
383
|
+
runtime=self.get_runtime_type().value,
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
final_result = None
|
|
387
|
+
async for chunk in self._stream_execute_impl(context, event_callback):
|
|
388
|
+
yield chunk
|
|
389
|
+
if chunk.finish_reason:
|
|
390
|
+
final_result = chunk
|
|
391
|
+
|
|
392
|
+
# 6. After execute hook
|
|
393
|
+
if final_result:
|
|
394
|
+
await self.after_execute(context, final_result)
|
|
395
|
+
|
|
396
|
+
self.logger.info(
|
|
397
|
+
"runtime_stream_complete",
|
|
398
|
+
execution_id=execution_id[:8],
|
|
399
|
+
)
|
|
400
|
+
|
|
401
|
+
except Exception as e:
|
|
402
|
+
# 7. Error hook
|
|
403
|
+
error_result = await self.on_error(context, e)
|
|
404
|
+
yield error_result
|
|
405
|
+
|
|
406
|
+
finally:
|
|
407
|
+
# 8. Cleanup
|
|
408
|
+
if self.get_capabilities().cancellation:
|
|
409
|
+
self.cancellation_manager.unregister(execution_id)
|
|
410
|
+
self._active_executions.pop(execution_id, None)
|
|
411
|
+
|
|
412
|
+
async def cancel(self, execution_id: str) -> bool:
|
|
413
|
+
"""
|
|
414
|
+
Cancel an in-progress execution.
|
|
415
|
+
|
|
416
|
+
Override _cancel_impl() to provide runtime-specific cancellation logic.
|
|
417
|
+
|
|
418
|
+
Args:
|
|
419
|
+
execution_id: ID of execution to cancel
|
|
420
|
+
|
|
421
|
+
Returns:
|
|
422
|
+
True if cancellation succeeded
|
|
423
|
+
"""
|
|
424
|
+
if not self.get_capabilities().cancellation:
|
|
425
|
+
self.logger.warning(
|
|
426
|
+
"runtime_cancel_not_supported",
|
|
427
|
+
runtime=self.get_runtime_type().value,
|
|
428
|
+
)
|
|
429
|
+
return False
|
|
430
|
+
|
|
431
|
+
try:
|
|
432
|
+
return await self._cancel_impl(execution_id)
|
|
433
|
+
except Exception as e:
|
|
434
|
+
self.logger.error(
|
|
435
|
+
"runtime_cancel_failed",
|
|
436
|
+
execution_id=execution_id[:8],
|
|
437
|
+
error=str(e),
|
|
438
|
+
)
|
|
439
|
+
return False
|
|
440
|
+
|
|
441
|
+
async def get_usage(self, execution_id: str) -> Dict[str, Any]:
|
|
442
|
+
"""
|
|
443
|
+
Get usage metrics for an execution.
|
|
444
|
+
|
|
445
|
+
Override _get_usage_impl() to provide runtime-specific usage tracking.
|
|
446
|
+
|
|
447
|
+
Args:
|
|
448
|
+
execution_id: ID of execution
|
|
449
|
+
|
|
450
|
+
Returns:
|
|
451
|
+
Usage metrics dict
|
|
452
|
+
"""
|
|
453
|
+
try:
|
|
454
|
+
return await self._get_usage_impl(execution_id)
|
|
455
|
+
except Exception as e:
|
|
456
|
+
self.logger.error(
|
|
457
|
+
"runtime_get_usage_failed",
|
|
458
|
+
execution_id=execution_id[:8],
|
|
459
|
+
error=str(e),
|
|
460
|
+
)
|
|
461
|
+
return {}
|
|
462
|
+
|
|
463
|
+
# ==================== Capabilities API ====================
|
|
464
|
+
|
|
465
|
+
def supports_streaming(self) -> bool:
|
|
466
|
+
"""Whether this runtime supports streaming execution."""
|
|
467
|
+
return self.get_capabilities().streaming
|
|
468
|
+
|
|
469
|
+
def supports_tools(self) -> bool:
|
|
470
|
+
"""Whether this runtime supports tool calling."""
|
|
471
|
+
return self.get_capabilities().tools
|
|
472
|
+
|
|
473
|
+
def supports_mcp(self) -> bool:
|
|
474
|
+
"""Whether this runtime supports MCP servers."""
|
|
475
|
+
return self.get_capabilities().mcp
|
|
476
|
+
|
|
477
|
+
def get_runtime_info(self) -> Dict[str, Any]:
|
|
478
|
+
"""
|
|
479
|
+
Get information about this runtime implementation.
|
|
480
|
+
|
|
481
|
+
Override to provide additional metadata.
|
|
482
|
+
|
|
483
|
+
Returns:
|
|
484
|
+
Dict with runtime metadata
|
|
485
|
+
"""
|
|
486
|
+
caps = self.get_capabilities()
|
|
487
|
+
return {
|
|
488
|
+
"runtime_type": self.get_runtime_type().value,
|
|
489
|
+
"supports_streaming": caps.streaming,
|
|
490
|
+
"supports_tools": caps.tools,
|
|
491
|
+
"supports_mcp": caps.mcp,
|
|
492
|
+
"supports_hooks": caps.hooks,
|
|
493
|
+
"supports_cancellation": caps.cancellation,
|
|
494
|
+
"supports_conversation_history": caps.conversation_history,
|
|
495
|
+
"supports_custom_tools": caps.custom_tools,
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
# ==================== Lifecycle Hooks (Override as Needed) ====================
|
|
499
|
+
|
|
500
|
+
async def before_execute(self, context: RuntimeExecutionContext) -> None:
|
|
501
|
+
"""
|
|
502
|
+
Hook called before execution starts.
|
|
503
|
+
|
|
504
|
+
Override to:
|
|
505
|
+
- Validate additional configuration
|
|
506
|
+
- Setup resources
|
|
507
|
+
- Initialize connections
|
|
508
|
+
- Log execution start
|
|
509
|
+
|
|
510
|
+
Args:
|
|
511
|
+
context: Execution context
|
|
512
|
+
"""
|
|
513
|
+
pass
|
|
514
|
+
|
|
515
|
+
async def after_execute(
|
|
516
|
+
self, context: RuntimeExecutionContext, result: RuntimeExecutionResult
|
|
517
|
+
) -> None:
|
|
518
|
+
"""
|
|
519
|
+
Hook called after successful execution.
|
|
520
|
+
|
|
521
|
+
Override to:
|
|
522
|
+
- Cleanup resources
|
|
523
|
+
- Log metrics
|
|
524
|
+
- Update statistics
|
|
525
|
+
- Trigger webhooks
|
|
526
|
+
|
|
527
|
+
Args:
|
|
528
|
+
context: Execution context
|
|
529
|
+
result: Execution result
|
|
530
|
+
"""
|
|
531
|
+
pass
|
|
532
|
+
|
|
533
|
+
async def on_error(
|
|
534
|
+
self, context: RuntimeExecutionContext, error: Exception
|
|
535
|
+
) -> RuntimeExecutionResult:
|
|
536
|
+
"""
|
|
537
|
+
Hook called when execution fails.
|
|
538
|
+
|
|
539
|
+
Override to:
|
|
540
|
+
- Custom error handling
|
|
541
|
+
- Error reporting
|
|
542
|
+
- Cleanup
|
|
543
|
+
- Fallback logic
|
|
544
|
+
|
|
545
|
+
Args:
|
|
546
|
+
context: Execution context
|
|
547
|
+
error: Exception that occurred
|
|
548
|
+
|
|
549
|
+
Returns:
|
|
550
|
+
RuntimeExecutionResult with error details
|
|
551
|
+
"""
|
|
552
|
+
self.logger.error(
|
|
553
|
+
"runtime_execution_failed",
|
|
554
|
+
execution_id=context.execution_id[:8],
|
|
555
|
+
runtime=self.get_runtime_type().value,
|
|
556
|
+
error=str(error),
|
|
557
|
+
error_type=type(error).__name__,
|
|
558
|
+
)
|
|
559
|
+
|
|
560
|
+
return RuntimeExecutionResult(
|
|
561
|
+
response="",
|
|
562
|
+
usage={},
|
|
563
|
+
success=False,
|
|
564
|
+
error=f"{type(error).__name__}: {str(error)}",
|
|
565
|
+
finish_reason="error",
|
|
566
|
+
)
|
|
567
|
+
|
|
568
|
+
# ==================== Helper Methods (Override as Needed) ====================
|
|
569
|
+
|
|
570
|
+
async def _cancel_impl(self, execution_id: str) -> bool:
|
|
571
|
+
"""
|
|
572
|
+
Runtime-specific cancellation implementation.
|
|
573
|
+
|
|
574
|
+
Override to provide custom cancellation logic.
|
|
575
|
+
|
|
576
|
+
Args:
|
|
577
|
+
execution_id: ID of execution to cancel
|
|
578
|
+
|
|
579
|
+
Returns:
|
|
580
|
+
True if successful
|
|
581
|
+
"""
|
|
582
|
+
return False
|
|
583
|
+
|
|
584
|
+
async def _get_usage_impl(self, execution_id: str) -> Dict[str, Any]:
|
|
585
|
+
"""
|
|
586
|
+
Runtime-specific usage tracking implementation.
|
|
587
|
+
|
|
588
|
+
Override to provide usage metrics.
|
|
589
|
+
|
|
590
|
+
Args:
|
|
591
|
+
execution_id: ID of execution
|
|
592
|
+
|
|
593
|
+
Returns:
|
|
594
|
+
Usage metrics dict
|
|
595
|
+
"""
|
|
596
|
+
return {}
|
|
597
|
+
|
|
598
|
+
def _validate_config(self, context: RuntimeExecutionContext) -> None:
|
|
599
|
+
"""
|
|
600
|
+
Validate runtime configuration.
|
|
601
|
+
|
|
602
|
+
Override to add custom validation logic.
|
|
603
|
+
Raise ValueError if configuration is invalid.
|
|
604
|
+
|
|
605
|
+
Args:
|
|
606
|
+
context: Execution context
|
|
607
|
+
|
|
608
|
+
Raises:
|
|
609
|
+
ValueError: If configuration is invalid
|
|
610
|
+
"""
|
|
611
|
+
# Base validation
|
|
612
|
+
if not context.prompt:
|
|
613
|
+
raise ValueError("Prompt is required")
|
|
614
|
+
if not context.execution_id:
|
|
615
|
+
raise ValueError("Execution ID is required")
|
|
616
|
+
|
|
617
|
+
# Runtime-specific requirements validation
|
|
618
|
+
try:
|
|
619
|
+
from control_plane_api.worker.runtimes.validation import RuntimeRequirementsRegistry
|
|
620
|
+
|
|
621
|
+
is_valid, errors = RuntimeRequirementsRegistry.validate_for_runtime(
|
|
622
|
+
self.get_runtime_type(), context
|
|
623
|
+
)
|
|
624
|
+
|
|
625
|
+
if not is_valid:
|
|
626
|
+
error_msg = "Runtime validation failed:\n" + "\n".join(f" - {err}" for err in errors)
|
|
627
|
+
raise ValueError(error_msg)
|
|
628
|
+
|
|
629
|
+
except ImportError:
|
|
630
|
+
# Validation module not available - skip validation
|
|
631
|
+
self.logger.warning("Runtime validation module not available, skipping validation")
|
|
632
|
+
except Exception as e:
|
|
633
|
+
self.logger.error(
|
|
634
|
+
"Runtime validation error",
|
|
635
|
+
error=str(e),
|
|
636
|
+
runtime=self.get_runtime_type().value,
|
|
637
|
+
)
|
|
638
|
+
raise
|
|
639
|
+
|
|
640
|
+
def _cache_execution_metadata(self, context: RuntimeExecutionContext) -> None:
|
|
641
|
+
"""
|
|
642
|
+
Cache execution metadata in Control Plane.
|
|
643
|
+
|
|
644
|
+
This enables:
|
|
645
|
+
- Execution tracking
|
|
646
|
+
- Real-time monitoring
|
|
647
|
+
- Analytics
|
|
648
|
+
|
|
649
|
+
Args:
|
|
650
|
+
context: Execution context
|
|
651
|
+
"""
|
|
652
|
+
try:
|
|
653
|
+
self.control_plane.cache_metadata(
|
|
654
|
+
context.execution_id,
|
|
655
|
+
"AGENT",
|
|
656
|
+
)
|
|
657
|
+
except Exception as e:
|
|
658
|
+
self.logger.warning(
|
|
659
|
+
"failed_to_cache_metadata",
|
|
660
|
+
execution_id=context.execution_id[:8],
|
|
661
|
+
error=str(e),
|
|
662
|
+
)
|
|
663
|
+
|
|
664
|
+
|
|
665
|
+
class RuntimeRegistry:
|
|
666
|
+
"""
|
|
667
|
+
Registry for runtime discovery and instantiation.
|
|
668
|
+
|
|
669
|
+
This registry allows runtimes to be:
|
|
670
|
+
- Automatically discovered
|
|
671
|
+
- Registered via decorator
|
|
672
|
+
- Instantiated by name or type
|
|
673
|
+
- Listed for discoverability
|
|
674
|
+
"""
|
|
675
|
+
|
|
676
|
+
_registry: Dict[RuntimeType, Type[BaseRuntime]] = {}
|
|
677
|
+
|
|
678
|
+
@classmethod
|
|
679
|
+
def register(cls, runtime_type: RuntimeType):
|
|
680
|
+
"""
|
|
681
|
+
Decorator to register a runtime.
|
|
682
|
+
|
|
683
|
+
Usage:
|
|
684
|
+
@RuntimeRegistry.register(RuntimeType.CLAUDE_CODE)
|
|
685
|
+
class ClaudeCodeRuntime(BaseRuntime):
|
|
686
|
+
...
|
|
687
|
+
|
|
688
|
+
Args:
|
|
689
|
+
runtime_type: Type identifier for this runtime
|
|
690
|
+
|
|
691
|
+
Returns:
|
|
692
|
+
Decorator function
|
|
693
|
+
"""
|
|
694
|
+
|
|
695
|
+
def decorator(runtime_class: Type[BaseRuntime]):
|
|
696
|
+
cls._registry[runtime_type] = runtime_class
|
|
697
|
+
logger.info(
|
|
698
|
+
"runtime_registered",
|
|
699
|
+
runtime_type=runtime_type.value,
|
|
700
|
+
runtime_class=runtime_class.__name__,
|
|
701
|
+
)
|
|
702
|
+
return runtime_class
|
|
703
|
+
|
|
704
|
+
return decorator
|
|
705
|
+
|
|
706
|
+
@classmethod
|
|
707
|
+
def get(cls, runtime_type: RuntimeType) -> Type[BaseRuntime]:
|
|
708
|
+
"""
|
|
709
|
+
Get runtime class by type.
|
|
710
|
+
|
|
711
|
+
Args:
|
|
712
|
+
runtime_type: Runtime type to get
|
|
713
|
+
|
|
714
|
+
Returns:
|
|
715
|
+
Runtime class
|
|
716
|
+
|
|
717
|
+
Raises:
|
|
718
|
+
ValueError: If runtime type not found
|
|
719
|
+
"""
|
|
720
|
+
if runtime_type not in cls._registry:
|
|
721
|
+
raise ValueError(
|
|
722
|
+
f"Runtime type '{runtime_type.value}' not registered. "
|
|
723
|
+
f"Available: {list(cls._registry.keys())}"
|
|
724
|
+
)
|
|
725
|
+
return cls._registry[runtime_type]
|
|
726
|
+
|
|
727
|
+
@classmethod
|
|
728
|
+
def create(
|
|
729
|
+
cls,
|
|
730
|
+
runtime_type: RuntimeType,
|
|
731
|
+
control_plane_client: Any,
|
|
732
|
+
cancellation_manager: Any,
|
|
733
|
+
**kwargs,
|
|
734
|
+
) -> BaseRuntime:
|
|
735
|
+
"""
|
|
736
|
+
Create runtime instance.
|
|
737
|
+
|
|
738
|
+
Args:
|
|
739
|
+
runtime_type: Type of runtime to create
|
|
740
|
+
control_plane_client: Control Plane client
|
|
741
|
+
cancellation_manager: Cancellation manager
|
|
742
|
+
**kwargs: Additional configuration
|
|
743
|
+
|
|
744
|
+
Returns:
|
|
745
|
+
Runtime instance
|
|
746
|
+
|
|
747
|
+
Raises:
|
|
748
|
+
ValueError: If runtime type not found
|
|
749
|
+
"""
|
|
750
|
+
runtime_class = cls.get(runtime_type)
|
|
751
|
+
return runtime_class(
|
|
752
|
+
control_plane_client=control_plane_client,
|
|
753
|
+
cancellation_manager=cancellation_manager,
|
|
754
|
+
**kwargs,
|
|
755
|
+
)
|
|
756
|
+
|
|
757
|
+
@classmethod
|
|
758
|
+
def list_available(cls) -> List[RuntimeType]:
|
|
759
|
+
"""
|
|
760
|
+
List all registered runtime types.
|
|
761
|
+
|
|
762
|
+
Returns:
|
|
763
|
+
List of available runtime types
|
|
764
|
+
"""
|
|
765
|
+
return list(cls._registry.keys())
|
|
766
|
+
|
|
767
|
+
@classmethod
|
|
768
|
+
def get_runtime_info_all(cls) -> Dict[str, Dict[str, Any]]:
|
|
769
|
+
"""
|
|
770
|
+
Get information about all registered runtimes.
|
|
771
|
+
|
|
772
|
+
Returns:
|
|
773
|
+
Dict mapping runtime type to info dict
|
|
774
|
+
"""
|
|
775
|
+
info = {}
|
|
776
|
+
for runtime_type, runtime_class in cls._registry.items():
|
|
777
|
+
# Instantiate temporarily to get info (mock dependencies)
|
|
778
|
+
try:
|
|
779
|
+
from unittest.mock import MagicMock
|
|
780
|
+
|
|
781
|
+
temp_instance = runtime_class(
|
|
782
|
+
control_plane_client=MagicMock(),
|
|
783
|
+
cancellation_manager=MagicMock(),
|
|
784
|
+
)
|
|
785
|
+
info[runtime_type.value] = temp_instance.get_runtime_info()
|
|
786
|
+
except Exception as e:
|
|
787
|
+
info[runtime_type.value] = {"error": str(e)}
|
|
788
|
+
|
|
789
|
+
return info
|