Skip to content

Commit 2d85b96

Browse files
azulusclaude
andauthored
ref(seer): Propagate viewer_context to similarity Seer call sites (#109719)
Pass `SeerViewerContext` through similarity/grouping code to all Seer call sites. PR #109697 added an optional `viewer_context` parameter to all Seer API wrapper functions. This PR threads it through `get_similarity_data_from_seer()` and updates all callers: - **Endpoint** (`group_similar_issues_embeddings`): Pass both `organization_id` and `user_id` - **Grouping ingest** (`seer.py`): Pass `organization_id` only from `event.project` (no user in ingest context) No behavior change — `viewer_context` defaults to `None` which preserves existing behavior. Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 9c4f0d7 commit 2d85b96

File tree

6 files changed

+30
-3
lines changed

6 files changed

+30
-3
lines changed

src/sentry/grouping/ingest/seer.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from sentry.grouping.variants import BaseVariant
1717
from sentry.models.grouphash import GroupHash
1818
from sentry.models.project import Project
19+
from sentry.seer.signed_seer_api import SeerViewerContext
1920
from sentry.seer.similarity.config import (
2021
get_grouping_model_version,
2122
get_new_model_version,
@@ -347,9 +348,11 @@ def get_seer_similar_issues(
347348

348349
request_data, seer_request_metric_tags = _build_seer_request(event, variants)
349350

351+
viewer_context = SeerViewerContext(organization_id=event.project.organization_id)
350352
seer_results = get_similarity_data_from_seer(
351353
request_data,
352354
{**seer_request_metric_tags, "hybrid_fingerprint": event_has_hybrid_fingerprint},
355+
viewer_context=viewer_context,
353356
)
354357

355358
# All of these will get overridden if we find a usable match
@@ -415,7 +418,9 @@ def get_seer_similar_issues(
415418

416419
# We only want this for the side effect, and we know it'll return no matches, so we don't
417420
# bother to capture the return value.
418-
get_similarity_data_from_seer(request_data, seer_request_metric_tags)
421+
get_similarity_data_from_seer(
422+
request_data, seer_request_metric_tags, viewer_context=viewer_context
423+
)
419424

420425
is_hybrid_fingerprint_case = (
421426
event_has_hybrid_fingerprint
@@ -644,8 +649,11 @@ def maybe_send_seer_for_new_model_training(
644649

645650
request_data, metric_tags = _build_seer_request(event, variants, training_mode=True)
646651

652+
viewer_context = SeerViewerContext(organization_id=event.project.organization_id)
647653
try:
648-
get_similarity_data_from_seer(request_data, metric_tags, raise_on_error=True)
654+
get_similarity_data_from_seer(
655+
request_data, metric_tags, raise_on_error=True, viewer_context=viewer_context
656+
)
649657
except Exception as e:
650658
sentry_sdk.capture_exception(
651659
e,

src/sentry/issues/endpoints/group_similar_issues_embeddings.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from sentry.issues.endpoints.bases.group import GroupEndpoint
1919
from sentry.models.group import Group
2020
from sentry.models.grouphash import GroupHash
21+
from sentry.seer.signed_seer_api import SeerViewerContext
2122
from sentry.seer.similarity.config import get_grouping_model_version
2223
from sentry.seer.similarity.similar_issues import get_similarity_data_from_seer
2324
from sentry.seer.similarity.types import SeerSimilarIssueData, SimilarIssuesEmbeddingsRequest
@@ -138,7 +139,12 @@ def get(self, request: Request, group: Group) -> Response:
138139

139140
logger.info("Similar issues embeddings parameters", extra=similar_issues_params)
140141

141-
results = get_similarity_data_from_seer(similar_issues_params)
142+
viewer_context = SeerViewerContext(
143+
organization_id=group.project.organization.id, user_id=request.user.id
144+
)
145+
results = get_similarity_data_from_seer(
146+
similar_issues_params, viewer_context=viewer_context
147+
)
142148

143149
analytics.record(
144150
GroupSimilarIssuesEmbeddingsCountEvent(

src/sentry/seer/signed_seer_api.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@
1515

1616
class SeerViewerContext(TypedDict, total=False):
1717
organization_id: int
18+
# TODO(jeremy.stanley): user_id is int | None as a temporary state while
19+
# consolidating viewer context across call sites. Some pass request.user.id
20+
# (which can be None for anonymous users), others omit the key entirely.
21+
# Once all call sites are wired up, tighten this to int and ensure callers
22+
# only set user_id when an authenticated user is present.
1823
user_id: int | None
1924

2025

src/sentry/seer/similarity/similar_issues.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ def get_similarity_data_from_seer(
6060
similar_issues_request: SimilarIssuesEmbeddingsRequest,
6161
metric_tags: Mapping[str, str | int | bool] | None = None,
6262
raise_on_error: bool = False,
63+
viewer_context: SeerViewerContext | None = None,
6364
) -> list[SeerSimilarIssueData]:
6465
"""
6566
Request similar issues data from seer and normalize the results. Returns similar groups
@@ -93,6 +94,7 @@ def get_similarity_data_from_seer(
9394
retries=options.get("seer.similarity.grouping-ingest-retries"),
9495
timeout=options.get("seer.similarity.grouping-ingest-timeout"),
9596
metric_tags={"referrer": referrer} if referrer else {},
97+
viewer_context=viewer_context,
9698
)
9799
except (TimeoutError, MaxRetryError) as e:
98100
logger.warning("get_seer_similar_issues.request_error", extra=logger_extra)

tests/sentry/grouping/seer_similarity/test_get_seer_similar_issues.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ def test_sends_expected_data_to_seer(self, mock_get_similarity_data: MagicMock)
117117
"training_mode": False,
118118
"hybrid_fingerprint": False,
119119
},
120+
viewer_context={"organization_id": self.project.organization_id},
120121
)
121122

122123
@patch("sentry.grouping.ingest.seer.metrics.incr")
@@ -165,6 +166,7 @@ def test_sends_second_seer_request_when_seer_matches_are_unusable(
165166
"training_mode": False,
166167
}
167168

169+
viewer_ctx = {"organization_id": self.project.organization_id}
168170
assert mock_get_similarity_data.call_count == 2
169171
assert mock_get_similarity_data.mock_calls == [
170172
# Initial call to Seer
@@ -181,6 +183,7 @@ def test_sends_second_seer_request_when_seer_matches_are_unusable(
181183
"training_mode": False,
182184
"hybrid_fingerprint": False,
183185
},
186+
viewer_context=viewer_ctx,
184187
),
185188
# Second call to store the event's data since the match that came back from Seer
186189
# wasn't usable
@@ -196,6 +199,7 @@ def test_sends_second_seer_request_when_seer_matches_are_unusable(
196199
"model_version": "v1",
197200
"training_mode": False,
198201
},
202+
viewer_context=viewer_ctx,
199203
),
200204
]
201205

tests/sentry/grouping/seer_similarity/test_seer.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ def test_simple(self, mock_get_similarity_data: MagicMock) -> None:
6969
"training_mode": False,
7070
"hybrid_fingerprint": False,
7171
},
72+
viewer_context={"organization_id": self.project.organization_id},
7273
)
7374

7475
@patch("sentry.grouping.ingest.seer.record_did_call_seer_metric")
@@ -206,4 +207,5 @@ def test_bypassed_platform_calls_seer_regardless_of_length(
206207
"training_mode": False,
207208
"hybrid_fingerprint": False,
208209
},
210+
viewer_context={"organization_id": self.project.organization_id},
209211
)

0 commit comments

Comments
 (0)