Global chat: receive more streaming statuses#4969
Open
hanna-paasivirta wants to merge 2 commits into
Open
Conversation
2 tasks
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.
Description
Apollo will start streaming the global chat response much earlier, so the user waits less before seeing output. To support this, it sends several types of streaming events, including status updates. This PR receives them in Lightning.
Apollo changes: OpenFn/apollo#573
How it works
There are two kinds of status:
streamingStatusmechanism (dots + italic text).To make persistent statuses survive a reload, assistant messages get a new
response_segmentsfield: a timeline of{"type": "text" | "status", "content": string}entries. The final Apollo response payload still contains exactly what it did before, including the full final message inresponse, so history building and everything downstream ofcontentis unchanged. Segments are display-only.Closes OpenFn/apollo#572
Backend
statusSSE event from Apollo is broadcast to the session channel asstreaming_segment. The payload is byte-identical to aresponse_segmentsentry, so live and reloaded timelines render through one code path.response_segmentscolumn onai_chat_messages(migration included, additive, backward compatible). The changeset validates the segment shape;AiAssistant.normalize_response_segments/1filters invalid entries and falls back tonil, which means flat legacy rendering.response_segmentson messages.MessageProcessorhandles{:error, %Ecto.Changeset{}}from save, marking the message as errored instead of crashing.error_message_from_body/1guards this and falls back to a generic HTTP error message.Frontend
streamingSegmentstimeline. Text chunks grow the trailing text segment, or open a new one after a status. Reset alongside the other streaming fields.AIChannelRegistry: statuses arriving over the wire are pinned to their position in the typewriter char buffer, and only enter the timeline once the drain reaches that position. This keeps the visible order identical to wire order despite the drain delay. A persistent status also clears any active thinking status immediately.MessageList: renders the woven timeline when segments exist (streaming, gated to the global assistant; or persistedresponse_segments), otherwise falls back to the flatcontentpath. Non-global chats are unchanged.__streaming__message id is trusted as global, since a workflow YAML streamed while a job is open can only come from the global assistant.Validation steps
Additional notes for the reviewer
Blast radius: which chats does this touch?
This is a global chat feature, but much of the code it changes is shared with job chat and workflow chat. Global chat is experimental; job and workflow chat are live services, so the shared paths deserve the careful review.
Global chat only (lighter review):
response_segmentson save (build_global_message).Shared with job and workflow chat (careful review):
process_stream/handle_sse_event) is one code path for all three streaming endpoints. The newstatushandler lives there. Apollo only sendsstatusevents on the global endpoint today, but if it ever sent them for job or workflow chat they would flow through.error_message_from_body), theChatMessageschema/changeset, theMessageProcessorchangeset-error handling, and channel message serialization.MessageListrender refactor: the flat rendering path all chats use was restructured into a conditional. Non-global chats must render exactly as before.AIChannelRegistrychar-drain loop now flushes status markers on every tick, for all chats' streams.workflow_templatepage. Now it applies from any page, and the__streaming__message id bypasses the workflow-template guard inuseAIWorkflowApplicationsfor all streams, including non-global workflow chat. This is intentional (workflow YAML writes to the shared Y.Doc), but it is the one place where live-service behavior changes.What to review
AIChannelRegistry.ts. This is the trickiest part.pendingStatusMarkersmust flush exactly when the drain passes their buffer position, including the final flush insidedrainThenRunbefore the message finalizes. Check the reset path (stopDraining) clears markers so nothing leaks into the next stream.AIAssistantPanelWrapper.tsxanduseAIWorkflowApplications.ts(see blast radius above). The claim is that a YAML instreamingChangeswhile a job is open can only come from the global assistant. Non-global workflow chat also streams YAML, so please sanity-check whether a workflow chat stream can be active while the user has a job open, and whether applying in that case is acceptable. Note the applied-ref is now only set when a change is actually handled, so a change that could not apply stays eligible if the user switches pages mid-stream.nil(flat message). Confirm silent degradation is the behavior we want, versus rejecting.AI Usage
Please disclose whether you've used AI anywhere in this PR (it's cool, we just
want to know!):
You can read more details in our
Responsible AI Policy
Pre-submission checklist
/reviewwith Claude Code)
(e.g.,
:owner,:admin,:editor,:viewer)