diff --git a/src/sentry/seer/autofix/autofix_agent.py b/src/sentry/seer/autofix/autofix_agent.py index a9276110ee48..fba4ae85ed74 100644 --- a/src/sentry/seer/autofix/autofix_agent.py +++ b/src/sentry/seer/autofix/autofix_agent.py @@ -35,6 +35,7 @@ from sentry.sentry_apps.metrics import SentryAppEventType from sentry.sentry_apps.tasks.sentry_apps import broadcast_webhooks_for_organization from sentry.sentry_apps.utils.webhooks import SeerActionType +from sentry.utils import metrics if TYPE_CHECKING: from sentry.models.group import Group @@ -294,6 +295,8 @@ def trigger_autofix_explorer( }, ) + metrics.incr("autofix.explorer.trigger", tags={"step": step.value, "referrer": referrer.value}) + return run_id @@ -407,6 +410,7 @@ def _get_relevant_repo( def trigger_coding_agent_handoff( group: Group, run_id: int, + referrer: AutofixReferrer, integration_id: int | None = None, provider: str | None = None, user_id: int | None = None, @@ -482,7 +486,7 @@ def trigger_coding_agent_handoff( prompt = generate_autofix_handoff_prompt(state, short_id=short_id) - return client.launch_coding_agents( + coding_agents = client.launch_coding_agents( run_id=run_id, integration_id=integration_id, provider=provider, @@ -493,8 +497,20 @@ def trigger_coding_agent_handoff( auto_create_pr=auto_create_pr, ) + metrics.incr( + "autofix.explorer.trigger", + tags={"step": "coding_agent_handoff", "referrer": referrer.value}, + ) + + return coding_agents + -def trigger_push_changes(group: Group, run_id: int, state: SeerRunState | None = None): +def trigger_push_changes( + group: Group, + run_id: int, + referrer: AutofixReferrer, + state: SeerRunState | None = None, +): client = get_autofix_explorer_client(group) if state is None: @@ -508,3 +524,8 @@ def trigger_push_changes(group: Group, run_id: int, state: SeerRunState | None = raise SeerPermissionError("Unknown run id for group") client.push_changes(run_id, blocking=False) + + metrics.incr( + "autofix.explorer.trigger", + tags={"step": "open_pr", "referrer": referrer.value}, + ) diff --git a/src/sentry/seer/autofix/on_completion_hook.py b/src/sentry/seer/autofix/on_completion_hook.py index 5aa2d3279bd3..e82ae9412bf2 100644 --- a/src/sentry/seer/autofix/on_completion_hook.py +++ b/src/sentry/seer/autofix/on_completion_hook.py @@ -416,7 +416,12 @@ def _push_changes(cls, group: Group, run_id: int, state: SeerRunState) -> None: ) try: - trigger_push_changes(group, run_id, state=state) + trigger_push_changes( + group, + run_id, + referrer=AutofixReferrer.ON_COMPLETION_HOOK, + state=state, + ) except Exception: logger.exception( "autofix.on_completion_hook.push_changes_failed", @@ -491,6 +496,7 @@ def _trigger_coding_agent_handoff( result = trigger_coding_agent_handoff( group=group, run_id=run_id, + referrer=AutofixReferrer.ON_COMPLETION_HOOK, integration_id=handoff_config.integration_id, ) logger.info( diff --git a/src/sentry/seer/endpoints/group_ai_autofix.py b/src/sentry/seer/endpoints/group_ai_autofix.py index 6c6ff6e21506..2d50de8b2c9d 100644 --- a/src/sentry/seer/endpoints/group_ai_autofix.py +++ b/src/sentry/seer/endpoints/group_ai_autofix.py @@ -252,6 +252,7 @@ def _post_explorer(self, request: Request, group: Group) -> Response: result = trigger_coding_agent_handoff( group=group, run_id=run_id, + referrer=AutofixReferrer.GROUP_AUTOFIX_ENDPOINT, integration_id=integration_id, provider=provider, user_id=request.user.id if request.user else None, @@ -264,7 +265,11 @@ def _post_explorer(self, request: Request, group: Group) -> Response: {"detail": "run_id is required for open_pr"}, status=status.HTTP_400_BAD_REQUEST ) try: - trigger_push_changes(group, run_id) + trigger_push_changes( + group, + run_id, + referrer=AutofixReferrer.GROUP_AUTOFIX_ENDPOINT, + ) except SeerPermissionError: return Response(status=status.HTTP_404_NOT_FOUND) return Response({"run_id": run_id}, status=status.HTTP_202_ACCEPTED) diff --git a/src/sentry/seer/entrypoints/operator.py b/src/sentry/seer/entrypoints/operator.py index ea2da1066804..9bf789887814 100644 --- a/src/sentry/seer/entrypoints/operator.py +++ b/src/sentry/seer/entrypoints/operator.py @@ -227,7 +227,11 @@ def trigger_autofix_explorer( run_id=None, ) elif stopping_point == AutofixStoppingPoint.OPEN_PR: - trigger_push_changes(group, run_id) + trigger_push_changes( + group, + run_id, + referrer=AutofixReferrer.SLACK, + ) else: # NOTE: Stopping point here is really just what # step to run next. Not the same as the stopping_point diff --git a/tests/sentry/seer/autofix/test_autofix_agent.py b/tests/sentry/seer/autofix/test_autofix_agent.py index 5a2543f84d63..8f912ce889e4 100644 --- a/tests/sentry/seer/autofix/test_autofix_agent.py +++ b/tests/sentry/seer/autofix/test_autofix_agent.py @@ -466,6 +466,7 @@ def test_trigger_coding_agent_handoff_success( result = trigger_coding_agent_handoff( group=self.group, run_id=123, + referrer=AutofixReferrer.UNKNOWN, integration_id=456, ) @@ -491,6 +492,7 @@ def test_trigger_coding_agent_handoff_no_repos(self, mock_client_class, mock_get result = trigger_coding_agent_handoff( group=self.group, run_id=123, + referrer=AutofixReferrer.UNKNOWN, integration_id=456, ) @@ -531,6 +533,7 @@ def test_trigger_coding_agent_handoff_generates_prompt_from_artifacts( trigger_coding_agent_handoff( group=self.group, run_id=123, + referrer=AutofixReferrer.UNKNOWN, integration_id=456, ) @@ -564,6 +567,7 @@ def test_trigger_coding_agent_handoff_uses_group_title_for_branch( trigger_coding_agent_handoff( group=self.group, run_id=123, + referrer=AutofixReferrer.UNKNOWN, integration_id=456, ) @@ -592,6 +596,7 @@ def test_trigger_coding_agent_handoff_fetches_auto_create_pr_from_preferences( trigger_coding_agent_handoff( group=self.group, run_id=123, + referrer=AutofixReferrer.UNKNOWN, integration_id=456, ) @@ -619,6 +624,7 @@ def test_trigger_coding_agent_handoff_defaults_auto_create_pr_false( trigger_coding_agent_handoff( group=self.group, run_id=123, + referrer=AutofixReferrer.UNKNOWN, integration_id=456, ) @@ -638,6 +644,7 @@ def test_trigger_coding_agent_handoff_no_preferences_returns_failure( result = trigger_coding_agent_handoff( group=self.group, run_id=123, + referrer=AutofixReferrer.UNKNOWN, integration_id=456, ) @@ -678,7 +685,12 @@ def test_trigger_coding_agent_handoff_filters_to_relevant_repo( ) mock_get_autofix_state.return_value = None - trigger_coding_agent_handoff(group=self.group, run_id=123, integration_id=456) + trigger_coding_agent_handoff( + group=self.group, + run_id=123, + referrer=AutofixReferrer.UNKNOWN, + integration_id=456, + ) repos = mock_client.launch_coding_agents.call_args.kwargs["repos"] assert len(repos) == 1 @@ -712,7 +724,12 @@ def test_trigger_coding_agent_handoff_falls_back_to_first_repo_when_no_relevant_ ) mock_get_autofix_state.return_value = None - trigger_coding_agent_handoff(group=self.group, run_id=123, integration_id=456) + trigger_coding_agent_handoff( + group=self.group, + run_id=123, + referrer=AutofixReferrer.UNKNOWN, + integration_id=456, + ) repos = mock_client.launch_coding_agents.call_args.kwargs["repos"] assert len(repos) == 1 @@ -760,7 +777,12 @@ def test_trigger_coding_agent_handoff_falls_back_when_relevant_repo_doesnt_match ) mock_get_autofix_state.return_value = None - trigger_coding_agent_handoff(group=self.group, run_id=123, integration_id=456) + trigger_coding_agent_handoff( + group=self.group, + run_id=123, + referrer=AutofixReferrer.UNKNOWN, + integration_id=456, + ) repos = mock_client.launch_coding_agents.call_args.kwargs["repos"] assert len(repos) == 1 @@ -822,7 +844,12 @@ def test_trigger_coding_agent_handoff_enriches_branch_name_from_autofix_state( status=AutofixStatus.COMPLETED, ) - trigger_coding_agent_handoff(group=self.group, run_id=123, integration_id=456) + trigger_coding_agent_handoff( + group=self.group, + run_id=123, + referrer=AutofixReferrer.UNKNOWN, + integration_id=456, + ) repos = mock_client.launch_coding_agents.call_args.kwargs["repos"] assert repos[0].branch_name == "main" @@ -852,7 +879,12 @@ def test_trigger_coding_agent_handoff_keeps_branch_name_from_preferences_when_se ] ) - trigger_coding_agent_handoff(group=self.group, run_id=123, integration_id=456) + trigger_coding_agent_handoff( + group=self.group, + run_id=123, + referrer=AutofixReferrer.UNKNOWN, + integration_id=456, + ) mock_get_autofix_state.assert_not_called() repos = mock_client.launch_coding_agents.call_args.kwargs["repos"] diff --git a/tests/sentry/seer/autofix/test_autofix_on_completion_hook.py b/tests/sentry/seer/autofix/test_autofix_on_completion_hook.py index 1d0098b8b244..2bfaf18aaf34 100644 --- a/tests/sentry/seer/autofix/test_autofix_on_completion_hook.py +++ b/tests/sentry/seer/autofix/test_autofix_on_completion_hook.py @@ -2,6 +2,7 @@ from unittest.mock import MagicMock, patch from sentry.seer.autofix.autofix_agent import AutofixStep +from sentry.seer.autofix.constants import AutofixReferrer from sentry.seer.autofix.on_completion_hook import ( PIPELINE_ORDER, STOPPING_POINT_TO_STEP, @@ -258,7 +259,12 @@ def test_maybe_continue_pipeline_pushes_changes_for_open_pr(self, mock_push_chan }, ) AutofixOnCompletionHook._maybe_continue_pipeline(self.organization, 123, state) - mock_push_changes.assert_called_once_with(self.group, 123, state=state) + mock_push_changes.assert_called_once_with( + self.group, + 123, + referrer=AutofixReferrer.ON_COMPLETION_HOOK, + state=state, + ) class TestPipelineConstants(TestCase):