Skip to content

Commit 9a5f34f

Browse files
committed
feat(autofix): Add analytics events for autofix phase start and completion
Record per-phase analytics events when each autofix pipeline phase (root_cause, solution, code_changes) starts and completes. Each phase gets its own event class so they can be queried independently. Events include group_id, referrer, organization_id, and project_id. Started events fire in trigger_autofix_explorer, completed events fire in the on_completion_hook webhook handler. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Agent transcript: https://claudescope.sentry.dev/share/botteEgndhtvdoXxpJ2frzQPLVabdvt8jNcgx9NaA3k
1 parent 3aae89a commit 9a5f34f

File tree

4 files changed

+90
-1
lines changed

4 files changed

+90
-1
lines changed

src/sentry/analytics/events/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from .api_token_deleted import * # noqa: F401,F403
99
from .auth_v2 import * # noqa: F401,F403
1010
from .autofix_automation_events import * # noqa: F401,F403
11+
from .autofix_events import * # noqa: F401,F403
1112
from .checkin_processing_error_stored import * # noqa: F401,F403
1213
from .codeowners_assignment import * # noqa: F401,F403
1314
from .codeowners_created import * # noqa: F401,F403
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
from sentry import analytics
2+
3+
4+
@analytics.eventclass()
5+
class AiAutofixPhaseEvent(analytics.Event):
6+
organization_id: int
7+
project_id: int
8+
group_id: int
9+
referrer: str
10+
11+
12+
@analytics.eventclass("ai.autofix.root_cause.started")
13+
class AiAutofixRootCauseStartedEvent(AiAutofixPhaseEvent):
14+
pass
15+
16+
17+
@analytics.eventclass("ai.autofix.solution.started")
18+
class AiAutofixSolutionStartedEvent(AiAutofixPhaseEvent):
19+
pass
20+
21+
22+
@analytics.eventclass("ai.autofix.code_changes.started")
23+
class AiAutofixCodeChangesStartedEvent(AiAutofixPhaseEvent):
24+
pass
25+
26+
27+
@analytics.eventclass("ai.autofix.root_cause.completed")
28+
class AiAutofixRootCauseCompletedEvent(AiAutofixPhaseEvent):
29+
pass
30+
31+
32+
@analytics.eventclass("ai.autofix.solution.completed")
33+
class AiAutofixSolutionCompletedEvent(AiAutofixPhaseEvent):
34+
pass
35+
36+
37+
@analytics.eventclass("ai.autofix.code_changes.completed")
38+
class AiAutofixCodeChangesCompletedEvent(AiAutofixPhaseEvent):
39+
pass
40+
41+
42+
analytics.register(AiAutofixRootCauseStartedEvent)
43+
analytics.register(AiAutofixSolutionStartedEvent)
44+
analytics.register(AiAutofixCodeChangesStartedEvent)
45+
analytics.register(AiAutofixRootCauseCompletedEvent)
46+
analytics.register(AiAutofixSolutionCompletedEvent)
47+
analytics.register(AiAutofixCodeChangesCompletedEvent)

src/sentry/seer/autofix/autofix_agent.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,16 @@
88
from pydantic import BaseModel
99
from rest_framework.exceptions import PermissionDenied
1010

11+
from sentry import analytics
12+
from sentry.analytics.events.autofix_events import (
13+
AiAutofixCodeChangesCompletedEvent,
14+
AiAutofixCodeChangesStartedEvent,
15+
AiAutofixPhaseEvent,
16+
AiAutofixRootCauseCompletedEvent,
17+
AiAutofixRootCauseStartedEvent,
18+
AiAutofixSolutionCompletedEvent,
19+
AiAutofixSolutionStartedEvent,
20+
)
1121
from sentry.constants import ENABLE_SEER_CODING_DEFAULT
1222
from sentry.seer.autofix.artifact_schemas import (
1323
ImpactAssessmentArtifact,
@@ -82,26 +92,36 @@ def __init__(
8292
artifact_schema: type[BaseModel] | None,
8393
prompt_fn: Callable[..., str],
8494
enable_coding: bool = False,
95+
started_event: type[AiAutofixPhaseEvent] | None = None,
96+
completed_event: type[AiAutofixPhaseEvent] | None = None,
8597
):
8698
self.artifact_schema = artifact_schema
8799
self.prompt_fn = prompt_fn
88100
self.enable_coding = enable_coding
101+
self.started_event = started_event
102+
self.completed_event = completed_event
89103

90104

91105
# Step configurations mapping step to its artifact schema and prompt
92106
STEP_CONFIGS: dict[AutofixStep, StepConfig] = {
93107
AutofixStep.ROOT_CAUSE: StepConfig(
94108
artifact_schema=RootCauseArtifact,
95109
prompt_fn=root_cause_prompt,
110+
started_event=AiAutofixRootCauseStartedEvent,
111+
completed_event=AiAutofixRootCauseCompletedEvent,
96112
),
97113
AutofixStep.SOLUTION: StepConfig(
98114
artifact_schema=SolutionArtifact,
99115
prompt_fn=solution_prompt,
116+
started_event=AiAutofixSolutionStartedEvent,
117+
completed_event=AiAutofixSolutionCompletedEvent,
100118
),
101119
AutofixStep.CODE_CHANGES: StepConfig(
102120
artifact_schema=None, # Code changes read from file_patches
103121
prompt_fn=code_changes_prompt,
104122
enable_coding=True,
123+
started_event=AiAutofixCodeChangesStartedEvent,
124+
completed_event=AiAutofixCodeChangesCompletedEvent,
105125
),
106126
AutofixStep.IMPACT_ASSESSMENT: StepConfig(
107127
artifact_schema=ImpactAssessmentArtifact,
@@ -215,6 +235,16 @@ def trigger_autofix_explorer(
215235
"""
216236

217237
config = STEP_CONFIGS[step]
238+
239+
if config.started_event is not None:
240+
analytics.record(
241+
config.started_event(
242+
organization_id=group.organization.id,
243+
project_id=group.project_id,
244+
group_id=group.id,
245+
referrer=referrer.value,
246+
)
247+
)
218248
client = get_autofix_explorer_client(
219249
group,
220250
intelligence_level=intelligence_level,

src/sentry/seer/autofix/on_completion_hook.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@
55

66
from django.utils import timezone
77

8-
from sentry import features
8+
from sentry import analytics, features
99
from sentry.models.group import Group
1010
from sentry.models.organization import Organization
1111
from sentry.models.project import Project
1212
from sentry.seer.autofix.autofix_agent import (
13+
STEP_CONFIGS,
1314
AutofixStep,
1415
trigger_autofix_explorer,
1516
trigger_coding_agent_handoff,
@@ -235,6 +236,16 @@ def _send_step_webhook(
235236
metrics.incr(
236237
"autofix.explorer.complete", tags={"step": current_step.value, "referrer": referrer}
237238
)
239+
completed_event_cls = STEP_CONFIGS[current_step].completed_event
240+
if completed_event_cls is not None and referrer is not None:
241+
analytics.record(
242+
completed_event_cls(
243+
organization_id=organization.id,
244+
project_id=group.project_id,
245+
group_id=group.id,
246+
referrer=referrer,
247+
)
248+
)
238249

239250
@classmethod
240251
def _maybe_trigger_supergroups_embedding(

0 commit comments

Comments
 (0)