Skip to content

feat(seer): Add get_metric_metadata RPC for Seer metrics agent#113462

Merged
isaacwang-sentry merged 3 commits into
masterfrom
isaac/feat/seer-rpc-metric-metadata
Apr 21, 2026
Merged

feat(seer): Add get_metric_metadata RPC for Seer metrics agent#113462
isaacwang-sentry merged 3 commits into
masterfrom
isaac/feat/seer-rpc-metric-metadata

Conversation

@isaacwang-sentry

Copy link
Copy Markdown
Member

Summary

  • Add a new cross-project RPC get_metric_metadata(org_id, project_ids, name_substrings, stats_period, limit) that returns distinct (metric.name, metric.type, metric.unit) tuples — plus per-row event counts — matching any of the provided metric-name substrings, ordered by count descending.
  • Registered in both seer_rpc.py and organization_seer_rpc.py. Implemented via /events/ with dataset=tracemetrics, selecting metric.name / metric.type / metric.unit / count() which implicitly groups by the non-aggregate fields.
  • Fix an unrelated-but-adjacent bug in the sync /search-agent/translate/ endpoint: SearchAgentTranslateEndpoint previously dropped options.metric_context (only forwarded model_name), so a metrics-tab fallback from async polling to the sync path would silently lose the metric scope. Signature of send_translate_agentic_request now accepts and forwards metric_context, matching the async /search-agent/start/ path.

This is the Sentry-side half of adding candidate discovery to Seer's metrics agent; the Seer-side consumer lives in getsentry/seer#TBD (pushed in parallel). Seer falls back to get_field_values when this RPC is unavailable, so the two PRs can ship independently — but this one should land first so the Seer tool has something to call.

Refs: follow-on to #113232 (which wired the metrics tab to Seer's metrics strategy).

Test plan

  • pytest tests/sentry/seer/assisted_query/test_metrics_tools.py -vv — new unit tests cover query shape (dataset=tracemetrics, sort=-count(), OR-combined metric.name:"*sub*" clauses), tuple-level dedupe, has_more detection via over-fetch, and graceful handling of rows missing metric.name / metric.type.
  • pytest tests/sentry/seer/endpoints/test_trace_explorer_ai_translate_agentic.py -vv — existing happy-path test updated to assert the new metric_context=None default; new test test_translate_forwards_metric_context verifies sync-path forwarding.
  • Manual: hit /api/0/organizations/<slug>/search-agent/translate/ with strategy=Metrics and options.metric_context={…} — confirm the payload reaching Seer carries metric_context in options.

🤖 Generated with Claude Code

Add a new cross-project RPC that returns distinct (metric.name, metric.type,
metric.unit) tuples matching any of a set of metric.name substrings, with
per-row event counts, ordered by count descending.

Seer's metrics assisted-query agent will consume this from a new
get_metric_candidates tool to short-circuit the iterative
get_field_values(metric.name) + get_field_values(metric.type) discovery loop
when the caller (e.g. Seer Explorer) does not scope the agent to a specific
metric via metric_context. A single RPC returns name + type + unit for up to
20 distinct tuples, and the Seer-side tool reranks and truncates before
passing candidates to the agent.

Registered in both seer_rpc.py and organization_seer_rpc.py dispatch tables.
Covered by unit tests that mock the events API client to verify the query
shape (dataset=tracemetrics, sort=-count(), substring OR-query), tuple-level
dedupe, has_more detection via over-fetch, and graceful handling of rows
missing metric.name or metric.type.

Also fixes SearchAgentTranslateEndpoint (sync /search-agent/translate/) to
forward metric_context through options, matching the async
/search-agent/start/ path. Previously the sync path only forwarded
model_name, so a metrics-tab fallback to the sync endpoint would silently
drop the metric scope and land on Seer as metric_context=None.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added the Scope: Backend Automatically applied to PRs that change backend components label Apr 20, 2026
@isaacwang-sentry
isaacwang-sentry marked this pull request as ready for review April 20, 2026 21:39
@isaacwang-sentry
isaacwang-sentry requested a review from a team as a code owner April 20, 2026 21:39
Comment thread src/sentry/seer/assisted_query/metrics_tools.py Outdated
Review feedback: silently returning empty candidates on internal failures
(Organization.DoesNotExist, events API errors) masked real outages as
"sparse metric catalog" responses. Seer's metric candidates tool only
caught RPC-transport failures and treated handler-side failures as
legitimate empty results, breaking Langfuse observability and misleading
the sub-agent into retrying dead substrings.

Return an explicit `error` key on handler-side failures — callers (Seer)
translate this into a tool failure and fall back to get_field_values.

Covered by new unit tests for the missing-org and events-query-failed
paths.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
We over-fetch by 1 (per_page = limit + 1) specifically to detect whether
Sentry has more matches than the caller asked for. The old check compared
the *filtered* candidates list to limit, so a single malformed row
(missing metric.name/metric.type) could push the post-filter count back
down to limit and silently report has_more=False — hiding the fact that
further matches existed.

Low severity in practice (tracemetrics rows are unlikely to have null
primary fields; Seer-side post-dedupe re-derives has_more anyway), but
the contract is wrong. Check raw_rows length instead.

New test pins the regression: 3 raw rows with one malformed → 2
candidates + has_more=True.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@isaacwang-sentry
isaacwang-sentry merged commit 4a7ed6f into master Apr 21, 2026
56 checks passed
@isaacwang-sentry
isaacwang-sentry deleted the isaac/feat/seer-rpc-metric-metadata branch April 21, 2026 16:05
isaacwang-sentry added a commit that referenced this pull request Apr 22, 2026
…#113616)

Fix the `get_metric_metadata` RPC handler to use `count(value)` instead
of zero-arg `count()` in its tracemetrics query.

The `tracemetrics` dataset parser requires `count()` to take an
attribute argument. Zero-arg `count()` parse-fails at the events layer
with `status=400 body={'detail': 'Invalid number of arguments for count,
was expecting 1 arguments', 'code': 'parse_error'}`. Every call to this
handler since it was merged in #113462 has been returning
`events_query_failed` — the Seer assisted-query metrics agent has never
once received real candidates from this tool and has been silently
falling back to `get_field_values` probes instead.

The bug wasn't caught because the existing tests mocked
`sentry.api.client` wholesale and never exercised the real parser. This
PR also adds `TestGetMetricMetadataIntegration`, which persists real
`TraceItem`s and hits the handler end-to-end via `TraceMetricsTestCase`
— this is the shape of test that would have caught the original bug, and
will catch future regressions in the aggregate-function shape.

While here, the `ApiError` catch block now attaches `status_code` and a
500-char body prefix to its `logger.exception` extras so future flakes
are debuggable from logs without a new deploy cycle.

Reproduction (before this PR, on prod):

```
POST /api/0/organizations/1/seer-rpc/get_metric_metadata/
{"args": {"org_id": 1, "project_ids": [1], "name_substrings": ["storage.put.size"], "stats_period": "7d", "limit": 20}}
```

returns `{"candidates": [], "has_more": false, "error":
"events_query_failed"}` on every substring, 25/25 tries. After this PR
the same request returns populated candidates.

Refs #113462

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot locked and limited conversation to collaborators May 7, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Scope: Backend Automatically applied to PRs that change backend components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants