From adb60462bc45131dd50b1c080b2aa1994a19c9f5 Mon Sep 17 00:00:00 2001 From: Rohan Agarwal Date: Tue, 14 Oct 2025 16:17:46 -0700 Subject: [PATCH 1/2] Fix grouped charts --- src/sentry/seer/explorer/tools.py | 22 +++++++++++++++++----- tests/sentry/seer/explorer/test_tools.py | 16 +++++++++++++--- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/sentry/seer/explorer/tools.py b/src/sentry/seer/explorer/tools.py index 72b7ef87ed0add..1fe14711bbaad7 100644 --- a/src/sentry/seer/explorer/tools.py +++ b/src/sentry/seer/explorer/tools.py @@ -56,11 +56,23 @@ def execute_trace_query_chart( ) data = resp.data - # Normalize response format: single-axis returns flat format, multi-axis returns nested - # We always want the nested format {"metric": {"data": [...]}} - if isinstance(data, dict) and "data" in data and len(y_axes) == 1: - # Single axis response - wrap it - metric_name = y_axes[0] + # Always normalize to the nested {"metric": {"data": [...]}} format for consistency + metric_is_single = len(y_axes) == 1 + metric_name = y_axes[0] if metric_is_single else None + + # Handle grouped data with single metric: wrap each group's data in the metric name + if group_by and metric_is_single: + return { + group_value: ( + {metric_name: group_data} + if isinstance(group_data, dict) and "data" in group_data + else group_data + ) + for group_value, group_data in data.items() + } + + # Handle non-grouped data with single metric: wrap data in the metric name + if metric_is_single and isinstance(data, dict) and "data" in data: return {metric_name: data} return data diff --git a/tests/sentry/seer/explorer/test_tools.py b/tests/sentry/seer/explorer/test_tools.py index cae0c8530fe4da..bca8dc987dd18f 100644 --- a/tests/sentry/seer/explorer/test_tools.py +++ b/tests/sentry/seer/explorer/test_tools.py @@ -258,10 +258,20 @@ def test_execute_trace_query_chart_with_groupby(self): # Should have different span.op values like "db", "http.client", etc. assert len(result) > 0 - # Each group should have the metric + # Each group should have the metric wrapped in normalized format + # Format: {"group_value": {"count()": {"data": [...]}}} for group_value, metrics in result.items(): - if isinstance(metrics, dict) and "count()" in metrics: - assert "data" in metrics["count()"] + assert isinstance( + metrics, dict + ), f"Expected dict for {group_value}, got {type(metrics)}" + assert ( + "count()" in metrics + ), f"Missing count() in metrics for {group_value}: {metrics.keys()}" + assert "data" in metrics["count()"], f"Missing data in count() for {group_value}" + + # Verify we can get actual count data + data_points = metrics["count()"]["data"] + assert isinstance(data_points, list) def test_execute_trace_query_table_with_groupby(self): """Test table query with group_by for aggregates mode""" From bd28b0619ba6ef8d448bb7cb9661b74b3b25701f Mon Sep 17 00:00:00 2001 From: Rohan Agarwal Date: Tue, 14 Oct 2025 16:26:07 -0700 Subject: [PATCH 2/2] typing --- src/sentry/seer/explorer/tools.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/sentry/seer/explorer/tools.py b/src/sentry/seer/explorer/tools.py index 1fe14711bbaad7..fbfeaf1cdc637a 100644 --- a/src/sentry/seer/explorer/tools.py +++ b/src/sentry/seer/explorer/tools.py @@ -59,21 +59,21 @@ def execute_trace_query_chart( # Always normalize to the nested {"metric": {"data": [...]}} format for consistency metric_is_single = len(y_axes) == 1 metric_name = y_axes[0] if metric_is_single else None - - # Handle grouped data with single metric: wrap each group's data in the metric name - if group_by and metric_is_single: - return { - group_value: ( - {metric_name: group_data} - if isinstance(group_data, dict) and "data" in group_data - else group_data - ) - for group_value, group_data in data.items() - } - - # Handle non-grouped data with single metric: wrap data in the metric name - if metric_is_single and isinstance(data, dict) and "data" in data: - return {metric_name: data} + if metric_name and metric_is_single: + # Handle grouped data with single metric: wrap each group's data in the metric name + if group_by: + return { + group_value: ( + {metric_name: group_data} + if isinstance(group_data, dict) and "data" in group_data + else group_data + ) + for group_value, group_data in data.items() + } + + # Handle non-grouped data with single metric: wrap data in the metric name + if isinstance(data, dict) and "data" in data: + return {metric_name: data} return data