diff --git a/src/sentry/seer/autofix/autofix_agent.py b/src/sentry/seer/autofix/autofix_agent.py index 2e0898cc375f7c..0901b60a14b85f 100644 --- a/src/sentry/seer/autofix/autofix_agent.py +++ b/src/sentry/seer/autofix/autofix_agent.py @@ -200,6 +200,7 @@ def trigger_autofix_explorer( stopping_point: AutofixStoppingPoint | None = None, intelligence_level: Literal["low", "medium", "high"] = "low", user_context: str | None = None, + insert_index: int | None = None, ) -> int: """ Start or continue an Explorer-based autofix run. @@ -247,6 +248,7 @@ def trigger_autofix_explorer( prompt_metadata=prompt_metadata, artifact_key=artifact_key, artifact_schema=artifact_schema, + insert_index=insert_index, ) payload = { diff --git a/src/sentry/seer/endpoints/group_ai_autofix.py b/src/sentry/seer/endpoints/group_ai_autofix.py index d2ccb4f613c8d5..a9c3bc6fa20bf8 100644 --- a/src/sentry/seer/endpoints/group_ai_autofix.py +++ b/src/sentry/seer/endpoints/group_ai_autofix.py @@ -129,6 +129,10 @@ class ExplorerAutofixRequestSerializer(CamelSnakeSerializer): required=False, help_text="Optional repository name for which to create the pull request. Do not pass a repository name to create pull requests in all relevant repositories.", ) + insert_index = serializers.IntegerField( + required=False, + help_text="Block index to insert at. When provided, truncates blocks after this point for retry-from-step.", + ) def validate(self, data: dict[str, Any]) -> dict[str, Any]: stopping_point = data.get("stopping_point", None) @@ -289,6 +293,7 @@ def _post_explorer(self, request: Request, group: Group) -> Response: run_id=run_id, intelligence_level=data["intelligence_level"], user_context=data.get("user_context"), + insert_index=data.get("insert_index"), ) return Response({"run_id": run_id}, status=status.HTTP_202_ACCEPTED) except SeerPermissionError as e: diff --git a/tests/sentry/seer/endpoints/test_group_ai_autofix.py b/tests/sentry/seer/endpoints/test_group_ai_autofix.py index e892e451bf051e..9df8e7a2251649 100644 --- a/tests/sentry/seer/endpoints/test_group_ai_autofix.py +++ b/tests/sentry/seer/endpoints/test_group_ai_autofix.py @@ -940,6 +940,35 @@ def test_stopping_point(self, mock_trigger_explorer): run_id=None, intelligence_level="low", user_context=None, + insert_index=None, + ) + + @patch("sentry.seer.endpoints.group_ai_autofix.trigger_autofix_explorer") + def test_insert_index_passed_through(self, mock_trigger_explorer): + """POST passes insert_index to trigger_autofix_explorer for retry-from-step.""" + for flag in EXPLORER_FLAGS: + mock_trigger_explorer.reset_mock() + group = self.create_group() + mock_trigger_explorer.return_value = 123 + + self.login_as(user=self.user) + with self.feature(flag): + response = self.client.post( + self._get_url(group.id, mode="explorer"), + data={"step": "solution", "run_id": 42, "insert_index": 3}, + format="json", + ) + + assert response.status_code == 202, f"Failed for {flag}: {response.data}" + mock_trigger_explorer.assert_called_once_with( + group=group, + step=AutofixStep.SOLUTION, + referrer=AutofixReferrer.GROUP_AUTOFIX_ENDPOINT, + stopping_point=None, + run_id=42, + intelligence_level="low", + user_context=None, + insert_index=3, ) @patch("sentry.seer.autofix.autofix._call_autofix")