Fix SSE/JSONL stream_item_type lost through include_router - #15426
Closed
rzsultan-boop wants to merge 2 commits into
Closed
rzsultan-boop wants to merge 2 commits into
rzsultan-boop wants to merge 2 commits into
Conversation
Author
Member
|
I don't like this approach as the logic is not clear - Please, review the alternative approach: #15077 |
Merging this PR will not alter performance
Comparing |
When SSE/JSONL endpoints are defined on an APIRouter and included via include_router(), the stream_item_type and stream_item_field attributes were dropped because the detection in APIRoute.__init__ only runs when response_model is a DefaultPlaceholder — but include_router passes the already-resolved value. After add_api_route, copy both stream_item_type and stream_item_field from the original route, then re-bake self.app so the handler closure captures the correct stream_item_field for runtime validation. Without re-baking, both runtime validation and OpenAPI schema generation remain broken because: - get_route_handler() closes over stream_item_field at __init__ time - openapi/utils.py reads stream_item_field, not stream_item_type
self.routes[-1] is typed as Starlette's BaseRoute, which lacks the stream_item_type/stream_item_field/app attributes used by APIRoute. Narrow the type with an isinstance check so mypy is satisfied and the access is defensively guarded even though the preceding isinstance(route, APIRoute) plus route_class_override=type(route) make it true in practice.
rzsultan-boop
force-pushed
the
fix/issue-15401-sse-stream-item-type-include-router
branch
from
April 27, 2026 15:50
b47295e to
137f8f4
Compare
Author
|
Closing in favor of #15077, which threads stream_item_type through init/add_api_route/include_router parameters — the established pattern for this codebase (see strict_content_type). Thanks for the review. |
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 #15401.
Problem
APIRoute.__init__only detects streaming return annotations inside theisinstance(response_model, DefaultPlaceholder)branch.include_routercalls
add_api_routewith the already-resolvedresponse_model(Nonefor streaming routes), so the detection is skipped on the merged route.
Both
stream_item_typeandstream_item_fieldstay None.Consequences:
contentSchema/itemSchemafor the route.__init__viarequest_response(self.get_route_handler()), closes overstream_item_field=None, so runtime validation and serialization ofstream items are silently disabled.
Fix
In
APIRouter.include_router, right afteradd_api_routeappends themerged route, copy
stream_item_typeandstream_item_fieldfrom thesource route, then rebuild
new_route.appso the handler closure picksup the restored field. An
isinstance(new_route, APIRoute)checknarrows Starlette's
BaseRoutefor mypy.Both SSE (
EventSourceResponse) and JSONL routes go through the samepath, so both are covered.
Tests
Four tests in
tests/test_sse.py:test_stream_item_type_preserved_through_include_routerasserts bothattributes are set after
include_router.test_sse_stream_item_field_set_on_direct_routebaseline fordirectly-registered routes.
test_sse_openapi_schema_after_include_routerverifies the itemmodel appears in
components.schemas.test_sse_runtime_serialization_after_include_routerend-to-endHTTP call via
TestClientchecking the serialized frame body.Full suite passes locally.
ruff check,ruff format --check, andmypy fastapi/routing.pyare clean.