Skip to content

Commit 09b1bb2

Browse files
committed
feat(autofix): store feedback user on write instead of hydrating on read
1 parent cbeb38b commit 09b1bb2

2 files changed

Lines changed: 11 additions & 38 deletions

File tree

src/sentry/seer/autofix/autofix_agent.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import logging
44
import re
55
from enum import StrEnum
6-
from typing import TYPE_CHECKING, Any, Literal, TypedDict, cast
6+
from typing import TYPE_CHECKING, Any, Literal, NotRequired, TypedDict, cast
77

88
from django.utils import timezone
99
from pydantic import BaseModel
@@ -71,6 +71,9 @@ class UserUIFeedbackSource(TypedDict):
7171
# use the same stable key (`user_id`) that `GroupSeen` uses to track which
7272
# users have viewed an issue.
7373
user_id: int
74+
# The serialized user, resolved at write time so the read path doesn't have
75+
# to hydrate it. ``None`` if the user could not be serialized.
76+
user: NotRequired[Any]
7477

7578

7679
# Discriminated on ``type``. Add new TypedDict variants to this union as more

src/sentry/seer/endpoints/group_ai_autofix.py

Lines changed: 7 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@
7070
from sentry.types.ratelimit import RateLimit, RateLimitCategory
7171
from sentry.users.services.user.serial import serialize_generic_user
7272
from sentry.users.services.user.service import user_service
73-
from sentry.utils import json
7473

7574
logger = logging.getLogger(__name__)
7675

@@ -91,41 +90,6 @@ def _parse_autofix_referrer(raw: str | None) -> AutofixReferrer:
9190
return AutofixReferrer.UNKNOWN
9291

9392

94-
def _hydrate_feedback_users(blocks: list[dict[str, Any]], request_user: Any) -> None:
95-
parsed_by_index: dict[int, dict[str, Any]] = {}
96-
user_ids: set[int] = set()
97-
for index, block in enumerate(blocks):
98-
metadata = (block.get("message") or {}).get("metadata") or {}
99-
raw = metadata.get("feedback")
100-
if not raw:
101-
continue
102-
try:
103-
feedback = json.loads(raw)
104-
except (ValueError, TypeError):
105-
continue
106-
user_id = (feedback.get("source") or {}).get("user_id")
107-
if not isinstance(user_id, int):
108-
continue
109-
parsed_by_index[index] = feedback
110-
user_ids.add(user_id)
111-
112-
if not user_ids:
113-
return
114-
115-
users = {
116-
u["id"]: u
117-
for u in user_service.serialize_many(
118-
filter={"user_ids": list(user_ids)},
119-
as_user=serialize_generic_user(request_user),
120-
)
121-
}
122-
123-
for index, feedback in parsed_by_index.items():
124-
user_id = feedback["source"]["user_id"]
125-
feedback["source"]["user"] = users.get(user_id)
126-
blocks[index]["message"]["metadata"]["feedback"] = json.dumps(feedback)
127-
128-
12993
class ExplorerAutofixRequestSerializer(CamelSnakeSerializer):
13094
"""Serializer for the agent-based autofix requests."""
13195

@@ -360,11 +324,18 @@ def post(
360324
and request.user
361325
and request.user.is_authenticated
362326
):
327+
# Serialize the user here on write so the read path (GET) doesn't have
328+
# to hydrate it from the stored user_id on every fetch.
329+
serialized_users = user_service.serialize_many(
330+
filter={"user_ids": [request.user.id]},
331+
as_user=serialize_generic_user(request.user),
332+
)
363333
feedback = Feedback(
364334
message=user_context,
365335
source={
366336
"type": "user-ui",
367337
"user_id": request.user.id,
338+
"user": serialized_users[0] if serialized_users else None,
368339
},
369340
)
370341
try:
@@ -469,7 +440,6 @@ def get(self, request: Request, group: Group) -> Response[AutofixStateResponse]:
469440

470441
run = get_seer_run(state.run_id, group.organization)
471442
blocks = [block.dict() for block in state.blocks]
472-
_hydrate_feedback_users(blocks, request.user)
473443
return Response(
474444
{
475445
"autofix": {

0 commit comments

Comments
 (0)