Skip to content

Commit d6ee402

Browse files
majdyzclaudegithub-actions[bot]
authored
feat(platform): Add execution analytics admin endpoint with feature flag bypass (#11327)
This PR adds a comprehensive execution analytics admin endpoint that generates AI-powered activity summaries and correctness scores for graph executions, with proper feature flag bypass for admin use. ### Changes 🏗️ **Backend Changes:** - Added admin endpoint: `/api/executions/admin/execution_analytics` - Implemented feature flag bypass with `skip_feature_flag=True` parameter for admin operations - Fixed async database client usage (`get_db_async_client`) to resolve async/await errors - Added batch processing with configurable size limits to handle large datasets - Comprehensive error handling and logging for troubleshooting - Renamed entire feature from "Activity Backfill" to "Execution Analytics" for clarity **Frontend Changes:** - Created clean admin UI for execution analytics generation at `/admin/execution-analytics` - Built form with graph ID input, model selection dropdown, and optional filters - Implemented results table with status badges and detailed execution information - Added CSV export functionality for analytics results - Integrated with generated TypeScript API client for proper authentication - Added proper error handling with toast notifications and loading states **Database & API:** - Fixed critical async/await issue by switching from sync to async database client - Updated router configuration and endpoint naming for consistency - Generated proper TypeScript types and API client integration - Applied feature flag filtering at API level while bypassing for admin operations ### Checklist 📋 #### For code changes: - [x] I have clearly listed my changes in the PR description - [x] I have made a test plan - [x] I have tested my changes according to the test plan: **Test Plan:** - [x] Admin can access execution analytics page at `/admin/execution-analytics` - [x] Form validation works correctly (requires graph ID, validates inputs) - [x] API endpoint `/api/executions/admin/execution_analytics` responds correctly - [x] Authentication works properly through generated API client - [x] Analytics generation works with different LLM models (gpt-4o-mini, gpt-4o, etc.) - [x] Results display correctly with appropriate status badges (success/failed/skipped) - [x] CSV export functionality downloads correct data - [x] Error handling displays appropriate toast messages - [x] Feature flag bypass works for admin users (generates analytics regardless of user flags) - [x] Batch processing handles multiple executions correctly - [x] Loading states show proper feedback during processing #### For configuration changes: - [x] `.env.default` is updated or already compatible with my changes - [x] `docker-compose.yml` is updated or already compatible with my changes - [x] No configuration changes required for this feature **Related to:** PR #11325 (base correctness score functionality) 🤖 Generated with [Claude Code](https://claude.ai/code) --------- Co-authored-by: Claude <[email protected]> Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: Zamil Majdy <[email protected]>
1 parent 58928b5 commit d6ee402

File tree

11 files changed

+1167
-7
lines changed

11 files changed

+1167
-7
lines changed

autogpt_platform/backend/backend/data/execution.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,12 @@ def to_db(self) -> GraphExecutionStats:
194194
correctness_score=self.correctness_score,
195195
)
196196

197+
def without_activity_features(self) -> "GraphExecutionMeta.Stats":
198+
"""Return a copy of stats with activity features (activity_status, correctness_score) set to None."""
199+
return self.model_copy(
200+
update={"activity_status": None, "correctness_score": None}
201+
)
202+
197203
stats: Stats | None
198204

199205
@staticmethod

autogpt_platform/backend/backend/executor/activity_status_generator.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ async def generate_activity_status_for_execution(
9191
db_client: "DatabaseManagerAsyncClient",
9292
user_id: str,
9393
execution_status: ExecutionStatus | None = None,
94+
model_name: str = "gpt-4o-mini",
95+
skip_feature_flag: bool = False,
9496
) -> ActivityStatusResponse | None:
9597
"""
9698
Generate an AI-based activity status summary and correctness assessment for a graph execution.
@@ -112,7 +114,9 @@ async def generate_activity_status_for_execution(
112114
or None if feature is disabled
113115
"""
114116
# Check LaunchDarkly feature flag for AI activity status generation with full context support
115-
if not await is_feature_enabled(Flag.AI_ACTIVITY_STATUS, user_id):
117+
if not skip_feature_flag and not await is_feature_enabled(
118+
Flag.AI_ACTIVITY_STATUS, user_id
119+
):
116120
logger.debug("AI activity status generation is disabled via LaunchDarkly")
117121
return None
118122

