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