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,465 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Team executor service with runtime abstraction support.
|
|
3
|
+
|
|
4
|
+
This version supports both Agno-based teams and Claude Code SDK runtime teams.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Dict, Any, Optional, List
|
|
8
|
+
from datetime import datetime, timezone
|
|
9
|
+
import structlog
|
|
10
|
+
import asyncio
|
|
11
|
+
import os
|
|
12
|
+
|
|
13
|
+
from control_plane_api.worker.control_plane_client import ControlPlaneClient
|
|
14
|
+
from control_plane_api.worker.services.session_service import SessionService
|
|
15
|
+
from control_plane_api.worker.services.cancellation_manager import CancellationManager
|
|
16
|
+
from runtimes import (
|
|
17
|
+
RuntimeFactory,
|
|
18
|
+
RuntimeType,
|
|
19
|
+
RuntimeExecutionContext,
|
|
20
|
+
)
|
|
21
|
+
from control_plane_api.worker.utils.streaming_utils import StreamingHelper
|
|
22
|
+
|
|
23
|
+
logger = structlog.get_logger()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class TeamExecutorServiceV2:
|
|
27
|
+
"""
|
|
28
|
+
Service for executing teams using runtime abstraction.
|
|
29
|
+
|
|
30
|
+
This service orchestrates team execution by:
|
|
31
|
+
1. Loading session history
|
|
32
|
+
2. Determining runtime type (Agno or Claude Code)
|
|
33
|
+
3. Delegating execution to appropriate runtime
|
|
34
|
+
4. Persisting session after execution
|
|
35
|
+
|
|
36
|
+
For Claude Code runtime:
|
|
37
|
+
- Team leader uses Claude Code SDK with Task tool
|
|
38
|
+
- Team members are executed as subagents via Task tool
|
|
39
|
+
- Streaming and tool hooks supported
|
|
40
|
+
|
|
41
|
+
For Agno runtime:
|
|
42
|
+
- Uses existing Agno Team implementation
|
|
43
|
+
- Full multi-agent coordination support
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
def __init__(
|
|
47
|
+
self,
|
|
48
|
+
control_plane: ControlPlaneClient,
|
|
49
|
+
session_service: SessionService,
|
|
50
|
+
cancellation_manager: CancellationManager,
|
|
51
|
+
):
|
|
52
|
+
"""
|
|
53
|
+
Initialize the team executor service.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
control_plane: Control Plane API client
|
|
57
|
+
session_service: Session management service
|
|
58
|
+
cancellation_manager: Execution cancellation manager
|
|
59
|
+
"""
|
|
60
|
+
self.control_plane = control_plane
|
|
61
|
+
self.session_service = session_service
|
|
62
|
+
self.cancellation_manager = cancellation_manager
|
|
63
|
+
self.runtime_factory = RuntimeFactory()
|
|
64
|
+
|
|
65
|
+
async def execute(self, input: Any) -> Dict[str, Any]:
|
|
66
|
+
"""
|
|
67
|
+
Execute a team using the configured runtime.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
input: TeamExecutionInput with execution details
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
Dict with response, usage, success flag, runtime_type, etc.
|
|
74
|
+
"""
|
|
75
|
+
execution_id = input.execution_id
|
|
76
|
+
|
|
77
|
+
print("\n" + "=" * 80)
|
|
78
|
+
print("🚀 TEAM EXECUTION START (Runtime-Abstracted)")
|
|
79
|
+
print("=" * 80)
|
|
80
|
+
print(f"Execution ID: {execution_id}")
|
|
81
|
+
print(f"Team ID: {input.team_id}")
|
|
82
|
+
print(f"Organization: {input.organization_id}")
|
|
83
|
+
print(f"Agent Count: {len(input.agents)}")
|
|
84
|
+
print(f"Session ID: {input.session_id}")
|
|
85
|
+
print(f"Prompt: {input.prompt[:100]}..." if len(input.prompt) > 100 else f"Prompt: {input.prompt}")
|
|
86
|
+
print("=" * 80 + "\n")
|
|
87
|
+
|
|
88
|
+
logger.info(
|
|
89
|
+
"team_execution_start",
|
|
90
|
+
execution_id=execution_id[:8],
|
|
91
|
+
team_id=input.team_id,
|
|
92
|
+
session_id=input.session_id,
|
|
93
|
+
agent_count=len(input.agents)
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
try:
|
|
97
|
+
# STEP 1: Load session history
|
|
98
|
+
print("📚 Loading session history...")
|
|
99
|
+
session_history = self.session_service.load_session(
|
|
100
|
+
execution_id=execution_id,
|
|
101
|
+
session_id=input.session_id
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
if session_history:
|
|
105
|
+
print(f"✅ Loaded {len(session_history)} messages from previous session\n")
|
|
106
|
+
else:
|
|
107
|
+
print("ℹ️ Starting new conversation\n")
|
|
108
|
+
|
|
109
|
+
# STEP 2: Determine runtime type from team configuration
|
|
110
|
+
team_config = getattr(input, "team_config", {}) or {}
|
|
111
|
+
runtime_type_str = team_config.get("runtime", "default")
|
|
112
|
+
runtime_type = self.runtime_factory.parse_runtime_type(runtime_type_str)
|
|
113
|
+
|
|
114
|
+
print(f"🔌 Runtime Type: {runtime_type.value}")
|
|
115
|
+
print(f" Framework: {self._get_framework_name(runtime_type)}\n")
|
|
116
|
+
|
|
117
|
+
logger.info(
|
|
118
|
+
"runtime_selected",
|
|
119
|
+
execution_id=execution_id[:8],
|
|
120
|
+
runtime=runtime_type.value,
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
# STEP 3: Execute based on runtime type
|
|
124
|
+
if runtime_type == RuntimeType.CLAUDE_CODE:
|
|
125
|
+
result = await self._execute_with_claude_code(input, session_history)
|
|
126
|
+
else:
|
|
127
|
+
# Fall back to Agno-based team execution
|
|
128
|
+
from control_plane_api.worker.services.team_executor import TeamExecutorService
|
|
129
|
+
|
|
130
|
+
agno_executor = TeamExecutorService(
|
|
131
|
+
self.control_plane,
|
|
132
|
+
self.session_service,
|
|
133
|
+
self.cancellation_manager
|
|
134
|
+
)
|
|
135
|
+
return await agno_executor.execute(input)
|
|
136
|
+
|
|
137
|
+
print("\n✅ Team execution completed!")
|
|
138
|
+
print(f" Response Length: {len(result['response'])} chars")
|
|
139
|
+
print(f" Success: {result['success']}\n")
|
|
140
|
+
|
|
141
|
+
logger.info(
|
|
142
|
+
"team_execution_completed",
|
|
143
|
+
execution_id=execution_id[:8],
|
|
144
|
+
success=result["success"],
|
|
145
|
+
response_length=len(result["response"]),
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
# STEP 4: Persist session
|
|
149
|
+
if result["success"] and result["response"]:
|
|
150
|
+
print("💾 Persisting session history...")
|
|
151
|
+
|
|
152
|
+
# Build new messages
|
|
153
|
+
new_messages = [
|
|
154
|
+
{"role": "user", "content": input.prompt},
|
|
155
|
+
{"role": "assistant", "content": result["response"]},
|
|
156
|
+
]
|
|
157
|
+
|
|
158
|
+
# Combine with previous history
|
|
159
|
+
complete_session = session_history + new_messages
|
|
160
|
+
|
|
161
|
+
success = self.session_service.persist_session(
|
|
162
|
+
execution_id=execution_id,
|
|
163
|
+
session_id=input.session_id or execution_id,
|
|
164
|
+
user_id=input.user_id,
|
|
165
|
+
messages=complete_session,
|
|
166
|
+
metadata={
|
|
167
|
+
"team_id": input.team_id,
|
|
168
|
+
"organization_id": input.organization_id,
|
|
169
|
+
"runtime_type": runtime_type.value,
|
|
170
|
+
"turn_count": len(complete_session),
|
|
171
|
+
"member_count": len(input.agents),
|
|
172
|
+
},
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
if success:
|
|
176
|
+
print(f"✅ Session persisted ({len(complete_session)} total messages)\n")
|
|
177
|
+
else:
|
|
178
|
+
print(f"⚠️ Session persistence failed\n")
|
|
179
|
+
|
|
180
|
+
# STEP 5: Print usage metrics
|
|
181
|
+
if result.get("usage"):
|
|
182
|
+
print(f"📊 Token Usage:")
|
|
183
|
+
print(f" Input: {result['usage'].get('prompt_tokens', 0)}")
|
|
184
|
+
print(f" Output: {result['usage'].get('completion_tokens', 0)}")
|
|
185
|
+
print(f" Total: {result['usage'].get('total_tokens', 0)}\n")
|
|
186
|
+
|
|
187
|
+
print("=" * 80)
|
|
188
|
+
print("🏁 TEAM EXECUTION END")
|
|
189
|
+
print("=" * 80 + "\n")
|
|
190
|
+
|
|
191
|
+
return result
|
|
192
|
+
|
|
193
|
+
except Exception as e:
|
|
194
|
+
print("\n" + "=" * 80)
|
|
195
|
+
print("❌ TEAM EXECUTION FAILED")
|
|
196
|
+
print("=" * 80)
|
|
197
|
+
print(f"Error: {str(e)}")
|
|
198
|
+
print("=" * 80 + "\n")
|
|
199
|
+
|
|
200
|
+
logger.error(
|
|
201
|
+
"team_execution_failed",
|
|
202
|
+
execution_id=execution_id[:8],
|
|
203
|
+
error=str(e)
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
return {
|
|
207
|
+
"success": False,
|
|
208
|
+
"error": str(e),
|
|
209
|
+
"model": input.model_id,
|
|
210
|
+
"usage": {},
|
|
211
|
+
"finish_reason": "error",
|
|
212
|
+
"runtime_type": runtime_type_str if "runtime_type_str" in locals() else "unknown",
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
async def _execute_with_claude_code(
|
|
216
|
+
self, input: Any, session_history: List[Dict]
|
|
217
|
+
) -> Dict[str, Any]:
|
|
218
|
+
"""
|
|
219
|
+
Execute team using Claude Code SDK.
|
|
220
|
+
|
|
221
|
+
Strategy:
|
|
222
|
+
- Team leader is a Claude Code agent with Task tool enabled
|
|
223
|
+
- Team members are defined as context for the leader
|
|
224
|
+
- Leader uses Task tool to delegate work to members
|
|
225
|
+
- Streaming and hooks provide real-time feedback
|
|
226
|
+
|
|
227
|
+
Args:
|
|
228
|
+
input: TeamExecutionInput
|
|
229
|
+
session_history: Previous conversation messages
|
|
230
|
+
|
|
231
|
+
Returns:
|
|
232
|
+
Result dict
|
|
233
|
+
"""
|
|
234
|
+
execution_id = input.execution_id
|
|
235
|
+
|
|
236
|
+
print(f"⚙️ Creating Claude Code team leader...")
|
|
237
|
+
|
|
238
|
+
# Create runtime instance
|
|
239
|
+
runtime = self.runtime_factory.create_runtime(
|
|
240
|
+
runtime_type=RuntimeType.CLAUDE_CODE,
|
|
241
|
+
control_plane_client=self.control_plane,
|
|
242
|
+
cancellation_manager=self.cancellation_manager,
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
print(f"✅ Runtime created: {runtime.get_runtime_info()}\n")
|
|
246
|
+
|
|
247
|
+
# STEP 1: Build team context for system prompt
|
|
248
|
+
team_context = self._build_team_context(input.agents)
|
|
249
|
+
|
|
250
|
+
system_prompt = f"""You are the team leader coordinating a team of specialized AI agents.
|
|
251
|
+
|
|
252
|
+
Your team members:
|
|
253
|
+
{team_context}
|
|
254
|
+
|
|
255
|
+
When you need a team member to perform a task:
|
|
256
|
+
1. Use the Task tool to delegate work to the appropriate agent
|
|
257
|
+
2. Provide clear instructions in the subagent_type parameter
|
|
258
|
+
3. Wait for their response before continuing
|
|
259
|
+
4. Synthesize the results into a cohesive answer
|
|
260
|
+
|
|
261
|
+
Your goal is to coordinate the team effectively to solve the user's request.
|
|
262
|
+
"""
|
|
263
|
+
|
|
264
|
+
print(f"📋 Team Context:")
|
|
265
|
+
print(f" Team Members: {len(input.agents)}")
|
|
266
|
+
for agent in input.agents:
|
|
267
|
+
print(f" - {agent.get('name')}: {agent.get('role', 'No role specified')[:60]}...")
|
|
268
|
+
print()
|
|
269
|
+
|
|
270
|
+
# STEP 2: Get skills for team leader (must include Task tool)
|
|
271
|
+
print(f"🔧 Fetching skills from Control Plane...")
|
|
272
|
+
skills = []
|
|
273
|
+
try:
|
|
274
|
+
# Get skills from first agent (team leader)
|
|
275
|
+
if input.agents:
|
|
276
|
+
leader_id = input.agents[0].get("id")
|
|
277
|
+
if leader_id:
|
|
278
|
+
skill_configs = self.control_plane.get_skills(leader_id)
|
|
279
|
+
if skill_configs:
|
|
280
|
+
print(f"✅ Resolved {len(skill_configs)} skills")
|
|
281
|
+
|
|
282
|
+
from services.skill_factory import SkillFactory
|
|
283
|
+
skills = SkillFactory.create_skills_from_list(skill_configs)
|
|
284
|
+
|
|
285
|
+
if skills:
|
|
286
|
+
print(f"✅ Instantiated {len(skills)} skill(s)")
|
|
287
|
+
except Exception as e:
|
|
288
|
+
print(f"⚠️ Error fetching skills: {str(e)}")
|
|
289
|
+
logger.error("skill_fetch_error", error=str(e))
|
|
290
|
+
|
|
291
|
+
# Always ensure Task tool is available for delegation
|
|
292
|
+
task_skill = {"type": "task", "name": "Task"}
|
|
293
|
+
if task_skill not in skills:
|
|
294
|
+
skills.append(task_skill)
|
|
295
|
+
print(f"✅ Added Task tool for team coordination\n")
|
|
296
|
+
|
|
297
|
+
# STEP 3: Build execution context
|
|
298
|
+
print("📦 Building execution context...")
|
|
299
|
+
team_config = getattr(input, "team_config", {}) or {}
|
|
300
|
+
context = RuntimeExecutionContext(
|
|
301
|
+
execution_id=execution_id,
|
|
302
|
+
agent_id=input.team_id, # Use team_id as agent_id
|
|
303
|
+
organization_id=input.organization_id,
|
|
304
|
+
prompt=input.prompt,
|
|
305
|
+
system_prompt=system_prompt,
|
|
306
|
+
conversation_history=session_history,
|
|
307
|
+
model_id=input.model_id,
|
|
308
|
+
model_config=getattr(input, "model_config", None),
|
|
309
|
+
agent_config=team_config,
|
|
310
|
+
skills=skills,
|
|
311
|
+
mcp_servers=getattr(input, "mcp_servers", None),
|
|
312
|
+
user_metadata=getattr(input, "user_metadata", None),
|
|
313
|
+
runtime_config=team_config.get("runtime_config"),
|
|
314
|
+
)
|
|
315
|
+
print("✅ Context ready\n")
|
|
316
|
+
|
|
317
|
+
# STEP 4: Execute via runtime with streaming
|
|
318
|
+
print("⚡ Executing team via Claude Code runtime...\n")
|
|
319
|
+
|
|
320
|
+
if runtime.supports_streaming():
|
|
321
|
+
result = await self._execute_streaming(runtime, context, input)
|
|
322
|
+
else:
|
|
323
|
+
exec_result = await runtime.execute(context)
|
|
324
|
+
result = {
|
|
325
|
+
"success": exec_result.success,
|
|
326
|
+
"response": exec_result.response,
|
|
327
|
+
"usage": exec_result.usage,
|
|
328
|
+
"model": exec_result.model or input.model_id,
|
|
329
|
+
"finish_reason": exec_result.finish_reason or "stop",
|
|
330
|
+
"tool_messages": exec_result.tool_messages or [],
|
|
331
|
+
"runtime_type": "claude_code",
|
|
332
|
+
"error": exec_result.error,
|
|
333
|
+
"team_member_count": len(input.agents),
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
return result
|
|
337
|
+
|
|
338
|
+
async def _execute_streaming(
|
|
339
|
+
self, runtime, context: RuntimeExecutionContext, input: Any
|
|
340
|
+
) -> Dict[str, Any]:
|
|
341
|
+
"""
|
|
342
|
+
Execute with streaming and publish events to Control Plane.
|
|
343
|
+
|
|
344
|
+
Args:
|
|
345
|
+
runtime: Runtime instance
|
|
346
|
+
context: Execution context
|
|
347
|
+
input: Original input for additional metadata
|
|
348
|
+
|
|
349
|
+
Returns:
|
|
350
|
+
Result dict
|
|
351
|
+
"""
|
|
352
|
+
# Create streaming helper for publishing events
|
|
353
|
+
streaming_helper = StreamingHelper(
|
|
354
|
+
control_plane_client=self.control_plane,
|
|
355
|
+
execution_id=context.execution_id
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
accumulated_response = ""
|
|
359
|
+
final_result = None
|
|
360
|
+
|
|
361
|
+
# Define event callback for publishing to Control Plane
|
|
362
|
+
def event_callback(event: Dict):
|
|
363
|
+
"""Callback to publish events to Control Plane SSE"""
|
|
364
|
+
event_type = event.get("type")
|
|
365
|
+
|
|
366
|
+
if event_type == "content_chunk":
|
|
367
|
+
# Publish content chunk
|
|
368
|
+
streaming_helper.publish_content_chunk(
|
|
369
|
+
content=event.get("content", ""),
|
|
370
|
+
message_id=event.get("message_id", context.execution_id),
|
|
371
|
+
)
|
|
372
|
+
elif event_type == "tool_start":
|
|
373
|
+
# Publish tool start
|
|
374
|
+
streaming_helper.publish_tool_start(
|
|
375
|
+
tool_name=event.get("tool_name"),
|
|
376
|
+
tool_execution_id=event.get("tool_execution_id"),
|
|
377
|
+
tool_args=event.get("tool_args", {}),
|
|
378
|
+
source="team_leader",
|
|
379
|
+
)
|
|
380
|
+
elif event_type == "tool_complete":
|
|
381
|
+
# Publish tool completion
|
|
382
|
+
streaming_helper.publish_tool_complete(
|
|
383
|
+
tool_name=event.get("tool_name"),
|
|
384
|
+
tool_execution_id=event.get("tool_execution_id"),
|
|
385
|
+
status=event.get("status", "success"),
|
|
386
|
+
output=event.get("output"),
|
|
387
|
+
error=event.get("error"),
|
|
388
|
+
source="team_leader",
|
|
389
|
+
)
|
|
390
|
+
|
|
391
|
+
# Stream execution
|
|
392
|
+
async for chunk in runtime.stream_execute(context, event_callback):
|
|
393
|
+
if chunk.response:
|
|
394
|
+
accumulated_response += chunk.response
|
|
395
|
+
print(chunk.response, end="", flush=True)
|
|
396
|
+
|
|
397
|
+
# Keep final result for metadata
|
|
398
|
+
if chunk.usage or chunk.finish_reason:
|
|
399
|
+
final_result = chunk
|
|
400
|
+
|
|
401
|
+
print() # New line after streaming
|
|
402
|
+
|
|
403
|
+
# Return final result with accumulated response
|
|
404
|
+
if final_result:
|
|
405
|
+
return {
|
|
406
|
+
"success": final_result.success,
|
|
407
|
+
"response": accumulated_response,
|
|
408
|
+
"usage": final_result.usage,
|
|
409
|
+
"model": final_result.model or input.model_id,
|
|
410
|
+
"finish_reason": final_result.finish_reason or "stop",
|
|
411
|
+
"tool_messages": final_result.tool_messages or [],
|
|
412
|
+
"runtime_type": "claude_code",
|
|
413
|
+
"error": final_result.error,
|
|
414
|
+
"team_member_count": len(input.agents),
|
|
415
|
+
}
|
|
416
|
+
else:
|
|
417
|
+
return {
|
|
418
|
+
"success": True,
|
|
419
|
+
"response": accumulated_response,
|
|
420
|
+
"usage": {},
|
|
421
|
+
"model": input.model_id,
|
|
422
|
+
"finish_reason": "stop",
|
|
423
|
+
"tool_messages": [],
|
|
424
|
+
"runtime_type": "claude_code",
|
|
425
|
+
"team_member_count": len(input.agents),
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
def _build_team_context(self, agents: List[Dict]) -> str:
|
|
429
|
+
"""
|
|
430
|
+
Build team context description for system prompt.
|
|
431
|
+
|
|
432
|
+
Args:
|
|
433
|
+
agents: List of agent configurations
|
|
434
|
+
|
|
435
|
+
Returns:
|
|
436
|
+
Formatted team context string
|
|
437
|
+
"""
|
|
438
|
+
context_lines = []
|
|
439
|
+
for i, agent in enumerate(agents, 1):
|
|
440
|
+
name = agent.get("name", f"Agent {i}")
|
|
441
|
+
role = agent.get("role", "No role specified")
|
|
442
|
+
agent_id = agent.get("id", "unknown")
|
|
443
|
+
|
|
444
|
+
context_lines.append(
|
|
445
|
+
f"{i}. **{name}** (ID: {agent_id})\n"
|
|
446
|
+
f" Role: {role}\n"
|
|
447
|
+
)
|
|
448
|
+
|
|
449
|
+
return "\n".join(context_lines)
|
|
450
|
+
|
|
451
|
+
def _get_framework_name(self, runtime_type: RuntimeType) -> str:
|
|
452
|
+
"""
|
|
453
|
+
Get friendly framework name for runtime type.
|
|
454
|
+
|
|
455
|
+
Args:
|
|
456
|
+
runtime_type: Runtime type enum
|
|
457
|
+
|
|
458
|
+
Returns:
|
|
459
|
+
Framework name string
|
|
460
|
+
"""
|
|
461
|
+
mapping = {
|
|
462
|
+
RuntimeType.DEFAULT: "Agno",
|
|
463
|
+
RuntimeType.CLAUDE_CODE: "Claude Code SDK",
|
|
464
|
+
}
|
|
465
|
+
return mapping.get(runtime_type, "Unknown")
|