This commit is contained in:
Edder Talmor 2026-04-24 19:26:50 -05:00 committed by GitHub
commit ec99f88a3d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 133 additions and 27 deletions

View file

@ -0,0 +1,47 @@
"""Tests for empty-response fallback handling in GatewayRunner."""
import sys
import types
import pytest
@pytest.fixture(autouse=True)
def _mock_dotenv(monkeypatch):
"""gateway.run imports dotenv at module level; stub it for tests."""
fake = types.ModuleType("dotenv")
fake.load_dotenv = lambda *a, **kw: None
monkeypatch.setitem(sys.modules, "dotenv", fake)
@pytest.fixture()
def runner():
from gateway.run import GatewayRunner
return GatewayRunner.__new__(GatewayRunner)
class TestEmptyResponseFallback:
def test_reasoning_only_message(self, runner):
message = runner._build_empty_response_message({
"empty_response_reasoning": "structured reasoning answer",
})
assert message == (
"⚠️ The model produced internal reasoning but no visible response after all retries. "
"Try again or rephrase your question."
)
def test_truly_empty_message(self, runner):
message = runner._build_empty_response_message({})
assert message == (
"⚠️ The model returned no content after all retries. "
"Try again or rephrase your question."
)
def test_response_fallback_detection_handles_new_and_legacy_forms(self, runner):
assert runner._is_empty_response_fallback(
{"response_is_empty_fallback": True},
"⚠️ The model returned no content after all retries. Try again or rephrase your question.",
)
assert runner._is_empty_response_fallback({}, "(empty)")
assert not runner._is_empty_response_fallback({}, "hello world")

View file

@ -250,6 +250,15 @@ def _mock_response(
resp.usage = None
return resp
EMPTY_REASONING_RESPONSE = (
"⚠️ The model produced internal reasoning but no visible response after all retries. "
"Try again or rephrase your question."
)
EMPTY_TRULY_EMPTY_RESPONSE = (
"⚠️ The model returned no content after all retries. "
"Try again or rephrase your question."
)
# ===================================================================
# Group 1: Pure Functions
@ -2256,7 +2265,9 @@ class TestRunConversation:
mock_compress.assert_not_called() # no compression triggered
assert result["completed"] is True
assert result["final_response"] == "(empty)"
assert result["final_response"] == EMPTY_TRULY_EMPTY_RESPONSE
assert result["response_is_empty_fallback"] is True
assert result["empty_response_reasoning"] is None
assert result["api_calls"] == 6 # 1 original + 2 prefill + 3 retries
def test_reasoning_only_response_prefill_then_empty(self, agent):
@ -2276,7 +2287,9 @@ class TestRunConversation:
):
result = agent.run_conversation("answer me")
assert result["completed"] is True
assert result["final_response"] == "(empty)"
assert result["final_response"] == EMPTY_REASONING_RESPONSE
assert result["response_is_empty_fallback"] is True
assert result["empty_response_reasoning"] == "structured reasoning answer"
assert result["api_calls"] == 6 # 1 original + 2 prefill + 3 retries
def test_reasoning_only_prefill_succeeds_on_continuation(self, agent):
@ -2323,7 +2336,9 @@ class TestRunConversation:
):
result = agent.run_conversation("answer me")
assert result["completed"] is True
assert result["final_response"] == "(empty)"
assert result["final_response"] == EMPTY_TRULY_EMPTY_RESPONSE
assert result["response_is_empty_fallback"] is True
assert result["empty_response_reasoning"] is None
assert result["api_calls"] == 4 # 1 original + 3 retries
def test_truly_empty_response_succeeds_on_nudge(self, agent):
@ -2419,7 +2434,8 @@ class TestRunConversation:
):
result = agent.run_conversation("answer me")
assert result["completed"] is True
assert result["final_response"] == "(empty)"
assert result["final_response"] == EMPTY_TRULY_EMPTY_RESPONSE
assert result["response_is_empty_fallback"] is True
def test_empty_response_emits_status_for_gateway(self, agent):
"""_emit_status is called during empty retries so gateway users see feedback."""
@ -2445,7 +2461,8 @@ class TestRunConversation:
):
result = agent.run_conversation("answer me")
assert result["final_response"] == "(empty)"
assert result["final_response"] == EMPTY_TRULY_EMPTY_RESPONSE
assert result["response_is_empty_fallback"] is True
# Should have emitted retry statuses (3 retries) + final failure
retry_msgs = [m for m in status_messages if "retrying" in m.lower()]
assert len(retry_msgs) == 3, f"Expected 3 retry status messages, got {len(retry_msgs)}: {status_messages}"