diff --git a/strix/core/execution.py b/strix/core/execution.py index dd9692d7..ea65612d 100644 --- a/strix/core/execution.py +++ b/strix/core/execution.py @@ -84,9 +84,6 @@ async def _compact_session( ) -# Retryable HTTP statuses for a model/provider call: request timeout + 5xx server errors. -# 429 is intentionally excluded here; a persistent rate limit is handled as a graceful, -# resumable scan stop in ``runner.run_strix_scan``. _TRANSIENT_MODEL_STATUS_CODES = frozenset({408, 500, 502, 503, 504}) _MAX_TRANSIENT_MODEL_RETRIES = 4 _TRANSIENT_MODEL_RETRY_BASE_DELAY_S = 2.0 @@ -99,21 +96,6 @@ def _model_error_status_code(exc: BaseException) -> int | None: def _is_transient_model_error(exc: BaseException) -> bool: - """Return whether a model/provider error is a transient upstream failure worth replaying. - - Three families are treated as transient: - - * network-level faults reaching the provider (connection reset, read timeout); - * retryable HTTP statuses (request timeout + 5xx) surfaced as ``APIStatusError``; - * mid-stream provider errors, where the server injects an ``{"error": ...}`` frame into - an already-200 SSE stream and the OpenAI SDK raises a bare ``APIError`` with no status - code. The agents SDK cannot replay these once tokens have streamed, so an untreated - one propagates and crashes the whole scan. - - A persistent rate limit is deliberately excluded (handled as a graceful scan stop at the - runner level), as are permanent 4xx client errors (they carry a status code and are - surfaced as ``APIStatusError``). - """ if isinstance(exc, RateLimitError): return False if isinstance(exc, APITimeoutError | APIConnectionError): @@ -554,8 +536,6 @@ async def _run_cycle( # noqa: PLR0912, PLR0915 exc, ) await asyncio.sleep(delay) - # The turn's input was already persisted to the session before the model - # call, so replay from session state to avoid duplicating it. if session is not None: input_data = [] continue diff --git a/tests/test_execution_transient_retry.py b/tests/test_execution_transient_retry.py index 790454fb..889eb96f 100644 --- a/tests/test_execution_transient_retry.py +++ b/tests/test_execution_transient_retry.py @@ -1,10 +1,3 @@ -"""Tests for transient model/provider error replay in the agent run cycle. - -A transient upstream error (e.g. a mid-stream ``openai.APIError`` injected into an -already-200 SSE stream) must be retried at the turn level instead of crashing the -whole scan, while permanent errors and rate limits keep their existing behavior. -""" - from __future__ import annotations from typing import Any, cast @@ -31,7 +24,6 @@ def _request() -> httpx.Request: def _midstream_api_error() -> APIError: - """A bare APIError with no status code — the mid-stream generic provider error.""" return APIError("An error occurred while processing the request.", _request(), body=None) @@ -64,7 +56,6 @@ def test_server_errors_are_transient() -> None: def test_rate_limit_is_not_retried_here() -> None: - # A persistent rate limit is handled as a graceful scan stop at the runner level. rate_limited = RateLimitError( "slow down", response=httpx.Response(429, request=_request()), body=None ) @@ -81,8 +72,6 @@ def test_client_errors_are_not_transient() -> None: class _FakeStream: - """Minimal stand-in for a streamed run result.""" - def __init__(self, exc: BaseException | None = None) -> None: self._exc = exc self._events: list[Any] = [] @@ -137,7 +126,6 @@ async def _run_once( async def test_run_cycle_retries_transient_midstream_error( monkeypatch: pytest.MonkeyPatch, ) -> None: - """A transient mid-stream error is replayed and the turn then succeeds.""" streams = [_FakeStream(exc=_midstream_api_error()), _FakeStream()] result, attempts, _coordinator = await _run_once(monkeypatch, streams) @@ -149,7 +137,6 @@ async def test_run_cycle_retries_transient_midstream_error( async def test_run_cycle_gives_up_after_max_retries( monkeypatch: pytest.MonkeyPatch, ) -> None: - """Persistent transient errors exhaust the bound, then propagate (non-interactive).""" streams = [ _FakeStream(exc=_midstream_api_error()) for _ in range(execution._MAX_TRANSIENT_MODEL_RETRIES + 1) @@ -162,7 +149,6 @@ async def test_run_cycle_gives_up_after_max_retries( async def test_run_cycle_does_not_retry_permanent_error( monkeypatch: pytest.MonkeyPatch, ) -> None: - """A permanent client error is not replayed; it propagates on the first attempt.""" bad_request = BadRequestError( "bad", response=httpx.Response(400, request=_request()), body=None )