From 335d59841e2d9b39fe26be05a3ab783c59f5006c Mon Sep 17 00:00:00 2001 From: Peter Ibekwe Date: Mon, 9 Mar 2026 22:58:36 -0700 Subject: [PATCH] Fixed external response de-dup and updated possible brittle test. --- .../WorkflowSession.cs | 65 ++++++++----------- .../WorkflowHostSmokeTests.cs | 2 +- 2 files changed, 27 insertions(+), 40 deletions(-) diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows/WorkflowSession.cs b/dotnet/src/Microsoft.Agents.AI.Workflows/WorkflowSession.cs index f08f0c85a3..c26c9b1985 100644 --- a/dotnet/src/Microsoft.Agents.AI.Workflows/WorkflowSession.cs +++ b/dotnet/src/Microsoft.Agents.AI.Workflows/WorkflowSession.cs @@ -204,10 +204,35 @@ internal sealed class WorkflowSession : AgentSession List<(ExternalResponse Response, string? ContentId)> externalResponses = []; bool hasMatchedExternalResponses = false; + // Tracks content IDs already matched to pending requests within this invocation, + // preventing duplicate responses for the same ID from being sent to the workflow engine. + HashSet? matchedContentIds = null; + foreach (ChatMessage message in messages) { List regularContents = []; - PartitionMessageContents(message, regularContents); + + foreach (AIContent content in message.Contents) + { + string? contentId = GetResponseContentId(content); + + // Skip duplicate response content for an already-matched content ID + if (contentId != null && matchedContentIds?.Contains(contentId) == true) + { + continue; + } + + if (contentId != null + && this.TryGetPendingRequest(contentId) is ExternalRequest pendingRequest) + { + externalResponses.Add((pendingRequest.CreateResponse(content), contentId)); + (matchedContentIds ??= new(StringComparer.OrdinalIgnoreCase)).Add(contentId); + } + else + { + regularContents.Add(content); + } + } if (regularContents.Count > 0) { @@ -236,44 +261,6 @@ internal sealed class WorkflowSession : AgentSession } return hasMatchedExternalResponses; - - void PartitionMessageContents(ChatMessage message, List regularContents) - { - foreach (AIContent content in message.Contents) - { - string? contentId = GetResponseContentId(content); - if (this.TryCreateExternalResponse(content) is ExternalResponse response) - { - externalResponses.Add((response, contentId)); - } - else - { - regularContents.Add(content); - } - } - } - } - - /// - /// Attempts to create an ExternalResponse from response content (FunctionResultContent or UserInputResponseContent) - /// by matching it to a pending request. - /// - private ExternalResponse? TryCreateExternalResponse(AIContent content) - { - string? contentId = GetResponseContentId(content); - if (contentId == null) - { - return null; - } - - ExternalRequest? pendingRequest = this.TryGetPendingRequest(contentId); - if (pendingRequest == null) - { - return null; - } - - // Create ExternalResponse via the pending request to ensure proper validation and wrapping - return pendingRequest.CreateResponse(content); } /// diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/WorkflowHostSmokeTests.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/WorkflowHostSmokeTests.cs index bd35e9825d..1ee9f748a8 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/WorkflowHostSmokeTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/WorkflowHostSmokeTests.cs @@ -440,7 +440,7 @@ public class WorkflowHostSmokeTests session).ToListAsync(); int functionCallCount = secondCallUpdates - .Where(u => u.RawRepresentation?.GetType().Name == "RequestInfoEvent") + .Where(u => u.RawRepresentation is RequestInfoEvent) .SelectMany(u => u.Contents.OfType()) .Count(c => c.CallId == CallId);