// Copyright (c) Microsoft. All rights reserved.
using System.ClientModel;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Logging;
using Microsoft.Shared.Diagnostics;
namespace OpenAI.Assistants;
///
/// 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 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
{
///
/// Gets a from a .
///
/// The assistant client.
/// The client result containing the assistant.
/// Optional chat options.
/// Provides a way to customize the creation of the underlying used by the agent.
/// An optional to use for resolving services required by the instances being invoked.
/// A instance that can be used to perform operations on the assistant.
[Obsolete("The Assistants API has been deprecated. Please use the Responses API instead.")]
public static ChatClientAgent AsAIAgent(
this AssistantClient assistantClient,
ClientResult assistantClientResult,
ChatOptions? chatOptions = null,
Func? clientFactory = null,
IServiceProvider? services = null)
{
if (assistantClientResult is null)
{
throw new ArgumentNullException(nameof(assistantClientResult));
}
return assistantClient.AsAIAgent(assistantClientResult.Value, chatOptions, clientFactory, services);
}
///
/// Gets a from an .
///
/// The assistant client.
/// The assistant metadata.
/// Optional chat options.
/// Provides a way to customize the creation of the underlying used by the agent.
/// An optional to use for resolving services required by the instances being invoked.
/// A instance that can be used to perform operations on the assistant.
[Obsolete("The Assistants API has been deprecated. Please use the Responses API instead.")]
public static ChatClientAgent AsAIAgent(
this AssistantClient assistantClient,
Assistant assistantMetadata,
ChatOptions? chatOptions = null,
Func? clientFactory = null,
IServiceProvider? services = null)
{
if (assistantMetadata is null)
{
throw new ArgumentNullException(nameof(assistantMetadata));
}
if (assistantClient is null)
{
throw new ArgumentNullException(nameof(assistantClient));
}
var chatClient = assistantClient.AsIChatClient(assistantMetadata.Id);
if (clientFactory is not null)
{
chatClient = clientFactory(chatClient);
}
if (!string.IsNullOrWhiteSpace(assistantMetadata.Instructions) && chatOptions?.Instructions is null)
{
chatOptions ??= new ChatOptions();
chatOptions.Instructions = assistantMetadata.Instructions;
}
return new ChatClientAgent(chatClient, options: new()
{
Id = assistantMetadata.Id,
Name = assistantMetadata.Name,
Description = assistantMetadata.Description,
ChatOptions = chatOptions
}, services: services);
}
///
/// 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.
/// Provides a way to customize the creation of the underlying used by the agent.
/// An optional to use for resolving services required by the instances being invoked.
/// The to monitor for cancellation requests. The default is .
/// A instance that can be used to perform operations on the assistant agent.
[Obsolete("The Assistants API has been deprecated. Please use the Responses API instead.")]
public static ChatClientAgent GetAIAgent(
this AssistantClient assistantClient,
string agentId,
ChatOptions? chatOptions = null,
Func? clientFactory = null,
IServiceProvider? services = 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 assistantClient.AsAIAgent(assistant, chatOptions, clientFactory, services);
}
///
/// 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.
/// Provides a way to customize the creation of the underlying used by the agent.
/// An optional to use for resolving services required by the instances being invoked.
/// The to monitor for cancellation requests. The default is .
/// A instance that can be used to perform operations on the assistant agent.
[Obsolete("The Assistants API has been deprecated. Please use the Responses API instead.")]
public static async Task GetAIAgentAsync(
this AssistantClient assistantClient,
string agentId,
ChatOptions? chatOptions = null,
Func? clientFactory = null,
IServiceProvider? services = 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 assistantResponse = await assistantClient.GetAssistantAsync(agentId, cancellationToken).ConfigureAwait(false);
return assistantClient.AsAIAgent(assistantResponse, chatOptions, clientFactory, services);
}
///
/// Gets a from a .
///
/// The assistant client.
/// The client result containing the assistant.
/// Full set of options to configure the agent.
/// Provides a way to customize the creation of the underlying used by the agent.
/// An optional to use for resolving services required by the instances being invoked.
/// A instance that can be used to perform operations on the assistant.
/// or is .
[Obsolete("The Assistants API has been deprecated. Please use the Responses API instead.")]
public static ChatClientAgent AsAIAgent(
this AssistantClient assistantClient,
ClientResult assistantClientResult,
ChatClientAgentOptions options,
Func? clientFactory = null,
IServiceProvider? services = null)
{
if (assistantClientResult is null)
{
throw new ArgumentNullException(nameof(assistantClientResult));
}
return assistantClient.AsAIAgent(assistantClientResult.Value, options, clientFactory, services);
}
///
/// Gets a from an .
///
/// The assistant client.
/// The assistant metadata.
/// Full set of options to configure the agent.
/// Provides a way to customize the creation of the underlying used by the agent.
/// An optional to use for resolving services required by the instances being invoked.
/// A instance that can be used to perform operations on the assistant.
/// or is .
[Obsolete("The Assistants API has been deprecated. Please use the Responses API instead.")]
public static ChatClientAgent AsAIAgent(
this AssistantClient assistantClient,
Assistant assistantMetadata,
ChatClientAgentOptions options,
Func? clientFactory = null,
IServiceProvider? services = null)
{
if (assistantMetadata is null)
{
throw new ArgumentNullException(nameof(assistantMetadata));
}
if (assistantClient is null)
{
throw new ArgumentNullException(nameof(assistantClient));
}
if (options is null)
{
throw new ArgumentNullException(nameof(options));
}
var chatClient = assistantClient.AsIChatClient(assistantMetadata.Id);
if (clientFactory is not null)
{
chatClient = clientFactory(chatClient);
}
if (string.IsNullOrWhiteSpace(options.ChatOptions?.Instructions) && !string.IsNullOrWhiteSpace(assistantMetadata.Instructions))
{
options.ChatOptions ??= new ChatOptions();
options.ChatOptions.Instructions = assistantMetadata.Instructions;
}
var mergedOptions = new ChatClientAgentOptions()
{
Id = assistantMetadata.Id,
Name = options.Name ?? assistantMetadata.Name,
Description = options.Description ?? assistantMetadata.Description,
ChatOptions = options.ChatOptions,
AIContextProviderFactory = options.AIContextProviderFactory,
ChatMessageStoreFactory = options.ChatMessageStoreFactory,
UseProvidedChatClientAsIs = options.UseProvidedChatClientAsIs
};
return new ChatClientAgent(chatClient, mergedOptions, services: services);
}
///
/// 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.
/// Full set of options to configure the agent.
/// Provides a way to customize the creation of the underlying used by the agent.
/// An optional to use for resolving services required by the instances being invoked.
/// The to monitor for cancellation requests. The default is .
/// A instance that can be used to perform operations on the assistant agent.
/// or is .
/// is empty or whitespace.
[Obsolete("The Assistants API has been deprecated. Please use the Responses API instead.")]
public static ChatClientAgent GetAIAgent(
this AssistantClient assistantClient,
string agentId,
ChatClientAgentOptions options,
Func? clientFactory = null,
IServiceProvider? services = 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));
}
if (options is null)
{
throw new ArgumentNullException(nameof(options));
}
var assistant = assistantClient.GetAssistant(agentId, cancellationToken);
return assistantClient.AsAIAgent(assistant, options, clientFactory, services);
}
///
/// 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.
/// Full set of options to configure the agent.
/// Provides a way to customize the creation of the underlying used by the agent.
/// An optional to use for resolving services required by the instances being invoked.
/// The to monitor for cancellation requests. The default is .
/// A instance that can be used to perform operations on the assistant agent.
/// or is .
/// is empty or whitespace.
[Obsolete("The Assistants API has been deprecated. Please use the Responses API instead.")]
public static async Task GetAIAgentAsync(
this AssistantClient assistantClient,
string agentId,
ChatClientAgentOptions options,
Func? clientFactory = null,
IServiceProvider? services = 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));
}
if (options is null)
{
throw new ArgumentNullException(nameof(options));
}
var assistantResponse = await assistantClient.GetAssistantAsync(agentId, cancellationToken).ConfigureAwait(false);
return assistantClient.AsAIAgent(assistantResponse, options, clientFactory, services);
}
///
/// 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.
/// Provides a way to customize the creation of the underlying used by the agent.
/// Optional logger factory for enabling logging within the agent.
/// An optional to use for resolving services required by the instances being invoked.
/// An instance backed by the OpenAI Assistant service.
/// Thrown when or is .
/// Thrown when is empty or whitespace.
[Obsolete("The Assistants API has been deprecated. Please use the Responses API instead.")]
public static ChatClientAgent CreateAIAgent(
this AssistantClient client,
string model,
string? instructions = null,
string? name = null,
string? description = null,
IList? tools = null,
Func? clientFactory = null,
ILoggerFactory? loggerFactory = null,
IServiceProvider? services = null) =>
client.CreateAIAgent(
model,
new ChatClientAgentOptions()
{
Name = name,
Description = description,
ChatOptions = tools is null && string.IsNullOrWhiteSpace(instructions) ? null : new ChatOptions()
{
Tools = tools,
Instructions = instructions
}
},
clientFactory,
loggerFactory,
services);
///
/// 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.
/// Provides a way to customize the creation of the underlying used by the agent.
/// Optional logger factory for enabling logging within the agent.
/// An optional to use for resolving services required by the instances being invoked.
/// An instance backed by the OpenAI Assistant service.
/// Thrown when or or is .
/// Thrown when is empty or whitespace.
[Obsolete("The Assistants API has been deprecated. Please use the Responses API instead.")]
public static ChatClientAgent CreateAIAgent(
this AssistantClient client,
string model,
ChatClientAgentOptions options,
Func? clientFactory = null,
ILoggerFactory? loggerFactory = null,
IServiceProvider? services = null)
{
Throw.IfNull(client);
Throw.IfNullOrEmpty(model);
Throw.IfNull(options);
var assistantOptions = new AssistantCreationOptions()
{
Name = options.Name,
Description = options.Description,
Instructions = options.ChatOptions?.Instructions,
};
// Convert AITools to ToolDefinitions and ToolResources
var toolDefinitionsAndResources = ConvertAIToolsToToolDefinitions(options.ChatOptions?.Tools);
if (toolDefinitionsAndResources.ToolDefinitions is { Count: > 0 })
{
toolDefinitionsAndResources.ToolDefinitions.ForEach(x => assistantOptions.Tools.Add(x));
}
if (toolDefinitionsAndResources.ToolResources is not null)
{
assistantOptions.ToolResources = toolDefinitionsAndResources.ToolResources;
}
// Create the assistant in the assistant service.
var assistantCreateResult = client.CreateAssistant(model, assistantOptions);
var assistantId = assistantCreateResult.Value.Id;
// Build the local agent object.
var chatClient = client.AsIChatClient(assistantId);
if (clientFactory is not null)
{
chatClient = clientFactory(chatClient);
}
var agentOptions = options.Clone();
agentOptions.Id = assistantId;
options.ChatOptions ??= new ChatOptions();
options.ChatOptions!.Tools = toolDefinitionsAndResources.FunctionToolsAndOtherTools;
return new ChatClientAgent(chatClient, agentOptions, loggerFactory, services);
}
///
/// 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.
/// Provides a way to customize the creation of the underlying used by the agent.
/// Optional logger factory for enabling logging within the agent.
/// An optional to use for resolving services required by the instances being invoked.
/// The to monitor for cancellation requests. The default is .
/// An instance backed by the OpenAI Assistant service.
/// Thrown when or is .
/// Thrown when is empty or whitespace.
[Obsolete("The Assistants API has been deprecated. Please use the Responses API instead.")]
public static async Task CreateAIAgentAsync(
this AssistantClient client,
string model,
string? instructions = null,
string? name = null,
string? description = null,
IList? tools = null,
Func? clientFactory = null,
ILoggerFactory? loggerFactory = null,
IServiceProvider? services = null,
CancellationToken cancellationToken = default) =>
await client.CreateAIAgentAsync(model,
new ChatClientAgentOptions()
{
Name = name,
Description = description,
ChatOptions = tools is null && string.IsNullOrWhiteSpace(instructions) ? null : new ChatOptions()
{
Tools = tools,
Instructions = instructions,
}
},
clientFactory,
loggerFactory,
services,
cancellationToken).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.
/// Provides a way to customize the creation of the underlying used by the agent.
/// Optional logger factory for enabling logging within the agent.
/// An optional to use for resolving services required by the instances being invoked.
/// The to monitor for cancellation requests. The default is .
/// An instance backed by the OpenAI Assistant service.
/// Thrown when or is .
/// Thrown when is empty or whitespace.
[Obsolete("The Assistants API has been deprecated. Please use the Responses API instead.")]
public static async Task CreateAIAgentAsync(
this AssistantClient client,
string model,
ChatClientAgentOptions options,
Func? clientFactory = null,
ILoggerFactory? loggerFactory = null,
IServiceProvider? services = null,
CancellationToken cancellationToken = default)
{
Throw.IfNull(client);
Throw.IfNull(model);
Throw.IfNull(options);
var assistantOptions = new AssistantCreationOptions()
{
Name = options.Name,
Description = options.Description,
Instructions = options.ChatOptions?.Instructions,
};
// Convert AITools to ToolDefinitions and ToolResources
var toolDefinitionsAndResources = ConvertAIToolsToToolDefinitions(options.ChatOptions?.Tools);
if (toolDefinitionsAndResources.ToolDefinitions is { Count: > 0 } toolDefinitions)
{
toolDefinitions.ForEach(x => assistantOptions.Tools.Add(x));
}
if (toolDefinitionsAndResources.ToolResources is not null)
{
assistantOptions.ToolResources = toolDefinitionsAndResources.ToolResources;
}
// Create the assistant in the assistant service.
var assistantCreateResult = await client.CreateAssistantAsync(model, assistantOptions, cancellationToken).ConfigureAwait(false);
var assistantId = assistantCreateResult.Value.Id;
// Build the local agent object.
var chatClient = client.AsIChatClient(assistantId);
if (clientFactory is not null)
{
chatClient = clientFactory(chatClient);
}
var agentOptions = options.Clone();
agentOptions.Id = assistantId;
options.ChatOptions ??= new ChatOptions();
options.ChatOptions!.Tools = toolDefinitionsAndResources.FunctionToolsAndOtherTools;
return new ChatClientAgent(chatClient, agentOptions, loggerFactory, services);
}
private static (List? ToolDefinitions, ToolResources? ToolResources, List? FunctionToolsAndOtherTools) ConvertAIToolsToToolDefinitions(IList? tools)
{
List? toolDefinitions = null;
ToolResources? toolResources = null;
List? functionToolsAndOtherTools = null;
if (tools is not null)
{
foreach (AITool tool in tools)
{
switch (tool)
{
case HostedCodeInterpreterTool codeTool:
toolDefinitions ??= [];
toolDefinitions.Add(new CodeInterpreterToolDefinition());
if (codeTool.Inputs is { Count: > 0 })
{
foreach (var input in codeTool.Inputs)
{
switch (input)
{
case HostedFileContent hostedFile:
// If the input is a HostedFileContent, we can use its ID directly.
toolResources ??= new();
toolResources.CodeInterpreter ??= new();
toolResources.CodeInterpreter.FileIds.Add(hostedFile.FileId);
break;
}
}
}
break;
case HostedFileSearchTool fileSearchTool:
toolDefinitions ??= [];
toolDefinitions.Add(new FileSearchToolDefinition
{
MaxResults = fileSearchTool.MaximumResultCount,
});
if (fileSearchTool.Inputs is { Count: > 0 })
{
foreach (var input in fileSearchTool.Inputs)
{
switch (input)
{
case HostedVectorStoreContent hostedVectorStore:
toolResources ??= new();
toolResources.FileSearch ??= new();
toolResources.FileSearch.VectorStoreIds.Add(hostedVectorStore.VectorStoreId);
break;
}
}
}
break;
default:
functionToolsAndOtherTools ??= [];
functionToolsAndOtherTools.Add(tool);
break;
}
}
}
return (toolDefinitions, toolResources, functionToolsAndOtherTools);
}
}