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,362 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Example of integrating analytics collection into agent execution.
|
|
3
|
+
|
|
4
|
+
This shows how to instrument your agent execution to automatically
|
|
5
|
+
track metrics without blocking performance.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
import os
|
|
10
|
+
import time
|
|
11
|
+
from typing import Any, Dict
|
|
12
|
+
from datetime import datetime, timezone
|
|
13
|
+
|
|
14
|
+
# Import analytics components
|
|
15
|
+
from control_plane_api.worker.services.analytics_collector import (
|
|
16
|
+
create_analytics_collector,
|
|
17
|
+
ExecutionContext,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
async def execute_agent_with_analytics_example():
|
|
22
|
+
"""
|
|
23
|
+
Example showing end-to-end analytics integration.
|
|
24
|
+
|
|
25
|
+
This demonstrates:
|
|
26
|
+
1. Setting up the analytics collector
|
|
27
|
+
2. Tracking LLM turns with token usage
|
|
28
|
+
3. Tracking tool calls
|
|
29
|
+
4. Tracking tasks
|
|
30
|
+
5. Async submission (non-blocking)
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
# ========================================================================
|
|
34
|
+
# 1. Setup Analytics Collector
|
|
35
|
+
# ========================================================================
|
|
36
|
+
|
|
37
|
+
collector = create_analytics_collector(
|
|
38
|
+
control_plane_url=os.getenv("CONTROL_PLANE_URL", "http://localhost:8000"),
|
|
39
|
+
api_key=os.getenv("KUBIYA_API_KEY"),
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
# Create execution context
|
|
43
|
+
ctx = ExecutionContext(
|
|
44
|
+
execution_id="550e8400-e29b-41d4-a716-446655440000", # From Temporal workflow
|
|
45
|
+
organization_id="org-123", # From execution record
|
|
46
|
+
turn_number=0,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
print("🚀 Starting agent execution with analytics tracking\n")
|
|
50
|
+
|
|
51
|
+
# ========================================================================
|
|
52
|
+
# 2. Track LLM Turn (Using LiteLLM)
|
|
53
|
+
# ========================================================================
|
|
54
|
+
|
|
55
|
+
print("📊 Turn 1: LLM Call with LiteLLM")
|
|
56
|
+
ctx = collector.start_turn(ctx)
|
|
57
|
+
|
|
58
|
+
# Simulate LiteLLM call
|
|
59
|
+
turn_start = time.time()
|
|
60
|
+
|
|
61
|
+
# In real code, you'd do:
|
|
62
|
+
# response = await litellm.acompletion(
|
|
63
|
+
# model="claude-sonnet-4",
|
|
64
|
+
# messages=[{"role": "user", "content": "Hello"}],
|
|
65
|
+
# )
|
|
66
|
+
|
|
67
|
+
# For this example, create a mock response
|
|
68
|
+
class MockUsage:
|
|
69
|
+
prompt_tokens = 1000
|
|
70
|
+
completion_tokens = 500
|
|
71
|
+
total_tokens = 1500
|
|
72
|
+
|
|
73
|
+
class MockMessage:
|
|
74
|
+
content = "This is the LLM response with detailed information about the task."
|
|
75
|
+
|
|
76
|
+
class MockChoice:
|
|
77
|
+
message = MockMessage()
|
|
78
|
+
finish_reason = "stop"
|
|
79
|
+
|
|
80
|
+
class MockLiteLLMResponse:
|
|
81
|
+
usage = MockUsage()
|
|
82
|
+
choices = [MockChoice()]
|
|
83
|
+
|
|
84
|
+
await asyncio.sleep(0.1) # Simulate API call
|
|
85
|
+
response = MockLiteLLMResponse()
|
|
86
|
+
|
|
87
|
+
# Record the turn (async, non-blocking)
|
|
88
|
+
collector.record_turn_from_litellm(
|
|
89
|
+
ctx=ctx,
|
|
90
|
+
response=response,
|
|
91
|
+
model="claude-sonnet-4",
|
|
92
|
+
finish_reason="stop",
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
print(f" ✓ Turn recorded: {response.usage.total_tokens} tokens")
|
|
96
|
+
print(f" ✓ Duration: {int((time.time() - turn_start) * 1000)}ms\n")
|
|
97
|
+
|
|
98
|
+
# ========================================================================
|
|
99
|
+
# 3. Track Tool Calls
|
|
100
|
+
# ========================================================================
|
|
101
|
+
|
|
102
|
+
print("🔧 Turn 1: Tool Calls")
|
|
103
|
+
|
|
104
|
+
# Tool call 1: Read file
|
|
105
|
+
tool_start = time.time()
|
|
106
|
+
await asyncio.sleep(0.05) # Simulate tool execution
|
|
107
|
+
tool_end = time.time()
|
|
108
|
+
|
|
109
|
+
collector.record_tool_call(
|
|
110
|
+
ctx=ctx,
|
|
111
|
+
tool_name="Read",
|
|
112
|
+
tool_input={"file_path": "/app/main.py"},
|
|
113
|
+
tool_output="def main():\n print('Hello')\n ...",
|
|
114
|
+
start_time=tool_start,
|
|
115
|
+
end_time=tool_end,
|
|
116
|
+
success=True,
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
print(f" ✓ Tool call recorded: Read (successful)")
|
|
120
|
+
|
|
121
|
+
# Tool call 2: Bash command
|
|
122
|
+
tool_start = time.time()
|
|
123
|
+
await asyncio.sleep(0.1) # Simulate tool execution
|
|
124
|
+
tool_end = time.time()
|
|
125
|
+
|
|
126
|
+
collector.record_tool_call(
|
|
127
|
+
ctx=ctx,
|
|
128
|
+
tool_name="Bash",
|
|
129
|
+
tool_input={"command": "ls -la"},
|
|
130
|
+
tool_output="total 48\ndrwxr-xr-x 10 user staff 320 Jan 8 14:00 .\n...",
|
|
131
|
+
start_time=tool_start,
|
|
132
|
+
end_time=tool_end,
|
|
133
|
+
success=True,
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
print(f" ✓ Tool call recorded: Bash (successful)\n")
|
|
137
|
+
|
|
138
|
+
# ========================================================================
|
|
139
|
+
# 4. Track LLM Turn (Using Agno)
|
|
140
|
+
# ========================================================================
|
|
141
|
+
|
|
142
|
+
print("📊 Turn 2: LLM Call with Agno")
|
|
143
|
+
ctx = collector.start_turn(ctx)
|
|
144
|
+
|
|
145
|
+
turn_start = time.time()
|
|
146
|
+
|
|
147
|
+
# In real code, you'd do:
|
|
148
|
+
# result = agent.run(message)
|
|
149
|
+
|
|
150
|
+
# For this example, create a mock Agno result
|
|
151
|
+
class MockMetrics:
|
|
152
|
+
input_tokens = 800
|
|
153
|
+
output_tokens = 600
|
|
154
|
+
total_tokens = 1400
|
|
155
|
+
input_token_details = {
|
|
156
|
+
"cache_read": 200,
|
|
157
|
+
"cache_creation": 100,
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
class MockAgnoResult:
|
|
161
|
+
metrics = MockMetrics()
|
|
162
|
+
content = "Based on the analysis, I recommend the following changes..."
|
|
163
|
+
run_id = "run-456"
|
|
164
|
+
|
|
165
|
+
await asyncio.sleep(0.15) # Simulate API call
|
|
166
|
+
result = MockAgnoResult()
|
|
167
|
+
|
|
168
|
+
# Record the turn (async, non-blocking)
|
|
169
|
+
collector.record_turn_from_agno(
|
|
170
|
+
ctx=ctx,
|
|
171
|
+
result=result,
|
|
172
|
+
model="claude-sonnet-4",
|
|
173
|
+
finish_reason="stop",
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
print(f" ✓ Turn recorded: {result.metrics.total_tokens} tokens (with cache)")
|
|
177
|
+
print(f" ✓ Cache read: {result.metrics.input_token_details['cache_read']} tokens")
|
|
178
|
+
print(f" ✓ Duration: {int((time.time() - turn_start) * 1000)}ms\n")
|
|
179
|
+
|
|
180
|
+
# ========================================================================
|
|
181
|
+
# 5. Track Tasks
|
|
182
|
+
# ========================================================================
|
|
183
|
+
|
|
184
|
+
print("📋 Task Tracking")
|
|
185
|
+
|
|
186
|
+
task_id = collector.record_task(
|
|
187
|
+
ctx=ctx,
|
|
188
|
+
task_number=1,
|
|
189
|
+
task_description="Analyze codebase and identify issues",
|
|
190
|
+
task_type="analysis",
|
|
191
|
+
status="completed",
|
|
192
|
+
started_at=datetime.now(timezone.utc).isoformat(),
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
print(f" ✓ Task recorded: {task_id}\n")
|
|
196
|
+
|
|
197
|
+
# ========================================================================
|
|
198
|
+
# 6. Wait for Analytics Submission
|
|
199
|
+
# ========================================================================
|
|
200
|
+
|
|
201
|
+
print("⏳ Waiting for analytics submission...")
|
|
202
|
+
|
|
203
|
+
# Wait for all analytics to be submitted (with timeout)
|
|
204
|
+
await collector.wait_for_submissions(timeout=5.0)
|
|
205
|
+
|
|
206
|
+
print("✅ All analytics submitted!\n")
|
|
207
|
+
|
|
208
|
+
# ========================================================================
|
|
209
|
+
# 7. Query Analytics
|
|
210
|
+
# ========================================================================
|
|
211
|
+
|
|
212
|
+
print("📈 Analytics Summary:")
|
|
213
|
+
print(f" - Total turns: 2")
|
|
214
|
+
print(f" - Total tokens: 2900")
|
|
215
|
+
print(f" - Total tool calls: 2")
|
|
216
|
+
print(f" - Total tasks: 1")
|
|
217
|
+
print(f" - Estimated cost: $0.0435")
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
async def execute_agent_with_error_handling_example():
|
|
221
|
+
"""
|
|
222
|
+
Example showing error handling in analytics.
|
|
223
|
+
|
|
224
|
+
Demonstrates that analytics failures don't break execution.
|
|
225
|
+
"""
|
|
226
|
+
|
|
227
|
+
collector = create_analytics_collector(
|
|
228
|
+
control_plane_url="http://localhost:8000",
|
|
229
|
+
api_key="test-key",
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
ctx = ExecutionContext(
|
|
233
|
+
execution_id="exec-123",
|
|
234
|
+
organization_id="org-456",
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
print("\n🔥 Error Handling Example\n")
|
|
238
|
+
|
|
239
|
+
# Start turn
|
|
240
|
+
ctx = collector.start_turn(ctx)
|
|
241
|
+
|
|
242
|
+
# Even if analytics submission fails, execution continues
|
|
243
|
+
try:
|
|
244
|
+
# Simulate a failed LLM call
|
|
245
|
+
raise Exception("LLM API timeout")
|
|
246
|
+
except Exception as e:
|
|
247
|
+
print(f"❌ LLM call failed: {e}")
|
|
248
|
+
|
|
249
|
+
# Record turn with error (still tracked for analytics)
|
|
250
|
+
class MockErrorResponse:
|
|
251
|
+
usage = None
|
|
252
|
+
|
|
253
|
+
collector.record_turn_from_litellm(
|
|
254
|
+
ctx=ctx,
|
|
255
|
+
response=MockErrorResponse(),
|
|
256
|
+
model="claude-sonnet-4",
|
|
257
|
+
finish_reason="error",
|
|
258
|
+
error_message=str(e),
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
print(" ✓ Error tracked in analytics (execution can continue)")
|
|
262
|
+
|
|
263
|
+
# Tool call that fails
|
|
264
|
+
tool_start = time.time()
|
|
265
|
+
try:
|
|
266
|
+
raise PermissionError("Access denied to file")
|
|
267
|
+
except Exception as e:
|
|
268
|
+
tool_end = time.time()
|
|
269
|
+
|
|
270
|
+
collector.record_tool_call(
|
|
271
|
+
ctx=ctx,
|
|
272
|
+
tool_name="Read",
|
|
273
|
+
tool_input={"file_path": "/restricted/file.txt"},
|
|
274
|
+
tool_output=None,
|
|
275
|
+
start_time=tool_start,
|
|
276
|
+
end_time=tool_end,
|
|
277
|
+
success=False,
|
|
278
|
+
error_message=str(e),
|
|
279
|
+
error_type=type(e).__name__,
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
print(f" ✓ Tool failure tracked: {type(e).__name__}")
|
|
283
|
+
|
|
284
|
+
print("\n✅ Execution continued despite errors (resilient)")
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
async def performance_test_example():
|
|
288
|
+
"""
|
|
289
|
+
Example showing performance characteristics.
|
|
290
|
+
|
|
291
|
+
Analytics should add minimal overhead (<5ms per submission).
|
|
292
|
+
"""
|
|
293
|
+
|
|
294
|
+
collector = create_analytics_collector(
|
|
295
|
+
control_plane_url="http://localhost:8000",
|
|
296
|
+
api_key="test-key",
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
ctx = ExecutionContext(
|
|
300
|
+
execution_id="perf-test",
|
|
301
|
+
organization_id="org-perf",
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
print("\n⚡ Performance Test\n")
|
|
305
|
+
|
|
306
|
+
# Test turn recording performance
|
|
307
|
+
turn_count = 10
|
|
308
|
+
start_time = time.time()
|
|
309
|
+
|
|
310
|
+
for i in range(turn_count):
|
|
311
|
+
ctx = collector.start_turn(ctx)
|
|
312
|
+
|
|
313
|
+
class MockResponse:
|
|
314
|
+
class Usage:
|
|
315
|
+
prompt_tokens = 1000
|
|
316
|
+
completion_tokens = 500
|
|
317
|
+
total_tokens = 1500
|
|
318
|
+
|
|
319
|
+
usage = Usage()
|
|
320
|
+
|
|
321
|
+
class Choice:
|
|
322
|
+
class Message:
|
|
323
|
+
content = "Response content"
|
|
324
|
+
|
|
325
|
+
message = Message()
|
|
326
|
+
finish_reason = "stop"
|
|
327
|
+
|
|
328
|
+
choices = [Choice()]
|
|
329
|
+
|
|
330
|
+
collector.record_turn_from_litellm(
|
|
331
|
+
ctx=ctx,
|
|
332
|
+
response=MockResponse(),
|
|
333
|
+
model="claude-sonnet-4",
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
end_time = time.time()
|
|
337
|
+
overhead_per_turn = ((end_time - start_time) / turn_count) * 1000
|
|
338
|
+
|
|
339
|
+
print(f" • Recorded {turn_count} turns")
|
|
340
|
+
print(f" • Overhead per turn: {overhead_per_turn:.2f}ms")
|
|
341
|
+
print(f" • Status: {'✓ Fast' if overhead_per_turn < 5 else '⚠️ Slow'}")
|
|
342
|
+
|
|
343
|
+
# Wait for submissions
|
|
344
|
+
await collector.wait_for_submissions()
|
|
345
|
+
|
|
346
|
+
print("\n✅ Performance test complete")
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
if __name__ == "__main__":
|
|
350
|
+
"""Run all examples"""
|
|
351
|
+
print("=" * 70)
|
|
352
|
+
print("Analytics Integration Examples")
|
|
353
|
+
print("=" * 70)
|
|
354
|
+
|
|
355
|
+
# Run examples
|
|
356
|
+
asyncio.run(execute_agent_with_analytics_example())
|
|
357
|
+
asyncio.run(execute_agent_with_error_handling_example())
|
|
358
|
+
asyncio.run(performance_test_example())
|
|
359
|
+
|
|
360
|
+
print("\n" + "=" * 70)
|
|
361
|
+
print("Examples complete! Check Control Plane for analytics data.")
|
|
362
|
+
print("=" * 70)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Data models for worker activities"""
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""Input dataclasses for all Temporal activities"""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Optional, List, Dict, Any
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass
|
|
8
|
+
class AgentExecutionInput:
|
|
9
|
+
"""Input for agent execution activity"""
|
|
10
|
+
execution_id: str
|
|
11
|
+
agent_id: str
|
|
12
|
+
organization_id: str
|
|
13
|
+
prompt: str
|
|
14
|
+
system_prompt: Optional[str] = None
|
|
15
|
+
model_id: Optional[str] = None
|
|
16
|
+
model_config: Optional[Dict[str, Any]] = None
|
|
17
|
+
mcp_servers: Optional[Dict[str, Any]] = None
|
|
18
|
+
session_id: Optional[str] = None
|
|
19
|
+
user_id: Optional[str] = None
|
|
20
|
+
|
|
21
|
+
def __post_init__(self):
|
|
22
|
+
if self.model_config is None:
|
|
23
|
+
self.model_config = {}
|
|
24
|
+
if self.mcp_servers is None:
|
|
25
|
+
self.mcp_servers = {}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class TeamExecutionInput:
|
|
30
|
+
"""Input for team execution activity"""
|
|
31
|
+
execution_id: str
|
|
32
|
+
team_id: str
|
|
33
|
+
organization_id: str
|
|
34
|
+
prompt: str
|
|
35
|
+
system_prompt: Optional[str] = None
|
|
36
|
+
model_id: Optional[str] = None
|
|
37
|
+
model_config: Optional[Dict[str, Any]] = None
|
|
38
|
+
mcp_servers: Optional[Dict[str, Any]] = None
|
|
39
|
+
session_id: Optional[str] = None
|
|
40
|
+
user_id: Optional[str] = None
|
|
41
|
+
team_config: Optional[Dict[str, Any]] = None
|
|
42
|
+
|
|
43
|
+
def __post_init__(self):
|
|
44
|
+
if self.model_config is None:
|
|
45
|
+
self.model_config = {}
|
|
46
|
+
if self.mcp_servers is None:
|
|
47
|
+
self.mcp_servers = {}
|
|
48
|
+
if self.team_config is None:
|
|
49
|
+
self.team_config = {}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@dataclass
|
|
53
|
+
class CancelExecutionInput:
|
|
54
|
+
"""Input for cancellation activity"""
|
|
55
|
+
execution_id: str
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@dataclass
|
|
59
|
+
class UpdateExecutionStatusInput:
|
|
60
|
+
"""Input for status update activity"""
|
|
61
|
+
execution_id: str
|
|
62
|
+
status: str
|
|
63
|
+
started_at: Optional[str] = None
|
|
64
|
+
completed_at: Optional[str] = None
|
|
65
|
+
response: Optional[str] = None
|
|
66
|
+
error_message: Optional[str] = None
|
|
67
|
+
usage: Optional[Dict[str, Any]] = None
|
|
68
|
+
execution_metadata: Optional[Dict[str, Any]] = None
|
|
69
|
+
|
|
70
|
+
def __post_init__(self):
|
|
71
|
+
if self.usage is None:
|
|
72
|
+
self.usage = {}
|
|
73
|
+
if self.execution_metadata is None:
|
|
74
|
+
self.execution_metadata = {}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@dataclass
|
|
78
|
+
class UpdateAgentStatusInput:
|
|
79
|
+
"""Input for agent status update"""
|
|
80
|
+
agent_id: str
|
|
81
|
+
organization_id: str
|
|
82
|
+
status: str
|
|
83
|
+
last_active_at: str
|
|
84
|
+
error_message: Optional[str] = None
|
|
85
|
+
state: Optional[Dict[str, Any]] = None
|
|
86
|
+
|
|
87
|
+
def __post_init__(self):
|
|
88
|
+
if self.state is None:
|
|
89
|
+
self.state = {}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Runtime abstraction layer for agent execution.
|
|
3
|
+
|
|
4
|
+
This package provides a pluggable runtime system that allows agents to be
|
|
5
|
+
powered by different frameworks (Agno, Claude Code SDK, etc.) without changing
|
|
6
|
+
the core workflow and activity logic.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from .base import (
|
|
10
|
+
RuntimeType,
|
|
11
|
+
RuntimeExecutionResult,
|
|
12
|
+
RuntimeExecutionContext,
|
|
13
|
+
RuntimeCapabilities,
|
|
14
|
+
BaseRuntime,
|
|
15
|
+
RuntimeRegistry,
|
|
16
|
+
)
|
|
17
|
+
from .factory import RuntimeFactory
|
|
18
|
+
from .default_runtime import DefaultRuntime
|
|
19
|
+
from .claude_code_runtime import ClaudeCodeRuntime
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"RuntimeType",
|
|
23
|
+
"RuntimeExecutionResult",
|
|
24
|
+
"RuntimeExecutionContext",
|
|
25
|
+
"RuntimeCapabilities",
|
|
26
|
+
"BaseRuntime",
|
|
27
|
+
"RuntimeRegistry",
|
|
28
|
+
"RuntimeFactory",
|
|
29
|
+
"DefaultRuntime",
|
|
30
|
+
"ClaudeCodeRuntime",
|
|
31
|
+
]
|