11from __future__ import annotations
22
33import logging
4- from typing import Any , NamedTuple
4+ from typing import Any , TypedDict
55
66from sentry import features
77from sentry .models .group import Group
@@ -57,9 +57,14 @@ def _build_event_context(group: Group, event: Any | None = None) -> str:
5757 return context
5858
5959
60+ class GeneratedExternalIssueDetails (TypedDict ):
61+ title : str | None
62+ description : str | None
63+
64+
6065def _make_generate_external_issue_details_request (
6166 group : Group , event : Any | None = None , viewer_context : SeerViewerContext | None = None
62- ) -> dict [ str , str ] | None :
67+ ) -> GeneratedExternalIssueDetails | None :
6368 logging_ctx : dict [str , Any ] = {"group_id" : group .id , "viewer_context" : viewer_context }
6469 context = _build_event_context (group , event = event )
6570
@@ -89,13 +94,17 @@ def _make_generate_external_issue_details_request(
8994 try :
9095 data = response .json ()
9196 except (json .JSONDecodeError , ValueError ):
92- logger .warning ("external_issues.seer_response_json_failed" , extra = logging_ctx )
97+ logger .warning (
98+ "external_issues.seer_response_json_failed" , extra = logging_ctx , exc_info = True
99+ )
93100 return None
94101 content = data .get ("content" )
95102 try :
96103 content = json .loads (content )
97104 except (json .JSONDecodeError , TypeError , ValueError ):
98- logger .warning ("external_issues.seer_response_parse_failed" , extra = logging_ctx )
105+ logger .warning (
106+ "external_issues.seer_response_parse_failed" , extra = logging_ctx , exc_info = True
107+ )
99108 return None
100109
101110 title = content .get ("title" )
@@ -109,35 +118,28 @@ def _make_generate_external_issue_details_request(
109118 return None
110119
111120
112- class GeneratedIssueDetails (NamedTuple ):
113- title : str | None = None
114- description : str | None = None
115-
116-
117121def maybe_generate_external_issue_details (
118122 * , group : Group , user : User | RpcUser , event : GroupEvent | None = None
119- ) -> GeneratedIssueDetails :
123+ ) -> GeneratedExternalIssueDetails :
120124 organization = group .organization
125+ empty_result = GeneratedExternalIssueDetails (title = None , description = None )
121126 if not features .has ("organizations:gen-ai-features" , organization , actor = user ):
122- return GeneratedIssueDetails ()
127+ return empty_result
123128 if organization .get_option ("sentry:hide_ai_features" , False ):
124- return GeneratedIssueDetails ()
129+ return empty_result
125130 if not features .has ("organizations:external-issues-ai-generate" , organization , actor = user ):
126- return GeneratedIssueDetails ()
131+ return empty_result
127132
128133 try :
129134 viewer_context = SeerViewerContext (organization_id = organization .id , user_id = user .id )
130135 result = _make_generate_external_issue_details_request (
131136 group , event = event , viewer_context = viewer_context
132137 )
133- # Open except block is wide but allows us to fallback to default title/description if anything fails.
134138 except Exception :
135- logger .error ("external_issues.generate_issue_details_failed" )
136- return GeneratedIssueDetails ()
139+ logger .error ("external_issues.generate_issue_details_failed" , exc_info = True )
140+ return empty_result
137141
138142 if not result :
139- return GeneratedIssueDetails ()
143+ return empty_result
140144
141- title : str | None = result .get ("title" )
142- description : str | None = result .get ("description" )
143- return GeneratedIssueDetails (title = title , description = description )
145+ return result
0 commit comments