66from django .conf import settings
77from django .utils import timezone
88
9- from sentry import features , options
9+ from sentry import options
1010from sentry import ratelimits as ratelimiter
11- from sentry .conf .server import SEER_SIMILARITY_MODEL_VERSION
1211from sentry .grouping .grouping_info import get_grouping_info_from_variants_legacy
1312from sentry .grouping .ingest .grouphash_metadata import (
1413 check_grouphashes_for_positive_fingerprint_match ,
1716from sentry .grouping .variants import BaseVariant
1817from sentry .models .grouphash import GroupHash
1918from sentry .models .project import Project
19+ from sentry .seer .similarity .config import get_grouping_model_version
2020from sentry .seer .similarity .similar_issues import get_similarity_data_from_seer
21- from sentry .seer .similarity .types import GroupingVersion , SimilarIssuesEmbeddingsRequest
21+ from sentry .seer .similarity .types import SimilarIssuesEmbeddingsRequest
2222from sentry .seer .similarity .utils import (
2323 SEER_INELIGIBLE_EVENT_PLATFORMS ,
2424 ReferrerOptions ,
@@ -257,10 +257,17 @@ def get_seer_similar_issues(
257257 event : Event ,
258258 event_grouphash : GroupHash ,
259259 variants : dict [str , BaseVariant ],
260+ training_mode : bool = False ,
260261) -> tuple [float | None , GroupHash | None ]:
261262 """
262263 Ask Seer for the given event's nearest neighbor(s) and return the stacktrace distance and
263264 matching GroupHash of the closest match (if any), or `(None, None)` if no match found.
265+
266+ Args:
267+ event: The event being grouped
268+ event_grouphash: The grouphash for this event
269+ variants: Grouping variants for the event
270+ training_mode: If True, only possibly insert embedding without returning matches
264271 """
265272 event_hash = event .get_primary_hash ()
266273 exception_type = get_path (event .data , "exception" , "values" , - 1 , "type" )
@@ -272,10 +279,7 @@ def get_seer_similar_issues(
272279 get_stacktrace_string (get_grouping_info_from_variants_legacy (variants )),
273280 )
274281
275- # Get model configuration from feature flags
276- use_v2_model = features .has ("projects:similarity-grouping-v2-model" , event .project )
277- model_version = GroupingVersion .V2 if use_v2_model else GroupingVersion .V1
278- training_mode = False # PR #B will add the smart logic
282+ model_version = get_grouping_model_version (event .project )
279283
280284 request_data : SimilarIssuesEmbeddingsRequest = {
281285 "event_id" : event .event_id ,
@@ -528,6 +532,8 @@ def maybe_check_seer_for_matching_grouphash(
528532
529533 timestamp = timezone .now ()
530534
535+ model_version = get_grouping_model_version (event .project )
536+
531537 gh_metadata .update (
532538 # Technically the time of the metadata record creation and the time of the Seer
533539 # request will be some milliseconds apart, but a) the difference isn't meaningful
@@ -541,9 +547,66 @@ def maybe_check_seer_for_matching_grouphash(
541547 date_added = gh_metadata .date_added or timestamp ,
542548 seer_date_sent = gh_metadata .date_added or timestamp ,
543549 seer_event_sent = event .event_id ,
544- seer_model = SEER_SIMILARITY_MODEL_VERSION ,
550+ seer_model = model_version . value ,
545551 seer_matched_grouphash = seer_matched_grouphash ,
546552 seer_match_distance = seer_match_distance ,
547553 )
548554
549555 return seer_matched_grouphash
556+
557+
558+ @sentry_sdk .tracing .trace
559+ def maybe_send_seer_for_new_model_training (
560+ event : Event ,
561+ existing_grouphash : GroupHash ,
562+ variants : dict [str , BaseVariant ],
563+ ) -> None :
564+ """
565+ Send a training_mode=true request to Seer to build embeddings for the new model
566+ version if the existing grouphash hasn't been sent to the new version yet.
567+
568+ This only happens for projects that have the new model rolled out. It helps
569+ build embeddings for existing groups without affecting production grouping decisions.
570+
571+ Args:
572+ event: The event being grouped
573+ existing_grouphash: The grouphash that was found for this event
574+ variants: Grouping variants for the event
575+ """
576+ from sentry .seer .similarity .config import should_send_new_model_embeddings
577+
578+ # Check if we should send embeddings for the new model
579+ gh_metadata = existing_grouphash .metadata
580+ grouphash_seer_model = gh_metadata .seer_model if gh_metadata else None
581+
582+ if should_send_new_model_embeddings (event .project , grouphash_seer_model ):
583+ had_metadata = gh_metadata is not None
584+ # Send training mode request (honor all checks like rate limits, circuit breaker, etc.)
585+ if should_call_seer_for_grouping (event , variants , existing_grouphash ):
586+ record_did_call_seer_metric (event , call_made = True , blocker = "none" )
587+
588+ try :
589+ # Call Seer with training_mode=True (results won't be used for grouping)
590+ get_seer_similar_issues (event , existing_grouphash , variants , training_mode = True )
591+
592+ # Record metrics for new model embedding requests
593+ metrics .incr (
594+ "seer.new_model_embedding_request" ,
595+ sample_rate = options .get ("seer.similarity.metrics_sample_rate" ),
596+ tags = {
597+ "platform" : event .platform or "unknown" ,
598+ "had_metadata" : had_metadata ,
599+ },
600+ )
601+ except Exception as e :
602+ sentry_sdk .capture_exception (
603+ e ,
604+ tags = {
605+ "event" : event .event_id ,
606+ "project" : event .project .id ,
607+ "grouphash" : existing_grouphash .hash ,
608+ },
609+ )
610+ else :
611+ # Not eligible for Seer call (e.g., rate limited, killswitch enabled)
612+ record_did_call_seer_metric (event , call_made = False , blocker = "new_model_embedding_check" )
0 commit comments