ref(seer): Type 8 stats/replay/profile/export RPC responses#117880
Open
azulus wants to merge 6 commits into
Open
ref(seer): Type 8 stats/replay/profile/export RPC responses#117880azulus wants to merge 6 commits into
azulus wants to merge 6 commits into
Conversation
Replaces the `dict[str, Any]` return annotations on three seer RPC methods that emit attribute / tag distribution data for the bug-prediction agent: - `get_baseline_tag_distribution` → `BaselineTagDistributionResponse` with typed `BaselineTagDistributionEntry` items (tag_key, tag_value, count) - `get_comparative_attribute_distributions` → `ComparativeAttributeDistributionsResponse` with the five named fields the spans frequency-stats endpoint produces (baseline_distribution, total_baseline, outliers_distribution, total_outliers, outliers_function_value) - `get_issues_stats` → `IssuesStatsResponse | None` — a `__root__`-based list passthrough since the issues-stats API shape is wider than the documented `id/count/userCount/firstSeen/lastSeen/stats/lifetime` contract Wire-identical across all paths.
Replaces the `dict[str, Any]` / TypedDict / un-annotated return types on five seer RPC methods with explicit Pydantic models: - `export_explorer_indexes` → `AgentExportIndexesResponse` — migrated from a TypedDict (`org_id, version, tables`) so the typed-registry contract holds - `get_replay_metadata` → `ReplayMetadataResponse | None` — `__root__` dict passthrough; the aggregate replay-event shape lives in the replays UI's `ReplayDetailsResponse` and is wider than what sentry-side can lock down - `rpc_get_profile_flamegraph` → `ProfileFlamegraphSuccessResponse | ProfileFlamegraphErrorResponse` discriminated by the presence of `execution_tree`/`metadata` vs `error` - `rpc_get_replay_summary_logs` → `ReplaySummaryLogsResponse` (`{"logs": [str, ...]}`) - `get_error_event_details` → `ErrorEventDetailsResponse | None` — `__root__` dict passthrough; the bare `EventSerializer` shape (a `SentryEventData`-ish dict the seer caller casts to its own model) is too wide for sentry to fix here without a coordinated update Wire-identical across all paths. The non-passthrough response models carry a small `__getitem__` / `__contains__` proxy so existing test sites and seer callers can read fields with `result["x"]` / `"x" in result` until they're migrated to attribute access.
The replay-metadata tests parse the returned value through a private `_ReplayMetadataResponse` schema as a contract check, but `get_replay_metadata` now returns the typed `ReplayMetadataResponse` (a `__root__` model) rather than a bare dict. `BaseModel.parse_obj` rejects a model instance, so pass `result.dict()` to round-trip through the wire shape.
`AgentExportIndexesResponse(**response.json())` raises pydantic's `ValidationError` when the seer response is valid JSON but missing or mistyped against the declared schema. The pre-existing handler only caught `JSONDecodeError`, so a schema mismatch would surface as an uncaught 500 instead of a `SeerApiError`. Add the missing arm so the caller sees the same `SeerApiError` shape it gets for malformed JSON.
Contributor
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 5932c95. Configure here.
`ProfileFlamegraphMetadata.start_ts`/`end_ts` come from float-valued `min(precise.start_ts)` / `max(precise.finish_ts)` aggregates and `thread_id` is the dict key from `_convert_profile_to_execution_tree`'s `dict[str, int]` count map (always a string). Typing them as `int | None` made Pydantic v1 silently truncate the fractional seconds and risk rejecting non-numeric thread ids. Type as `float | None` / `str | None` to match the upstream values.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Replaces the
dict[str, Any]/ TypedDict / un-annotated return types on eight seer RPC methods with explicit Pydantic models so the seer-side SDK consumer sees declared field shapes.Bug-prediction stats cluster (three methods that emit attribute / tag distribution data for the bug-prediction agent):
get_baseline_tag_distribution→BaselineTagDistributionResponsewith typedBaselineTagDistributionEntryitems (tag_key, tag_value, count)get_comparative_attribute_distributions→ComparativeAttributeDistributionsResponsewith the five named fields the spans frequency-stats endpoint produces (baseline_distribution, total_baseline, outliers_distribution, total_outliers, outliers_function_value)get_issues_stats→IssuesStatsResponse | None—__root__-based list passthrough since the issues-stats API shape is wider than the documentedid/count/userCount/firstSeen/lastSeen/stats/lifetimecontractReplay / profile / export / error-event singletons:
export_explorer_indexes→AgentExportIndexesResponse— migrated from a TypedDict (org_id, version, tables) so the typed-registry contract holdsget_replay_metadata→ReplayMetadataResponse | None—__root__dict passthrough; the aggregate replay-event shape lives in the replays UI'sReplayDetailsResponseand is wider than what sentry-side can lock downrpc_get_profile_flamegraph→ProfileFlamegraphSuccessResponse | ProfileFlamegraphErrorResponsediscriminated by the presence ofexecution_tree/metadatavserrorrpc_get_replay_summary_logs→ReplaySummaryLogsResponse({"logs": [str, ...]})get_error_event_details→ErrorEventDetailsResponse | None—__root__dict passthrough; the bareEventSerializershape (aSentryEventData-ish dict the seer caller casts to its own model) is too wide for sentry to fix here without a coordinated updateWire-identical across all paths. The non-passthrough response models carry a small
__getitem__/__contains__proxy so existing test sites and seer callers can read fields withresult["x"]/"x" in resultuntil they're migrated to attribute access.