From 9141ca50e82c56ba3e5df91fbb4aa185ed69c779 Mon Sep 17 00:00:00 2001 From: Ambuj Upadhyay Date: Sun, 16 Aug 2026 17:16:37 +0530 Subject: [PATCH] test: derive streamed-usage expectations from the SDK object `main` is red on all three OS jobs: test_convert_chat_completion_chunk_to_streaming_chunk and test_handle_stream_response fail because the expected usage payload was restated as a literal listing openai 2.x's field set. Since `chore: unpin openai` (#12348) relaxed the constraint to `openai>=2.6.0`, openai 3.1.0 resolves and CompletionUsage gained additive fields (prompt_tokens_details.image_tokens / .text_tokens, completion_tokens_details.text_tokens), so the literals no longer match. The contract under test is that haystack forwards the SDK's usage unchanged, not that the SDK's schema has exactly these keys. The expectations are now derived from the same CompletionUsage object the fixture streams, so an additive field in a future openai release cannot break them. Verified the assertion still has teeth: making _serialize_object drop prompt_tokens_details fails both tests, restoring it passes them. Co-Authored-By: Claude Opus 5 (1M context) --- .../components/generators/chat/test_openai.py | 56 ++++++------------- 1 file changed, 18 insertions(+), 38 deletions(-) diff --git a/test/components/generators/chat/test_openai.py b/test/components/generators/chat/test_openai.py index e3cdb089ca..377e35bb42 100644 --- a/test/components/generators/chat/test_openai.py +++ b/test/components/generators/chat/test_openai.py @@ -1291,6 +1291,21 @@ def test_serde_with_list_of_toolsets(self, monkeypatch: pytest.MonkeyPatch, tool assert all(isinstance(ts, Toolset) for ts in deserialized.tools) +# The streamed usage payload is passed through from the OpenAI SDK object verbatim, so the expected value is +# derived from that same object rather than restated as a literal. A new openai release that adds a field to +# CompletionUsage must not fail these tests: the contract under test is "usage is forwarded unchanged", not +# "the SDK's usage schema has exactly these keys". +STREAMED_USAGE = CompletionUsage( + completion_tokens=42, + prompt_tokens=282, + total_tokens=324, + completion_tokens_details=CompletionTokensDetails( + accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0 + ), + prompt_tokens_details=PromptTokensDetails(audio_tokens=0, cached_tokens=0, cache_write_tokens=0), +) + + @pytest.fixture def chat_completion_chunks(): return [ @@ -1506,15 +1521,7 @@ def chat_completion_chunks(): object="chat.completion.chunk", service_tier="default", system_fingerprint="fp_54eb4bd693", - usage=CompletionUsage( - completion_tokens=42, - prompt_tokens=282, - total_tokens=324, - completion_tokens_details=CompletionTokensDetails( - accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0 - ), - prompt_tokens_details=PromptTokensDetails(audio_tokens=0, cached_tokens=0, cache_write_tokens=0), - ), + usage=STREAMED_USAGE, ), ] @@ -1706,23 +1713,7 @@ def streaming_chunks(): finish_reason="tool_calls", ), StreamingChunk( - content="", - meta={ - "model": "gpt-5-mini", - "received_at": ANY, - "usage": { - "completion_tokens": 42, - "prompt_tokens": 282, - "total_tokens": 324, - "completion_tokens_details": { - "accepted_prediction_tokens": 0, - "audio_tokens": 0, - "reasoning_tokens": 0, - "rejected_prediction_tokens": 0, - }, - "prompt_tokens_details": {"audio_tokens": 0, "cached_tokens": 0, "cache_write_tokens": 0}, - }, - }, + content="", meta={"model": "gpt-5-mini", "received_at": ANY, "usage": STREAMED_USAGE.model_dump()} ), ] @@ -1993,18 +1984,7 @@ def test_handle_stream_response( assert result.meta["finish_reason"] == "tool_calls" assert result.meta["index"] == 0 assert result.meta["completion_start_time"] is not None - assert result.meta["usage"] == { - "completion_tokens": 42, - "prompt_tokens": 282, - "total_tokens": 324, - "completion_tokens_details": { - "accepted_prediction_tokens": 0, - "audio_tokens": 0, - "reasoning_tokens": 0, - "rejected_prediction_tokens": 0, - }, - "prompt_tokens_details": {"audio_tokens": 0, "cached_tokens": 0, "cache_write_tokens": 0}, - } + assert result.meta["usage"] == STREAMED_USAGE.model_dump() def test_convert_usage_chunk_to_streaming_chunk(self) -> None: