Improve streaming timeout handling and keep-alive logic - #950
Conversation
Enhances StreamingResponse to better handle streaming timeouts and keep-alive messages, especially for M365 Copilot (BizChat). Adds customizable timeout messages, periodic "working" notices, and clearer distinction between user cancellations and timeouts. Refactors interface docs and switches Release debug symbols to portable for improved cross-platform support.
There was a problem hiding this comment.
Pull request overview
This PR extends the Builder’s StreamingResponse / IStreamingResponse surface to better handle channel-specific streaming timeouts (notably M365 Copilot/BizChat) by adding a customizable “taking too long” message, a new API to send a stream-timeout notification without canceling underlying work, and by adjusting streaming timer behavior. It also tweaks Release build PDB generation to improve cross-platform debugging.
Changes:
- Added timeout/keep-alive behaviors to
StreamingResponse(BizChat working notices + timeout handling) and introducedSendStreamTimedOutNotificationandStreamingTakingTooLongMessage. - Updated
IStreamingResponseAPI surface (new property + method; removed unusedcitationsparam fromQueueTextChunk). - Changed Release
DebugTypetoportableinBuild.Shared.props.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| src/libraries/Builder/Microsoft.Agents.Builder/StreamingResponse.cs | Adds BizChat keep-alive/timeout logic, new stream-timeout notification API, and update-vs-send behavior on timeout. |
| src/libraries/Builder/Microsoft.Agents.Builder/IStreamingResponse.cs | Extends interface with timeout message + timeout notification method; removes unused param from QueueTextChunk. |
| src/Build.Shared.props | Switches Release PDB format to portable for better cross-platform debugging. |
Comments suppressed due to low confidence (3)
src/libraries/Builder/Microsoft.Agents.Builder/StreamingResponse.cs:817
SendIntermediateMessageuses_streamStartTime.Valuewithout a null-guard. If_streamStartTimeis cleared concurrently (e.g.,ResetAsyncdisposing the timer while a callback is still queued/running), this can throw inside anasync voidtimer callback and crash the process. Make the timer callback resilient by snapshotting/initializing_streamStartTimebefore computing elapsed time, and avoid.Valuehere.
IActivity activity = null;
// update run elapsed time
_streamElapsedTime = DateTime.UtcNow - _streamStartTime.Value;
src/libraries/Builder/Microsoft.Agents.Builder/StreamingResponse.cs:445
SendStreamTimedOutNotificationcallsStopStream()and flipsIsStreamingChannelwithout taking the samelock (this)used by the timer path. This can race withSendIntermediateMessage(which calls_timer.Change(...)under lock) and produce intermittentNullReferenceException/ObjectDisposed failures while streaming is active. Synchronize the stop/disable with the existing lock (and set_queueEmptyso anyEndStreamAsyncwaiter can complete).
await SendActivityAsync(CreateStreamStoppedMessage(message), cancellationToken).ConfigureAwait(false);
IsStreamingChannel = false; // Disabled Streaming for this channel / Switch to Async Mode.
StopStream(); // stop the stream timers
return true;
src/libraries/Builder/Microsoft.Agents.Builder/StreamingResponse.cs:836
- BizChat keep-alive logic can queue a "working" notice even when a new text chunk is already pending. At this point in the callback
_queue.Countmay still be 0 becauseQueueNextChunkActivity()hasn't run yet, while_messageUpdatedmay already be true (new content buffered). This can cause redundant keep-alive messages to be injected during active streaming.
TimeSpan timeSinceLastPass = DateTime.UtcNow - (_lastPassTime ?? _streamStartTime.Value);
if (timeSinceLastPass > BizChatWorkingNoticeInterval
&& _queue.Count == 0)
{
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: a23f6452-2f7b-436a-adc5-988f577ca1a2
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: a23f6452-2f7b-436a-adc5-988f577ca1a2
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: a23f6452-2f7b-436a-adc5-988f577ca1a2
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: a23f6452-2f7b-436a-adc5-988f577ca1a2
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: a23f6452-2f7b-436a-adc5-988f577ca1a2
Enhances StreamingResponse to better handle streaming timeouts and keep-alive messages, especially for M365 Copilot (BizChat). Adds customizable timeout messages, periodic "working" notices, and clearer distinction between user cancellations and timeouts. Refactors interface docs and switches Release debug symbols to portable for improved cross-platform support.
This pull request updates the
IStreamingResponseinterface to improve support for handling streaming timeouts and cancellation notifications, and makes minor build configuration changes. The main changes are the addition of new properties and methods for timeout messaging and cancellation, and a small update to the build debug type.Streaming timeout and cancellation handling:
StreamingTakingTooLongMessageproperty toIStreamingResponseto allow specifying a message that is sent to the user when a stream exceeds a timeout.SendStreamTimedOutNotificationmethod toIStreamingResponseto enable sending a final message to the client when a stream is cancelled, without stopping the underlying operation.Other interface changes:
citationsparameter comment from theQueueTextChunkmethod inIStreamingResponse.Build configuration:
DebugTypefor Release builds inBuild.Shared.propsfrompdbonlytoportablefor both AnyCPU and x64 platforms, improving debugging support across platforms. [1] [2]