.NET: [BREAKING] Change namespaces of the Microsoft.Agents.AI.OpenAI classes (#2627)

* change namespaces for agents and extension methods of the Microsoft.Agents.AI.OpenAI package

* remove unnecessary namespace

* remove unused namespaces

* fix compilation issues and rrolled back removed run methods

* sort usings

* add extension methods for AIAgent to work with OpenAI Responses primitives

* Move OpenAIChatClientAgent and OpenAIResponseClientAgent to samples

* sort usings

* sort usings
This commit is contained in:
SergeyMenshykh
2025-12-09 04:58:44 -08:00
committed by GitHub
Unverified
parent 71d9d268dd
commit 69225b4aa2
58 changed files with 362 additions and 58 deletions
@@ -4,6 +4,7 @@ using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Logging;
using OpenAI;
using OpenAI.Chat;
namespace A2A;
@@ -6,6 +6,7 @@ using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using OpenAI;
using OpenAI.Chat;
namespace A2AServer;
@@ -6,7 +6,7 @@ using Azure.AI.OpenAI;
using Azure.Identity;
using Microsoft.Agents.AI.Hosting.AGUI.AspNetCore;
using Microsoft.Extensions.AI;
using OpenAI;
using OpenAI.Chat;
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpClient().AddLogging();
@@ -7,7 +7,7 @@ using Microsoft.Agents.AI;
using Microsoft.Agents.AI.Hosting.AzureFunctions;
using Microsoft.Azure.Functions.Worker.Builder;
using Microsoft.Extensions.Hosting;
using OpenAI;
using OpenAI.Chat;
// Get the Azure OpenAI endpoint and deployment name from environment variables.
string endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT")
@@ -7,7 +7,7 @@ using Microsoft.Agents.AI;
using Microsoft.Agents.AI.Hosting.AzureFunctions;
using Microsoft.Azure.Functions.Worker.Builder;
using Microsoft.Extensions.Hosting;
using OpenAI;
using OpenAI.Chat;
// Get the Azure OpenAI endpoint and deployment name from environment variables.
string endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT")
@@ -7,7 +7,7 @@ using Microsoft.Agents.AI;
using Microsoft.Agents.AI.Hosting.AzureFunctions;
using Microsoft.Azure.Functions.Worker.Builder;
using Microsoft.Extensions.Hosting;
using OpenAI;
using OpenAI.Chat;
// Get the Azure OpenAI endpoint and deployment name from environment variables.
string endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT")
@@ -7,7 +7,7 @@ using Microsoft.Agents.AI;
using Microsoft.Agents.AI.Hosting.AzureFunctions;
using Microsoft.Azure.Functions.Worker.Builder;
using Microsoft.Extensions.Hosting;
using OpenAI;
using OpenAI.Chat;
// Get the Azure OpenAI endpoint and deployment name from environment variables.
string endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT")
@@ -7,7 +7,7 @@ using Microsoft.Agents.AI;
using Microsoft.Agents.AI.Hosting.AzureFunctions;
using Microsoft.Azure.Functions.Worker.Builder;
using Microsoft.Extensions.Hosting;
using OpenAI;
using OpenAI.Chat;
// Get the Azure OpenAI endpoint and deployment name from environment variables.
string endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT")
@@ -11,7 +11,7 @@ using Microsoft.Extensions.AI;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using OpenAI;
using OpenAI.Chat;
// Get the Azure OpenAI endpoint and deployment name from environment variables.
string endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT")
@@ -13,7 +13,7 @@ using Microsoft.Agents.AI.DurableTask;
using Microsoft.Agents.AI.Hosting.AzureFunctions;
using Microsoft.Azure.Functions.Worker.Builder;
using Microsoft.Extensions.Hosting;
using OpenAI;
using OpenAI.Chat;
// Get the Azure OpenAI endpoint and deployment name from environment variables.
string endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT")
@@ -10,7 +10,7 @@ using Azure.AI.OpenAI;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using OpenAI;
using OpenAI.Chat;
var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
@@ -9,6 +9,7 @@ using System.ClientModel.Primitives;
using Azure.Identity;
using Microsoft.Agents.AI;
using OpenAI;
using OpenAI.Chat;
var endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_OPENAI_ENDPOINT is not set.");
var apiKey = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_OPENAI_APIKEY");
@@ -5,7 +5,7 @@
using Azure.AI.OpenAI;
using Azure.Identity;
using Microsoft.Agents.AI;
using OpenAI;
using OpenAI.Chat;
var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
@@ -5,7 +5,7 @@
using Azure.AI.OpenAI;
using Azure.Identity;
using Microsoft.Agents.AI;
using OpenAI;
using OpenAI.Responses;
var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
@@ -9,6 +9,7 @@
using Microsoft.Agents.AI;
using OpenAI;
using OpenAI.Assistants;
var apiKey = Environment.GetEnvironmentVariable("OPENAI_APIKEY") ?? throw new InvalidOperationException("OPENAI_APIKEY is not set.");
var model = Environment.GetEnvironmentVariable("OPENAI_MODEL") ?? "gpt-4o-mini";
@@ -5,6 +5,7 @@
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using OpenAI;
using OpenAI.Chat;
var apiKey = Environment.GetEnvironmentVariable("OPENAI_APIKEY") ?? throw new InvalidOperationException("OPENAI_APIKEY is not set.");
var model = Environment.GetEnvironmentVariable("OPENAI_MODEL") ?? "gpt-4o-mini";
@@ -4,6 +4,7 @@
using Microsoft.Agents.AI;
using OpenAI;
using OpenAI.Responses;
var apiKey = Environment.GetEnvironmentVariable("OPENAI_APIKEY") ?? throw new InvalidOperationException("OPENAI_APIKEY is not set.");
var model = Environment.GetEnvironmentVariable("OPENAI_MODEL") ?? "gpt-4o-mini";
@@ -9,7 +9,7 @@ using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.VectorData;
using Microsoft.SemanticKernel.Connectors.InMemory;
using OpenAI;
using OpenAI.Chat;
var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
@@ -11,7 +11,7 @@ using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Agents.AI.Mem0;
using Microsoft.Extensions.AI;
using OpenAI;
using OpenAI.Chat;
var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
@@ -12,7 +12,6 @@ using Azure.AI.OpenAI;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using OpenAI;
using OpenAI.Chat;
using SampleApp;
@@ -3,11 +3,10 @@
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Logging;
using Microsoft.Shared.Diagnostics;
using OpenAI.Chat;
using ChatMessage = OpenAI.Chat.ChatMessage;
namespace OpenAI;
namespace OpenAIChatClientSample;
/// <summary>
/// Provides an <see cref="AIAgent"/> backed by an OpenAI chat completion implementation.
@@ -45,7 +44,7 @@ public class OpenAIChatClientAgent : DelegatingAIAgent
/// <param name="loggerFactory">Optional instance of <see cref="ILoggerFactory"/></param>
public OpenAIChatClientAgent(
ChatClient client, ChatClientAgentOptions options, ILoggerFactory? loggerFactory = null) :
base(new ChatClientAgent(Throw.IfNull(client).AsIChatClient(), options, loggerFactory))
base(new ChatClientAgent((client ?? throw new ArgumentNullException(nameof(client))).AsIChatClient(), options, loggerFactory))
{
}
@@ -4,6 +4,7 @@
using OpenAI;
using OpenAI.Chat;
using OpenAIChatClientSample;
string apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY") ?? throw new InvalidOperationException("OPENAI_API_KEY is not set.");
string model = Environment.GetEnvironmentVariable("OPENAI_MODEL") ?? "gpt-4o-mini";
@@ -4,10 +4,9 @@ using System.Runtime.CompilerServices;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Logging;
using Microsoft.Shared.Diagnostics;
using OpenAI.Responses;
namespace OpenAI;
namespace OpenAIResponseClientSample;
/// <summary>
/// Provides an <see cref="AIAgent"/> backed by an OpenAI Responses implementation.
@@ -45,7 +44,7 @@ public class OpenAIResponseClientAgent : DelegatingAIAgent
/// <param name="loggerFactory">Optional instance of <see cref="ILoggerFactory"/></param>
public OpenAIResponseClientAgent(
OpenAIResponseClient client, ChatClientAgentOptions options, ILoggerFactory? loggerFactory = null) :
base(new ChatClientAgent(Throw.IfNull(client).AsIChatClient(), options, loggerFactory))
base(new ChatClientAgent((client ?? throw new ArgumentNullException(nameof(client))).AsIChatClient(), options, loggerFactory))
{
}
@@ -4,6 +4,7 @@
using OpenAI;
using OpenAI.Responses;
using OpenAIResponseClientSample;
var apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY") ?? throw new InvalidOperationException("OPENAI_API_KEY is not set.");
var model = Environment.GetEnvironmentVariable("OPENAI_MODEL") ?? "gpt-4o-mini";
@@ -12,7 +12,7 @@ using Microsoft.Agents.AI.Samples;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.VectorData;
using Microsoft.SemanticKernel.Connectors.InMemory;
using OpenAI;
using OpenAI.Chat;
var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
@@ -10,7 +10,7 @@ using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.VectorData;
using Microsoft.SemanticKernel.Connectors.Qdrant;
using OpenAI;
using OpenAI.Chat;
using Qdrant.Client;
var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
@@ -10,7 +10,7 @@ using Azure.AI.OpenAI;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using OpenAI;
using OpenAI.Chat;
var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
@@ -5,7 +5,7 @@
using Azure.AI.OpenAI;
using Azure.Identity;
using Microsoft.Agents.AI;
using OpenAI;
using OpenAI.Chat;
var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
@@ -5,7 +5,7 @@
using Azure.AI.OpenAI;
using Azure.Identity;
using Microsoft.Agents.AI;
using OpenAI;
using OpenAI.Chat;
var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
@@ -8,7 +8,7 @@ using Azure.AI.OpenAI;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using OpenAI;
using OpenAI.Chat;
var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
@@ -10,7 +10,8 @@ using Azure.AI.OpenAI;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using OpenAI;
using OpenAI.Chat;
using ChatMessage = Microsoft.Extensions.AI.ChatMessage;
var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
@@ -8,7 +8,6 @@ using System.Text.Json.Serialization;
using Azure.AI.OpenAI;
using Azure.Identity;
using Microsoft.Agents.AI;
using OpenAI;
using OpenAI.Chat;
using SampleApp;
@@ -6,7 +6,7 @@ using System.Text.Json;
using Azure.AI.OpenAI;
using Azure.Identity;
using Microsoft.Agents.AI;
using OpenAI;
using OpenAI.Chat;
var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
@@ -11,8 +11,9 @@ using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.VectorData;
using Microsoft.SemanticKernel.Connectors.InMemory;
using OpenAI;
using OpenAI.Chat;
using SampleApp;
using ChatMessage = Microsoft.Extensions.AI.ChatMessage;
var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
@@ -6,7 +6,7 @@ using Azure.AI.OpenAI;
using Azure.Identity;
using Azure.Monitor.OpenTelemetry.Exporter;
using Microsoft.Agents.AI;
using OpenAI;
using OpenAI.Chat;
using OpenTelemetry;
using OpenTelemetry.Trace;
@@ -5,7 +5,8 @@
using Azure.AI.OpenAI;
using Azure.Identity;
using Microsoft.Extensions.AI;
using OpenAI;
using OpenAI.Chat;
using ChatMessage = Microsoft.Extensions.AI.ChatMessage;
var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
var deploymentName = System.Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o";
@@ -7,7 +7,7 @@ using Azure.AI.OpenAI;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using OpenAI;
using OpenAI.Chat;
var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
@@ -12,7 +12,7 @@ using Azure.AI.OpenAI;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using OpenAI;
using OpenAI.Responses;
var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-5";
@@ -14,7 +14,7 @@ using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.DependencyInjection;
using OpenAI;
using OpenAI.Chat;
var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
@@ -9,7 +9,8 @@ using Azure.AI.OpenAI;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using OpenAI;
using OpenAI.Chat;
using ChatMessage = Microsoft.Extensions.AI.ChatMessage;
var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
@@ -5,7 +5,7 @@
using Azure.AI.OpenAI;
using Azure.Identity;
using Microsoft.Agents.AI;
using OpenAI;
using OpenAI.Responses;
var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
@@ -7,7 +7,7 @@ using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using ModelContextProtocol.Client;
using OpenAI;
using OpenAI.Chat;
var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
@@ -11,7 +11,7 @@ using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Extensions.Logging;
using ModelContextProtocol.Client;
using OpenAI;
using OpenAI.Chat;
var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
@@ -8,7 +8,7 @@ using Azure.AI.OpenAI;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using OpenAI;
using OpenAI.Responses;
var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
@@ -5,11 +5,11 @@ using OpenAI.Chat;
namespace Microsoft.Agents.AI.OpenAI;
internal sealed class AsyncStreamingUpdateCollectionResult : AsyncCollectionResult<StreamingChatCompletionUpdate>
internal sealed class AsyncStreamingChatCompletionUpdateCollectionResult : AsyncCollectionResult<StreamingChatCompletionUpdate>
{
private readonly IAsyncEnumerable<AgentRunResponseUpdate> _updates;
internal AsyncStreamingUpdateCollectionResult(IAsyncEnumerable<AgentRunResponseUpdate> updates)
internal AsyncStreamingChatCompletionUpdateCollectionResult(IAsyncEnumerable<AgentRunResponseUpdate> updates)
{
this._updates = updates;
}
@@ -0,0 +1,48 @@
// Copyright (c) Microsoft. All rights reserved.
using System.ClientModel;
using OpenAI.Responses;
namespace Microsoft.Agents.AI.OpenAI;
internal sealed class AsyncStreamingResponseUpdateCollectionResult : AsyncCollectionResult<StreamingResponseUpdate>
{
private readonly IAsyncEnumerable<AgentRunResponseUpdate> _updates;
internal AsyncStreamingResponseUpdateCollectionResult(IAsyncEnumerable<AgentRunResponseUpdate> updates)
{
this._updates = updates;
}
public override ContinuationToken? GetContinuationToken(ClientResult page) => null;
public override async IAsyncEnumerable<ClientResult> GetRawPagesAsync()
{
yield return ClientResult.FromValue(this._updates, new StreamingUpdatePipelineResponse(this._updates));
}
protected async override IAsyncEnumerable<StreamingResponseUpdate> GetValuesFromPageAsync(ClientResult page)
{
var updates = ((ClientResult<IAsyncEnumerable<AgentRunResponseUpdate>>)page).Value;
await foreach (var update in updates.ConfigureAwait(false))
{
switch (update.RawRepresentation)
{
case StreamingResponseUpdate rawUpdate:
yield return rawUpdate;
break;
case Extensions.AI.ChatResponseUpdate { RawRepresentation: StreamingResponseUpdate rawUpdate }:
yield return rawUpdate;
break;
default:
// TODO: The OpenAI library does not currently expose model factory methods for creating
// StreamingResponseUpdates. We are thus unable to manufacture such instances when there isn't
// already one in the update and instead skip them.
break;
}
}
}
}
@@ -1,12 +1,12 @@
// Copyright (c) Microsoft. All rights reserved.
using System.ClientModel;
using Microsoft.Agents.AI;
using Microsoft.Agents.AI.OpenAI;
using Microsoft.Shared.Diagnostics;
using OpenAI.Chat;
using OpenAI.Responses;
namespace OpenAI;
namespace Microsoft.Agents.AI;
/// <summary>
/// Provides extension methods for <see cref="AIAgent"/> to simplify interaction with OpenAI chat messages
@@ -69,6 +69,60 @@ public static class AIAgentWithOpenAIExtensions
IAsyncEnumerable<AgentRunResponseUpdate> response = agent.RunStreamingAsync([.. messages.AsChatMessages()], thread, options, cancellationToken);
return new AsyncStreamingUpdateCollectionResult(response);
return new AsyncStreamingChatCompletionUpdateCollectionResult(response);
}
/// <summary>
/// Runs the AI agent with a collection of OpenAI response items and returns the response as a native OpenAI <see cref="OpenAIResponse"/>.
/// </summary>
/// <param name="agent">The AI agent to run.</param>
/// <param name="messages">The collection of OpenAI response items to send to the agent.</param>
/// <param name="thread">The conversation thread to continue with this invocation. If not provided, creates a new thread. The thread will be mutated with the provided messages and agent response.</param>
/// <param name="options">Optional parameters for agent invocation.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A <see cref="Task{OpenAIResponse}"/> representing the asynchronous operation that returns a native OpenAI <see cref="OpenAIResponse"/> response.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="agent"/> or <paramref name="messages"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException">Thrown when the agent's response cannot be converted to an <see cref="OpenAIResponse"/>, typically when the underlying representation is not an OpenAI response.</exception>
/// <exception cref="NotSupportedException">Thrown when any message in <paramref name="messages"/> has a type that is not supported by the message conversion method.</exception>
/// <remarks>
/// This method converts the OpenAI response items to the Microsoft Extensions AI format using the appropriate conversion method,
/// runs the agent with the converted message collection, and then extracts the native OpenAI <see cref="OpenAIResponse"/> from the response using <see cref="AgentRunResponseExtensions.AsOpenAIResponse"/>.
/// </remarks>
public static async Task<OpenAIResponse> RunAsync(this AIAgent agent, IEnumerable<ResponseItem> messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default)
{
Throw.IfNull(agent);
Throw.IfNull(messages);
var response = await agent.RunAsync(messages.AsChatMessages(), thread, options, cancellationToken).ConfigureAwait(false);
return response.AsOpenAIResponse();
}
/// <summary>
/// Runs the AI agent in streaming mode with a collection of OpenAI response items and returns the response as a collection of native OpenAI <see cref="StreamingResponseUpdate"/>.
/// </summary>
/// <param name="agent">The AI agent to run.</param>
/// <param name="messages">The collection of OpenAI response items to send to the agent.</param>
/// <param name="thread">The conversation thread to continue with this invocation. If not provided, creates a new thread. The thread will be mutated with the provided messages and agent response updates.</param>
/// <param name="options">Optional parameters for agent invocation.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>An <see cref="AsyncCollectionResult{StreamingResponseUpdate}"/> representing the asynchronous enumerable that yields native OpenAI <see cref="StreamingResponseUpdate"/> instances as they are streamed.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="agent"/> or <paramref name="messages"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException">Thrown when the agent's response cannot be converted to <see cref="StreamingResponseUpdate"/> instances, typically when the underlying representation is not an OpenAI response.</exception>
/// <exception cref="NotSupportedException">Thrown when any message in <paramref name="messages"/> has a type that is not supported by the message conversion method.</exception>
/// <remarks>
/// This method converts the OpenAI response items to the Microsoft Extensions AI format using the appropriate conversion method,
/// runs the agent in streaming mode, and then yields native OpenAI <see cref="StreamingResponseUpdate"/> instances as they are produced.
/// The method attempts to extract <see cref="StreamingResponseUpdate"/> from the underlying response representation. If a raw update is not available,
/// it is skipped because the OpenAI library does not currently expose model factory methods for creating such instances.
/// </remarks>
public static AsyncCollectionResult<StreamingResponseUpdate> RunStreamingAsync(this AIAgent agent, IEnumerable<ResponseItem> messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default)
{
Throw.IfNull(agent);
Throw.IfNull(messages);
IAsyncEnumerable<AgentRunResponseUpdate> response = agent.RunStreamingAsync([.. messages.AsChatMessages()], thread, options, cancellationToken);
return new AsyncStreamingResponseUpdateCollectionResult(response);
}
}
@@ -5,7 +5,7 @@ using Microsoft.Shared.Diagnostics;
using OpenAI.Chat;
using OpenAI.Responses;
namespace OpenAI;
namespace Microsoft.Agents.AI;
/// <summary>
/// Provides extension methods for <see cref="AgentRunResponse"/> and <see cref="AgentRunResponseUpdate"/> instances to
@@ -5,9 +5,8 @@ using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Logging;
using Microsoft.Shared.Diagnostics;
using OpenAI.Assistants;
namespace OpenAI;
namespace OpenAI.Assistants;
/// <summary>
/// Provides extension methods for OpenAI <see cref="AssistantClient"/>
@@ -4,9 +4,8 @@ using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Logging;
using Microsoft.Shared.Diagnostics;
using OpenAI.Chat;
namespace OpenAI;
namespace OpenAI.Chat;
/// <summary>
/// Provides extension methods for <see cref="ChatClient"/>
@@ -4,9 +4,8 @@ using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Logging;
using Microsoft.Shared.Diagnostics;
using OpenAI.Responses;
namespace OpenAI;
namespace OpenAI.Responses;
/// <summary>
/// Provides extension methods for <see cref="OpenAIResponseClient"/>
@@ -8,7 +8,7 @@ using Microsoft.DurableTask.Client;
using Microsoft.DurableTask.Client.Entities;
using Microsoft.DurableTask.Entities;
using Microsoft.Extensions.Configuration;
using OpenAI;
using OpenAI.Chat;
using Xunit.Abstractions;
namespace Microsoft.Agents.AI.DurableTask.IntegrationTests;
@@ -8,7 +8,7 @@ using Microsoft.DurableTask;
using Microsoft.DurableTask.Client;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Configuration;
using OpenAI;
using OpenAI.Chat;
using Xunit.Abstractions;
namespace Microsoft.Agents.AI.DurableTask.IntegrationTests;
@@ -6,7 +6,7 @@ using Microsoft.DurableTask;
using Microsoft.DurableTask.Client;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Configuration;
using OpenAI;
using OpenAI.Chat;
using Xunit.Abstractions;
namespace Microsoft.Agents.AI.DurableTask.IntegrationTests;
@@ -0,0 +1,198 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Moq;
using ChatMessage = Microsoft.Extensions.AI.ChatMessage;
using ChatRole = Microsoft.Extensions.AI.ChatRole;
using OpenAIChatMessage = OpenAI.Chat.ChatMessage;
using TextContent = Microsoft.Extensions.AI.TextContent;
namespace Microsoft.Agents.AI.OpenAI.UnitTests.Extensions;
/// <summary>
/// Unit tests for the <see cref="AIAgentWithOpenAIExtensions"/> class.
/// </summary>
public sealed class AIAgentWithOpenAIExtensionsTests
{
/// <summary>
/// Verify that RunAsync throws ArgumentNullException when agent is null.
/// </summary>
[Fact]
public async Task RunAsync_WithNullAgent_ThrowsArgumentNullExceptionAsync()
{
// Arrange
AIAgent? agent = null;
var messages = new List<OpenAIChatMessage>
{
OpenAIChatMessage.CreateUserMessage("Test message")
};
// Act & Assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => agent!.RunAsync(messages));
Assert.Equal("agent", exception.ParamName);
}
/// <summary>
/// Verify that RunAsync throws ArgumentNullException when messages is null.
/// </summary>
[Fact]
public async Task RunAsync_WithNullMessages_ThrowsArgumentNullExceptionAsync()
{
// Arrange
var mockAgent = new Mock<AIAgent>();
IEnumerable<OpenAIChatMessage>? messages = null;
// Act & Assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => mockAgent.Object.RunAsync(messages!));
Assert.Equal("messages", exception.ParamName);
}
/// <summary>
/// Verify that the RunAsync extension method calls the underlying agent's RunAsync with converted messages and parameters.
/// </summary>
[Fact]
public async Task RunAsync_CallsUnderlyingAgentAsync()
{
// Arrange
var mockAgent = new Mock<AIAgent>();
var mockThread = new Mock<AgentThread>();
var options = new AgentRunOptions();
var cancellationToken = new CancellationToken(false);
const string TestMessageText = "Hello, assistant!";
const string ResponseText = "This is the assistant's response.";
var openAiMessages = new List<OpenAIChatMessage>
{
OpenAIChatMessage.CreateUserMessage(TestMessageText)
};
var responseMessage = new ChatMessage(ChatRole.Assistant, [new TextContent(ResponseText)]);
mockAgent
.Setup(a => a.RunAsync(It.IsAny<IEnumerable<ChatMessage>>(), It.IsAny<AgentThread?>(), It.IsAny<AgentRunOptions?>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new AgentRunResponse([responseMessage]));
// Act
var result = await mockAgent.Object.RunAsync(openAiMessages, mockThread.Object, options, cancellationToken);
// Assert
mockAgent.Verify(
a => a.RunAsync(
It.Is<IEnumerable<ChatMessage>>(msgs =>
msgs.ToList().Count == 1 &&
msgs.ToList()[0].Text == TestMessageText),
mockThread.Object,
options,
cancellationToken),
Times.Once);
Assert.NotNull(result);
Assert.NotEmpty(result.Content);
Assert.Equal(ResponseText, result.Content.Last().Text);
}
/// <summary>
/// Verify that RunStreamingAsync throws ArgumentNullException when agent is null.
/// </summary>
[Fact]
public void RunStreamingAsync_WithNullAgent_ThrowsArgumentNullException()
{
// Arrange
AIAgent? agent = null;
var messages = new List<OpenAIChatMessage>
{
OpenAIChatMessage.CreateUserMessage("Test message")
};
// Act & Assert
Assert.Throws<ArgumentNullException>(
"agent",
() => agent!.RunStreamingAsync(messages));
}
/// <summary>
/// Verify that RunStreamingAsync throws ArgumentNullException when messages is null.
/// </summary>
[Fact]
public void RunStreamingAsync_WithNullMessages_ThrowsArgumentNullException()
{
// Arrange
var mockAgent = new Mock<AIAgent>();
IEnumerable<OpenAIChatMessage>? messages = null;
// Act & Assert
var exception = Assert.Throws<ArgumentNullException>(
() => mockAgent.Object.RunStreamingAsync(messages!));
Assert.Equal("messages", exception.ParamName);
}
/// <summary>
/// Verify that the RunStreamingAsync extension method calls the underlying agent's RunStreamingAsync with converted messages and parameters.
/// </summary>
[Fact]
public async Task RunStreamingAsync_CallsUnderlyingAgentAsync()
{
// Arrange
var mockAgent = new Mock<AIAgent>();
var mockThread = new Mock<AgentThread>();
var options = new AgentRunOptions();
var cancellationToken = new CancellationToken(false);
const string TestMessageText = "Hello, assistant!";
const string ResponseText1 = "This is ";
const string ResponseText2 = "the assistant's response.";
var openAiMessages = new List<OpenAIChatMessage>
{
OpenAIChatMessage.CreateUserMessage(TestMessageText)
};
var responseUpdates = new List<AgentRunResponseUpdate>
{
new(ChatRole.Assistant, ResponseText1),
new(ChatRole.Assistant, ResponseText2)
};
mockAgent
.Setup(a => a.RunStreamingAsync(It.IsAny<IEnumerable<ChatMessage>>(), It.IsAny<AgentThread?>(), It.IsAny<AgentRunOptions?>(), It.IsAny<CancellationToken>()))
.Returns(ToAsyncEnumerableAsync(responseUpdates));
// Act
var result = mockAgent.Object.RunStreamingAsync(openAiMessages, mockThread.Object, options, cancellationToken);
var updateCount = 0;
await foreach (var update in result)
{
updateCount++;
}
// Assert
mockAgent.Verify(
a => a.RunStreamingAsync(
It.Is<IEnumerable<ChatMessage>>(msgs =>
msgs.ToList().Count == 1 &&
msgs.ToList()[0].Text == TestMessageText),
mockThread.Object,
options,
cancellationToken),
Times.Once);
Assert.True(updateCount > 0, "Expected at least one streaming update");
}
/// <summary>
/// Helper method to convert a list of AgentRunResponseUpdate to an async enumerable.
/// </summary>
private static async IAsyncEnumerable<AgentRunResponseUpdate> ToAsyncEnumerableAsync(IEnumerable<AgentRunResponseUpdate> updates)
{
foreach (var update in updates)
{
yield return await Task.FromResult(update);
}
}
}
@@ -10,7 +10,6 @@ using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.AI;
using OpenAI;
using OpenAI.Assistants;
namespace Microsoft.Agents.AI.OpenAI.UnitTests.Extensions;
@@ -6,7 +6,8 @@ using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.AI;
using OpenAI;
using OpenAI.Chat;
using ChatMessage = Microsoft.Extensions.AI.ChatMessage;
using OpenAIChatClient = OpenAI.Chat.ChatClient;
namespace Microsoft.Agents.AI.OpenAI.UnitTests.Extensions;
@@ -7,7 +7,6 @@ using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.AI;
using OpenAI;
using OpenAI.Responses;
namespace Microsoft.Agents.AI.OpenAI.UnitTests.Extensions;