Skip to content

Commit

Permalink
[C#] fix: ignore redundant action when submitting tool outputs (#927)
Browse files Browse the repository at this point in the history
## Linked issues

closes: #925

## Details

Fix `KeyNotFound` exception when executed actions are more than
assistant required actions.

#### Change details

- Only submit required actions and ignore others
- UT added

## Attestation Checklist

- [x] My code follows the style guidelines of this project

- I have checked for/fixed spelling, linting, and other errors
- I have commented my code for clarity
- I have made corresponding changes to the documentation (we use
[TypeDoc](https://typedoc.org/) to document our code)
- My changes generate no new warnings
- I have added tests that validates my changes, and provides sufficient
test coverage. I have tested with:
  - Local testing
  - E2E testing in Teams
- New and existing unit tests pass locally with my changes

### Additional information

> Feel free to add other relevant information below
  • Loading branch information
swatDong authored Nov 29, 2023
1 parent 9235231 commit 7e028a1
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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<AssistantsState>(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<ITurnContext>();
var turnState = await _CreateAssistantsState();
turnState.Temp!.Input = "hello";
turnState.Temp.ActionOutputs["other-action"] = "should not be used";

var aiOptions = new AIOptions<AssistantsState>(planner);
var ai = new AI<AssistantsState>(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()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,12 +230,12 @@ private async Task<Plan> _SubmitActionResultsAsync(TState state, CancellationTok
// Map the action outputs to tool outputs
List<ToolOutput> toolOutputs = new();
Dictionary<string, string> toolMap = state.SubmitToolMap;
foreach (KeyValuePair<string, string> action in state.Temp!.ActionOutputs)
foreach (KeyValuePair<string, string> 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
});
}

Expand Down

0 comments on commit 7e028a1

Please sign in to comment.