mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
fix(coderd/x/chatd): forward local status events to fix delayed-startup banner (#23650)
## Problem
The agent chat delayed-startup banner ("Response startup is taking
longer than expected") could appear even though the model was already
streaming.
The root cause is in `Subscribe()`: `message_part` events were delivered
via the fast local in-process stream, while `status` events were
delivered via PostgreSQL pubsub. Both feed into the same `select`
statement, and Go's `select` picks whichever channel is ready first —
there is no ordering guarantee between channels. So a `message_part`
could outrun the `status=running` that logically precedes it.
The frontend saw content arrive while it still thought the chat was
pending, triggering the banner.
## Fix
Also forward `status` events from the local channel, alongside
`message_part`.
Both event types already travel through the same FIFO subscriber
channel: `publishStatus()` is called before the first `message_part`, so
channel ordering guarantees the frontend sees `status=running` before
any content.
Pubsub still delivers a duplicate `status` event later; the frontend
deduplicates it (`setChatStatus` is idempotent — it early-returns when
the status hasn't changed).
This commit is contained in:
+14
-3
@@ -2871,9 +2871,20 @@ func (p *Server) Subscribe(
|
||||
continue
|
||||
}
|
||||
if hasPubsub {
|
||||
// Only forward message_part events from local
|
||||
// (durable events come via pubsub + cache).
|
||||
if event.Type == codersdk.ChatStreamEventTypeMessagePart {
|
||||
// Forward transient events from local.
|
||||
// Durable events (messages, queue updates)
|
||||
// come via pubsub + cache. Status is
|
||||
// included alongside message_part because
|
||||
// both travel through the same ordered
|
||||
// channel: publishStatus is called before
|
||||
// the first message_part, so FIFO delivery
|
||||
// guarantees the frontend sees
|
||||
// status=running before any content.
|
||||
// Pubsub will deliver a duplicate status
|
||||
// later; the frontend deduplicates it
|
||||
// (setChatStatus is idempotent).
|
||||
if event.Type == codersdk.ChatStreamEventTypeMessagePart ||
|
||||
event.Type == codersdk.ChatStreamEventTypeStatus {
|
||||
select {
|
||||
case <-mergedCtx.Done():
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user