Repository navigation
[API server][Bug fix] Keep /generate streaming when the form omits streaming on FastAPI 0.147+ - #714
Merged
Conversation
… field FastAPI 0.147 reads an omitted bool form field as False instead of its declared default, so a /generate request without streaming got the aggregated answer. Check the parsed form for the field and fall back to True when it is absent, matching the Rust frontend.
merceod
approved these changes
Oct 20, 2026
merceod
left a comment
Collaborator
There was a problem hiding this comment.
Reproduced the version dependence on CPU with the real route. With FastAPI 0.150.0 and Starlette 1.9.0 (unpacked next to the venv), main fails the three omitted-field cases (urlencoded and multipart /generate without streaming get the aggregated JSON body, and video_frame output without the field is refused) and this head passes all 39 tests in the two files; with FastAPI 0.141.1 all 39 pass on both arms, as the description says. The fix is the smallest one that works: the parsed form is already cached on the request, so the extra await request.form() costs nothing, streaming is the only bool form field in the package, and an explicit value still wins. ruff clean. Nothing to fix.
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.
Fixes #713.
What does this PR do?
FastAPI 0.147.0 reads a
boolform field that the request leaves out asFalse(an unchecked HTML checkbox) instead of its declared default. mstar does not pinfastapiand CI now installs 0.150.0, so on the Python frontend a/generaterequest withoutstreamingquietly gets one aggregated JSON body instead of the NDJSON stream. That hits the documentedcurl ... /generate -F 'text=...'commands (quickstart,examples/curl.sh, the windowedcurl -sNvideo example), andoutput_modalities=video_framewithout the field fails with 400. The Rust frontend still streams by default.generate()now usesTruewhen the parsed form (already cached on the request) has nostreamingkey; an explicit value is read as before.streamingis the onlyboolform field in the package (details below).How was it tested?
5 new cases in
test/modular/test_generate_route.pypost to the real/generateroute with a stand-in server: nostreamingfield (urlencoded and multipart) must stream NDJSON,streaming=false(both) must return JSON, andvideo_frameoutput without the field must be accepted. With FastAPI 0.147.0 and 0.150.0 (Starlette 1.9.0, as CI resolves), 3 of the 5 fail on main and all pass on the head; with 0.143.3 and 0.146.0 all 5 pass on both. The twotest_video_frame_protocol.pycases that callgenerate()directly now pass a request stub with aform().ruff check .passes. The CPU Core selection fromci.yml(same paths,-n 4 --dist worksteal) on the head: 4655 passed, 866 skipped with FastAPI 0.150.0, and the same with 0.143.3; the TP lockstep shard passed 7 of 7 on both.Not tested
No served-model run: the change is in request parsing before submission, and the route tests drive the real FastAPI app. The Rust frontend is untouched; it already defaults
streamingto true.Form fields audited
generate()is the only route declaringForm(...)parameters.streaming: bool = Form(True)is the onlyboolone, so the only one the FastAPI change affects;text,input_modalities,model_kwargsandrequest_idareOptional[str] = Form(None)andoutput_modalitiesisstr = Form("text"), and those keep their defaults./v1/audio/transcriptionsand/v1/images/editsreadrequest.form()themselves, so FastAPI's parameter parsing does not touch them. There is noboolform field defaulting toFalsein the package.Design notes
Optional[bool] = Form(None)and mappingNoneto the default does not work: FastAPI 0.147+ applies the same rule tobool | None(_extract_form_bodyinfastapi/dependencies/utils.pychecksannotation in (bool, bool | None)), so the omitted field still arrives asFalse. Measured on 0.147.0 and 0.150.0.strand parsing it by hand would also work, but changes the OpenAPI schema from boolean to string and duplicates pydantic's bool parsing. The presence check keeps the schema and validation as they are.fastapi<0.147would also restore the default, but holds every install back on FastAPI for one field, and the bug comes back once the pin is lifted. The presence check works on both sides of 0.147, so no pin is added.Checklist
ruff check .passesAI assistance: written with AI coding agent