From 23db9569ce9e4d4faceaec85fece61f4cfcae3ef Mon Sep 17 00:00:00 2001 From: Ben Thomas Date: Fri, 3 Apr 2026 16:35:44 -0700 Subject: [PATCH] Renaming and merging hosting extensions. (#5091) * Rename AddAgentFrameworkHandler to AddFoundryResponses and add MapFoundryResponses - Rename extension methods: AddAgentFrameworkHandler -> AddFoundryResponses, MapAgentFrameworkHandler -> MapFoundryResponses - AddFoundryResponses now calls AddResponsesServer() internally - Add MapFoundryResponses() extension on IEndpointRouteBuilder - Update sample and tests to use new API names - Remove redundant AddResponsesServer() and /ready endpoint from sample Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fixing numbering in sample. --------- Co-authored-by: alliscode Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../FoundryResponsesHosting/Program.cs | 25 ++++----- .../Hosting/ServiceCollectionExtensions.cs | 51 ++++++++++++------- .../ServiceCollectionExtensionsTests.cs | 22 ++++---- 3 files changed, 53 insertions(+), 45 deletions(-) diff --git a/dotnet/samples/04-hosting/FoundryResponsesHosting/Program.cs b/dotnet/samples/04-hosting/FoundryResponsesHosting/Program.cs index 10a82d1b87..59fa723949 100644 --- a/dotnet/samples/04-hosting/FoundryResponsesHosting/Program.cs +++ b/dotnet/samples/04-hosting/FoundryResponsesHosting/Program.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. // This sample demonstrates hosting agent-framework agents as Foundry Hosted Agents // using the Azure AI Responses Server SDK. @@ -16,7 +16,6 @@ // - AZURE_OPENAI_DEPLOYMENT - the model deployment name (default: "gpt-4o") using System.ComponentModel; -using Azure.AI.AgentServer.Responses; using Azure.AI.OpenAI; using Azure.Identity; using Microsoft.Agents.AI; @@ -29,22 +28,16 @@ using ModelContextProtocol.Client; var builder = WebApplication.CreateBuilder(args); // --------------------------------------------------------------------------- -// 1. Register the Azure AI Responses Server SDK +// 1. Create the shared Azure OpenAI chat client // --------------------------------------------------------------------------- -builder.Services.AddResponsesServer(); - -// --------------------------------------------------------------------------- -// 2. Create the shared Azure OpenAI chat client -// --------------------------------------------------------------------------- -var endpoint = new Uri(Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") - ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.")); +var endpoint = new Uri(Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.")); var deployment = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT") ?? "gpt-4o"; var azureClient = new AzureOpenAIClient(endpoint, new DefaultAzureCredential()); IChatClient chatClient = azureClient.GetChatClient(deployment).AsIChatClient(); // --------------------------------------------------------------------------- -// 3. DEMO 1: Tool Agent — local tools + Microsoft Learn MCP +// 2. DEMO 1: Tool Agent — local tools + Microsoft Learn MCP // --------------------------------------------------------------------------- Console.WriteLine("Connecting to Microsoft Learn MCP server..."); McpClient mcpClient = await McpClient.CreateAsync(new HttpClientTransport(new() @@ -72,7 +65,7 @@ builder.AddAIAgent( .WithAITools(mcpTools.Cast().ToArray()); // --------------------------------------------------------------------------- -// 4. DEMO 2: Triage Workflow — routes to specialist agents +// 3. DEMO 2: Triage Workflow — routes to specialist agents // --------------------------------------------------------------------------- ChatClientAgent triageAgent = new( chatClient, @@ -113,9 +106,9 @@ builder.AddAIAgent("triage-workflow", (_, key) => triageWorkflow.AsAIAgent(name: key)); // --------------------------------------------------------------------------- -// 5. Wire up the agent-framework handler as the IResponseHandler +// 4. Wire up the agent-framework handler and Responses Server SDK // --------------------------------------------------------------------------- -builder.Services.AddAgentFrameworkHandler(); +builder.Services.AddFoundryResponses(); var app = builder.Build(); @@ -124,10 +117,10 @@ app.Lifetime.ApplicationStopping.Register(() => mcpClient.DisposeAsync().AsTask().GetAwaiter().GetResult()); // --------------------------------------------------------------------------- -// 6. Routes +// 5. Routes // --------------------------------------------------------------------------- app.MapGet("/ready", () => Results.Ok("ready")); -app.MapResponsesServer(); +app.MapFoundryResponses(); app.MapGet("/", () => Results.Content(Pages.Home, "text/html")); app.MapGet("/tool-demo", () => Results.Content(Pages.ToolDemo, "text/html")); diff --git a/dotnet/src/Microsoft.Agents.AI.Foundry/Hosting/ServiceCollectionExtensions.cs b/dotnet/src/Microsoft.Agents.AI.Foundry/Hosting/ServiceCollectionExtensions.cs index d8e8a83f29..e3ff61ad0c 100644 --- a/dotnet/src/Microsoft.Agents.AI.Foundry/Hosting/ServiceCollectionExtensions.cs +++ b/dotnet/src/Microsoft.Agents.AI.Foundry/Hosting/ServiceCollectionExtensions.cs @@ -2,78 +2,93 @@ using System; using Azure.AI.AgentServer.Responses; +using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; namespace Microsoft.Agents.AI.Foundry.Hosting; /// -/// Extension methods for to register the agent-framework -/// response handler with the Azure AI Responses Server SDK. +/// Extension methods for registering agent-framework agents as Foundry Hosted Agents +/// using the Azure AI Responses Server SDK. /// -public static class AgentFrameworkResponsesServiceCollectionExtensions +public static class FoundryHostingExtensions { /// - /// Registers as the - /// for the Azure AI Responses Server SDK. Agents are resolved from keyed DI services + /// Registers the Azure AI Responses Server SDK and + /// as the . Agents are resolved from keyed DI services /// using the agent.name or metadata["entity_id"] from incoming requests. /// /// /// - /// Call this method after AddResponsesServer() and after registering your - /// instances (e.g., via AddAIAgent()). + /// This method calls AddResponsesServer() internally, so you do not need to + /// call it separately. Register your instances before calling this. /// /// /// Example: /// - /// builder.Services.AddResponsesServer(); /// builder.AddAIAgent("my-agent", ...); - /// builder.Services.AddAgentFrameworkHandler(); + /// builder.Services.AddFoundryResponses(); /// /// var app = builder.Build(); - /// app.MapResponsesServer(); + /// app.MapFoundryResponses(); /// /// /// /// The service collection. /// The service collection for chaining. - public static IServiceCollection AddAgentFrameworkHandler(this IServiceCollection services) + public static IServiceCollection AddFoundryResponses(this IServiceCollection services) { ArgumentNullException.ThrowIfNull(services); + services.AddResponsesServer(); services.TryAddSingleton(); return services; } /// - /// Registers a specific as the handler for all incoming requests, - /// regardless of the agent.name in the request. + /// Registers the Azure AI Responses Server SDK and a specific + /// as the handler for all incoming requests, regardless of the agent.name in the request. /// /// /// /// Use this overload when hosting a single agent. The provided agent instance is - /// registered both as a keyed service and as the default . + /// registered as both a keyed service and the default . + /// This method calls AddResponsesServer() internally. /// /// /// Example: /// - /// builder.Services.AddResponsesServer(); - /// builder.Services.AddAgentFrameworkHandler(myAgent); + /// builder.Services.AddFoundryResponses(myAgent); /// /// var app = builder.Build(); - /// app.MapResponsesServer(); + /// app.MapFoundryResponses(); /// /// /// /// The service collection. /// The agent instance to register. /// The service collection for chaining. - public static IServiceCollection AddAgentFrameworkHandler(this IServiceCollection services, AIAgent agent) + public static IServiceCollection AddFoundryResponses(this IServiceCollection services, AIAgent agent) { ArgumentNullException.ThrowIfNull(services); ArgumentNullException.ThrowIfNull(agent); + services.AddResponsesServer(); services.TryAddSingleton(agent); services.TryAddSingleton(); return services; } + + /// + /// Maps the Responses API routes for the agent-framework handler to the endpoint routing pipeline. + /// + /// The endpoint route builder. + /// Optional route prefix (e.g., "/openai/v1"). Default: empty (routes at /responses). + /// The endpoint route builder for chaining. + public static IEndpointRouteBuilder MapFoundryResponses(this IEndpointRouteBuilder endpoints, string prefix = "") + { + ArgumentNullException.ThrowIfNull(endpoints); + endpoints.MapResponsesServer(prefix); + return endpoints; + } } diff --git a/dotnet/tests/Microsoft.Agents.AI.Foundry.UnitTests/Hosting/ServiceCollectionExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.Foundry.UnitTests/Hosting/ServiceCollectionExtensionsTests.cs index ee61cef71a..d3fffaed5a 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Foundry.UnitTests/Hosting/ServiceCollectionExtensionsTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Foundry.UnitTests/Hosting/ServiceCollectionExtensionsTests.cs @@ -12,12 +12,12 @@ namespace Microsoft.Agents.AI.Foundry.UnitTests.Hosting; public class ServiceCollectionExtensionsTests { [Fact] - public void AddAgentFrameworkHandler_RegistersResponseHandler() + public void AddFoundryResponses_RegistersResponseHandler() { var services = new ServiceCollection(); services.AddLogging(); - services.AddAgentFrameworkHandler(); + services.AddFoundryResponses(); var descriptor = services.FirstOrDefault( d => d.ServiceType == typeof(ResponseHandler)); @@ -26,33 +26,33 @@ public class ServiceCollectionExtensionsTests } [Fact] - public void AddAgentFrameworkHandler_CalledTwice_RegistersOnce() + public void AddFoundryResponses_CalledTwice_RegistersOnce() { var services = new ServiceCollection(); services.AddLogging(); - services.AddAgentFrameworkHandler(); - services.AddAgentFrameworkHandler(); + services.AddFoundryResponses(); + services.AddFoundryResponses(); var count = services.Count(d => d.ServiceType == typeof(ResponseHandler)); Assert.Equal(1, count); } [Fact] - public void AddAgentFrameworkHandler_NullServices_ThrowsArgumentNullException() + public void AddFoundryResponses_NullServices_ThrowsArgumentNullException() { Assert.Throws( - () => AgentFrameworkResponsesServiceCollectionExtensions.AddAgentFrameworkHandler(null!)); + () => FoundryHostingExtensions.AddFoundryResponses(null!)); } [Fact] - public void AddAgentFrameworkHandler_WithAgent_RegistersAgentAndHandler() + public void AddFoundryResponses_WithAgent_RegistersAgentAndHandler() { var services = new ServiceCollection(); services.AddLogging(); var mockAgent = new Mock(); - services.AddAgentFrameworkHandler(mockAgent.Object); + services.AddFoundryResponses(mockAgent.Object); var handlerDescriptor = services.FirstOrDefault( d => d.ServiceType == typeof(ResponseHandler)); @@ -64,10 +64,10 @@ public class ServiceCollectionExtensionsTests } [Fact] - public void AddAgentFrameworkHandler_WithNullAgent_ThrowsArgumentNullException() + public void AddFoundryResponses_WithNullAgent_ThrowsArgumentNullException() { var services = new ServiceCollection(); Assert.Throws( - () => services.AddAgentFrameworkHandler(null!)); + () => services.AddFoundryResponses(null!)); } }