mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
.NET: Enable access to hosted AIAgents via OpenAI Chat Completions (#1302)
* non-streaming chat completion * support streaming * simplify frontend clients + nit * nit * use baseaddress * rm unnecessary * refactor * remove conversation id for chatcompletions agent client * nits
This commit is contained in:
committed by
GitHub
Unverified
parent
ea8f751108
commit
f42a3ee6b9
@@ -89,6 +89,9 @@ app.MapA2A(agentName: "knights-and-knaves", path: "/a2a/knights-and-knaves", age
|
||||
app.MapOpenAIResponses("pirate");
|
||||
app.MapOpenAIResponses("knights-and-knaves");
|
||||
|
||||
app.MapOpenAIChatCompletions("pirate");
|
||||
app.MapOpenAIChatCompletions("knights-and-knaves");
|
||||
|
||||
// Map the agents HTTP endpoints
|
||||
app.MapAgentDiscovery("/agents");
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ using Microsoft.Extensions.AI;
|
||||
|
||||
namespace AgentWebChat.Web;
|
||||
|
||||
internal sealed class A2AAgentClient : IAgentClient
|
||||
internal sealed class A2AAgentClient : AgentClientBase
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly Uri _uri;
|
||||
@@ -25,7 +25,7 @@ internal sealed class A2AAgentClient : IAgentClient
|
||||
this._uri = baseUri;
|
||||
}
|
||||
|
||||
public async IAsyncEnumerable<AgentRunResponseUpdate> RunStreamingAsync(
|
||||
public async override IAsyncEnumerable<AgentRunResponseUpdate> RunStreamingAsync(
|
||||
string agentName,
|
||||
IList<ChatMessage> messages,
|
||||
string? threadId = null,
|
||||
@@ -126,7 +126,7 @@ internal sealed class A2AAgentClient : IAgentClient
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<AgentCard?> GetAgentCardAsync(string agentName, CancellationToken cancellationToken = default)
|
||||
public async override Task<AgentCard?> GetAgentCardAsync(string agentName, CancellationToken cancellationToken = default)
|
||||
{
|
||||
this._logger.LogInformation("Retrieving agent card for {Agent}", agentName);
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<NoWarn>$(NoWarn);CA1812</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
@inject ILogger<Home> Logger
|
||||
@inject A2AAgentClient A2AActorClient
|
||||
@inject OpenAIResponsesAgentClient OpenAIResponsesAgentClient
|
||||
@inject OpenAIChatCompletionsAgentClient OpenAIChatCompletionsAgentClient
|
||||
@rendermode InteractiveServer
|
||||
@using System.Text
|
||||
@using System.Text.Json
|
||||
@@ -52,14 +53,18 @@
|
||||
<label for="protocol-select" class="protocol-select-label">Choose communication protocol:</label>
|
||||
<div class="protocol-select-wrapper">
|
||||
<select id="protocol-select" class="protocol-select" @bind="selectedProtocol" disabled="@(isStreaming)">
|
||||
<option value="OpenAIResponses">OpenAI Responses</option>
|
||||
<option value="A2A">A2A (Agent-to-Agent)</option>
|
||||
<option value="OpenAIResponses">OpenAI Responses</option>
|
||||
<option value="OpenAIChatCompletions">OpenAI ChatCompletions</option>
|
||||
<option value="A2A">A2A (Agent-to-Agent)</option>
|
||||
</select>
|
||||
<div class="protocol-info">
|
||||
@switch (selectedProtocol)
|
||||
{
|
||||
case Protocol.OpenAIResponses:
|
||||
<span class="protocol-description">֎ OpenAI Responses</span>
|
||||
break;
|
||||
case Protocol.OpenAIChatCompletions:
|
||||
<span class="protocol-description">֎ OpenAI ChatCompletions</span>
|
||||
break;
|
||||
case Protocol.A2A:
|
||||
default:
|
||||
@@ -903,7 +908,8 @@
|
||||
private enum Protocol
|
||||
{
|
||||
A2A, // Agent-to-Agent protocol
|
||||
OpenAIResponses
|
||||
OpenAIResponses,
|
||||
OpenAIChatCompletions
|
||||
}
|
||||
|
||||
private sealed class Conversation
|
||||
@@ -1080,11 +1086,11 @@
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
// Select the appropriate client based on protocol
|
||||
IAgentClient agentClient = selectedProtocol switch
|
||||
AgentClientBase agentClient = selectedProtocol switch
|
||||
{
|
||||
Protocol.OpenAIResponses => OpenAIResponsesAgentClient,
|
||||
Protocol.OpenAIChatCompletions => OpenAIChatCompletionsAgentClient,
|
||||
Protocol.A2A or _ => A2AActorClient
|
||||
};
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace AgentWebChat.Web;
|
||||
/// <summary>
|
||||
/// Interface for clients that can interact with agents and provide streaming responses.
|
||||
/// </summary>
|
||||
public interface IAgentClient
|
||||
internal abstract class AgentClientBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Runs an agent with the specified messages and returns a streaming response.
|
||||
@@ -19,7 +19,7 @@ public interface IAgentClient
|
||||
/// <param name="threadId">Optional thread identifier for conversation continuity.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>An asynchronous enumerable of agent response updates.</returns>
|
||||
IAsyncEnumerable<AgentRunResponseUpdate> RunStreamingAsync(
|
||||
public abstract IAsyncEnumerable<AgentRunResponseUpdate> RunStreamingAsync(
|
||||
string agentName,
|
||||
IList<ChatMessage> messages,
|
||||
string? threadId = null,
|
||||
@@ -31,7 +31,8 @@ public interface IAgentClient
|
||||
/// <param name="agentName">The name of the agent.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>The agent card if supported, null otherwise.</returns>
|
||||
Task<AgentCard?> GetAgentCardAsync(string agentName, CancellationToken cancellationToken = default);
|
||||
public virtual Task<AgentCard?> GetAgentCardAsync(string agentName, CancellationToken cancellationToken = default)
|
||||
=> Task.FromResult<AgentCard?>(null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.ClientModel;
|
||||
using System.ClientModel.Primitives;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Microsoft.Agents.AI;
|
||||
using Microsoft.Extensions.AI;
|
||||
using OpenAI;
|
||||
using OpenAI.Chat;
|
||||
using ChatMessage = Microsoft.Extensions.AI.ChatMessage;
|
||||
|
||||
namespace AgentWebChat.Web;
|
||||
|
||||
/// <summary>
|
||||
/// Is a simple frontend client which exercises the ability of exposed agent to communicate via OpenAI ChatCompletions protocol.
|
||||
/// </summary>
|
||||
internal sealed class OpenAIChatCompletionsAgentClient(HttpClient httpClient) : AgentClientBase
|
||||
{
|
||||
public async override IAsyncEnumerable<AgentRunResponseUpdate> RunStreamingAsync(
|
||||
string agentName,
|
||||
IList<ChatMessage> messages,
|
||||
string? threadId = null,
|
||||
[EnumeratorCancellation] CancellationToken cancellationToken = default)
|
||||
{
|
||||
OpenAIClientOptions options = new()
|
||||
{
|
||||
Endpoint = new Uri(httpClient.BaseAddress!, $"/{agentName}/v1/"),
|
||||
Transport = new HttpClientPipelineTransport(httpClient)
|
||||
};
|
||||
|
||||
var openAiClient = new ChatClient(model: "myModel!", credential: new ApiKeyCredential("dummy-key"), options: options).AsIChatClient();
|
||||
await foreach (var update in openAiClient.GetStreamingResponseAsync(messages, cancellationToken: cancellationToken))
|
||||
{
|
||||
yield return new AgentRunResponseUpdate(update);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.ClientModel;
|
||||
using System.ClientModel.Primitives;
|
||||
using System.Runtime.CompilerServices;
|
||||
using A2A;
|
||||
using Microsoft.Agents.AI;
|
||||
using Microsoft.Extensions.AI;
|
||||
using OpenAI;
|
||||
@@ -13,16 +13,9 @@ namespace AgentWebChat.Web;
|
||||
/// <summary>
|
||||
/// Is a simple frontend client which exercises the ability of exposed agent to communicate via OpenAI Responses protocol.
|
||||
/// </summary>
|
||||
internal sealed class OpenAIResponsesAgentClient : IAgentClient
|
||||
internal sealed class OpenAIResponsesAgentClient(HttpClient httpClient) : AgentClientBase
|
||||
{
|
||||
private readonly Uri _baseUri;
|
||||
|
||||
public OpenAIResponsesAgentClient(string baseUri)
|
||||
{
|
||||
this._baseUri = new Uri(baseUri.TrimEnd('/'));
|
||||
}
|
||||
|
||||
public async IAsyncEnumerable<AgentRunResponseUpdate> RunStreamingAsync(
|
||||
public async override IAsyncEnumerable<AgentRunResponseUpdate> RunStreamingAsync(
|
||||
string agentName,
|
||||
IList<ChatMessage> messages,
|
||||
string? threadId = null,
|
||||
@@ -30,7 +23,8 @@ internal sealed class OpenAIResponsesAgentClient : IAgentClient
|
||||
{
|
||||
OpenAIClientOptions options = new()
|
||||
{
|
||||
Endpoint = new Uri(this._baseUri, $"/{agentName}/v1/")
|
||||
Endpoint = new Uri(httpClient.BaseAddress!, $"/{agentName}/v1/"),
|
||||
Transport = new HttpClientPipelineTransport(httpClient)
|
||||
};
|
||||
|
||||
var openAiClient = new OpenAIResponseClient(model: "myModel!", credential: new ApiKeyCredential("dummy-key"), options: options).AsIChatClient();
|
||||
@@ -44,7 +38,4 @@ internal sealed class OpenAIResponsesAgentClient : IAgentClient
|
||||
yield return new AgentRunResponseUpdate(update);
|
||||
}
|
||||
}
|
||||
|
||||
public Task<AgentCard?> GetAgentCardAsync(string agentName, CancellationToken cancellationToken = default)
|
||||
=> Task.FromResult<AgentCard?>(null!);
|
||||
}
|
||||
|
||||
@@ -23,7 +23,9 @@ Uri a2aAddress = new("http://localhost:5390/a2a");
|
||||
|
||||
builder.Services.AddHttpClient<AgentDiscoveryClient>(client => client.BaseAddress = baseAddress);
|
||||
builder.Services.AddSingleton(sp => new A2AAgentClient(sp.GetRequiredService<ILogger<A2AAgentClient>>(), a2aAddress));
|
||||
builder.Services.AddSingleton(sp => new OpenAIResponsesAgentClient("http://localhost:5390"));
|
||||
|
||||
builder.Services.AddHttpClient<OpenAIResponsesAgentClient>(client => client.BaseAddress = baseAddress);
|
||||
builder.Services.AddHttpClient<OpenAIChatCompletionsAgentClient>(client => client.BaseAddress = baseAddress);
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.Buffers;
|
||||
using System.ClientModel.Primitives;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Net.ServerSentEvents;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Agents.AI.Hosting.OpenAI.ChatCompletions.Utils;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using OpenAI.Chat;
|
||||
using ChatMessage = Microsoft.Extensions.AI.ChatMessage;
|
||||
|
||||
namespace Microsoft.Agents.AI.Hosting.OpenAI.ChatCompletions;
|
||||
|
||||
internal sealed class AIAgentChatCompletionsProcessor
|
||||
{
|
||||
private readonly AIAgent _agent;
|
||||
|
||||
public AIAgentChatCompletionsProcessor(AIAgent agent)
|
||||
{
|
||||
this._agent = agent;
|
||||
}
|
||||
|
||||
public async Task<IResult> CreateChatCompletionAsync(ChatCompletionOptions chatCompletionOptions, CancellationToken cancellationToken)
|
||||
{
|
||||
AgentThread? agentThread = null; // not supported to resolve from conversationId
|
||||
|
||||
var inputItems = chatCompletionOptions.GetMessages();
|
||||
var chatMessages = inputItems.AsChatMessages();
|
||||
|
||||
if (chatCompletionOptions.GetStream())
|
||||
{
|
||||
return new OpenAIStreamingChatCompletionResult(this._agent, chatMessages);
|
||||
}
|
||||
|
||||
var agentResponse = await this._agent.RunAsync(chatMessages, agentThread, cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
return new OpenAIChatCompletionResult(agentResponse);
|
||||
}
|
||||
|
||||
private sealed class OpenAIChatCompletionResult(AgentRunResponse agentRunResponse) : IResult
|
||||
{
|
||||
public async Task ExecuteAsync(HttpContext httpContext)
|
||||
{
|
||||
// note: OpenAI SDK types provide their own serialization implementation
|
||||
// so we cant simply return IResult wrap for the typed-object.
|
||||
// instead writing to the response body can be done.
|
||||
|
||||
var cancellationToken = httpContext.RequestAborted;
|
||||
var response = httpContext.Response;
|
||||
|
||||
var chatResponse = agentRunResponse.AsChatResponse();
|
||||
var openAIChatCompletion = chatResponse.AsOpenAIChatCompletion();
|
||||
var openAIChatCompletionJsonModel = openAIChatCompletion as IJsonModel<ChatCompletion>;
|
||||
Debug.Assert(openAIChatCompletionJsonModel is not null);
|
||||
|
||||
var writer = new Utf8JsonWriter(response.BodyWriter, new JsonWriterOptions { SkipValidation = false });
|
||||
openAIChatCompletionJsonModel.Write(writer, ModelReaderWriterOptions.Json);
|
||||
await writer.FlushAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class OpenAIStreamingChatCompletionResult(AIAgent agent, IEnumerable<ChatMessage> chatMessages) : IResult
|
||||
{
|
||||
public Task ExecuteAsync(HttpContext httpContext)
|
||||
{
|
||||
var cancellationToken = httpContext.RequestAborted;
|
||||
var response = httpContext.Response;
|
||||
|
||||
// Set SSE headers
|
||||
response.Headers.ContentType = "text/event-stream";
|
||||
response.Headers.CacheControl = "no-cache,no-store";
|
||||
response.Headers.Connection = "keep-alive";
|
||||
response.Headers.ContentEncoding = "identity";
|
||||
httpContext.Features.GetRequiredFeature<IHttpResponseBodyFeature>().DisableBuffering();
|
||||
|
||||
return SseFormatter.WriteAsync(
|
||||
source: this.GetStreamingResponsesAsync(cancellationToken),
|
||||
destination: response.Body,
|
||||
itemFormatter: (sseItem, bufferWriter) =>
|
||||
{
|
||||
var sseDataJsonModel = (IJsonModel<StreamingChatCompletionUpdate>)sseItem.Data;
|
||||
var json = sseDataJsonModel.Write(ModelReaderWriterOptions.Json);
|
||||
bufferWriter.Write(json);
|
||||
},
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
private async IAsyncEnumerable<SseItem<StreamingChatCompletionUpdate>> GetStreamingResponsesAsync([EnumeratorCancellation] CancellationToken cancellationToken = default)
|
||||
{
|
||||
AgentThread? agentThread = null;
|
||||
|
||||
var agentRunResponseUpdates = agent.RunStreamingAsync(chatMessages, thread: agentThread, cancellationToken: cancellationToken);
|
||||
var chatResponseUpdates = agentRunResponseUpdates.AsChatResponseUpdatesAsync();
|
||||
await foreach (var streamingChatCompletionUpdate in chatResponseUpdates.AsOpenAIStreamingChatCompletionUpdatesAsync(cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
yield return new SseItem<StreamingChatCompletionUpdate>(streamingChatCompletionUpdate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using Microsoft.Shared.Diagnostics;
|
||||
using OpenAI.Chat;
|
||||
|
||||
namespace Microsoft.Agents.AI.Hosting.OpenAI.ChatCompletions.Utils;
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1810:Initialize reference type static fields inline", Justification = "Specifically for accessing hidden members")]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1065:Do not raise exceptions in unexpected locations", Justification = "Specifically for accessing hidden members")]
|
||||
internal static class ChatCompletionsOptionsExtensions
|
||||
{
|
||||
private static readonly Func<ChatCompletionOptions, bool?> _getStreamNullable;
|
||||
private static readonly Func<ChatCompletionOptions, IList<ChatMessage>> _getMessages;
|
||||
|
||||
static ChatCompletionsOptionsExtensions()
|
||||
{
|
||||
// OpenAI SDK does not have a simple way to get the input as a c# object.
|
||||
// However, it does parse most of the interesting fields into internal properties of `ChatCompletionsOptions` object.
|
||||
|
||||
// --- Stream (internal bool? Stream { get; set; }) ---
|
||||
const string streamPropName = "Stream";
|
||||
var streamProp = typeof(ChatCompletionOptions).GetProperty(streamPropName, BindingFlags.Instance | BindingFlags.NonPublic)
|
||||
?? throw new MissingMemberException(typeof(ChatCompletionOptions).FullName!, streamPropName);
|
||||
var streamGetter = streamProp.GetGetMethod(nonPublic: true) ?? throw new MissingMethodException($"{streamPropName} getter not found.");
|
||||
|
||||
_getStreamNullable = streamGetter.CreateDelegate<Func<ChatCompletionOptions, bool?>>();
|
||||
|
||||
// --- Messages (internal IList<OpenAI.Chat.ChatMessage> Messages { get; set; }) ---
|
||||
const string inputPropName = "Messages";
|
||||
var inputProp = typeof(ChatCompletionOptions).GetProperty(inputPropName, BindingFlags.Instance | BindingFlags.NonPublic)
|
||||
?? throw new MissingMemberException(typeof(ChatCompletionOptions).FullName!, inputPropName);
|
||||
var inputGetter = inputProp.GetGetMethod(nonPublic: true)
|
||||
?? throw new MissingMethodException($"{inputPropName} getter not found.");
|
||||
|
||||
_getMessages = inputGetter.CreateDelegate<Func<ChatCompletionOptions, IList<ChatMessage>>>();
|
||||
}
|
||||
|
||||
public static IList<ChatMessage> GetMessages(this ChatCompletionOptions options)
|
||||
{
|
||||
Throw.IfNull(options);
|
||||
return _getMessages(options);
|
||||
}
|
||||
|
||||
public static bool GetStream(this ChatCompletionOptions options)
|
||||
{
|
||||
Throw.IfNull(options);
|
||||
return _getStreamNullable(options) ?? false;
|
||||
}
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.ClientModel.Primitives;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Threading;
|
||||
using Microsoft.Agents.AI.Hosting.OpenAI.ChatCompletions;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using OpenAI.Chat;
|
||||
|
||||
namespace Microsoft.Agents.AI.Hosting.OpenAI;
|
||||
|
||||
public static partial class EndpointRouteBuilderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Maps OpenAI ChatCompletions API endpoints to the specified <see cref="IEndpointRouteBuilder"/> for the given <see cref="AIAgent"/>.
|
||||
/// </summary>
|
||||
/// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the OpenAI ChatCompletions endpoints to.</param>
|
||||
/// <param name="agentName">The name of the AI agent service registered in the dependency injection container. This name is used to resolve the <see cref="AIAgent"/> instance from the keyed services.</param>
|
||||
/// <param name="path">Custom route path for the chat completions endpoint.</param>
|
||||
public static void MapOpenAIChatCompletions(
|
||||
this IEndpointRouteBuilder endpoints,
|
||||
string agentName,
|
||||
[StringSyntax("Route")] string? path = null)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(endpoints);
|
||||
ArgumentNullException.ThrowIfNull(agentName);
|
||||
if (path is null)
|
||||
{
|
||||
ValidateAgentName(agentName);
|
||||
}
|
||||
|
||||
var agent = endpoints.ServiceProvider.GetRequiredKeyedService<AIAgent>(agentName);
|
||||
|
||||
path ??= $"/{agentName}/v1/chat/completions";
|
||||
var chatCompletionsRouteGroup = endpoints.MapGroup(path);
|
||||
MapChatCompletions(chatCompletionsRouteGroup, agent);
|
||||
}
|
||||
|
||||
private static void MapChatCompletions(IEndpointRouteBuilder routeGroup, AIAgent agent)
|
||||
{
|
||||
var endpointAgentName = agent.DisplayName;
|
||||
var chatCompletionsProcessor = new AIAgentChatCompletionsProcessor(agent);
|
||||
|
||||
routeGroup.MapPost("/", async (HttpContext requestContext, CancellationToken cancellationToken) =>
|
||||
{
|
||||
var requestBinary = await BinaryData.FromStreamAsync(requestContext.Request.Body, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var chatCompletionOptions = new ChatCompletionOptions();
|
||||
var chatCompletionOptionsJsonModel = chatCompletionOptions as IJsonModel<ChatCompletionOptions>;
|
||||
Debug.Assert(chatCompletionOptionsJsonModel is not null);
|
||||
|
||||
chatCompletionOptions = chatCompletionOptionsJsonModel.Create(requestBinary, ModelReaderWriterOptions.Json);
|
||||
if (chatCompletionOptions is null)
|
||||
{
|
||||
return Results.BadRequest("Invalid request payload.");
|
||||
}
|
||||
|
||||
return await chatCompletionsProcessor.CreateChatCompletionAsync(chatCompletionOptions, cancellationToken).ConfigureAwait(false);
|
||||
}).WithName(endpointAgentName + "/CreateChatCompletion");
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -15,9 +15,9 @@ using OpenAI.Responses;
|
||||
namespace Microsoft.Agents.AI.Hosting.OpenAI;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for mapping OpenAI Responses capabilities to an <see cref="AIAgent"/>.
|
||||
/// Provides extension methods for mapping OpenAI capabilities to an <see cref="AIAgent"/>.
|
||||
/// </summary>
|
||||
public static class EndpointRouteBuilderExtensions
|
||||
public static partial class EndpointRouteBuilderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Maps OpenAI Responses API endpoints to the specified <see cref="IEndpointRouteBuilder"/> for the given <see cref="AIAgent"/>.
|
||||
Reference in New Issue
Block a user