MCPcopy Index your code
hub / github.com/TanStack/ai / streamResponse

Method streamResponse

packages/ai-client/src/chat-client.ts:849–1087  ·  view source on GitHub ↗

* Stream a response from the LLM. * Returns true if the stream completed successfully, false on abort or error.

()

Source from the content-addressed store, hash-verified

847 * Returns true if the stream completed successfully, false on abort or error.
848 */
849 private async streamResponse(): Promise<boolean> {
850 // Guard against concurrent streams - if already loading, skip
851 if (this.isLoading) {
852 return false
853 }
854
855 // Track generation so a superseded stream's cleanup doesn't clobber the new one
856 const generation = ++this.streamGeneration
857 const runId = `run-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
858 this.currentRunId = runId
859
860 this.setIsLoading(true)
861 this.setStatus('submitted')
862 this.setError(undefined)
863 this.errorReportedGeneration = null
864 this.abortController = new AbortController()
865 // Capture the signal immediately so that a concurrent stop() or
866 // sendMessage() that reassigns this.abortController cannot cause
867 // connect() to receive a stale or null signal.
868 const signal = this.abortController.signal
869 // Reset pending tool executions for the new stream
870 this.pendingToolExecutions.clear()
871 let streamCompletedSuccessfully = false
872 let activeDevtoolsRunId: string | null = null
873 let runTerminalEventEmitted = false
874
875 try {
876 // Get UIMessages with parts (preserves approval state and client tool results)
877 const messages = this.processor.getMessages()
878 const clientTools = new Map(this.clientToolsRef.current)
879 const runtimeContext = this.context
880
881 // Call onResponse callback
882 await this.callbacksRef.current.onResponse()
883
884 // If the stream was cancelled during the onResponse await (e.g. stop()
885 // from a callback or unmount, or reload() superseding this stream),
886 // bail out before allocating waitForProcessing() — otherwise the
887 // resolveProcessing() that ran during cancellation is a no-op and the
888 // await processingComplete below would deadlock.
889 if (signal.aborted) {
890 return false
891 }
892
893 // Merge sources for the wire `forwardedProps` field, in priority
894 // order (later spreads win):
895 // 1. Legacy `body` option (deprecated).
896 // 2. Canonical `forwardedProps` option (wins over `body`).
897 // 3. Per-message `body` arg passed to `sendMessage` (highest).
898 // The AG-UI standard `threadId` is sent at the wire's top level for
899 // run/conversation correlation, so we no longer auto-emit a separate
900 // `conversationId` here — `chat({ threadId })` server-side covers the
901 // same role for devtools/observability.
902 const mergedBody = {
903 ...this.bodyOption,
904 ...this.forwardedPropsOption,
905 ...this.pendingMessageBody,
906 }

Callers 4

sendMessageMethod · 0.95
appendMethod · 0.95
reloadMethod · 0.95
checkForContinuationMethod · 0.95

Calls 15

setIsLoadingMethod · 0.95
setStatusMethod · 0.95
setErrorMethod · 0.95
generateUniqueIdMethod · 0.95
ensureSubscriptionMethod · 0.95
waitForProcessingMethod · 0.95
reportStreamErrorMethod · 0.95
shouldAutoSendMethod · 0.95
checkForContinuationMethod · 0.95
nowMethod · 0.80

Tested by

no test coverage detected