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.

Files changed (185) hide show
  1. control_plane_api/README.md +266 -0
  2. control_plane_api/__init__.py +0 -0
  3. control_plane_api/__version__.py +1 -0
  4. control_plane_api/alembic/README +1 -0
  5. control_plane_api/alembic/env.py +98 -0
  6. control_plane_api/alembic/script.py.mako +28 -0
  7. control_plane_api/alembic/versions/1382bec74309_initial_migration_with_all_models.py +251 -0
  8. control_plane_api/alembic/versions/1f54bc2a37e3_add_analytics_tables.py +162 -0
  9. control_plane_api/alembic/versions/2e4cb136dc10_rename_toolset_ids_to_skill_ids_in_teams.py +30 -0
  10. control_plane_api/alembic/versions/31cd69a644ce_add_skill_templates_table.py +28 -0
  11. control_plane_api/alembic/versions/89e127caa47d_add_jobs_and_job_executions_tables.py +161 -0
  12. control_plane_api/alembic/versions/add_llm_models_table.py +51 -0
  13. control_plane_api/alembic/versions/b0e10697f212_add_runtime_column_to_teams_simple.py +42 -0
  14. control_plane_api/alembic/versions/ce43b24b63bf_add_execution_trigger_source_and_fix_.py +155 -0
  15. control_plane_api/alembic/versions/d4eaf16e3f8d_rename_toolsets_to_skills.py +84 -0
  16. control_plane_api/alembic/versions/efa2dc427da1_rename_metadata_to_custom_metadata.py +32 -0
  17. control_plane_api/alembic/versions/f973b431d1ce_add_workflow_executor_to_skill_types.py +44 -0
  18. control_plane_api/alembic.ini +148 -0
  19. control_plane_api/api/index.py +12 -0
  20. control_plane_api/app/__init__.py +11 -0
  21. control_plane_api/app/activities/__init__.py +20 -0
  22. control_plane_api/app/activities/agent_activities.py +379 -0
  23. control_plane_api/app/activities/team_activities.py +410 -0
  24. control_plane_api/app/activities/temporal_cloud_activities.py +577 -0
  25. control_plane_api/app/config/__init__.py +35 -0
  26. control_plane_api/app/config/api_config.py +354 -0
  27. control_plane_api/app/config/model_pricing.py +318 -0
  28. control_plane_api/app/config.py +95 -0
  29. control_plane_api/app/database.py +135 -0
  30. control_plane_api/app/exceptions.py +408 -0
  31. control_plane_api/app/lib/__init__.py +11 -0
  32. control_plane_api/app/lib/job_executor.py +312 -0
  33. control_plane_api/app/lib/kubiya_client.py +235 -0
  34. control_plane_api/app/lib/litellm_pricing.py +166 -0
  35. control_plane_api/app/lib/planning_tools/__init__.py +22 -0
  36. control_plane_api/app/lib/planning_tools/agents.py +155 -0
  37. control_plane_api/app/lib/planning_tools/base.py +189 -0
  38. control_plane_api/app/lib/planning_tools/environments.py +214 -0
  39. control_plane_api/app/lib/planning_tools/resources.py +240 -0
  40. control_plane_api/app/lib/planning_tools/teams.py +198 -0
  41. control_plane_api/app/lib/policy_enforcer_client.py +939 -0
  42. control_plane_api/app/lib/redis_client.py +436 -0
  43. control_plane_api/app/lib/supabase.py +71 -0
  44. control_plane_api/app/lib/temporal_client.py +138 -0
  45. control_plane_api/app/lib/validation/__init__.py +20 -0
  46. control_plane_api/app/lib/validation/runtime_validation.py +287 -0
  47. control_plane_api/app/main.py +128 -0
  48. control_plane_api/app/middleware/__init__.py +8 -0
  49. control_plane_api/app/middleware/auth.py +513 -0
  50. control_plane_api/app/middleware/exception_handler.py +267 -0
  51. control_plane_api/app/middleware/rate_limiting.py +384 -0
  52. control_plane_api/app/middleware/request_id.py +202 -0
  53. control_plane_api/app/models/__init__.py +27 -0
  54. control_plane_api/app/models/agent.py +79 -0
  55. control_plane_api/app/models/analytics.py +206 -0
  56. control_plane_api/app/models/associations.py +81 -0
  57. control_plane_api/app/models/environment.py +63 -0
  58. control_plane_api/app/models/execution.py +93 -0
  59. control_plane_api/app/models/job.py +179 -0
  60. control_plane_api/app/models/llm_model.py +75 -0
  61. control_plane_api/app/models/presence.py +49 -0
  62. control_plane_api/app/models/project.py +47 -0
  63. control_plane_api/app/models/session.py +38 -0
  64. control_plane_api/app/models/team.py +66 -0
  65. control_plane_api/app/models/workflow.py +55 -0
  66. control_plane_api/app/policies/README.md +121 -0
  67. control_plane_api/app/policies/approved_users.rego +62 -0
  68. control_plane_api/app/policies/business_hours.rego +51 -0
  69. control_plane_api/app/policies/rate_limiting.rego +100 -0
  70. control_plane_api/app/policies/tool_restrictions.rego +86 -0
  71. control_plane_api/app/routers/__init__.py +4 -0
  72. control_plane_api/app/routers/agents.py +364 -0
  73. control_plane_api/app/routers/agents_v2.py +1260 -0
  74. control_plane_api/app/routers/analytics.py +1014 -0
  75. control_plane_api/app/routers/context_manager.py +562 -0
  76. control_plane_api/app/routers/environment_context.py +270 -0
  77. control_plane_api/app/routers/environments.py +715 -0
  78. control_plane_api/app/routers/execution_environment.py +517 -0
  79. control_plane_api/app/routers/executions.py +1911 -0
  80. control_plane_api/app/routers/health.py +92 -0
  81. control_plane_api/app/routers/health_v2.py +326 -0
  82. control_plane_api/app/routers/integrations.py +274 -0
  83. control_plane_api/app/routers/jobs.py +1344 -0
  84. control_plane_api/app/routers/models.py +82 -0
  85. control_plane_api/app/routers/models_v2.py +361 -0
  86. control_plane_api/app/routers/policies.py +639 -0
  87. control_plane_api/app/routers/presence.py +234 -0
  88. control_plane_api/app/routers/projects.py +902 -0
  89. control_plane_api/app/routers/runners.py +379 -0
  90. control_plane_api/app/routers/runtimes.py +172 -0
  91. control_plane_api/app/routers/secrets.py +155 -0
  92. control_plane_api/app/routers/skills.py +1001 -0
  93. control_plane_api/app/routers/skills_definitions.py +140 -0
  94. control_plane_api/app/routers/task_planning.py +1256 -0
  95. control_plane_api/app/routers/task_queues.py +654 -0
  96. control_plane_api/app/routers/team_context.py +270 -0
  97. control_plane_api/app/routers/teams.py +1400 -0
  98. control_plane_api/app/routers/worker_queues.py +1545 -0
  99. control_plane_api/app/routers/workers.py +935 -0
  100. control_plane_api/app/routers/workflows.py +204 -0
  101. control_plane_api/app/runtimes/__init__.py +6 -0
  102. control_plane_api/app/runtimes/validation.py +344 -0
  103. control_plane_api/app/schemas/job_schemas.py +295 -0
  104. control_plane_api/app/services/__init__.py +1 -0
  105. control_plane_api/app/services/agno_service.py +619 -0
  106. control_plane_api/app/services/litellm_service.py +190 -0
  107. control_plane_api/app/services/policy_service.py +525 -0
  108. control_plane_api/app/services/temporal_cloud_provisioning.py +150 -0
  109. control_plane_api/app/skills/__init__.py +44 -0
  110. control_plane_api/app/skills/base.py +229 -0
  111. control_plane_api/app/skills/business_intelligence.py +189 -0
  112. control_plane_api/app/skills/data_visualization.py +154 -0
  113. control_plane_api/app/skills/docker.py +104 -0
  114. control_plane_api/app/skills/file_generation.py +94 -0
  115. control_plane_api/app/skills/file_system.py +110 -0
  116. control_plane_api/app/skills/python.py +92 -0
  117. control_plane_api/app/skills/registry.py +65 -0
  118. control_plane_api/app/skills/shell.py +102 -0
  119. control_plane_api/app/skills/workflow_executor.py +469 -0
  120. control_plane_api/app/utils/workflow_executor.py +354 -0
  121. control_plane_api/app/workflows/__init__.py +11 -0
  122. control_plane_api/app/workflows/agent_execution.py +507 -0
  123. control_plane_api/app/workflows/agent_execution_with_skills.py +222 -0
  124. control_plane_api/app/workflows/namespace_provisioning.py +326 -0
  125. control_plane_api/app/workflows/team_execution.py +399 -0
  126. control_plane_api/scripts/seed_models.py +239 -0
  127. control_plane_api/worker/__init__.py +0 -0
  128. control_plane_api/worker/activities/__init__.py +0 -0
  129. control_plane_api/worker/activities/agent_activities.py +1241 -0
  130. control_plane_api/worker/activities/approval_activities.py +234 -0
  131. control_plane_api/worker/activities/runtime_activities.py +388 -0
  132. control_plane_api/worker/activities/skill_activities.py +267 -0
  133. control_plane_api/worker/activities/team_activities.py +1217 -0
  134. control_plane_api/worker/config/__init__.py +31 -0
  135. control_plane_api/worker/config/worker_config.py +275 -0
  136. control_plane_api/worker/control_plane_client.py +529 -0
  137. control_plane_api/worker/examples/analytics_integration_example.py +362 -0
  138. control_plane_api/worker/models/__init__.py +1 -0
  139. control_plane_api/worker/models/inputs.py +89 -0
  140. control_plane_api/worker/runtimes/__init__.py +31 -0
  141. control_plane_api/worker/runtimes/base.py +789 -0
  142. control_plane_api/worker/runtimes/claude_code_runtime.py +1443 -0
  143. control_plane_api/worker/runtimes/default_runtime.py +617 -0
  144. control_plane_api/worker/runtimes/factory.py +173 -0
  145. control_plane_api/worker/runtimes/validation.py +93 -0
  146. control_plane_api/worker/services/__init__.py +1 -0
  147. control_plane_api/worker/services/agent_executor.py +422 -0
  148. control_plane_api/worker/services/agent_executor_v2.py +383 -0
  149. control_plane_api/worker/services/analytics_collector.py +457 -0
  150. control_plane_api/worker/services/analytics_service.py +464 -0
  151. control_plane_api/worker/services/approval_tools.py +310 -0
  152. control_plane_api/worker/services/approval_tools_agno.py +207 -0
  153. control_plane_api/worker/services/cancellation_manager.py +177 -0
  154. control_plane_api/worker/services/data_visualization.py +827 -0
  155. control_plane_api/worker/services/jira_tools.py +257 -0
  156. control_plane_api/worker/services/runtime_analytics.py +328 -0
  157. control_plane_api/worker/services/session_service.py +194 -0
  158. control_plane_api/worker/services/skill_factory.py +175 -0
  159. control_plane_api/worker/services/team_executor.py +574 -0
  160. control_plane_api/worker/services/team_executor_v2.py +465 -0
  161. control_plane_api/worker/services/workflow_executor_tools.py +1418 -0
  162. control_plane_api/worker/tests/__init__.py +1 -0
  163. control_plane_api/worker/tests/e2e/__init__.py +0 -0
  164. control_plane_api/worker/tests/e2e/test_execution_flow.py +571 -0
  165. control_plane_api/worker/tests/integration/__init__.py +0 -0
  166. control_plane_api/worker/tests/integration/test_control_plane_integration.py +308 -0
  167. control_plane_api/worker/tests/unit/__init__.py +0 -0
  168. control_plane_api/worker/tests/unit/test_control_plane_client.py +401 -0
  169. control_plane_api/worker/utils/__init__.py +1 -0
  170. control_plane_api/worker/utils/chunk_batcher.py +305 -0
  171. control_plane_api/worker/utils/retry_utils.py +60 -0
  172. control_plane_api/worker/utils/streaming_utils.py +373 -0
  173. control_plane_api/worker/worker.py +753 -0
  174. control_plane_api/worker/workflows/__init__.py +0 -0
  175. control_plane_api/worker/workflows/agent_execution.py +589 -0
  176. control_plane_api/worker/workflows/team_execution.py +429 -0
  177. kubiya_control_plane_api-0.3.4.dist-info/METADATA +229 -0
  178. kubiya_control_plane_api-0.3.4.dist-info/RECORD +182 -0
  179. kubiya_control_plane_api-0.3.4.dist-info/entry_points.txt +2 -0
  180. kubiya_control_plane_api-0.3.4.dist-info/top_level.txt +1 -0
  181. kubiya_control_plane_api-0.1.0.dist-info/METADATA +0 -66
  182. kubiya_control_plane_api-0.1.0.dist-info/RECORD +0 -5
  183. kubiya_control_plane_api-0.1.0.dist-info/top_level.txt +0 -1
  184. {kubiya_control_plane_api-0.1.0.dist-info/licenses → control_plane_api}/LICENSE +0 -0
  185. {kubiya_control_plane_api-0.1.0.dist-info → kubiya_control_plane_api-0.3.4.dist-info}/WHEEL +0 -0
