mirror of
https://github.com/usestrix/strix.git
synced 2026-10-13 03:57:59 +00:00
fix(execution): match only replies that mean the model takes no images
This commit is contained in:
parent
8b6b63f596
commit
9fda996d1f
2 changed files with 49 additions and 18 deletions
|
|
@ -6,6 +6,7 @@ import asyncio
|
|||
import contextlib
|
||||
import inspect
|
||||
import logging
|
||||
import re
|
||||
import uuid
|
||||
from collections.abc import Callable
|
||||
from dataclasses import replace
|
||||
|
|
@ -57,6 +58,16 @@ logger = logging.getLogger(__name__)
|
|||
StreamEventSink = Callable[[str, Any], None]
|
||||
|
||||
_INPUT_REJECTION_CODES = frozenset({400, 404, 422})
|
||||
# Replies meaning "this model takes no images", not image errors in general: a
|
||||
# context overflow that counts "image/vision expansion" must not match.
|
||||
_IMAGE_REJECTION = re.compile(
|
||||
r"no endpoints found that support image input" # OpenRouter
|
||||
r"|image_url is only supported by certain models" # OpenAI
|
||||
r"|is not a multimodal model|at most 0 image\(s\)" # vLLM
|
||||
r"|does not support image input" # LiteLLM's Fireworks check
|
||||
r"|doesn't support the image field", # Bedrock Converse
|
||||
re.IGNORECASE,
|
||||
)
|
||||
_MAX_COMPACTIONS_PER_CYCLE = 2
|
||||
|
||||
|
||||
|
|
@ -157,7 +168,9 @@ def _model_error_status_code(exc: BaseException) -> int | None:
|
|||
|
||||
|
||||
def _is_image_rejection(exc: BaseException) -> bool:
|
||||
return _model_error_status_code(exc) in _INPUT_REJECTION_CODES and "image" in str(exc).lower()
|
||||
return _model_error_status_code(exc) in _INPUT_REJECTION_CODES and bool(
|
||||
_IMAGE_REJECTION.search(str(exc))
|
||||
)
|
||||
|
||||
|
||||
def _is_transient_model_error(exc: BaseException) -> bool:
|
||||
|
|
|
|||
|
|
@ -175,6 +175,41 @@ async def test_run_cycle_does_not_retry_permanent_error(
|
|||
await _run_once(monkeypatch, streams)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("status", "message", "expected"),
|
||||
[
|
||||
(
|
||||
404,
|
||||
"litellm.NotFoundError: NotFoundError: OpenrouterException - "
|
||||
'{"error":{"message":"No endpoints found that support image input","code":404,'
|
||||
'"metadata":{"failed_routing_step":"Filter by Image Support"}}}',
|
||||
True,
|
||||
),
|
||||
(400, "Invalid content type. image_url is only supported by certain models.", True),
|
||||
(400, "my-model is not a multimodal model", True),
|
||||
(400, "This model doesn't support the image field for user messages.", True),
|
||||
(
|
||||
400,
|
||||
"OpenRouterException: Message: This model's maximum context length is 1048576 "
|
||||
"tokens, but the request requires 1073484 tokens (942412 input including "
|
||||
"image/vision expansion + 131072 for the completion). Reduce the input length "
|
||||
"or max_tokens.",
|
||||
False,
|
||||
),
|
||||
(400, "At most 5 image(s) may be provided in one prompt.", False),
|
||||
(
|
||||
400,
|
||||
"Upstream error from Relace: Generation stopped with an incomplete tool call",
|
||||
False,
|
||||
),
|
||||
(500, "No endpoints found that support image input", False),
|
||||
],
|
||||
)
|
||||
def test_is_image_rejection(status: int, message: str, expected: bool) -> None:
|
||||
exc = APIStatusError(message, response=httpx.Response(status, request=_request()), body=None)
|
||||
assert execution._is_image_rejection(exc) is expected
|
||||
|
||||
|
||||
async def _run_with_image_session(
|
||||
monkeypatch: pytest.MonkeyPatch, streams: list[_FakeStream]
|
||||
) -> tuple[Any, int, int]:
|
||||
|
|
@ -208,20 +243,3 @@ async def test_run_cycle_strips_images_on_image_rejection(
|
|||
assert result is streams[1]
|
||||
assert attempts == 2
|
||||
assert strips == 1
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_run_cycle_does_not_strip_images_on_unrelated_client_error(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
bad_request = BadRequestError(
|
||||
"Upstream error from Relace: Generation stopped with an incomplete tool call",
|
||||
response=httpx.Response(400, request=_request()),
|
||||
body=None,
|
||||
)
|
||||
streams = [_FakeStream(exc=bad_request), _FakeStream()]
|
||||
result, attempts, strips = await _run_with_image_session(monkeypatch, streams)
|
||||
|
||||
assert result is streams[1]
|
||||
assert attempts == 2
|
||||
assert strips == 0
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue