Skip to content

Commit e640bd3

Browse files
authored
.Net Agents - Add incomplete status to termination check for OpenAIAssistantAgent (#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 😄
1 parent cde12d3 commit e640bd3

File tree

4 files changed

+71
-13
lines changed

4 files changed

+71
-13
lines changed

dotnet/src/Agents/OpenAI/Internal/AssistantRunOptionsFactory.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,5 +65,5 @@ public static RunCreationOptions GenerateOptions(OpenAIAssistantDefinition defin
6565
=>
6666
setting.HasValue && (!agentSetting.HasValue || !EqualityComparer<TValue>.Default.Equals(setting.Value, agentSetting.Value)) ?
6767
setting.Value :
68-
null;
68+
agentSetting;
6969
}

dotnet/src/Agents/OpenAI/Internal/AssistantThreadActions.cs

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,6 @@ internal static class AssistantThreadActions
2828
RunStatus.Cancelling,
2929
];
3030

31-
private static readonly HashSet<RunStatus> s_terminalStatuses =
32-
[
33-
RunStatus.Expired,
34-
RunStatus.Failed,
35-
RunStatus.Cancelled,
36-
];
37-
3831
/// <summary>
3932
/// Create a new assistant thread.
4033
/// </summary>
@@ -199,7 +192,7 @@ public static async IAsyncEnumerable<ChatMessageContent> GetMessagesAsync(Assist
199192
await PollRunStatusAsync().ConfigureAwait(false);
200193

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

489482
// Is in terminal state?
490-
if (s_terminalStatuses.Contains(run.Status))
483+
if (run.Status.IsTerminal && run.Status != RunStatus.Completed)
491484
{
492485
throw new KernelException($"Agent Failure - Run terminated: {run.Status} [{run.Id}]: {run.LastError?.Message ?? "Unknown"}");
493486
}

dotnet/src/Agents/UnitTests/OpenAI/Internal/AssistantRunOptionsFactoryTests.cs

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ public void AssistantRunOptionsFactoryExecutionOptionsNullTest()
3939
Assert.NotNull(options);
4040
Assert.Empty(options.AdditionalMessages);
4141
Assert.Null(options.InstructionsOverride);
42-
Assert.Null(options.Temperature);
4342
Assert.Null(options.NucleusSamplingFactor);
4443
Assert.Equal("test", options.AdditionalInstructions);
44+
Assert.Equal(0.5F, options.Temperature);
4545
Assert.Empty(options.Metadata);
4646
}
4747

@@ -69,9 +69,9 @@ public void AssistantRunOptionsFactoryExecutionOptionsEquivalentTest()
6969

7070
// Assert
7171
Assert.NotNull(options);
72-
Assert.Equal("test", options.InstructionsOverride);
73-
Assert.Null(options.Temperature);
7472
Assert.Null(options.NucleusSamplingFactor);
73+
Assert.Equal("test", options.InstructionsOverride);
74+
Assert.Equal(0.5F, options.Temperature);
7575
}
7676

7777
/// <summary>
@@ -174,4 +174,31 @@ public void AssistantRunOptionsFactoryExecutionOptionsMessagesTest()
174174
// Assert
175175
Assert.Single(options.AdditionalMessages);
176176
}
177+
178+
/// <summary>
179+
/// Verify run options generation with <see cref="OpenAIAssistantInvocationOptions"/> metadata.
180+
/// </summary>
181+
[Fact]
182+
public void AssistantRunOptionsFactoryExecutionOptionsMaxTokensTest()
183+
{
184+
// Arrange
185+
OpenAIAssistantDefinition definition =
186+
new("gpt-anything")
187+
{
188+
Temperature = 0.5F,
189+
ExecutionOptions =
190+
new()
191+
{
192+
MaxCompletionTokens = 4096,
193+
MaxPromptTokens = 1024,
194+
},
195+
};
196+
197+
// Act
198+
RunCreationOptions options = AssistantRunOptionsFactory.GenerateOptions(definition, null, null);
199+
200+
// Assert
201+
Assert.Equal(1024, options.MaxInputTokenCount);
202+
Assert.Equal(4096, options.MaxOutputTokenCount);
203+
}
177204
}

dotnet/src/IntegrationTests/Agents/OpenAIAssistantAgentTests.cs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,44 @@ await OpenAIAssistantAgent.CreateAsync(
127127
finally
128128
{
129129
await agent.DeleteThreadAsync(threadId);
130+
await agent.DeleteAsync();
131+
}
132+
}
133+
134+
/// <summary>
135+
/// Integration test for <see cref="OpenAIAssistantAgent"/> using function calling
136+
/// and targeting Azure OpenAI services.
137+
/// </summary>
138+
[RetryFact(typeof(HttpOperationException))]
139+
public async Task AzureOpenAIAssistantAgentTokensAsync()
140+
{
141+
var azureOpenAIConfiguration = this._configuration.GetSection("AzureOpenAI").Get<AzureOpenAIConfiguration>();
142+
Assert.NotNull(azureOpenAIConfiguration);
143+
144+
OpenAIAssistantAgent agent =
145+
await OpenAIAssistantAgent.CreateAsync(
146+
OpenAIClientProvider.ForAzureOpenAI(new AzureCliCredential(), new Uri(azureOpenAIConfiguration.Endpoint)),
147+
new(azureOpenAIConfiguration.ChatDeploymentName!)
148+
{
149+
Instructions = "Repeat the user all of the user messages",
150+
ExecutionOptions = new()
151+
{
152+
MaxCompletionTokens = 16,
153+
}
154+
},
155+
new Kernel());
156+
157+
string threadId = await agent.CreateThreadAsync();
158+
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.");
159+
try
160+
{
161+
await agent.AddChatMessageAsync(threadId, functionResultMessage);
162+
await Assert.ThrowsAsync<KernelException>(() => agent.InvokeAsync(threadId).ToArrayAsync().AsTask());
163+
}
164+
finally
165+
{
166+
await agent.DeleteThreadAsync(threadId);
167+
await agent.DeleteAsync();
130168
}
131169
}
132170

0 commit comments

Comments
 (0)