Streaming endpoints
Modal fastapi_endpoints support streaming responses using FastAPI’s StreamingResponse class. This class accepts asynchronous generators, synchronous generators, or
any Python object that implements the iterator protocol,
and can be used with Modal Functions!
Simple example
This simple example combines Modal’s @modal.fastapi_endpoint decorator with a StreamingResponse object to produce a real-time SSE response.
If you serve this web endpoint and hit it with curl, you will see the ten SSE
events progressively appear in your terminal over a ~5 second period.
The MIME type of text/event-stream is important in this example, as it tells
the downstream web server to return responses immediately, rather than buffering
them in byte chunks (which is more efficient for compression).
You can still return other content types like large files in streams, but they are not guaranteed to arrive as real-time events.
Streaming responses with .remote
A Modal Function wrapping a generator function body can have its response passed
directly into a StreamingResponse. This is particularly useful if you want to
do some GPU processing in one Modal Function that is called by a CPU-based web
endpoint Modal Function.
Streaming responses with .map and .starmap
You can also combine Modal Function parallelization with streaming responses, enabling applications to service a request by farming out to dozens of containers and iteratively returning result chunks to the client.
This snippet will spread the ten map_me(i) executions across containers, and
return each string response part as it completes. By default the results will be
ordered, but if this isn’t necessary pass order_outputs=False as keyword
argument to the .map call.
Asynchronous streaming
The example above uses a synchronous generator, which automatically runs on its
own thread, but in asynchronous applications, a loop over a .map or .starmap call can block the event loop. This will stop the StreamingResponse from
returning response parts iteratively to the client.
To avoid this, you can use the .aio() method to convert a synchronous .map into its async version. Also, other blocking calls should be offloaded to a
separate thread with asyncio.to_thread(). For example:
Further examples
- Complete code the for the simple examples given above is available in our modal-examples Github repository.
- An end-to-end example of streaming Youtube video transcriptions with OpenAI’s whisper model.