fix(agent): reject router timeout shim responses

This commit is contained in:
Jakub Wolniewicz
2026-07-21 07:46:37 +02:00
committed by Teknium
parent 462c1c14dd
commit d4a496373d
2 changed files with 60 additions and 2 deletions

View File

@@ -110,6 +110,32 @@ def _add_prompt_cache_key(
api_kwargs["prompt_cache_key"] = cache_key
_ROUTER_TIMEOUT_SHIM = "Connect timeout, please try again later."
def _has_positive_completion_tokens(usage: Any) -> bool:
"""Return whether a response usage object proves text was generated."""
for field in ("completion_tokens", "output_tokens"):
value = usage.get(field) if isinstance(usage, dict) else getattr(usage, field, None)
if isinstance(value, (int, float)) and not isinstance(value, bool) and value > 0:
return True
return False
def _is_router_timeout_shim(response: Any) -> bool:
"""Recognize a router failure encoded as a successful ChatCompletion."""
choices = getattr(response, "choices", None)
if not isinstance(choices, list) or len(choices) != 1:
return False
message = getattr(choices[0], "message", None)
content = getattr(message, "content", None)
if not isinstance(content, str) or content.strip() != _ROUTER_TIMEOUT_SHIM:
return False
if getattr(message, "tool_calls", None):
return False
return not _has_positive_completion_tokens(getattr(response, "usage", None))
def _reasoning_config_for_model(model: str, reasoning_config: dict | None) -> dict | None:
"""Clamp Hermes' extended effort set (``ultra``) to the OpenAI-compat wire vocabulary.
@@ -603,8 +629,10 @@ class ChatCompletionsTransport(ProviderTransport):
)
def validate_response(self, response: Any) -> bool:
"""Check that response has valid choices."""
return bool(response is not None and getattr(response, "choices", None))
"""Check that response has valid choices and is not a router failure shim."""
if response is None or not getattr(response, "choices", None):
return False
return not _is_router_timeout_shim(response)
def extract_cache_stats(self, response: Any) -> dict[str, int] | None:
"""Cache stats from prompt_tokens_details (OpenRouter/OpenAI) or DeepSeek's top-level prompt_cache_hit_tokens."""

View File

@@ -584,6 +584,36 @@ class TestChatCompletionsValidate:
@pytest.mark.parametrize("usage", [None, SimpleNamespace(completion_tokens=0)])
def test_rejects_known_router_timeout_shim_without_generated_tokens(self, transport, usage):
"""#68396: an HTTP-200 router timeout shim with no generated tokens is not a completion."""
response = SimpleNamespace(
choices=[SimpleNamespace(message=SimpleNamespace(
content="Connect timeout, please try again later.",
tool_calls=None,
))],
usage=usage,
)
assert transport.validate_response(response) is False
@pytest.mark.parametrize(
("content", "tool_calls", "usage"),
[
("Connect timeout, please try again later.", None, SimpleNamespace(completion_tokens=1)),
("The router said: Connect timeout, please try again later.", None, None),
("Connect timeout, please try again later.", [SimpleNamespace()], None),
],
)
def test_accepts_non_shim_timeout_text(self, transport, content, tool_calls, usage):
"""Positive controls (#68396): generated tokens, embedded phrase, or tool calls stay valid."""
response = SimpleNamespace(
choices=[SimpleNamespace(message=SimpleNamespace(content=content, tool_calls=tool_calls))],
usage=usage,
)
assert transport.validate_response(response) is True
def test_valid(self, transport):
r = SimpleNamespace(choices=[SimpleNamespace(message=SimpleNamespace(content="hi"))])
assert transport.validate_response(r) is True