// Copyright (c) Microsoft. All rights reserved.
using System.Text;
using Azure.AI.Agents.Persistent;
using GettingStarted.Tools.Abstractions;
using GettingStarted.Tools.Extensions;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.AI.Agents;
using OpenAI;
using OpenAI.Files;
#pragma warning disable OPENAI001
namespace GettingStarted.Tools;
public sealed class CodeInterpreterTools(ITestOutputHelper output) : AgentSample(output)
{
[Theory]
[InlineData(ChatClientProviders.OpenAIAssistant)]
[InlineData(ChatClientProviders.AzureAIAgentsPersistent)]
public async Task RunningWithFileReferenceAsync(ChatClientProviders provider)
{
var fileId = await UploadTestFileAsync(provider);
var chatOptions = new ChatOptions()
{
Tools = [new NewHostedCodeInterpreterTool { FileIds = [fileId] }]
};
var agentOptions = new ChatClientAgentOptions
{
Name = "HelpfulAssistant",
Instructions = "You are a helpful assistant.",
// Transformation is required until the abstraction will be added to either SDK provider or M.E.AI and
// implementations will handle new properties/classes.
ChatOptions = TransformChatOptions(chatOptions, provider)
};
using var chatClient = await base.GetChatClientAsync(provider, agentOptions);
ChatClientAgent agent = new(chatClient, agentOptions);
var thread = agent.GetNewThread();
// Prompt which allows to verify that the data was processed from file correctly and current datetime is returned.
const string Prompt = "Calculate the total number of items, identify the most frequently puchased item and return the result with today's datetime.";
var assistantOutput = new StringBuilder();
var codeInterpreterOutput = new StringBuilder();
await foreach (var update in agent.RunStreamingAsync(Prompt, thread))
{
if (!string.IsNullOrWhiteSpace(update.Text))
{
assistantOutput.Append(update.Text);
}
else if (update.RawRepresentation is not null)
{
ProcessRawRepresentationOutput(update.RawRepresentation, codeInterpreterOutput, provider);
}
}
Console.WriteLine("Assistant Output:");
Console.WriteLine(assistantOutput.ToString());
Console.WriteLine("Code interpreter Output:");
Console.WriteLine(codeInterpreterOutput.ToString());
}
#region private
///
/// This method creates a raw representation of tools from newly proposed abstractions, so underlying SDKs can work with it.
/// Once the tool abstraction is added to either SDK provider or M.E.AI, this method can be removed.
/// The logic under each provider case should go to related SDK.
///
private static ChatOptions TransformChatOptions(ChatOptions chatOptions, ChatClientProviders provider)
{
return provider switch
{
ChatClientProviders.OpenAIAssistant => chatOptions.ToOpenAIAssistantChatOptions(),
ChatClientProviders.AzureAIAgentsPersistent => chatOptions.ToAzureAIPersistentAgentChatOptions(),
_ => chatOptions
};
}
private Task UploadTestFileAsync(ChatClientProviders provider)
{
var filePath = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, "Tools", "Files", "groceries.txt"));
return UploadFileAsync(filePath, provider);
}
private async Task UploadFileAsync(string filePath, ChatClientProviders provider)
{
switch (provider)
{
case ChatClientProviders.OpenAIAssistant:
var fileClient = GetOpenAIFileClient();
OpenAIFile openAIFileInfo = await fileClient.UploadFileAsync(filePath, FileUploadPurpose.Assistants);
return openAIFileInfo.Id;
case ChatClientProviders.AzureAIAgentsPersistent:
PersistentAgentFileInfo persistentAgentFileInfo = await AzureAIPersistentAgentsClient.Files.UploadFileAsync(filePath, PersistentAgentFilePurpose.Agents);
return persistentAgentFileInfo.Id;
default:
throw new NotSupportedException($"Client provider {provider} is not supported.");
}
}
private static void ProcessRawRepresentationOutput(object rawRepresentation, StringBuilder builder, ChatClientProviders provider)
{
switch (provider)
{
case ChatClientProviders.OpenAIAssistant:
if (rawRepresentation is OpenAI.Assistants.RunStepDetailsUpdate openAIStepDetailsUpdate)
{
builder.Append(openAIStepDetailsUpdate.CodeInterpreterInput);
builder.Append(string.Join(string.Empty, openAIStepDetailsUpdate.CodeInterpreterOutputs.SelectMany(l => l.Logs)));
}
break;
case ChatClientProviders.AzureAIAgentsPersistent:
if (rawRepresentation is Azure.AI.Agents.Persistent.RunStepDetailsUpdate persistentAgentStepDetailsUpdate)
{
builder.Append(persistentAgentStepDetailsUpdate.CodeInterpreterInput);
builder.Append(string.Join(string.Empty, persistentAgentStepDetailsUpdate
.CodeInterpreterOutputs
.OfType().SelectMany(l => l.Logs)));
}
break;
}
}
private OpenAIFileClient GetOpenAIFileClient() => OpenAIClient.GetOpenAIFileClient();
#endregion
}