Skip to content

Commit f8d7a7e

Browse files
authored
.Net Agents - Protect against null-reference for Assistant file-search result when streaming (#9742)
### 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: #9723 Must distinguish between file-search result and function-call result for assistant streaming without triggering `NullReferenceException` ### Description <!-- Describe your changes, the overall approach, the underlying design. These notes will help understanding how your code works. Thanks! --> A file-search result provide any streaming result; rather, it results in an annotation on the assistant message. Still it must be distinguished from a function-call result. Since `RunStepDetailsUpdate.FunctionName` property throws a `NullReferenceException` when being evaluated, using `RunStepDetailsUpdate.FunctionResult` property. Merged fix to Open AI SDK: openai/openai-dotnet#293 ### 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 c436624 commit f8d7a7e

File tree

4 files changed

+56
-7
lines changed

4 files changed

+56
-7
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,7 @@ public static async IAsyncEnumerable<StreamingChatMessageContent> InvokeStreamin
451451
{
452452
yield return toolContent;
453453
}
454-
else
454+
else if (detailsUpdate.FunctionOutput != null)
455455
{
456456
yield return
457457
new StreamingChatMessageContent(AuthorRole.Assistant, null)

dotnet/src/IntegrationTests/Agents/OpenAIAssistantAgentTests.cs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System;
33
using System.ClientModel;
44
using System.ComponentModel;
5+
using System.IO;
56
using System.Linq;
67
using System.Text;
78
using System.Threading.Tasks;
@@ -11,6 +12,8 @@
1112
using Microsoft.SemanticKernel.Agents;
1213
using Microsoft.SemanticKernel.Agents.OpenAI;
1314
using Microsoft.SemanticKernel.ChatCompletion;
15+
using OpenAI.Files;
16+
using OpenAI.VectorStores;
1417
using SemanticKernel.IntegrationTests.TestSettings;
1518
using xRetry;
1619
using Xunit;
@@ -216,6 +219,56 @@ await OpenAIAssistantAgent.CreateAsync(
216219
finally
217220
{
218221
await agent.DeleteThreadAsync(threadId);
222+
await agent.DeleteAsync();
223+
}
224+
}
225+
226+
/// <summary>
227+
/// Integration test for <see cref="OpenAIAssistantAgent"/> using function calling
228+
/// and targeting Open AI services.
229+
/// </summary>
230+
[Fact]
231+
public async Task AzureOpenAIAssistantAgentStreamingFileSearchAsync()
232+
{
233+
var azureOpenAIConfiguration = this._configuration.GetSection("AzureOpenAI").Get<AzureOpenAIConfiguration>();
234+
Assert.NotNull(azureOpenAIConfiguration);
235+
236+
OpenAIClientProvider provider = OpenAIClientProvider.ForAzureOpenAI(new AzureCliCredential(), new Uri(azureOpenAIConfiguration.Endpoint));
237+
OpenAIAssistantAgent agent =
238+
await OpenAIAssistantAgent.CreateAsync(
239+
provider,
240+
new(azureOpenAIConfiguration.ChatDeploymentName!),
241+
new Kernel());
242+
243+
// Upload file - Using a table of fictional employees.
244+
OpenAIFileClient fileClient = provider.Client.GetOpenAIFileClient();
245+
await using Stream stream = File.OpenRead("TestData/employees.pdf")!;
246+
OpenAIFile fileInfo = await fileClient.UploadFileAsync(stream, "employees.pdf", FileUploadPurpose.Assistants);
247+
248+
// Create a vector-store
249+
VectorStoreClient vectorStoreClient = provider.Client.GetVectorStoreClient();
250+
CreateVectorStoreOperation result =
251+
await vectorStoreClient.CreateVectorStoreAsync(waitUntilCompleted: false,
252+
new VectorStoreCreationOptions()
253+
{
254+
FileIds = { fileInfo.Id }
255+
});
256+
257+
string threadId = await agent.CreateThreadAsync();
258+
try
259+
{
260+
await agent.AddChatMessageAsync(threadId, new(AuthorRole.User, "Who works in sales?"));
261+
ChatHistory messages = [];
262+
var chunks = await agent.InvokeStreamingAsync(threadId, messages: messages).ToArrayAsync();
263+
Assert.NotEmpty(chunks);
264+
Assert.Single(messages);
265+
}
266+
finally
267+
{
268+
await agent.DeleteThreadAsync(threadId);
269+
await agent.DeleteAsync();
270+
await vectorStoreClient.DeleteVectorStoreAsync(result.VectorStoreId);
271+
await fileClient.DeleteFileAsync(fileInfo.Id);
219272
}
220273
}
221274

dotnet/src/IntegrationTests/IntegrationTests.csproj

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
33
<AssemblyName>IntegrationTests</AssemblyName>
44
<RootNamespace>SemanticKernel.IntegrationTests</RootNamespace>
55
<TargetFramework>net8.0</TargetFramework>
66
<IsTestProject>true</IsTestProject>
77
<IsPackable>false</IsPackable>
8-
<NoWarn>$(NoWarn);CA2007,CA1861,VSTHRD111,SKEXP0001,SKEXP0010,SKEXP0020,SKEXP0040,SKEXP0050,SKEXP0060,SKEXP0070,SKEXP0080,SKEXP0110</NoWarn>
8+
<NoWarn>$(NoWarn);CA2007,CA1861,VSTHRD111,SKEXP0001,SKEXP0010,SKEXP0020,SKEXP0040,SKEXP0050,SKEXP0060,SKEXP0070,SKEXP0080,SKEXP0110,OPENAI001</NoWarn>
99
<UserSecretsId>b7762d10-e29b-4bb1-8b74-b6d69a667dd4</UserSecretsId>
1010
</PropertyGroup>
1111
<ItemGroup>
@@ -39,7 +39,6 @@
3939
<None Remove="prompts\GenerateStoryHandlebars.yaml" />
4040
<None Remove="skills\FunSkill\Joke\config.json" />
4141
<None Remove="skills\FunSkill\Joke\skprompt.txt" />
42-
<None Remove="TestData\semantic-kernel-info.txt" />
4342
</ItemGroup>
4443
<ItemGroup>
4544
<PackageReference Include="Microsoft.Extensions.Configuration" />
@@ -179,9 +178,6 @@
179178
<EmbeddedResource Include="skills\FunSkill\Joke\skprompt.txt">
180179
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
181180
</EmbeddedResource>
182-
<EmbeddedResource Include="TestData\semantic-kernel-info.txt">
183-
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
184-
</EmbeddedResource>
185181
</ItemGroup>
186182

187183
<ItemGroup>
42.4 KB
Binary file not shown.

0 commit comments

Comments
 (0)