.Net: FileSearch Tool Sample + API Proposal Updates for MEAI.Abstractions/Azure.AI.Agents (#210)

* Wip, bringing persistent chat client back

* Bring back code interpreter resource logic

* Add file search samples and proposal for new OpenAIAssistantChatClient and AgentPersistantChatCLient

* Update dotnet/samples/GettingStarted/External/MEAI.Abstractions/NewHostedFileSearchTool.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update dotnet/samples/GettingStarted/Steps/Step04_ChatClientAgent_UsingFileSearchTools.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update dotnet/samples/GettingStarted/External/Azure.AI.Agents.Persistent/NewPersistentAgentsChatClient.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update MEAI abstractions to reflect latest discussions, updated underlying chatclients

* Remove unneeded comments

* Address PR feedback

* Address PR feedback

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Roger Barreto
2025-07-29 10:41:35 +00:00
committed by GitHub
co-authored by Copilot
parent dff700e469
commit 02814a4fc0
10 changed files with 881 additions and 39 deletions
@@ -3,9 +3,9 @@
using System.Text;
using Azure.AI.Agents.Persistent;
using Azure.Identity;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.AI.Agents;
using Microsoft.Shared.Samples;
using OpenAI.Assistants;
using OpenAI.Files;
namespace Steps;
@@ -21,8 +21,10 @@ public sealed class Step03_ChatClientAgent_UsingCodeInterpreterTools(ITestOutput
[InlineData(ChatClientProviders.OpenAIAssistant)]
public async Task RunningWithFileReferenceAsync(ChatClientProviders provider)
{
var codeInterpreterTool = new NewHostedCodeInterpreterTool();
codeInterpreterTool.FileIds.Add(await UploadFileAsync("Resources/groceries.txt", provider));
var codeInterpreterTool = new NewHostedCodeInterpreterTool()
{
Inputs = [new HostedFileContent(await UploadFileAsync("Resources/groceries.txt", provider))]
};
var agentOptions = new ChatClientAgentOptions(
name: "HelpfulAssistant",
@@ -51,9 +53,9 @@ public sealed class Step03_ChatClientAgent_UsingCodeInterpreterTools(ITestOutput
assistantOutput.Append(update.Text);
}
if (update.RawRepresentation is not null)
if (update.RawRepresentation is ChatResponseUpdate chatUpdate && chatUpdate.RawRepresentation is not null)
{
codeInterpreterOutput.Append(GetCodeInterpreterOutput(update.RawRepresentation, provider));
codeInterpreterOutput.Append(GetCodeInterpreterOutput(chatUpdate.RawRepresentation, provider));
}
}
@@ -0,0 +1,136 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Text;
using Azure.AI.Agents.Persistent;
using Azure.Identity;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.AI.Agents;
using Microsoft.Shared.Samples;
using OpenAI.Files;
using OpenAI.VectorStores;
namespace Steps;
/// <summary>
/// Demonstrates how to use <see cref="ChatClientAgent"/> with file search tools and file references.
/// Shows uploading files to different providers and using them with file search capabilities to retrieve and analyze information from documents.
/// </summary>
public sealed class Step04_ChatClientAgent_UsingFileSearchTools(ITestOutputHelper output) : AgentSample(output)
{
[Theory]
[InlineData(ChatClientProviders.AzureAIAgentsPersistent)]
[InlineData(ChatClientProviders.OpenAIAssistant)]
public async Task RunningWithFileReferenceAsync(ChatClientProviders provider)
{
// Upload a file to the specified provider.
var fileId = await UploadFileAsync("Resources/employees.pdf", provider);
// Create a vector store for the uploaded file to enable file search capabilities.
var vectorStoreId = await CreateVectorStoreAsync([fileId], provider);
// Create a file search tool that can access the vector store.
var fileSearchTool = new NewHostedFileSearchTool()
{
Inputs = [new HostedVectorStoreContent(vectorStoreId)],
};
var agentOptions = new ChatClientAgentOptions
{
Name = "FileSearchAssistant",
Instructions = "You are a helpful assistant that can search through uploaded documents to answer questions. Use the file search tool to find relevant information from the uploaded files.",
ChatOptions = new() { Tools = [fileSearchTool] }
};
// Create the server-side agent Id when applicable (depending on the provider).
agentOptions.Id = await base.AgentCreateAsync(provider, agentOptions);
using var chatClient = base.GetChatClient(provider, agentOptions);
ChatClientAgent agent = new(chatClient, agentOptions);
var thread = agent.GetNewThread();
// Prompt which allows to verify that the file search functionality works correctly with the uploaded document.
const string Prompt = "Who is the youngest employee?";
var assistantOutput = new StringBuilder();
await foreach (var update in agent.RunStreamingAsync(Prompt, thread))
{
if (!string.IsNullOrWhiteSpace(update.Text))
{
assistantOutput.Append(update.Text);
}
}
Console.WriteLine("Assistant Output:");
Console.WriteLine(assistantOutput.ToString());
// Clean up the server-side agent after use when applicable (depending on the provider).
await base.AgentCleanUpAsync(provider, agent, thread);
}
#region private
/// <summary>
/// Uploads a file to the specified chat client provider and returns the file ID.
/// </summary>
/// <param name="filePath">Path to the file to be uploaded.</param>
/// <param name="provider">The chat client provider to use for uploading the file.</param>
/// <returns>The ID of the uploaded file.</returns>
/// <exception cref="NotSupportedException"></exception>
private async Task<string> UploadFileAsync(string filePath, ChatClientProviders provider)
{
switch (provider)
{
case ChatClientProviders.OpenAIAssistant:
var fileClient = new OpenAIFileClient(TestConfiguration.OpenAI.ApiKey);
OpenAIFile openAIFileInfo = await fileClient.UploadFileAsync(filePath, FileUploadPurpose.Assistants);
return openAIFileInfo.Id;
case ChatClientProviders.AzureAIAgentsPersistent:
var persistentAgentsClient = new PersistentAgentsClient(TestConfiguration.AzureAI.Endpoint, new AzureCliCredential());
PersistentAgentFileInfo persistentAgentFileInfo = await persistentAgentsClient.Files.UploadFileAsync(filePath, PersistentAgentFilePurpose.Agents);
return persistentAgentFileInfo.Id;
default:
throw new NotSupportedException($"Client provider {provider} is not supported.");
}
}
private Task<string> CreateVectorStoreAsync(IEnumerable<string> fileIds, ChatClientProviders provider)
{
switch (provider)
{
case ChatClientProviders.OpenAIAssistant:
return CreateVectorStoreOpenAIAssistantAsync(fileIds);
case ChatClientProviders.AzureAIAgentsPersistent:
return CreateVectorStoreAzureAIAgentsPersistentAsync(fileIds);
default:
throw new NotSupportedException($"Client provider {provider} is not supported.");
}
}
private async Task<string> CreateVectorStoreOpenAIAssistantAsync(IEnumerable<string> fileIds)
{
var vectorStoreClient = new VectorStoreClient(TestConfiguration.OpenAI.ApiKey);
VectorStoreCreationOptions options = new();
foreach (var fileId in fileIds)
{
options.FileIds.Add(fileId);
}
var vectorStore = await vectorStoreClient.CreateVectorStoreAsync(waitUntilCompleted: true, options);
return vectorStore.VectorStoreId;
}
private async Task<string> CreateVectorStoreAzureAIAgentsPersistentAsync(IEnumerable<string> fileIds)
{
var client = new PersistentAgentsClient(TestConfiguration.AzureAI.Endpoint, new AzureCliCredential());
var vectorStore = await client.VectorStores.CreateVectorStoreAsync(fileIds);
return vectorStore.Value.Id;
}
#endregion
}