Description
Chat responses arrive as a single block instead of streaming word-by-word. The backend SSE streaming endpoint works correctly (verified via curl), but the frontend does not render chunks incrementally — the full response appears at once after the LLM finishes generating.
Additionally, the typing indicator (3 bouncing dots) disappears prematurely instead of remaining visible until the first chunk arrives.
What's already been done
- Nginx SSE config —
proxy_buffering off, proxy_cache off, proxy_http_version 1.1, chunked_transfer_encoding off, X-Accel-Buffering: no headers are all set
- Backend —
StreamingResponse with text/event-stream media type, Cache-Control: no-cache header. Partial chunks are emitted with event.partial=True from ADK's LiteLlm adapter
- Frontend — SSE parsing with
ReadableStream reader, requestAnimationFrame yield between state updates to force browser paint. Typing indicator deferred until first chunk via started flag
Suspected root cause
React 18's automatic batching may be coalescing the rapid setMessages calls despite the requestAnimationFrame yield. The chunks may also arrive in large batches from the network layer rather than individually.
Relevant files
backend/app/api/routes/chat.py — streaming endpoint (send_message_stream)
frontend/src/pages/chat.tsx — sendMessage SSE reader loop
frontend/nginx.conf — proxy config
Notes
- Debug
print statements are still in the streaming endpoint (chat.py:235-239) — remove once streaming is fixed
- Conversation context/follow-ups work correctly (ADK
InMemorySessionService singleton with event replay)
Description
Chat responses arrive as a single block instead of streaming word-by-word. The backend SSE streaming endpoint works correctly (verified via
curl), but the frontend does not render chunks incrementally — the full response appears at once after the LLM finishes generating.Additionally, the typing indicator (3 bouncing dots) disappears prematurely instead of remaining visible until the first chunk arrives.
What's already been done
proxy_buffering off,proxy_cache off,proxy_http_version 1.1,chunked_transfer_encoding off,X-Accel-Buffering: noheaders are all setStreamingResponsewithtext/event-streammedia type,Cache-Control: no-cacheheader. Partial chunks are emitted withevent.partial=Truefrom ADK's LiteLlm adapterReadableStreamreader,requestAnimationFrameyield between state updates to force browser paint. Typing indicator deferred until first chunk viastartedflagSuspected root cause
React 18's automatic batching may be coalescing the rapid
setMessagescalls despite therequestAnimationFrameyield. The chunks may also arrive in large batches from the network layer rather than individually.Relevant files
backend/app/api/routes/chat.py— streaming endpoint (send_message_stream)frontend/src/pages/chat.tsx—sendMessageSSE reader loopfrontend/nginx.conf— proxy configNotes
printstatements are still in the streaming endpoint (chat.py:235-239) — remove once streaming is fixedInMemorySessionServicesingleton with event replay)