mirror of
https://github.com/usestrix/strix.git
synced 2026-08-21 10:48:59 +02:00
refactor(context): detect overflow via LiteLLM's typed error only
Drop the brittle substring matching for context-overflow detection. LiteLLM already normalises every provider's overflow error to ContextWindowExceededError and maintains the provider-specific matching upstream, so is_context_overflow just checks that type.
This commit is contained in:
+9
-22
@@ -13,6 +13,7 @@ import logging
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
import litellm
|
||||
from litellm.exceptions import ContextWindowExceededError
|
||||
|
||||
from strix.config import load_settings
|
||||
from strix.core.sessions import replace_session_items, session_write_lock
|
||||
@@ -30,30 +31,16 @@ _TOOL_OUTPUT_MAX_CHARS = 2_000
|
||||
_MIN_ITEMS_TO_COMPACT = 6
|
||||
_HEAD_TRUNCATED_MARKER = "\n\n[... older conversation omitted to fit the summary request ...]\n\n"
|
||||
|
||||
# Substrings that identify a context-window-overflow error across providers.
|
||||
# Deliberately excludes rate-limit/throttle wording, which must not trigger
|
||||
# compaction.
|
||||
_OVERFLOW_MARKERS = (
|
||||
"context length",
|
||||
"context window",
|
||||
"maximum context",
|
||||
"context_length_exceeded",
|
||||
"too many tokens",
|
||||
"reduce the length",
|
||||
"input is too long",
|
||||
"prompt is too long",
|
||||
"exceeds the maximum",
|
||||
"string too long",
|
||||
)
|
||||
|
||||
|
||||
def is_context_overflow(exc: BaseException) -> bool:
|
||||
"""Whether ``exc`` looks like a model context-window-overflow error."""
|
||||
overflow_error = getattr(litellm, "ContextWindowExceededError", None)
|
||||
if overflow_error is not None and isinstance(exc, overflow_error):
|
||||
return True
|
||||
message = str(exc).lower()
|
||||
return any(marker in message for marker in _OVERFLOW_MARKERS)
|
||||
"""Whether ``exc`` is a model context-window-overflow error.
|
||||
|
||||
LiteLLM normalises every provider's overflow error to
|
||||
``ContextWindowExceededError`` (a ``BadRequestError`` subclass) and keeps
|
||||
the provider-specific detection upstream, so we rely on that type rather
|
||||
than matching error message strings ourselves.
|
||||
"""
|
||||
return isinstance(exc, ContextWindowExceededError)
|
||||
|
||||
|
||||
_SUMMARY_INSTRUCTIONS = """\
|
||||
|
||||
@@ -6,6 +6,7 @@ from types import SimpleNamespace
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
import pytest
|
||||
from litellm.exceptions import ContextWindowExceededError, RateLimitError
|
||||
|
||||
from strix.config import ContextSettings
|
||||
from strix.llm import compaction
|
||||
@@ -70,12 +71,17 @@ def _has_orphan_tool_output(items: list[Any]) -> bool:
|
||||
return any(i["call_id"] not in call_ids for i in items if compaction._is_tool_output(i))
|
||||
|
||||
|
||||
def test_is_context_overflow_matches_and_excludes() -> None:
|
||||
assert compaction.is_context_overflow(
|
||||
RuntimeError("This model's maximum context length is 8192")
|
||||
def test_is_context_overflow_uses_litellm_typed_error() -> None:
|
||||
# LiteLLM maps every provider's overflow error to this type; anything else
|
||||
# (including a rate-limit error) must not trigger compaction.
|
||||
overflow = ContextWindowExceededError(
|
||||
message="context length exceeded", model="m", llm_provider="openai"
|
||||
)
|
||||
assert compaction.is_context_overflow(ValueError("input is too long for the model"))
|
||||
assert not compaction.is_context_overflow(RuntimeError("rate limit exceeded, retry later"))
|
||||
assert compaction.is_context_overflow(overflow)
|
||||
assert not compaction.is_context_overflow(
|
||||
RateLimitError(message="slow down", model="m", llm_provider="openai")
|
||||
)
|
||||
assert not compaction.is_context_overflow(RuntimeError("maximum context length is 8192"))
|
||||
|
||||
|
||||
def test_select_split_never_orphans_tool_output(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
|
||||
Reference in New Issue
Block a user