diff --git a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/AITests/AssistantsPlannerTests.cs b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/AITests/AssistantsPlannerTests.cs index 5a7249734..9bce95a2a 100644 --- a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/AITests/AssistantsPlannerTests.cs +++ b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/AITests/AssistantsPlannerTests.cs @@ -262,6 +262,69 @@ public async Task Test_ContinueTaskAsync_Assistant_RequiresAction() Assert.Equal("test-tool-id", turnState.SubmitToolMap.First().Value); } + [Fact] + public async Task Test_ContinueTaskAsync_Assistant_IgnoreRedundantAction() + { + // Arrange + var testClient = new TestAssistantsOpenAIClient(); + var planner = new AssistantsPlanner(new("test-key", "test-assistant-id") + { + PollingInterval = TimeSpan.FromMilliseconds(100) + }); + planner.GetType().GetField("_openAIClient", BindingFlags.Instance | BindingFlags.NonPublic)!.SetValue(planner, testClient); + var turnContextMock = new Mock(); + var turnState = await _CreateAssistantsState(); + turnState.Temp!.Input = "hello"; + turnState.Temp.ActionOutputs["other-action"] = "should not be used"; + + var aiOptions = new AIOptions(planner); + var ai = new AI(aiOptions); + + testClient.RemainingActions.Enqueue(new() + { + SubmitToolOutputs = new() + { + ToolCalls = new() + { + new() + { + Id = "test-tool-id", + Function = new() + { + Name = "test-action", + Arguments = "{}" + } + } + } + } + }); + testClient.RemainingRunStatus.Enqueue("requires_action"); + testClient.RemainingRunStatus.Enqueue("in_progress"); + testClient.RemainingRunStatus.Enqueue("completed"); + testClient.RemainingMessages.Enqueue("welcome"); + + // Act + var plan1 = await planner.ContinueTaskAsync(turnContextMock.Object, turnState, ai, CancellationToken.None); + turnState.Temp.ActionOutputs["test-action"] = "test-output"; + var plan2 = await planner.ContinueTaskAsync(turnContextMock.Object, turnState, ai, CancellationToken.None); + + // Assert + Assert.NotNull(plan1); + Assert.NotNull(plan1.Commands); + Assert.Single(plan1.Commands); + Assert.Equal(AIConstants.DoCommand, plan1.Commands[0].Type); + Assert.Equal("test-action", ((PredictedDoCommand)plan1.Commands[0]).Action); + Assert.NotNull(plan2); + Assert.NotNull(plan2.Commands); + Assert.Single(plan2.Commands); + Assert.Equal(AIConstants.SayCommand, plan2.Commands[0].Type); + Assert.Equal("welcome", ((PredictedSayCommand)plan2.Commands[0]).Response); + Assert.Single(turnState.SubmitToolMap); + Assert.Equal("test-action", turnState.SubmitToolMap.First().Key); + Assert.Equal("test-tool-id", turnState.SubmitToolMap.First().Value); + } + + [Fact] public async Task Test_ContinueTaskAsync_Assistant_MultipleMessages() { diff --git a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/AI/Planners/AssistantsPlanner.cs b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/AI/Planners/AssistantsPlanner.cs index da189996a..4f73383f7 100644 --- a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/AI/Planners/AssistantsPlanner.cs +++ b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/AI/Planners/AssistantsPlanner.cs @@ -230,12 +230,12 @@ private async Task _SubmitActionResultsAsync(TState state, CancellationTok // Map the action outputs to tool outputs List toolOutputs = new(); Dictionary toolMap = state.SubmitToolMap; - foreach (KeyValuePair action in state.Temp!.ActionOutputs) + foreach (KeyValuePair requiredAction in toolMap) { toolOutputs.Add(new() { - ToolCallId = toolMap[action.Key], - Output = action.Value + ToolCallId = requiredAction.Value, + Output = state.Temp!.ActionOutputs.ContainsKey(requiredAction.Key) ? state.Temp!.ActionOutputs[requiredAction.Key] : string.Empty }); }