@@ -0,0 +1,383 @@
1
+ """
2
+ Refactored Agent executor service using runtime abstraction.
3
+
4
+ This version delegates execution to pluggable runtime implementations,
5
+ making the code more maintainable and extensible.
6
+ """
7
+
8
+ from typing import Dict, Any, Optional
9
+ import structlog
10
+
11
+ from control_plane_api.worker.control_plane_client import ControlPlaneClient
12
+ from control_plane_api.worker.services.session_service import SessionService
13
+ from control_plane_api.worker.services.cancellation_manager import CancellationManager
14
+ from runtimes import (
15
+ RuntimeFactory,
16
+ RuntimeType,
17
+ RuntimeExecutionContext,
18
+ )
19
+ from control_plane_api.worker.utils.streaming_utils import StreamingHelper
20
+
21
+ logger = structlog.get_logger()
22
+
23
+
24
+ class AgentExecutorServiceV2:
25
+ """
26
+ Service for executing agents using runtime abstraction.
27
+
28
+ This service orchestrates agent execution by:
29
+ 1. Loading session history
30
+ 2. Selecting appropriate runtime based on agent config
31
+ 3. Delegating execution to the runtime
32
+ 4. Persisting session after execution
33
+ """
34
+
35
+ def __init__(
36
+ self,
37
+ control_plane: ControlPlaneClient,
38
+ session_service: SessionService,
39
+ cancellation_manager: CancellationManager,
40
+ ):
41
+ """
42
+ Initialize the agent executor service.
43
+
44
+ Args:
45
+ control_plane: Control Plane API client
46
+ session_service: Session management service
47
+ cancellation_manager: Execution cancellation manager
48
+ """
49
+ self.control_plane = control_plane
50
+ self.session_service = session_service
51
+ self.cancellation_manager = cancellation_manager
52
+ self.runtime_factory = RuntimeFactory()
53
+
54
+ async def execute(self, input: Any) -> Dict[str, Any]:
55
+ """
56
+ Execute an agent using the configured runtime.
57
+
58
+ This method:
59
+ 1. Loads session history
60
+ 2. Determines runtime type from agent config
61
+ 3. Creates runtime instance
62
+ 4. Executes agent via runtime
63
+ 5. Persists session
64
+ 6. Returns standardized result
65
+
66
+ Args:
67
+ input: AgentExecutionInput with execution details
68
+
69
+ Returns:
70
+ Dict with response, usage, success flag, runtime_type, etc.
71
+ """
72
+ execution_id = input.execution_id
73
+
74
+ print("\n" + "=" * 80)
75
+ print("🤖 AGENT EXECUTION START (Runtime-Abstracted)")
76
+ print("=" * 80)
77
+ print(f"Execution ID: {execution_id}")
78
+ print(f"Agent ID: {input.agent_id}")
79
+ print(f"Organization: {input.organization_id}")
80
+ print(f"Model: {input.model_id or 'default'}")
81
+ print(f"Session ID: {input.session_id}")
82
+ print(
83
+ f"Prompt: {input.prompt[:100]}..."
84
+ if len(input.prompt) > 100
85
+ else f"Prompt: {input.prompt}"
86
+ )
87
+ print("=" * 80 + "\n")
88
+
89
+ logger.info(
90
+ "agent_execution_start",
91
+ execution_id=execution_id[:8],
92
+ agent_id=input.agent_id,
93
+ session_id=input.session_id,
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, session_id=input.session_id
101
+ )
102
+
103
+ if session_history:
104
+ print(
105
+ f"✅ Loaded {len(session_history)} messages from previous session\n"
106
+ )
107
+ else:
108
+ print("ℹ️ Starting new conversation\n")
109
+
110
+ # STEP 2: Determine runtime type
111
+ agent_config = input.agent_config or {}
112
+ runtime_type_str = agent_config.get("runtime", "default")
113
+ runtime_type = self.runtime_factory.parse_runtime_type(runtime_type_str)
114
+
115
+ print(f"🔌 Runtime Type: {runtime_type.value}")
116
+ print(f" Framework: {self._get_framework_name(runtime_type)}\n")
117
+
118
+ logger.info(
119
+ "runtime_selected",
120
+ execution_id=execution_id[:8],
121
+ runtime=runtime_type.value,
122
+ )
123
+
124
+ # STEP 3: Create runtime instance
125
+ print(f"⚙️ Creating runtime instance...")
126
+ runtime = self.runtime_factory.create_runtime(
127
+ runtime_type=runtime_type,
128
+ control_plane_client=self.control_plane,
129
+ cancellation_manager=self.cancellation_manager,
130
+ )
131
+ print(f"✅ Runtime created: {runtime.get_runtime_info()}\n")
132
+
133
+ # STEP 4: Get skills (if runtime supports tools)
134
+ skills = []
135
+ if runtime.supports_tools():
136
+ print(f"🔧 Fetching skills from Control Plane...")
137
+ try:
138
+ skill_configs = self.control_plane.get_skills(input.agent_id)
139
+ if skill_configs:
140
+ print(f"✅ Resolved {len(skill_configs)} skills")
141
+ print(f" Types: {[t.get('type') for t in skill_configs]}")
142
+ print(f" Names: {[t.get('name') for t in skill_configs]}")
143
+ print(f" Enabled: {[t.get('enabled', True) for t in skill_configs]}\n")
144
+
145
+ # DEBUG: Show full config for workflow_executor skills
146
+ for cfg in skill_configs:
147
+ if cfg.get('type') in ['workflow_executor', 'workflow']:
148
+ print(f"🔍 Workflow Executor Skill Config:")
149
+ print(f" Name: {cfg.get('name')}")
150
+ print(f" Type: {cfg.get('type')}")
151
+ print(f" Enabled: {cfg.get('enabled', True)}")
152
+ print(f" Config Keys: {list(cfg.get('configuration', {}).keys())}\n")
153
+
154
+ # Import here to avoid circular dependency
155
+ from services.skill_factory import SkillFactory
156
+
157
+ skills = SkillFactory.create_skills_from_list(
158
+ skill_configs
159
+ )
160
+
161
+ if skills:
162
+ print(f"✅ Instantiated {len(skills)} skill(s)")
163
+ # Show types of instantiated skills
164
+ skill_types = [type(s).__name__ for s in skills]
165
+ print(f" Skill classes: {skill_types}\n")
166
+ else:
167
+ print(f"⚠️ No skills were instantiated (all disabled or failed)\n")
168
+ else:
169
+ print(f"⚠️ No skills found for agent\n")
170
+ except Exception as e:
171
+ print(f"❌ Error fetching skills: {str(e)}\n")
172
+ logger.error("skill_fetch_error", error=str(e), exc_info=True)
173
+
174
+ # STEP 5: Build execution context
175
+ print("📦 Building execution context...")
176
+ context = RuntimeExecutionContext(
177
+ execution_id=execution_id,
178
+ agent_id=input.agent_id,
179
+ organization_id=input.organization_id,
180
+ prompt=input.prompt,
181
+ system_prompt=input.system_prompt,
182
+ conversation_history=session_history,
183
+ model_id=input.model_id,
184
+ model_config=input.model_config,
185
+ agent_config=agent_config,
186
+ skills=skills,
187
+ mcp_servers=input.mcp_servers,
188
+ user_metadata=input.user_metadata,
189
+ runtime_config=agent_config.get("runtime_config"),
190
+ )
191
+ print("✅ Context ready\n")
192
+
193
+ # STEP 6: Execute via runtime (with streaming if supported)
194
+ print("⚡ Executing via runtime...\n")
195
+
196
+ if runtime.supports_streaming():
197
+ result = await self._execute_streaming(runtime, context, input)
198
+ else:
199
+ result = await runtime.execute(context)
200
+
201
+ print("\n✅ Runtime execution completed!")
202
+ print(f" Response Length: {len(result.response)} chars")
203
+ print(f" Success: {result.success}\n")
204
+
205
+ logger.info(
206
+ "agent_execution_completed",
207
+ execution_id=execution_id[:8],
208
+ success=result.success,
209
+ response_length=len(result.response),
210
+ )
211
+
212
+ # STEP 7: Persist session
213
+ if result.success and result.response:
214
+ print("💾 Persisting session history...")
215
+
216
+ # Build new messages
217
+ new_messages = [
218
+ {"role": "user", "content": input.prompt},
219
+ {"role": "assistant", "content": result.response},
220
+ ]
221
+
222
+ # Combine with previous history
223
+ complete_session = session_history + new_messages
224
+
225
+ success = self.session_service.persist_session(
226
+ execution_id=execution_id,
227
+ session_id=input.session_id or execution_id,
228
+ user_id=input.user_id,
229
+ messages=complete_session,
230
+ metadata={
231
+ "agent_id": input.agent_id,
232
+ "organization_id": input.organization_id,
233
+ "runtime_type": runtime_type.value,
234
+ "turn_count": len(complete_session),
235
+ },
236
+ )
237
+
238
+ if success:
239
+ print(
240
+ f"✅ Session persisted ({len(complete_session)} total messages)\n"
241
+ )
242
+ else:
243
+ print(f"⚠️ Session persistence failed\n")
244
+
245
+ # STEP 8: Print usage metrics
246
+ if result.usage:
247
+ print(f"📊 Token Usage:")
248
+ print(f" Input: {result.usage.get('prompt_tokens', 0)}")
249
+ print(f" Output: {result.usage.get('completion_tokens', 0)}")
250
+ print(f" Total: {result.usage.get('total_tokens', 0)}\n")
251
+
252
+ print("=" * 80)
253
+ print("🏁 AGENT EXECUTION END")
254
+ print("=" * 80 + "\n")
255
+
256
+ # Return standardized result
257
+ return {
258
+ "success": result.success,
259
+ "response": result.response,
260
+ "usage": result.usage,
261
+ "model": result.model or input.model_id,
262
+ "finish_reason": result.finish_reason or "stop",
263
+ "run_id": result.run_id,
264
+ "tool_messages": result.tool_messages or [],
265
+ "runtime_type": runtime_type.value,
266
+ "error": result.error,
267
+ }
268
+
269
+ except Exception as e:
270
+ print("\n" + "=" * 80)
271
+ print("❌ AGENT EXECUTION FAILED")
272
+ print("=" * 80)
273
+ print(f"Error: {str(e)}")
274
+ print("=" * 80 + "\n")
275
+
276
+ logger.error(
277
+ "agent_execution_failed", execution_id=execution_id[:8], error=str(e)
278
+ )
279
+
280
+ return {
281
+ "success": False,
282
+ "error": str(e),
283
+ "model": input.model_id,
284
+ "usage": {},
285
+ "finish_reason": "error",
286
+ "runtime_type": runtime_type_str if "runtime_type_str" in locals() else "unknown",
287
+ }
288
+
289
+ async def _execute_streaming(
290
+ self, runtime, context: RuntimeExecutionContext, input: Any
291
+ ) -> Any:
292
+ """
293
+ Execute with streaming and publish events to Control Plane.
294
+
295
+ Args:
296
+ runtime: Runtime instance
297
+ context: Execution context
298
+ input: Original input for additional metadata
299
+
300
+ Returns:
301
+ Final RuntimeExecutionResult
302
+ """
303
+ # Create streaming helper for publishing events
304
+ streaming_helper = StreamingHelper(
305
+ control_plane_client=self.control_plane, execution_id=context.execution_id
306
+ )
307
+
308
+ accumulated_response = ""
309
+ final_result = None
310
+
311
+ # Define event callback for publishing to Control Plane
312
+ def event_callback(event: Dict):
313
+ """Callback to publish events to Control Plane SSE"""
314
+ event_type = event.get("type")
315
+
316
+ if event_type == "content_chunk":
317
+ # Publish content chunk
318
+ streaming_helper.publish_content_chunk(
319
+ content=event.get("content", ""),
320
+ message_id=event.get("message_id", context.execution_id),
321
+ )
322
+ elif event_type == "tool_start":
323
+ # Publish tool start
324
+ streaming_helper.publish_tool_start(
325
+ tool_name=event.get("tool_name"),
326
+ tool_execution_id=event.get("tool_execution_id"),
327
+ tool_args=event.get("tool_args", {}),
328
+ source="agent",
329
+ )
330
+ elif event_type == "tool_complete":
331
+ # Publish tool completion
332
+ streaming_helper.publish_tool_complete(
333
+ tool_name=event.get("tool_name"),
334
+ tool_execution_id=event.get("tool_execution_id"),
335
+ status=event.get("status", "success"),
336
+ output=event.get("output"),
337
+ error=event.get("error"),
338
+ source="agent",
339
+ )
340
+
341
+ # Stream execution
342
+ async for chunk in runtime.stream_execute(context, event_callback):
343
+ if chunk.response:
344
+ accumulated_response += chunk.response
345
+ print(chunk.response, end="", flush=True)
346
+
347
+ # Keep final result for metadata
348
+ if chunk.usage or chunk.finish_reason:
349
+ final_result = chunk
350
+
351
+ print() # New line after streaming
352
+
353
+ # Return final result with accumulated response
354
+ if final_result:
355
+ # Update response with accumulated content
356
+ final_result.response = accumulated_response
357
+ return final_result
358
+ else:
359
+ # Create final result if not provided
360
+ from runtimes.base import RuntimeExecutionResult
361
+
362
+ return RuntimeExecutionResult(
363
+ response=accumulated_response,
364
+ usage={},
365
+ success=True,
366
+ finish_reason="stop",
367
+ )
368
+
369
+ def _get_framework_name(self, runtime_type: RuntimeType) -> str:
370
+ """
371
+ Get friendly framework name for runtime type.
372
+
373
+ Args:
374
+ runtime_type: Runtime type enum
375
+
376
+ Returns:
377
+ Framework name string
378
+ """
379
+ mapping = {
380
+ RuntimeType.DEFAULT: "Agno",
381
+ RuntimeType.CLAUDE_CODE: "Claude Code SDK",
382
+ }
383
+ return mapping.get(runtime_type, "Unknown")