2424
2525
2626@region_silo_endpoint
27- class OrganizationCliBugPredictionEndpoint (OrganizationEndpoint ):
27+ class OrganizationCodeReviewLocalEndpoint (OrganizationEndpoint ):
2828 """
29- Handle CLI-initiated bug prediction requests.
29+ Handle local code review requests from sentry-cli .
3030
3131 Synchronously polls Seer and returns results.
3232 """
@@ -39,7 +39,7 @@ class OrganizationCliBugPredictionEndpoint(OrganizationEndpoint):
3939
4040 def post (self , request : Request , organization : Organization ) -> Response :
4141 """
42- Trigger bug prediction for a git diff from sentry-cli.
42+ Trigger local code review for a git diff from sentry-cli.
4343
4444 This endpoint:
4545 1. Validates the request (diff size, file count, etc.)
@@ -50,22 +50,29 @@ def post(self, request: Request, organization: Organization) -> Response:
5050
5151 Returns 200 with predictions on success, various error codes on failure.
5252 """
53+ # Check if feature is globally enabled
54+ if not settings .CODE_REVIEW_LOCAL_ENABLED :
55+ return Response (
56+ {"detail" : "Local code review is not enabled" },
57+ status = 503 ,
58+ )
59+
5360 # Check feature flag
54- if not features .has ("organizations:cli-bug-prediction " , organization ):
61+ if not features .has ("organizations:code-review-local " , organization ):
5562 return Response (
56- {"detail" : "CLI bug prediction is not enabled for this organization" },
63+ {"detail" : "Local code review is not enabled for this organization" },
5764 status = 403 ,
5865 )
5966
6067 # Rate limiting
61- user_key = f"cli_bug_prediction :user:{ request .user .id } "
62- org_key = f"cli_bug_prediction :org:{ organization .id } "
68+ user_key = f"code_review_local :user:{ request .user .id } "
69+ org_key = f"code_review_local :org:{ organization .id } "
6370
64- user_limit , user_window = settings .CLI_BUG_PREDICTION_USER_RATE_LIMIT
65- org_limit , org_window = settings .CLI_BUG_PREDICTION_ORG_RATE_LIMIT
71+ user_limit , user_window = settings .CODE_REVIEW_LOCAL_USER_RATE_LIMIT
72+ org_limit , org_window = settings .CODE_REVIEW_LOCAL_ORG_RATE_LIMIT
6673
6774 if ratelimits .backend .is_limited (user_key , limit = user_limit , window = user_window ):
68- metrics .incr ("cli_bug_prediction .rate_limited" , tags = {"type" : "user" })
75+ metrics .incr ("code_review_local .rate_limited" , tags = {"type" : "user" })
6976 return Response (
7077 {
7178 "detail" : f"Rate limit exceeded. Maximum { user_limit } requests per { user_window // 3600 } hour(s) per user"
@@ -74,7 +81,7 @@ def post(self, request: Request, organization: Organization) -> Response:
7481 )
7582
7683 if ratelimits .backend .is_limited (org_key , limit = org_limit , window = org_window ):
77- metrics .incr ("cli_bug_prediction .rate_limited" , tags = {"type" : "org" })
84+ metrics .incr ("code_review_local .rate_limited" , tags = {"type" : "org" })
7885 return Response (
7986 {
8087 "detail" : f"Organization rate limit exceeded. Maximum { org_limit } requests per { org_window // 3600 } hour(s)"
@@ -90,13 +97,8 @@ def post(self, request: Request, organization: Organization) -> Response:
9097 validated_data = serializer .validated_data
9198 repo_data = validated_data ["repository" ]
9299 diff = validated_data ["diff" ]
93- current_branch = validated_data .get ("current_branch" )
94100 commit_message = validated_data .get ("commit_message" )
95101
96- # Record rate limits
97- ratelimits .backend .record (user_key , limit = user_limit , window = user_window )
98- ratelimits .backend .record (org_key , limit = org_limit , window = org_window )
99-
100102 # Resolve repository
101103 try :
102104 repository = self ._resolve_repository (
@@ -115,20 +117,23 @@ def post(self, request: Request, organization: Organization) -> Response:
115117
116118 # Log request
117119 logger .info (
118- "cli_bug_prediction .request" ,
120+ "code_review_local .request" ,
119121 extra = {
120122 "organization_id" : organization .id ,
121123 "user_id" : request .user .id ,
122124 "repository_id" : repository .id ,
123125 "diff_size_bytes" : len (diff ),
124- "has_commit_message" : commit_message is not None ,
125- "has_current_branch" : current_branch is not None ,
126126 },
127127 )
128128
129- metrics .incr ("cli_bug_prediction .request" , tags = {"org" : organization .slug })
129+ metrics .incr ("code_review_local .request" , tags = {"org" : organization .slug })
130130
131131 # Trigger Seer
132+ # user.id is guaranteed to be non-None since this endpoint requires authentication
133+ user_id = request .user .id
134+ assert user_id is not None
135+ user_name = request .user .username or getattr (request .user , "email" , None ) or str (user_id )
136+
132137 try :
133138 trigger_response = trigger_cli_bug_prediction (
134139 repo_provider = repo_data ["provider" ],
@@ -139,35 +144,35 @@ def post(self, request: Request, organization: Organization) -> Response:
139144 diff = diff ,
140145 organization_id = organization .id ,
141146 organization_slug = organization .slug ,
142- user_id = request . user . id ,
143- user_name = request . user . username or request . user . email or str ( request . user . id ) ,
147+ user_id = user_id ,
148+ user_name = user_name ,
144149 commit_message = commit_message ,
145150 )
146151 except (UrllibTimeoutError , MaxRetryError ):
147152 logger .exception (
148- "cli_bug_prediction .trigger.timeout" ,
153+ "code_review_local .trigger.timeout" ,
149154 extra = {
150155 "organization_id" : organization .id ,
151156 "user_id" : request .user .id ,
152157 },
153158 )
154159 return Response (
155- {"detail" : "Bug prediction service is temporarily unavailable" }, status = 503
160+ {"detail" : "Code review service is temporarily unavailable" }, status = 503
156161 )
157162 except ValueError :
158163 logger .exception (
159- "cli_bug_prediction .trigger.error" ,
164+ "code_review_local .trigger.error" ,
160165 extra = {
161166 "organization_id" : organization .id ,
162167 "user_id" : request .user .id ,
163168 },
164169 )
165- return Response ({"detail" : "Failed to start bug prediction analysis" }, status = 502 )
170+ return Response ({"detail" : "Failed to start code review analysis" }, status = 502 )
166171
167172 run_id = trigger_response ["run_id" ]
168173
169174 logger .info (
170- "cli_bug_prediction .seer_triggered" ,
175+ "code_review_local .seer_triggered" ,
171176 extra = {
172177 "seer_run_id" : run_id ,
173178 "organization_id" : organization .id ,
@@ -179,19 +184,19 @@ def post(self, request: Request, organization: Organization) -> Response:
179184 try :
180185 final_response = self ._poll_seer_for_results (
181186 run_id = run_id ,
182- timeout_seconds = settings .CLI_BUG_PREDICTION_TIMEOUT ,
183- poll_interval_seconds = settings .CLI_BUG_PREDICTION_POLL_INTERVAL ,
187+ timeout_seconds = settings .CODE_REVIEW_LOCAL_TIMEOUT ,
188+ poll_interval_seconds = settings .CODE_REVIEW_LOCAL_POLL_INTERVAL ,
184189 )
185190 except TimeoutError :
186191 logger .exception (
187- "cli_bug_prediction .timeout" ,
192+ "code_review_local .timeout" ,
188193 extra = {
189194 "seer_run_id" : run_id ,
190195 "organization_id" : organization .id ,
191196 "user_id" : request .user .id ,
192197 },
193198 )
194- metrics .incr ("cli_bug_prediction .timeout" )
199+ metrics .incr ("code_review_local .timeout" )
195200 return Response (
196201 {
197202 "detail" : "Analysis exceeded maximum processing time (10 minutes). Please try again with a smaller diff."
@@ -202,23 +207,23 @@ def post(self, request: Request, organization: Organization) -> Response:
202207 # Seer returned error status
203208 status_code , error_code , error_message = self ._map_seer_error_to_response (str (e ))
204209 logger .exception (
205- "cli_bug_prediction .seer_error" ,
210+ "code_review_local .seer_error" ,
206211 extra = {
207212 "seer_run_id" : run_id ,
208213 "organization_id" : organization .id ,
209214 "user_id" : request .user .id ,
210215 "mapped_status" : status_code ,
211216 },
212217 )
213- metrics .incr ("cli_bug_prediction .seer_error" , tags = {"error_code" : error_code })
218+ metrics .incr ("code_review_local .seer_error" , tags = {"error_code" : error_code })
214219 return Response ({"detail" : error_message }, status = status_code )
215220
216221 # Success
217222 predictions = final_response .get ("predictions" , [])
218223 diagnostics = final_response .get ("diagnostics" , {})
219224
220225 logger .info (
221- "cli_bug_prediction .completed" ,
226+ "code_review_local .completed" ,
222227 extra = {
223228 "seer_run_id" : run_id ,
224229 "organization_id" : organization .id ,
@@ -228,8 +233,8 @@ def post(self, request: Request, organization: Organization) -> Response:
228233 },
229234 )
230235
231- metrics .incr ("cli_bug_prediction .completed" , tags = {"status" : "success" })
232- metrics .incr ("cli_bug_prediction .predictions" , amount = len (predictions ))
236+ metrics .incr ("code_review_local .completed" , tags = {"status" : "success" })
237+ metrics .incr ("code_review_local .predictions" , amount = len (predictions ))
233238
234239 response_data = {
235240 "status" : final_response .get ("status" ),
@@ -289,7 +294,7 @@ def _poll_seer_for_results(
289294
290295 attempt += 1
291296 logger .debug (
292- "cli_bug_prediction .polling" ,
297+ "code_review_local .polling" ,
293298 extra = {
294299 "seer_run_id" : run_id ,
295300 "attempt" : attempt ,
@@ -302,7 +307,7 @@ def _poll_seer_for_results(
302307 except (UrllibTimeoutError , MaxRetryError ):
303308 # If status check times out, wait and retry
304309 logger .warning (
305- "cli_bug_prediction .poll.timeout" ,
310+ "code_review_local .poll.timeout" ,
306311 extra = {"seer_run_id" : run_id , "attempt" : attempt },
307312 )
308313 time .sleep (poll_interval_seconds )
@@ -362,4 +367,4 @@ def _map_seer_error_to_response(self, seer_error_message: str) -> tuple[int, str
362367 )
363368
364369 # Default to bad gateway for unknown Seer errors
365- return (502 , "bad_gateway" , "Bug prediction service encountered an error" )
370+ return (502 , "bad_gateway" , "Code review service encountered an error" )
0 commit comments