From 6c37ce845060551479c84d5a048d0152fbf656ca Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Tue, 10 Feb 2026 10:50:41 -0500 Subject: [PATCH] Fix streaming path to include chat history in messages sent to chat client (#3798) RunCoreStreamingAsync was passing inputMessagesForProviders (which lacks chat history) to GetStreamingResponseAsync instead of inputMessagesForChatClient (which includes chat history). This caused streaming runs to lose conversation context on subsequent calls. The non-streaming path (RunCoreAsync) already correctly used inputMessagesForChatClient. This aligns the streaming path to match. Also adds a unit test that validates chat history is included in messages sent to the chat client during streaming on subsequent calls. Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: lokitoth <6936551+lokitoth@users.noreply.github.com> --- .../ChatClient/ChatClientAgent.cs | 2 +- .../ChatClient/ChatClientAgentTests.cs | 39 +++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgent.cs b/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgent.cs index 5878d877b2..37e673f710 100644 --- a/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgent.cs +++ b/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgent.cs @@ -226,7 +226,7 @@ public sealed partial class ChatClientAgent : AIAgent try { // Using the enumerator to ensure we consider the case where no updates are returned for notification. - responseUpdatesEnumerator = chatClient.GetStreamingResponseAsync(inputMessagesForProviders, chatOptions, cancellationToken).GetAsyncEnumerator(cancellationToken); + responseUpdatesEnumerator = chatClient.GetStreamingResponseAsync(inputMessagesForChatClient, chatOptions, cancellationToken).GetAsyncEnumerator(cancellationToken); } catch (Exception ex) { diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgentTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgentTests.cs index 41fb29bfed..c23e8cffaf 100644 --- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgentTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgentTests.cs @@ -1320,6 +1320,45 @@ public partial class ChatClientAgentTests mockFactory.Verify(f => f(It.IsAny(), It.IsAny()), Times.Once); } + /// + /// Verify that RunStreamingAsync includes chat history in messages sent to the chat client on subsequent calls. + /// + [Fact] + public async Task RunStreamingAsyncIncludesChatHistoryInMessagesToChatClientAsync() + { + // Arrange + List> capturedMessages = []; + Mock mockService = new(); + ChatResponseUpdate[] returnUpdates = + [ + new ChatResponseUpdate(role: ChatRole.Assistant, content: "response"), + ]; + mockService.Setup( + s => s.GetStreamingResponseAsync( + It.IsAny>(), + It.IsAny(), + It.IsAny())) + .Returns(ToAsyncEnumerableAsync(returnUpdates)) + .Callback, ChatOptions?, CancellationToken>((msgs, _, _) => capturedMessages.Add(msgs.ToList())); + ChatClientAgent agent = new(mockService.Object, options: new() + { + ChatOptions = new() { Instructions = "test instructions" }, + }); + + // Act + ChatClientAgentSession? session = await agent.CreateSessionAsync() as ChatClientAgentSession; + await agent.RunStreamingAsync([new(ChatRole.User, "first")], session).ToListAsync(); + await agent.RunStreamingAsync([new(ChatRole.User, "second")], session).ToListAsync(); + + // Assert - the second call should include chat history (first user message + first response) plus the new message + Assert.Equal(2, capturedMessages.Count); + var secondCallMessages = capturedMessages[1].ToList(); + Assert.Equal(3, secondCallMessages.Count); + Assert.Equal("first", secondCallMessages[0].Text); + Assert.Equal("response", secondCallMessages[1].Text); + Assert.Equal("second", secondCallMessages[2].Text); + } + /// /// Verify that RunStreamingAsync throws when a factory is provided and the chat client returns a conversation id. ///