fix(agent): reject router timeout shim responses
This commit is contained in:
committed by
Teknium
parent
462c1c14dd
commit
d4a496373d
@@ -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."""
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user