fix(sglang): _build_client_args clobbers user's extra_body instead of merging#1925
Conversation
… merging
`inference_kwargs.update(output_type_args)` is a shallow dict update.
When output_type is Regex/CFG, `format_output_type` returns
`{"extra_body": {...}}`, so this replaces the user's own `extra_body=`
kwarg wholesale instead of merging into it -- a user-supplied
`extra_body={"user_key": ...}` is silently dropped entirely. The
sibling `vllm.py`'s `_build_client_args` already handles this
correctly (pop the user's extra_body, merge, reassign).
Fix both the sync `SGLang._build_client_args` and the duplicated
async `AsyncSGLang._build_client_args`: when `output_type_args`
contains an `extra_body` key, merge it into the user's existing
`extra_body` instead of letting the blanket `.update()` clobber it.
Other top-level keys (e.g. `response_format` for JsonSchema output
types) are unaffected and still applied normally.
AI-Generated disclosure: found via an AI-assisted code review pass
(Claude Code) across outlines/src/outlines/models/; I personally
reproduced the clobbering with a minimal repro, verified the fix
against both the extra_body path (CFG/regex) and the response_format
path (JsonSchema) to confirm no regression, and ran the full
tests/models/test_sglang.py suite (21 tests) before submitting.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Verified the clobber: |
|
Related, filed separately since it's a distinct bug from what this PR fixes: #1931. The |
|
Thanks for independently verifying this, and for splitting #1931 out rather than folding it in here — you're right that the aliasing is a distinct bug from the clobber. |
|
Makes sense, thanks for confirming. Filed #1931 with the full repro for anyone who picks it up next. |
|
📚 Documentation preview: https://dottxt-ai.github.io/outlines/pr-preview/pr-1925/ Preview updates automatically with each commit. |
Problem
SGLang._build_client_args(and the duplicatedAsyncSGLang._build_client_args) builds the client kwargs with a shallowinference_kwargs.update(output_type_args). Whenoutput_typeisRegexorCFG,format_output_typereturns{"extra_body": {...}}for the structured-output payload. The blanket.update()then replaces the user's ownextra_body=kwarg wholesale instead of merging into it — any keys the caller passed inextra_bodyare silently dropped.The sibling
vllm.py::_build_client_argsalready handles this correctly: it pops the user'sextra_body, merges the structured-output payload into it, and reassigns.sglang.pynever got the same treatment.Fix
In both
SGLang._build_client_argsandAsyncSGLang._build_client_args: whenoutput_type_argscontains anextra_bodykey, merge it into the user's existingextra_bodydict instead of letting.update()clobber it. Other top-level keys (e.g.response_formatforJsonSchemaoutput types) are unaffected and still applied normally via the existing.update()path.Testing
test_sglang_build_client_args_merges_user_extra_body(parametrized overSGLang/AsyncSGLang) asserting both the user's key and the structured-output payload survive in the mergedextra_body.src/outlines/models/sglang.py(not the test file) reproduces the failure (KeyError: 'user_key') on both parametrizations; reapplying the fix passes.tests/models/test_sglang.pysuite: 21 passed.ruff checkandpre-commit run --files src/outlines/models/sglang.py tests/models/test_sglang.py(mypy + ruff + hygiene hooks) all pass.AI-Generated disclosure
Found via an AI-assisted code review pass (Claude Code) across
outlines/src/outlines/models/. I personally reproduced the clobbering with a minimal repro, verified the fix against both theextra_bodypath (CFG/regex) and theresponse_formatpath (JsonSchema) to confirm no regression, and ran the full test suite before submitting.