strix/tests/core/test_execution.py
Mads Hvelplund c91e4ca1b9
fix: bypass image-strip recovery for ContextWindowExceededError
ContextWindowExceededError carries status_code=400, which matched the
_INPUT_REJECTION_CODES guard and triggered image-strip retry logic.
Image stripping cannot reduce token count, so the agent wasted up to
3 retry cycles before parking as failed.

Add an explicit isinstance check before the status-code guard to detect
context-window overflow and park the agent as 'failed' immediately.
2026-06-19 13:40:45 +02:00

151 lines
4.8 KiB
Python

"""Tests for strix/core/execution.py — Fix 1: context-window bypass."""
from __future__ import annotations
from typing import TYPE_CHECKING
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
if TYPE_CHECKING:
from collections.abc import AsyncGenerator
from litellm.exceptions import ContextWindowExceededError
from strix.core.execution import _run_cycle
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
def _context_window_exc() -> ContextWindowExceededError:
return ContextWindowExceededError(
message="prompt is too long: 1023797 tokens > 1000000 maximum",
model="claude-sonnet-4-6",
llm_provider="anthropic",
)
def _make_coordinator() -> AsyncMock:
coordinator = AsyncMock()
coordinator.is_shutting_down = False
return coordinator
async def _empty_events() -> AsyncGenerator[None]:
"""Async generator that yields nothing — simulates a completed stream."""
for _ in ():
yield
def _make_stream(exception: Exception | None = None) -> MagicMock:
stream = MagicMock()
stream.stream_events.return_value = _empty_events()
stream.run_loop_exception = exception
return stream
# ---------------------------------------------------------------------------
# Fix 1 tests
# ---------------------------------------------------------------------------
async def test_context_window_error_sets_status_failed() -> None:
"""ContextWindowExceededError must park the agent as 'failed', not retry."""
exc = _context_window_exc()
coordinator = _make_coordinator()
with (
patch("strix.core.execution.Runner") as mock_runner,
patch(
"strix.core.execution.strip_all_images_from_session", return_value=True
) as mock_strip,
):
mock_runner.run_streamed.return_value = _make_stream(exception=exc)
with pytest.raises(ContextWindowExceededError):
await _run_cycle(
MagicMock(), # agent
coordinator,
"agent-001",
input_data=[],
run_config=MagicMock(),
context={}, # no parent_id → will re-raise
max_turns=10,
session=AsyncMock(), # non-None so image-strip guard could trigger
interactive=True,
event_sink=None,
hooks=None,
)
# Image-strip must NOT have been attempted.
mock_strip.assert_not_called()
# Agent must be parked as failed.
coordinator.set_status.assert_called_once_with("agent-001", "failed")
async def test_context_window_error_returns_none_for_child_agent() -> None:
"""Child agents (parent_id set) must return None rather than re-raising."""
exc = _context_window_exc()
coordinator = _make_coordinator()
with patch("strix.core.execution.Runner") as mock_runner:
mock_runner.run_streamed.return_value = _make_stream(exception=exc)
result = await _run_cycle(
MagicMock(),
coordinator,
"child-001",
input_data=[],
run_config=MagicMock(),
context={"parent_id": "root-001"},
max_turns=10,
session=AsyncMock(),
interactive=True,
event_sink=None,
hooks=None,
)
assert result is None
coordinator.set_status.assert_called_once_with("child-001", "failed")
async def test_non_context_window_400_still_triggers_image_strip() -> None:
"""A plain 400 error (not ContextWindowExceededError) must still attempt image stripping."""
class _Plain400Error(Exception):
"""Minimal stand-in for any non-context-window 400 error."""
status_code = 400
plain_400 = _Plain400Error("some other bad request")
coordinator = _make_coordinator()
with (
patch("strix.core.execution.Runner") as mock_runner,
patch(
"strix.core.execution.strip_all_images_from_session", return_value=False
) as mock_strip,
):
mock_runner.run_streamed.return_value = _make_stream(exception=plain_400)
# Not interactive → re-raises immediately without retry when strip returns False.
with pytest.raises(_Plain400Error):
await _run_cycle(
MagicMock(),
coordinator,
"agent-002",
input_data=[],
run_config=MagicMock(),
context={},
max_turns=10,
session=AsyncMock(),
interactive=False,
event_sink=None,
hooks=None,
)
# strip WAS attempted (it's the right path for plain 400).
mock_strip.assert_called_once()