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,354 @@
|
|
|
1
|
+
"""Workflow execution utilities using kubiya-sdk."""
|
|
2
|
+
import json
|
|
3
|
+
import logging
|
|
4
|
+
from typing import Dict, Any, AsyncGenerator, Optional, Union
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
|
|
7
|
+
logger = logging.getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class WorkflowExecutor:
|
|
11
|
+
"""Execute workflows using the kubiya-sdk.
|
|
12
|
+
|
|
13
|
+
This class provides methods to execute workflows defined in:
|
|
14
|
+
- JSON format (compatible with Kubiya workflow schema)
|
|
15
|
+
- Python DSL (using kubiya_sdk.StatefulWorkflow)
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, api_key: Optional[str] = None, base_url: Optional[str] = None):
|
|
19
|
+
"""Initialize the workflow executor.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
api_key: Kubiya API key (optional, will use environment variable if not provided)
|
|
23
|
+
base_url: Kubiya API base URL (optional, defaults to production)
|
|
24
|
+
"""
|
|
25
|
+
self.api_key = api_key
|
|
26
|
+
self.base_url = base_url or "https://api.kubiya.ai"
|
|
27
|
+
|
|
28
|
+
async def execute_json_workflow(
|
|
29
|
+
self,
|
|
30
|
+
workflow_definition: Union[str, Dict[str, Any]],
|
|
31
|
+
parameters: Optional[Dict[str, Any]] = None,
|
|
32
|
+
runner: Optional[str] = None,
|
|
33
|
+
stream: bool = True,
|
|
34
|
+
timeout: int = 3600
|
|
35
|
+
) -> AsyncGenerator[Dict[str, Any], None]:
|
|
36
|
+
"""Execute a JSON workflow definition.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
workflow_definition: Workflow definition as JSON string or dict
|
|
40
|
+
parameters: Parameters to inject into the workflow
|
|
41
|
+
runner: Optional runner/environment name
|
|
42
|
+
stream: Whether to stream execution events
|
|
43
|
+
timeout: Maximum execution timeout in seconds
|
|
44
|
+
|
|
45
|
+
Yields:
|
|
46
|
+
Execution events as dictionaries
|
|
47
|
+
|
|
48
|
+
Raises:
|
|
49
|
+
ValueError: If workflow definition is invalid
|
|
50
|
+
RuntimeError: If execution fails
|
|
51
|
+
"""
|
|
52
|
+
try:
|
|
53
|
+
# Parse workflow definition if it's a string
|
|
54
|
+
if isinstance(workflow_definition, str):
|
|
55
|
+
workflow_data = json.loads(workflow_definition)
|
|
56
|
+
else:
|
|
57
|
+
workflow_data = workflow_definition
|
|
58
|
+
|
|
59
|
+
# Validate workflow structure
|
|
60
|
+
self._validate_workflow_structure(workflow_data)
|
|
61
|
+
|
|
62
|
+
# Initialize kubiya client if available
|
|
63
|
+
try:
|
|
64
|
+
from kubiya_sdk import KubiyaClient
|
|
65
|
+
|
|
66
|
+
client_kwargs = {}
|
|
67
|
+
if self.api_key:
|
|
68
|
+
client_kwargs['api_key'] = self.api_key
|
|
69
|
+
if self.base_url:
|
|
70
|
+
client_kwargs['base_url'] = self.base_url
|
|
71
|
+
|
|
72
|
+
client = KubiyaClient(**client_kwargs)
|
|
73
|
+
|
|
74
|
+
# Execute workflow
|
|
75
|
+
logger.info(
|
|
76
|
+
"Starting workflow execution",
|
|
77
|
+
workflow_name=workflow_data.get('name'),
|
|
78
|
+
stream=stream,
|
|
79
|
+
runner=runner
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
execution_start = datetime.utcnow()
|
|
83
|
+
event_count = 0
|
|
84
|
+
|
|
85
|
+
if stream:
|
|
86
|
+
# Stream execution events
|
|
87
|
+
async for event in client.workflows.execute(
|
|
88
|
+
workflow_definition=workflow_data,
|
|
89
|
+
parameters=parameters or {},
|
|
90
|
+
runner=runner,
|
|
91
|
+
stream=True
|
|
92
|
+
):
|
|
93
|
+
event_count += 1
|
|
94
|
+
|
|
95
|
+
# Enhance event with metadata
|
|
96
|
+
if isinstance(event, dict):
|
|
97
|
+
event['_timestamp'] = datetime.utcnow().isoformat()
|
|
98
|
+
event['_event_number'] = event_count
|
|
99
|
+
|
|
100
|
+
yield event
|
|
101
|
+
|
|
102
|
+
execution_end = datetime.utcnow()
|
|
103
|
+
duration = (execution_end - execution_start).total_seconds()
|
|
104
|
+
|
|
105
|
+
logger.info(
|
|
106
|
+
"Workflow execution completed",
|
|
107
|
+
workflow_name=workflow_data.get('name'),
|
|
108
|
+
duration_seconds=duration,
|
|
109
|
+
event_count=event_count
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
else:
|
|
113
|
+
# Non-streaming execution
|
|
114
|
+
result = await client.workflows.execute(
|
|
115
|
+
workflow_definition=workflow_data,
|
|
116
|
+
parameters=parameters or {},
|
|
117
|
+
runner=runner,
|
|
118
|
+
stream=False
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
execution_end = datetime.utcnow()
|
|
122
|
+
duration = (execution_end - execution_start).total_seconds()
|
|
123
|
+
|
|
124
|
+
logger.info(
|
|
125
|
+
"Workflow execution completed",
|
|
126
|
+
workflow_name=workflow_data.get('name'),
|
|
127
|
+
duration_seconds=duration
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
yield {
|
|
131
|
+
'type': 'execution_complete',
|
|
132
|
+
'result': result,
|
|
133
|
+
'duration_seconds': duration,
|
|
134
|
+
'_timestamp': execution_end.isoformat()
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
except ImportError:
|
|
138
|
+
# If kubiya_sdk is not available, provide a mock response for testing
|
|
139
|
+
logger.warning("kubiya-sdk not available, returning mock execution events")
|
|
140
|
+
|
|
141
|
+
yield {
|
|
142
|
+
'type': 'workflow_start',
|
|
143
|
+
'workflow_name': workflow_data.get('name'),
|
|
144
|
+
'_timestamp': datetime.utcnow().isoformat()
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
for step in workflow_data.get('steps', []):
|
|
148
|
+
yield {
|
|
149
|
+
'type': 'step_start',
|
|
150
|
+
'step_name': step.get('name'),
|
|
151
|
+
'_timestamp': datetime.utcnow().isoformat()
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
yield {
|
|
155
|
+
'type': 'step_complete',
|
|
156
|
+
'step_name': step.get('name'),
|
|
157
|
+
'_timestamp': datetime.utcnow().isoformat()
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
yield {
|
|
161
|
+
'type': 'workflow_complete',
|
|
162
|
+
'workflow_name': workflow_data.get('name'),
|
|
163
|
+
'_timestamp': datetime.utcnow().isoformat()
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
except json.JSONDecodeError as e:
|
|
167
|
+
logger.error("Invalid JSON in workflow definition", error=str(e))
|
|
168
|
+
raise ValueError(f"Invalid JSON in workflow definition: {str(e)}")
|
|
169
|
+
except Exception as e:
|
|
170
|
+
logger.error("Workflow execution failed", error=str(e), workflow_name=workflow_data.get('name') if 'workflow_data' in locals() else 'unknown')
|
|
171
|
+
raise RuntimeError(f"Workflow execution failed: {str(e)}")
|
|
172
|
+
|
|
173
|
+
async def execute_python_dsl(
|
|
174
|
+
self,
|
|
175
|
+
dsl_code: str,
|
|
176
|
+
parameters: Optional[Dict[str, Any]] = None,
|
|
177
|
+
runner: Optional[str] = None,
|
|
178
|
+
timeout: int = 3600
|
|
179
|
+
) -> AsyncGenerator[Dict[str, Any], None]:
|
|
180
|
+
"""Execute a Python DSL workflow.
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
dsl_code: Python code defining the workflow using kubiya_sdk
|
|
184
|
+
parameters: Parameters to pass to the workflow
|
|
185
|
+
runner: Optional runner/environment name
|
|
186
|
+
timeout: Maximum execution timeout in seconds
|
|
187
|
+
|
|
188
|
+
Yields:
|
|
189
|
+
Execution events as dictionaries
|
|
190
|
+
|
|
191
|
+
Raises:
|
|
192
|
+
ValueError: If DSL code is invalid
|
|
193
|
+
RuntimeError: If execution fails
|
|
194
|
+
"""
|
|
195
|
+
try:
|
|
196
|
+
# Import kubiya_sdk modules
|
|
197
|
+
try:
|
|
198
|
+
from kubiya_sdk import StatefulWorkflow, run_workflow_with_progress
|
|
199
|
+
from kubiya_sdk.workflows import step, tool_step
|
|
200
|
+
except ImportError as e:
|
|
201
|
+
raise RuntimeError(f"kubiya-sdk not available: {str(e)}")
|
|
202
|
+
|
|
203
|
+
# Execute DSL code to create workflow
|
|
204
|
+
namespace = {
|
|
205
|
+
'__builtins__': __builtins__,
|
|
206
|
+
'StatefulWorkflow': StatefulWorkflow,
|
|
207
|
+
'step': step,
|
|
208
|
+
'tool_step': tool_step,
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
exec(dsl_code, namespace)
|
|
212
|
+
|
|
213
|
+
# Find the workflow object
|
|
214
|
+
workflow = None
|
|
215
|
+
for name, obj in namespace.items():
|
|
216
|
+
if name.startswith('_'):
|
|
217
|
+
continue
|
|
218
|
+
if hasattr(obj, '__class__'):
|
|
219
|
+
class_name = obj.__class__.__name__
|
|
220
|
+
if 'StatefulWorkflow' in class_name or 'Workflow' in class_name:
|
|
221
|
+
workflow = obj
|
|
222
|
+
break
|
|
223
|
+
|
|
224
|
+
if not workflow:
|
|
225
|
+
raise ValueError("No workflow found in DSL code")
|
|
226
|
+
|
|
227
|
+
logger.info(
|
|
228
|
+
"Starting Python DSL workflow execution",
|
|
229
|
+
workflow_name=getattr(workflow, 'name', 'unknown')
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
execution_start = datetime.utcnow()
|
|
233
|
+
event_count = 0
|
|
234
|
+
|
|
235
|
+
# Execute workflow with progress streaming
|
|
236
|
+
async for event in run_workflow_with_progress(workflow, parameters or {}):
|
|
237
|
+
event_count += 1
|
|
238
|
+
|
|
239
|
+
# Enhance event with metadata
|
|
240
|
+
if isinstance(event, dict):
|
|
241
|
+
event['_timestamp'] = datetime.utcnow().isoformat()
|
|
242
|
+
event['_event_number'] = event_count
|
|
243
|
+
|
|
244
|
+
yield event
|
|
245
|
+
|
|
246
|
+
execution_end = datetime.utcnow()
|
|
247
|
+
duration = (execution_end - execution_start).total_seconds()
|
|
248
|
+
|
|
249
|
+
logger.info(
|
|
250
|
+
"Python DSL workflow execution completed",
|
|
251
|
+
workflow_name=getattr(workflow, 'name', 'unknown'),
|
|
252
|
+
duration_seconds=duration,
|
|
253
|
+
event_count=event_count
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
except Exception as e:
|
|
257
|
+
logger.error("Python DSL workflow execution failed", error=str(e))
|
|
258
|
+
raise RuntimeError(f"Python DSL workflow execution failed: {str(e)}")
|
|
259
|
+
|
|
260
|
+
def _validate_workflow_structure(self, workflow_data: Dict[str, Any]) -> None:
|
|
261
|
+
"""Validate basic workflow structure.
|
|
262
|
+
|
|
263
|
+
Args:
|
|
264
|
+
workflow_data: Workflow definition to validate
|
|
265
|
+
|
|
266
|
+
Raises:
|
|
267
|
+
ValueError: If workflow structure is invalid
|
|
268
|
+
"""
|
|
269
|
+
required_fields = ['name', 'steps']
|
|
270
|
+
|
|
271
|
+
for field in required_fields:
|
|
272
|
+
if field not in workflow_data:
|
|
273
|
+
raise ValueError(f"Missing required field: {field}")
|
|
274
|
+
|
|
275
|
+
if not isinstance(workflow_data['steps'], list):
|
|
276
|
+
raise ValueError("Steps must be a list")
|
|
277
|
+
|
|
278
|
+
if len(workflow_data['steps']) == 0:
|
|
279
|
+
raise ValueError("Workflow must have at least one step")
|
|
280
|
+
|
|
281
|
+
# Validate each step
|
|
282
|
+
step_names = set()
|
|
283
|
+
for step in workflow_data['steps']:
|
|
284
|
+
if 'name' not in step:
|
|
285
|
+
raise ValueError("Each step must have a 'name' field")
|
|
286
|
+
|
|
287
|
+
if step['name'] in step_names:
|
|
288
|
+
raise ValueError(f"Duplicate step name: {step['name']}")
|
|
289
|
+
step_names.add(step['name'])
|
|
290
|
+
|
|
291
|
+
if 'executor' not in step:
|
|
292
|
+
raise ValueError(f"Step '{step['name']}' must have an 'executor' field")
|
|
293
|
+
|
|
294
|
+
executor = step['executor']
|
|
295
|
+
if not isinstance(executor, dict) or 'type' not in executor:
|
|
296
|
+
raise ValueError(f"Step '{step['name']}' executor must be a dict with 'type' field")
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
# Convenience function for executing workflows
|
|
300
|
+
async def execute_workflow(
|
|
301
|
+
workflow_definition: Union[str, Dict[str, Any]],
|
|
302
|
+
workflow_type: str = "json",
|
|
303
|
+
parameters: Optional[Dict[str, Any]] = None,
|
|
304
|
+
runner: Optional[str] = None,
|
|
305
|
+
stream: bool = True,
|
|
306
|
+
timeout: int = 3600,
|
|
307
|
+
api_key: Optional[str] = None,
|
|
308
|
+
base_url: Optional[str] = None
|
|
309
|
+
) -> AsyncGenerator[Dict[str, Any], None]:
|
|
310
|
+
"""Convenience function to execute a workflow.
|
|
311
|
+
|
|
312
|
+
Args:
|
|
313
|
+
workflow_definition: Workflow definition (JSON string/dict or Python DSL code)
|
|
314
|
+
workflow_type: Type of workflow ("json" or "python_dsl")
|
|
315
|
+
parameters: Parameters to inject into the workflow
|
|
316
|
+
runner: Optional runner/environment name
|
|
317
|
+
stream: Whether to stream execution events
|
|
318
|
+
timeout: Maximum execution timeout in seconds
|
|
319
|
+
api_key: Kubiya API key (optional)
|
|
320
|
+
base_url: Kubiya API base URL (optional)
|
|
321
|
+
|
|
322
|
+
Yields:
|
|
323
|
+
Execution events as dictionaries
|
|
324
|
+
|
|
325
|
+
Raises:
|
|
326
|
+
ValueError: If workflow_type is invalid or workflow definition is invalid
|
|
327
|
+
RuntimeError: If execution fails
|
|
328
|
+
"""
|
|
329
|
+
executor = WorkflowExecutor(api_key=api_key, base_url=base_url)
|
|
330
|
+
|
|
331
|
+
if workflow_type == "json":
|
|
332
|
+
async for event in executor.execute_json_workflow(
|
|
333
|
+
workflow_definition=workflow_definition,
|
|
334
|
+
parameters=parameters,
|
|
335
|
+
runner=runner,
|
|
336
|
+
stream=stream,
|
|
337
|
+
timeout=timeout
|
|
338
|
+
):
|
|
339
|
+
yield event
|
|
340
|
+
|
|
341
|
+
elif workflow_type == "python_dsl":
|
|
342
|
+
if not isinstance(workflow_definition, str):
|
|
343
|
+
raise ValueError("Python DSL workflow must be provided as a string")
|
|
344
|
+
|
|
345
|
+
async for event in executor.execute_python_dsl(
|
|
346
|
+
dsl_code=workflow_definition,
|
|
347
|
+
parameters=parameters,
|
|
348
|
+
runner=runner,
|
|
349
|
+
timeout=timeout
|
|
350
|
+
):
|
|
351
|
+
yield event
|
|
352
|
+
|
|
353
|
+
else:
|
|
354
|
+
raise ValueError(f"Invalid workflow_type: {workflow_type}. Must be 'json' or 'python_dsl'")
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""Temporal workflow definitions"""
|
|
2
|
+
|
|
3
|
+
from control_plane_api.app.workflows.agent_execution import AgentExecutionWorkflow, AgentExecutionInput
|
|
4
|
+
from control_plane_api.app.workflows.team_execution import TeamExecutionWorkflow, TeamExecutionInput
|
|
5
|
+
|
|
6
|
+
__all__ = [
|
|
7
|
+
"AgentExecutionWorkflow",
|
|
8
|
+
"AgentExecutionInput",
|
|
9
|
+
"TeamExecutionWorkflow",
|
|
10
|
+
"TeamExecutionInput",
|
|
11
|
+
]
|