test(vllm): stabilize Responses tests (pin temperature=0; drop unstable native structured-JSON test) - #1011
Merged
leseb merged 6 commits intoSep 9, 2026
Conversation
test_structured_json_output intermittently failed with status=="incomplete" instead of "completed". On the native /v1/responses passthrough (full-flow.yaml) vLLM is started with --reasoning-parser deepseek_r1 and emits a separate reasoning item whose chain-of-thought tokens are debited from the shared max_output_tokens budget. The test sent no temperature pin, so at the default sampling temperature the small Qwen3-0.6B model intermittently ignored the soft /no_think hint, produced a long <think> block, and exhausted the 512-token budget before the constrained JSON completed. Pin temperature=0 (greedy decoding) so the reasoning-item length is deterministic for the fixed prompt: a passing run stays passing. Reasoning stays enabled and still competes for the unchanged 512-token budget, so the realism the existing comment documents is preserved and no assertion is weakened. Apply the same pin to the other 16 model-exercising tests that assert status=="completed" or exact markers without pinning temperature, closing the whole latent-flake class (native, IRR-streaming, compaction, and Responses-to-Chat-Completions translation paths alike, since temperature is in the translation allowlist). The two intentional max_output_tokens=1 incomplete tests and the existing temperature-passthrough tests are left unchanged. Signed-off-by: Sébastien Han <seb@redhat.com>
leseb
enabled auto-merge
September 8, 2026 15:21
franciscojavierarceo
approved these changes
Sep 8, 2026
temperature=0 alone did not stabilize test_structured_json_output: on the native /v1/responses passthrough vLLM debits the separate reasoning item against the shared max_output_tokens, and the deterministic greedy preamble overruns the 512-token budget before the constrained JSON completes (status=="incomplete", reproduced on both vllm-responses and vllm-responses-postgres). The identical request on the Chat Completions translation path completes in <128 tokens, confirming the answer is tiny and only the reasoning preamble needs room. Raise the budget to 2048 (well under the 4096-token model context) so, combined with greedy decoding, completion is deterministic. Signed-off-by: Sébastien Han <seb@redhat.com>
praxis-bot
reviewed
Sep 8, 2026
praxis-bot
left a comment
There was a problem hiding this comment.
praxis-bot review
Scope: 1 file changed, 33 insertions, 4 deletions — test-only.
Summary
Clean change. All 17 model-exercising tests that assert status == "completed" without a prior temperature pin now have temperature=0 (greedy decoding), making vLLM output deterministic and eliminating the flake caused by non-deterministic reasoning-token length on Qwen3-0.6B.
Verified
- Coverage is complete. Every
responses.create()call that feeds an assertion onstatus == "completed"or exact output markers now pinstemperature=0. The 22createcalls across 17 tests (multi-turn tests pin both turns) match the PR description. - Exclusions are correct.
test_max_output_tokens_reports_incompleteandtest_streaming_incomplete_round_trip(max_output_tokens=1, assert"incomplete") are correctly left unpinned.test_generation_parameters_are_reflectedandtest_generation_parameters_round_tripalready had explicittemperature=0pre-PR. max_output_tokensbump (512 to 2048) is reasonable. The model context is 4096 tokens; 2048 gives generous headroom for the reasoning preamble without exhausting context. The accompanying comment accurately explains the rationale.- No assertions weakened. All existing assertions are preserved; the only behavioral change is deterministic sampling.
- Agentic and file-search tests that accept
status in ("completed", "incomplete")are correctly excluded since they already tolerate truncation.
No findings.
…fits" This reverts commit aaad214. Signed-off-by: Sébastien Han <seb@redhat.com>
test_structured_json_output exercised native /v1/responses structured output (text.format = json_schema) end-to-end against the small CI model. It could not be stabilized: on the native passthrough vLLM lets the model reason UNCONSTRAINED before the guided-JSON grammar applies, so Qwen3-0.6B's reasoning preamble overruns the max_output_tokens budget before the JSON completes (status=="incomplete"). This reproduced deterministically at temperature=0 with both 512 and 2048 token budgets (both vllm-responses and vllm-responses-postgres jobs), so no budget bump or temperature pin fixes it. The identical structured-JSON round-trip through the Chat Completions translation path (test_structured_output_round_trip) stays green at 128 tokens because that path applies the json_schema grammar from the first token, leaving no room to ramble. Remove the fragile native model-behavior test. Structured-output coverage is retained by test_structured_output_round_trip (model round-trip through the translation path) and by the native text.format / json_schema passthrough unit and integration coverage in apis/src/openai/responses/. The temperature=0 pins on the other model-exercising tests are kept. Signed-off-by: Sébastien Han <seb@redhat.com>
leseb
disabled auto-merge
September 9, 2026 08:46
…all round-trip The chat-translation function_call_and_output round-trip flaked once in CI (the SQLite job) at max_output_tokens=128 on its second turn, while the PostgreSQL job passed the same commit. That second turn is free-text (tool_choice="none", no schema) and rehydrates prior items from the store backend, so the cause is not yet established: neither a plain budget overrun nor a rehydration/storage-path difference between backends is ruled out. Treat this as a controlled experiment, not a proven fix. Widen only that one budget to 512 and attach failure diagnostics (status, incomplete_details, usage, output item types, output_text length) so the next failure -- on either backend -- is analyzable instead of an opaque 'incomplete' == 'completed'. Signed-off-by: Sébastien Han <seb@redhat.com>
Signed-off-by: Sébastien Han <seb@redhat.com>
leseb
enabled auto-merge
September 9, 2026 10:01
8 tasks
leseb
added a commit
to leseb/praxis-ai
that referenced
this pull request
Sep 9, 2026
Keep the praxis-proxy#276 branch current with origin/main (5ba5345), which advanced by three commits since the last sync: - praxis-proxy#1005 fix(responses): conform SSE error events to ResponseErrorEvent schema - praxis-proxy#922 test: provider-native error formatters through the proxy pipeline - praxis-proxy#1011 test(vllm): stabilize Responses tests (pin temperature=0; drop the unstable native structured-JSON test) The merge auto-resolved with no conflicts. Two files overlapped and were combined by git without manual reconciliation: - tests/integration/sdk/openai/test_openai_responses_vllm.py: praxis-proxy#1011's temperature=0 pins and the dropped structured-JSON test coexist with the praxis-proxy#276 streaming assertions (translated_agentic_client fixture, text_deltas output_index ordering for MCP and web_search) added in b38d5e1. - apis/src/openai/responses/stream_events/tests.rs: non-overlapping hunks. apis/src/openai/responses/stream_events/mod.rs merged silently even though praxis-proxy#1005 also touched it (SSE error-event schema path) — the changed regions do not overlap the praxis-proxy#276 local-tool synthesis logic. No behavior change. Verified on the merged tree: make build, make lint, and make doc all exit 0; stream_events unit tests 79/0. Signed-off-by: Sébastien Han <seb@redhat.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
test_openai_responses_vllm.py::test_structured_json_outputis a pre-existing flake: it intermittently fails withstatus == "incomplete"instead of"completed"(observed re-running the vLLM jobs on unrelated PRs). It is not caused by any production change.Root cause
Under
examples/configs/openai/responses/full-flow.yamlthe request is a native/v1/responsespassthrough to vLLM (no translation, no IRR). vLLM is launched with--reasoning-parser deepseek_r1, so it emits a separatereasoningoutput item whose chain-of-thought tokens are debited from the sharedmax_output_tokensbudget.On the native path vLLM lets the model reason unconstrained before the
json_schemaguided-decoding grammar applies to the message, so the smallQwen/Qwen3-0.6Bmodel produces a long<think>preamble that exhausts the budget before the constrained JSON completes →status == "incomplete".What was tried, and why the flaky test is removed rather than pinned
temperature=0(greedy). This stabilizes the class of value/status flakes and every other model-exercising test passes with it — but it did not fixtest_structured_json_output. Greedy simply locks in the long-reasoning path, so the test failed deterministically atmax_output_tokens=512on bothvllm-responsesandvllm-responses-postgres.512 → 2048. Also failed 2/2 with the sameincompleteresult — the native-path reasoning preamble overruns any sane budget for this prompt+schema on the tiny model. The budget lever is a dead end.The identical structured-JSON round-trip through the Chat Completions translation path (
test_structured_output_round_trip) stays green at just 128 tokens, because that path applies thejson_schemagrammar from the first token, leaving no room to ramble. That confirms the answer itself is tiny and the failure is purely the native-path reasoning contention on a 0.6B CI model — not a proxy defect.Fix
temperature=0on the model-exercising tests that assertedstatus == "completed"/ exact markers without a temperature pin (native, IRR-streaming, compaction, and Responses-to-Chat-Completions translation paths —temperatureis in the translation allowlist, verified bytest_generation_parameters_round_trip). This removes sampling-variance flakiness across the suite; all these tests are green in CI. Multi-turn tests pin both turns.test_structured_json_output. It cannot be stabilized without weakening its assertion or risking a different deterministic failure, and it is a model-behavior artifact of the tiny CI model rather than proxy coverage.Coverage retained
test_structured_output_round_trip— structured-JSON round-trip through the model via the Responses-to-Chat-Completions translation path.text.format/json_schemapassthrough is covered by the unit and integration tests inapis/src/openai/responses/(e.g.responses_to_chat_completions/tests.rs,mod.rs).Explicitly left unchanged
test_max_output_tokens_reports_incompleteandtest_streaming_incomplete_round_trip(max_output_tokens=1) — they rely on truncation to assertstatus == "incomplete".test_generation_parameters_are_reflected/test_generation_parameters_round_trip— keep their explicittemperature=0+ passthrough assertions.Not done here (deliberately)
chat_template_kwargs: {enable_thinking: false}): with thedeepseek_r1parser that risks routing the answer intoreasoning_contentand emptyingoutput_text— a worse, deterministic failure.seed(no-op under greedy).Verification
python3 -m py_compilepasses.temperature=0pins retained on the other model-exercising tests, no2048,test_structured_json_outputfully removed.vLLM IntegrationCI jobs (vllm-responses,vllm-responses-postgres).