@@ -273,7 +277,7 @@ async def generate_activity_status_for_execution(
273277
prompt=prompt[1]["content"], # User prompt content
274278
sys_prompt=prompt[0]["content"], # System prompt content
275279
expected_format=expected_format,
276-
model=LlmModel.GPT4O_MINI,
280+
model=LlmModel(model_name),
277281
credentials=credentials_input, # type: ignore
278282
max_tokens=150,
279283
retry=3,
@@ -306,7 +310,7 @@ async def generate_activity_status_for_execution(
306310
return activity_response
307311

308312
except Exception as e:
309-
logger.error(
313+
logger.exception(
310314
f"Failed to generate activity status for execution {graph_exec_id}: {str(e)}"
311315
)
312316
return None

autogpt_platform/backend/backend/server/rest_api.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import backend.server.routers.postmark.postmark
2525
import backend.server.routers.v1
2626
import backend.server.v2.admin.credit_admin_routes
27+
import backend.server.v2.admin.execution_analytics_routes
2728
import backend.server.v2.admin.store_admin_routes
2829
import backend.server.v2.builder
2930
import backend.server.v2.builder.routes
@@ -269,6 +270,11 @@ async def validation_error_handler(
269270
tags=["v2", "admin"],
270271
prefix="/api/credits",
271272
)
273+
app.include_router(
274+
backend.server.v2.admin.execution_analytics_routes.router,
275+
tags=["v2", "admin"],
276+
prefix="/api/executions",
277+
)
272278
app.include_router(
273279
backend.server.v2.library.routes.router, tags=["v2"], prefix="/api/library"
274280
)

autogpt_platform/backend/backend/server/routers/v1.py

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
from backend.util.clients import get_scheduler_client
9090
from backend.util.cloud_storage import get_cloud_storage_handler
9191
from backend.util.exceptions import GraphValidationError, NotFoundError
92+
from backend.util.feature_flag import Flag, is_feature_enabled
9293
from backend.util.json import dumps
9394
from backend.util.settings import Settings
9495
from backend.util.timezone_utils import (
@@ -109,6 +110,39 @@ def _create_file_size_error(size_bytes: int, max_size_mb: int) -> HTTPException:
109110
settings = Settings()
110111
logger = logging.getLogger(__name__)
111112

113+
114+
async def hide_activity_summaries_if_disabled(
115+
executions: list[execution_db.GraphExecutionMeta], user_id: str
116+
) -> list[execution_db.GraphExecutionMeta]:
117+
"""Hide activity summaries and scores if AI_ACTIVITY_STATUS feature is disabled."""
118+
if await is_feature_enabled(Flag.AI_ACTIVITY_STATUS, user_id):
119+
return executions # Return as-is if feature is enabled
120+
121+
# Filter out activity features if disabled
122+
filtered_executions = []
123+
for execution in executions:
124+
if execution.stats:
125+
filtered_stats = execution.stats.without_activity_features()
126+
execution = execution.model_copy(update={"stats": filtered_stats})
127+
filtered_executions.append(execution)
128+
return filtered_executions
129+
130+
131+
async def hide_activity_summary_if_disabled(
132+
execution: execution_db.GraphExecution | execution_db.GraphExecutionWithNodes,
133+
user_id: str,
134+
) -> execution_db.GraphExecution | execution_db.GraphExecutionWithNodes:
135+
"""Hide activity summary and score for a single execution if AI_ACTIVITY_STATUS feature is disabled."""
136+
if await is_feature_enabled(Flag.AI_ACTIVITY_STATUS, user_id):
137+
return execution # Return as-is if feature is enabled
138+
139+
# Filter out activity features if disabled
140+
if execution.stats:
141+
filtered_stats = execution.stats.without_activity_features()
142+
return execution.model_copy(update={"stats": filtered_stats})
143+
return execution
144+
145+
112146
# Define the API routes
113147
v1_router = APIRouter()
114148

@@ -986,7 +1020,12 @@ async def list_graphs_executions(
9861020
page=1,
9871021
page_size=250,
9881022
)
989-
return paginated_result.executions
1023+
1024+
# Apply feature flags to filter out disabled features
1025+
filtered_executions = await hide_activity_summaries_if_disabled(
1026+
paginated_result.executions, user_id
1027+
)
1028+
return filtered_executions
9901029

9911030

9921031
@v1_router.get(
@@ -1003,13 +1042,21 @@ async def list_graph_executions(
10031042
25, ge=1, le=100, description="Number of executions per page"
10041043
),
10051044
) -> execution_db.GraphExecutionsPaginated:
1006-
return await execution_db.get_graph_executions_paginated(
1045+
paginated_result = await execution_db.get_graph_executions_paginated(
10071046
graph_id=graph_id,
10081047
user_id=user_id,
10091048
page=page,
10101049
page_size=page_size,
10111050
)
10121051

1052+
# Apply feature flags to filter out disabled features
1053+
filtered_executions = await hide_activity_summaries_if_disabled(
1054+
paginated_result.executions, user_id
1055+
)
1056+
return execution_db.GraphExecutionsPaginated(
1057+
executions=filtered_executions, pagination=paginated_result.pagination
1058+
)
1059+
10131060

10141061
@v1_router.get(
10151062
path="/graphs/{graph_id}/executions/{graph_exec_id}",
@@ -1038,6 +1085,9 @@ async def get_graph_execution(
10381085
status_code=404, detail=f"Graph execution #{graph_exec_id} not found."
10391086
)
10401087

1088+
# Apply feature flags to filter out disabled features
1089+
result = await hide_activity_summary_if_disabled(result, user_id)
1090+
10411091
return result
10421092

10431093

0 commit comments

Comments
 (0)