// 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); } }