// Copyright (c) Microsoft. All rights reserved.
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Logging;
using Microsoft.Shared.Diagnostics;
using OpenAI.Assistants;
namespace OpenAI;
///
/// Provides extension methods for OpenAI
/// to simplify the creation of AI agents that work with OpenAI services.
///
///
/// These extensions bridge the gap between OpenAI SDK client objects and the Microsoft Extensions AI Agent framework,
/// allowing developers to easily create AI agents that leverage OpenAI's chat completion and response services.
/// The methods handle the conversion from OpenAI clients to instances and then wrap them
/// in objects that implement the interface.
///
public static class OpenAIAssistantClientExtensions
{
///
/// Retrieves an existing server side agent, wrapped as a using the provided .
///
/// The to create the with.
/// The ID of the server side agent to create a for.
/// Options that should apply to all runs of the agent.
/// The to monitor for cancellation requests. The default is .
/// A instance that can be used to perform operations on the assistant agent.
public static ChatClientAgent GetAIAgent(
this AssistantClient assistantClient,
string agentId,
ChatOptions? chatOptions = null,
CancellationToken cancellationToken = default)
{
if (assistantClient is null)
{
throw new ArgumentNullException(nameof(assistantClient));
}
if (string.IsNullOrWhiteSpace(agentId))
{
throw new ArgumentException($"{nameof(agentId)} should not be null or whitespace.", nameof(agentId));
}
var assistant = assistantClient.GetAssistant(agentId, cancellationToken);
return assistant.AsAIAgent(assistantClient, chatOptions);
}
///
/// Retrieves an existing server side agent, wrapped as a using the provided .
///
/// The to create the with.
/// The ID of the server side agent to create a for.
/// Options that should apply to all runs of the agent.
/// The to monitor for cancellation requests. The default is .
/// A instance that can be used to perform operations on the assistant agent.
public static async Task GetAIAgentAsync(
this AssistantClient assistantClient,
string agentId,
ChatOptions? chatOptions = null,
CancellationToken cancellationToken = default)
{
if (assistantClient is null)
{
throw new ArgumentNullException(nameof(assistantClient));
}
if (string.IsNullOrWhiteSpace(agentId))
{
throw new ArgumentException($"{nameof(agentId)} should not be null or whitespace.", nameof(agentId));
}
var assistanceResponse = await assistantClient.GetAssistantAsync(agentId, cancellationToken).ConfigureAwait(false);
return assistanceResponse.AsAIAgent(assistantClient, chatOptions);
}
///
/// Creates an AI agent from an using the OpenAI Assistant API.
///
/// The OpenAI to use for the agent.
/// The model identifier to use (e.g., "gpt-4").
/// Optional system instructions that define the agent's behavior and personality.
/// Optional name for the agent for identification purposes.
/// Optional description of the agent's capabilities and purpose.
/// Optional collection of AI tools that the agent can use during conversations.
/// Optional logger factory for enabling logging within the agent.
/// An instance backed by the OpenAI Assistant service.
/// Thrown when or is .
/// Thrown when is empty or whitespace.
public static AIAgent CreateAIAgent(this AssistantClient client, string model, string? instructions = null, string? name = null, string? description = null, IList? tools = null, ILoggerFactory? loggerFactory = null) =>
client.CreateAIAgent(
model,
new ChatClientAgentOptions()
{
Name = name,
Description = description,
Instructions = instructions,
ChatOptions = tools is null ? null : new ChatOptions()
{
Tools = tools,
}
},
loggerFactory);
///
/// Creates an AI agent from an using the OpenAI Assistant API.
///
/// The OpenAI to use for the agent.
/// The model identifier to use (e.g., "gpt-4").
/// Full set of options to configure the agent.
/// Optional logger factory for enabling logging within the agent.
/// An instance backed by the OpenAI Assistant service.
/// Thrown when or or is .
/// Thrown when is empty or whitespace.
public static AIAgent CreateAIAgent(this AssistantClient client, string model, ChatClientAgentOptions options, ILoggerFactory? loggerFactory = null)
{
Throw.IfNull(client);
Throw.IfNullOrEmpty(model);
Throw.IfNull(options);
var assistantOptions = new AssistantCreationOptions()
{
Name = options.Name,
Description = options.Description,
Instructions = options.Instructions,
};
if (options.ChatOptions?.Tools is not null)
{
foreach (AITool tool in options.ChatOptions.Tools)
{
switch (tool)
{
// Attempting to set the tools at the agent level throws
// https://github.com/dotnet/extensions/issues/6743
//case AIFunction aiFunction:
// assistantOptions.Tools.Add(ToOpenAIAssistantsFunctionToolDefinition(aiFunction));
// break;
case HostedCodeInterpreterTool:
var codeInterpreterToolDefinition = new CodeInterpreterToolDefinition();
assistantOptions.Tools.Add(codeInterpreterToolDefinition);
break;
}
}
}
var assistantCreateResult = client.CreateAssistant(model, assistantOptions);
var assistantId = assistantCreateResult.Value.Id;
var agentOptions = new ChatClientAgentOptions()
{
Id = assistantId,
Name = options.Name,
Description = options.Description,
Instructions = options.Instructions,
ChatOptions = options.ChatOptions?.Tools is null ? null : new ChatOptions()
{
Tools = options.ChatOptions.Tools,
}
};
return new ChatClientAgent(client.AsIChatClient(assistantId), agentOptions, loggerFactory);
}
///
/// Creates an AI agent from an using the OpenAI Assistant API.
///
/// The OpenAI to use for the agent.
/// The model identifier to use (e.g., "gpt-4").
/// Optional system instructions that define the agent's behavior and personality.
/// Optional name for the agent for identification purposes.
/// Optional description of the agent's capabilities and purpose.
/// Optional collection of AI tools that the agent can use during conversations.
/// Optional logger factory for enabling logging within the agent.
/// An instance backed by the OpenAI Assistant service.
/// Thrown when or is .
/// Thrown when is empty or whitespace.
public static async Task CreateAIAgentAsync(this AssistantClient client, string model, string? instructions = null, string? name = null, string? description = null, IList? tools = null, ILoggerFactory? loggerFactory = null) =>
await client.CreateAIAgentAsync(
model,
new ChatClientAgentOptions()
{
Name = name,
Description = description,
Instructions = instructions,
ChatOptions = tools is null ? null : new ChatOptions()
{
Tools = tools,
}
},
loggerFactory).ConfigureAwait(false);
///
/// Creates an AI agent from an using the OpenAI Assistant API.
///
/// The OpenAI to use for the agent.
/// The model identifier to use (e.g., "gpt-4").
/// Full set of options to configure the agent.
/// Optional logger factory for enabling logging within the agent.
/// An instance backed by the OpenAI Assistant service.
/// Thrown when or is .
/// Thrown when is empty or whitespace.
public static async Task CreateAIAgentAsync(this AssistantClient client, string model, ChatClientAgentOptions options, ILoggerFactory? loggerFactory = null)
{
Throw.IfNull(client);
Throw.IfNull(model);
Throw.IfNull(options);
var assistantOptions = new AssistantCreationOptions()
{
Name = options.Name,
Description = options.Description,
Instructions = options.Instructions,
};
if (options.ChatOptions?.Tools is not null)
{
foreach (AITool tool in options.ChatOptions.Tools)
{
switch (tool)
{
// Attempting to set the tools at the agent level throws
// https://github.com/dotnet/extensions/issues/6743
//case AIFunction aiFunction:
// assistantOptions.Tools.Add(ToOpenAIAssistantsFunctionToolDefinition(aiFunction));
// break;
case HostedCodeInterpreterTool:
var codeInterpreterToolDefinition = new CodeInterpreterToolDefinition();
assistantOptions.Tools.Add(codeInterpreterToolDefinition);
break;
}
}
}
var assistantCreateResult = await client.CreateAssistantAsync(model, assistantOptions).ConfigureAwait(false);
var assistantId = assistantCreateResult.Value.Id;
var agentOptions = new ChatClientAgentOptions()
{
Id = assistantId,
Name = options.Name,
Description = options.Description,
Instructions = options.Instructions,
ChatOptions = options.ChatOptions?.Tools is null ? null : new ChatOptions()
{
Tools = options.ChatOptions.Tools,
}
};
return new ChatClientAgent(client.AsIChatClient(assistantId), agentOptions, loggerFactory);
}
}