mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
a71f768331
* delete the AIAgent.DisplayName property * use agent name as a first value for activity display name * Update dotnet/src/Microsoft.Agents.AI.Workflows/Specialized/HandoffAgentExecutor.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
171 lines
8.3 KiB
C#
171 lines
8.3 KiB
C#
// Copyright (c) Microsoft. All rights reserved.
|
|
|
|
using System;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using Microsoft.Agents.AI;
|
|
using Microsoft.Agents.AI.Hosting;
|
|
using Microsoft.Agents.AI.Hosting.OpenAI;
|
|
using Microsoft.Agents.AI.Hosting.OpenAI.Conversations;
|
|
using Microsoft.Agents.AI.Hosting.OpenAI.Responses;
|
|
using Microsoft.AspNetCore.Http;
|
|
using Microsoft.AspNetCore.Routing;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
|
|
namespace Microsoft.AspNetCore.Builder;
|
|
|
|
/// <summary>
|
|
/// Provides extension methods for mapping OpenAI capabilities to an <see cref="AIAgent"/>.
|
|
/// </summary>
|
|
public static partial class MicrosoftAgentAIHostingOpenAIEndpointRouteBuilderExtensions
|
|
{
|
|
/// <summary>
|
|
/// Maps OpenAI Responses API endpoints to the specified <see cref="IEndpointRouteBuilder"/> for the given <see cref="IHostedAgentBuilder"/>.
|
|
/// </summary>
|
|
/// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the OpenAI Responses endpoints to.</param>
|
|
/// <param name="agentBuilder">The builder for <see cref="AIAgent"/> to map the OpenAI Responses endpoints for.</param>
|
|
public static IEndpointConventionBuilder MapOpenAIResponses(this IEndpointRouteBuilder endpoints, IHostedAgentBuilder agentBuilder)
|
|
=> MapOpenAIResponses(endpoints, agentBuilder, path: null);
|
|
|
|
/// <summary>
|
|
/// Maps OpenAI Responses API endpoints to the specified <see cref="IEndpointRouteBuilder"/> for the given <see cref="IHostedAgentBuilder"/>.
|
|
/// </summary>
|
|
/// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the OpenAI Responses endpoints to.</param>
|
|
/// <param name="agentBuilder">The builder for <see cref="AIAgent"/> to map the OpenAI Responses endpoints for.</param>
|
|
/// <param name="path">Custom route path for the OpenAI Responses endpoint.</param>
|
|
public static IEndpointConventionBuilder MapOpenAIResponses(this IEndpointRouteBuilder endpoints, IHostedAgentBuilder agentBuilder, string? path)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(endpoints);
|
|
ArgumentNullException.ThrowIfNull(agentBuilder);
|
|
|
|
var agent = endpoints.ServiceProvider.GetRequiredKeyedService<AIAgent>(agentBuilder.Name);
|
|
return MapOpenAIResponses(endpoints, agent, path);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Maps OpenAI Responses 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 Responses endpoints to.</param>
|
|
/// <param name="agent">The <see cref="AIAgent"/> instance to map the OpenAI Responses endpoints for.</param>
|
|
public static IEndpointConventionBuilder MapOpenAIResponses(this IEndpointRouteBuilder endpoints, AIAgent agent) =>
|
|
MapOpenAIResponses(endpoints, agent, responsesPath: null);
|
|
|
|
/// <summary>
|
|
/// Maps OpenAI Responses 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 Responses endpoints to.</param>
|
|
/// <param name="agent">The <see cref="AIAgent"/> instance to map the OpenAI Responses endpoints for.</param>
|
|
/// <param name="responsesPath">Custom route path for the responses endpoint.</param>
|
|
public static IEndpointConventionBuilder MapOpenAIResponses(
|
|
this IEndpointRouteBuilder endpoints,
|
|
AIAgent agent,
|
|
[StringSyntax("Route")] string? responsesPath)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(endpoints);
|
|
ArgumentNullException.ThrowIfNull(agent);
|
|
ArgumentException.ThrowIfNullOrWhiteSpace(agent.Name, nameof(agent.Name));
|
|
ValidateAgentName(agent.Name);
|
|
|
|
responsesPath ??= $"/{agent.Name}/v1/responses";
|
|
|
|
// Create an executor for this agent
|
|
var executor = new AIAgentResponseExecutor(agent);
|
|
var storageOptions = endpoints.ServiceProvider.GetService<InMemoryStorageOptions>() ?? new InMemoryStorageOptions();
|
|
var conversationStorage = endpoints.ServiceProvider.GetService<IConversationStorage>();
|
|
var responsesService = new InMemoryResponsesService(executor, storageOptions, conversationStorage);
|
|
|
|
var handlers = new ResponsesHttpHandler(responsesService);
|
|
|
|
var group = endpoints.MapGroup(responsesPath);
|
|
var endpointAgentName = agent.Name ?? agent.Id;
|
|
|
|
// Create response endpoint
|
|
group.MapPost("/", handlers.CreateResponseAsync)
|
|
.WithName(endpointAgentName + "/CreateResponse")
|
|
.WithSummary("Creates a model response for the given input");
|
|
|
|
// Get response endpoint
|
|
group.MapGet("{responseId}", handlers.GetResponseAsync)
|
|
.WithName(endpointAgentName + "/GetResponse")
|
|
.WithSummary("Retrieves a response by ID");
|
|
|
|
// Cancel response endpoint
|
|
group.MapPost("{responseId}/cancel", handlers.CancelResponseAsync)
|
|
.WithName(endpointAgentName + "/CancelResponse")
|
|
.WithSummary("Cancels an in-progress response");
|
|
|
|
// Delete response endpoint
|
|
group.MapDelete("{responseId}", handlers.DeleteResponseAsync)
|
|
.WithName(endpointAgentName + "/DeleteResponse")
|
|
.WithSummary("Deletes a response");
|
|
|
|
// List response input items endpoint
|
|
group.MapGet("{responseId}/input_items", handlers.ListResponseInputItemsAsync)
|
|
.WithName(endpointAgentName + "/ListResponseInputItems")
|
|
.WithSummary("Lists the input items for a response");
|
|
|
|
return group;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Maps OpenAI Responses API endpoints to the specified <see cref="IEndpointRouteBuilder"/>.
|
|
/// </summary>
|
|
/// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the OpenAI Responses endpoints to.</param>
|
|
public static IEndpointConventionBuilder MapOpenAIResponses(this IEndpointRouteBuilder endpoints) =>
|
|
MapOpenAIResponses(endpoints, responsesPath: null);
|
|
|
|
/// <summary>
|
|
/// Maps OpenAI Responses API endpoints to the specified <see cref="IEndpointRouteBuilder"/>.
|
|
/// </summary>
|
|
/// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the OpenAI Responses endpoints to.</param>
|
|
/// <param name="responsesPath">Custom route path for the responses endpoint.</param>
|
|
public static IEndpointConventionBuilder MapOpenAIResponses(
|
|
this IEndpointRouteBuilder endpoints,
|
|
[StringSyntax("Route")] string? responsesPath)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(endpoints);
|
|
|
|
responsesPath ??= "/v1/responses";
|
|
var responsesService = endpoints.ServiceProvider.GetService<IResponsesService>()
|
|
?? throw new InvalidOperationException("IResponsesService is not registered. Call AddOpenAIResponses() in your service configuration.");
|
|
var handlers = new ResponsesHttpHandler(responsesService);
|
|
|
|
var group = endpoints.MapGroup(responsesPath);
|
|
|
|
// Create response endpoint
|
|
group.MapPost("/", handlers.CreateResponseAsync)
|
|
.WithName("CreateResponse")
|
|
.WithSummary("Creates a model response for the given input");
|
|
|
|
// Get response endpoint
|
|
group.MapGet("{responseId}", handlers.GetResponseAsync)
|
|
.WithName("GetResponse")
|
|
.WithSummary("Retrieves a response by ID");
|
|
|
|
// Cancel response endpoint
|
|
group.MapPost("{responseId}/cancel", handlers.CancelResponseAsync)
|
|
.WithName("CancelResponse")
|
|
.WithSummary("Cancels an in-progress response");
|
|
|
|
// Delete response endpoint
|
|
group.MapDelete("{responseId}", handlers.DeleteResponseAsync)
|
|
.WithName("DeleteResponse")
|
|
.WithSummary("Deletes a response");
|
|
|
|
// List response input items endpoint
|
|
group.MapGet("{responseId}/input_items", handlers.ListResponseInputItemsAsync)
|
|
.WithName("ListResponseInputItems")
|
|
.WithSummary("Lists the input items for a response");
|
|
|
|
return group;
|
|
}
|
|
|
|
private static void ValidateAgentName([NotNull] string agentName)
|
|
{
|
|
var escaped = Uri.EscapeDataString(agentName);
|
|
if (!string.Equals(escaped, agentName, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
throw new ArgumentException($"Agent name '{agentName}' contains characters invalid for URL routes.", nameof(agentName));
|
|
}
|
|
}
|
|
}
|