Skip to content

Commit

Permalink
.Net Agents - Add incomplete status to termination check for `OpenA…
Browse files Browse the repository at this point in the history
…IAssistantAgent` (#9740)

### Motivation and Context
<!-- Thank you for your contribution to the semantic-kernel repo!
Please help reviewers and future users, providing the following
information:
  1. Why is this change required?
  2. What problem does it solve?
  3. What scenario does it contribute to?
  4. If it fixes an open issue, please link to the issue here.
-->

Fixes: #9729
Fixes: #9672

Include `incomplete` run status in termination check

### Description
<!-- Describe your changes, the overall approach, the underlying design.
These notes will help understanding how your code works. Thanks! -->

The run-status `incomplete` was added in the V2 API and indicates the
run has failed due to a status check, such as content filter or token
usage.

### Contribution Checklist
<!-- Before submitting this PR, please make sure: -->

- [X] The code builds clean without any errors or warnings
- [X] The PR follows the [SK Contribution
Guidelines](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md)
and the [pre-submission formatting
script](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md#development-scripts)
raises no violations
- [X] All unit tests pass, and I have added new tests where possible
- [X] I didn't break anyone 😄
  • Loading branch information
crickman authored Nov 18, 2024
1 parent cde12d3 commit e640bd3
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -65,5 +65,5 @@ public static RunCreationOptions GenerateOptions(OpenAIAssistantDefinition defin
=>
setting.HasValue && (!agentSetting.HasValue || !EqualityComparer<TValue>.Default.Equals(setting.Value, agentSetting.Value)) ?
setting.Value :
null;
agentSetting;
}
11 changes: 2 additions & 9 deletions dotnet/src/Agents/OpenAI/Internal/AssistantThreadActions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,6 @@ internal static class AssistantThreadActions
RunStatus.Cancelling,
];

private static readonly HashSet<RunStatus> s_terminalStatuses =
[
RunStatus.Expired,
RunStatus.Failed,
RunStatus.Cancelled,
];

/// <summary>
/// Create a new assistant thread.
/// </summary>
Expand Down Expand Up @@ -199,7 +192,7 @@ public static async IAsyncEnumerable<ChatMessageContent> GetMessagesAsync(Assist
await PollRunStatusAsync().ConfigureAwait(false);

// Is in terminal state?
if (s_terminalStatuses.Contains(run.Status))
if (run.Status.IsTerminal && run.Status != RunStatus.Completed)
{
throw new KernelException($"Agent Failure - Run terminated: {run.Status} [{run.Id}]: {run.LastError?.Message ?? "Unknown"}");
}
Expand Down Expand Up @@ -487,7 +480,7 @@ public static async IAsyncEnumerable<StreamingChatMessageContent> InvokeStreamin
}

// Is in terminal state?
if (s_terminalStatuses.Contains(run.Status))
if (run.Status.IsTerminal && run.Status != RunStatus.Completed)
{
throw new KernelException($"Agent Failure - Run terminated: {run.Status} [{run.Id}]: {run.LastError?.Message ?? "Unknown"}");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ public void AssistantRunOptionsFactoryExecutionOptionsNullTest()
Assert.NotNull(options);
Assert.Empty(options.AdditionalMessages);
Assert.Null(options.InstructionsOverride);
Assert.Null(options.Temperature);
Assert.Null(options.NucleusSamplingFactor);
Assert.Equal("test", options.AdditionalInstructions);
Assert.Equal(0.5F, options.Temperature);
Assert.Empty(options.Metadata);
}

Expand Down Expand Up @@ -69,9 +69,9 @@ public void AssistantRunOptionsFactoryExecutionOptionsEquivalentTest()

// Assert
Assert.NotNull(options);
Assert.Equal("test", options.InstructionsOverride);
Assert.Null(options.Temperature);
Assert.Null(options.NucleusSamplingFactor);
Assert.Equal("test", options.InstructionsOverride);
Assert.Equal(0.5F, options.Temperature);
}

/// <summary>
Expand Down Expand Up @@ -174,4 +174,31 @@ public void AssistantRunOptionsFactoryExecutionOptionsMessagesTest()
// Assert
Assert.Single(options.AdditionalMessages);
}

/// <summary>
/// Verify run options generation with <see cref="OpenAIAssistantInvocationOptions"/> metadata.
/// </summary>
[Fact]
public void AssistantRunOptionsFactoryExecutionOptionsMaxTokensTest()
{
// Arrange
OpenAIAssistantDefinition definition =
new("gpt-anything")
{
Temperature = 0.5F,
ExecutionOptions =
new()
{
MaxCompletionTokens = 4096,
MaxPromptTokens = 1024,
},
};

// Act
RunCreationOptions options = AssistantRunOptionsFactory.GenerateOptions(definition, null, null);

// Assert
Assert.Equal(1024, options.MaxInputTokenCount);
Assert.Equal(4096, options.MaxOutputTokenCount);
}
}
38 changes: 38 additions & 0 deletions dotnet/src/IntegrationTests/Agents/OpenAIAssistantAgentTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,44 @@ await OpenAIAssistantAgent.CreateAsync(
finally
{
await agent.DeleteThreadAsync(threadId);
await agent.DeleteAsync();
}
}

/// <summary>
/// Integration test for <see cref="OpenAIAssistantAgent"/> using function calling
/// and targeting Azure OpenAI services.
/// </summary>
[RetryFact(typeof(HttpOperationException))]
public async Task AzureOpenAIAssistantAgentTokensAsync()
{
var azureOpenAIConfiguration = this._configuration.GetSection("AzureOpenAI").Get<AzureOpenAIConfiguration>();
Assert.NotNull(azureOpenAIConfiguration);

OpenAIAssistantAgent agent =
await OpenAIAssistantAgent.CreateAsync(
OpenAIClientProvider.ForAzureOpenAI(new AzureCliCredential(), new Uri(azureOpenAIConfiguration.Endpoint)),
new(azureOpenAIConfiguration.ChatDeploymentName!)
{
Instructions = "Repeat the user all of the user messages",
ExecutionOptions = new()
{
MaxCompletionTokens = 16,
}
},
new Kernel());

string threadId = await agent.CreateThreadAsync();
ChatMessageContent functionResultMessage = new(AuthorRole.User, "A long time ago there lived a king who was famed for his wisdom through all the land. Nothing was hidden from him, and it seemed as if news of the most secret things was brought to him through the air. But he had a strange custom; every day after dinner, when the table was cleared, and no one else was present, a trusty servant had to bring him one more dish. It was covered, however, and even the servant did not know what was in it, neither did anyone know, for the king never took off the cover to eat of it until he was quite alone.");
try
{
await agent.AddChatMessageAsync(threadId, functionResultMessage);
await Assert.ThrowsAsync<KernelException>(() => agent.InvokeAsync(threadId).ToArrayAsync().AsTask());
}
finally
{
await agent.DeleteThreadAsync(threadId);
await agent.DeleteAsync();
}
}

Expand Down

0 comments on commit e640bd3

Please sign in to comment.