From 46053674915f813f12bdb4a78573d63dbe800e32 Mon Sep 17 00:00:00 2001 From: chris-colinsky Date: Sat, 18 Jul 2026 18:55:21 -0700 Subject: [PATCH] Test OpenAI embed 400/422 fail-loud mapping Spec's v0.16.0 review flagged that the OpenAI embed error path (400/422 to provider_invalid_request) was untested, unlike the parametrized Cohere and TEI equivalents. This is the fail-loud behavior the count-based batch chunking relies on -- an over-token chunk aborts the whole call rather than returning a partial. Add the missing parametrized test so the tag-gating behavior is test-protected, not inspection-only. --- tests/unit/test_retrieval_provider.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/unit/test_retrieval_provider.py b/tests/unit/test_retrieval_provider.py index a3ba596..1934717 100644 --- a/tests/unit/test_retrieval_provider.py +++ b/tests/unit/test_retrieval_provider.py @@ -602,6 +602,24 @@ def handler(_req: httpx.Request) -> httpx.Response: assert response.usage.input_tokens == 0 +@pytest.mark.parametrize("status", [400, 422]) +async def test_openai_embed_invalid_request_maps_to_provider_invalid_request(status: int) -> None: + # The OpenAI embed error path: a 400 / 422 (malformed or over-length + # request) surfaces provider_invalid_request. This is the fail-loud behavior + # the count-based batch chunking relies on -- an over-token chunk (each + # slice within the 2048-input count cap but exceeding OpenAI's summed-token + # ceiling) aborts the whole call rather than returning a partial or a + # truncated result. The sibling Cohere / TEI mappings have the equivalent + # test; this closes the gap for the OpenAI mapping. + def handler(_req: httpx.Request) -> httpx.Response: + return httpx.Response(status, json={"error": {"message": "input exceeds the maximum context length"}}) + + provider = _openai_embed_provider(handler) + with pytest.raises(ProviderInvalidRequest): + await provider.embed(["a very long document that exceeds the embedding model context window"]) + await provider.aclose() + + def _embedding_event() -> EmbeddingEvent: return EmbeddingEvent( invocation_id="inv",