mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
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 <bentho@microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
Unverified
parent
eb67379033
commit
23db9569ce
@@ -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<AITool>().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"));
|
||||
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for <see cref="IServiceCollection"/> 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.
|
||||
/// </summary>
|
||||
public static class AgentFrameworkResponsesServiceCollectionExtensions
|
||||
public static class FoundryHostingExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Registers <see cref="AgentFrameworkResponseHandler"/> as the <see cref="ResponseHandler"/>
|
||||
/// for the Azure AI Responses Server SDK. Agents are resolved from keyed DI services
|
||||
/// Registers the Azure AI Responses Server SDK and <see cref="AgentFrameworkResponseHandler"/>
|
||||
/// as the <see cref="ResponseHandler"/>. Agents are resolved from keyed DI services
|
||||
/// using the <c>agent.name</c> or <c>metadata["entity_id"]</c> from incoming requests.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Call this method <b>after</b> <c>AddResponsesServer()</c> and after registering your
|
||||
/// <see cref="AIAgent"/> instances (e.g., via <c>AddAIAgent()</c>).
|
||||
/// This method calls <c>AddResponsesServer()</c> internally, so you do not need to
|
||||
/// call it separately. Register your <see cref="AIAgent"/> instances before calling this.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Example:
|
||||
/// <code>
|
||||
/// builder.Services.AddResponsesServer();
|
||||
/// builder.AddAIAgent("my-agent", ...);
|
||||
/// builder.Services.AddAgentFrameworkHandler();
|
||||
/// builder.Services.AddFoundryResponses();
|
||||
///
|
||||
/// var app = builder.Build();
|
||||
/// app.MapResponsesServer();
|
||||
/// app.MapFoundryResponses();
|
||||
/// </code>
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <param name="services">The service collection.</param>
|
||||
/// <returns>The service collection for chaining.</returns>
|
||||
public static IServiceCollection AddAgentFrameworkHandler(this IServiceCollection services)
|
||||
public static IServiceCollection AddFoundryResponses(this IServiceCollection services)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(services);
|
||||
services.AddResponsesServer();
|
||||
services.TryAddSingleton<ResponseHandler, AgentFrameworkResponseHandler>();
|
||||
return services;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a specific <see cref="AIAgent"/> as the handler for all incoming requests,
|
||||
/// regardless of the <c>agent.name</c> in the request.
|
||||
/// Registers the Azure AI Responses Server SDK and a specific <see cref="AIAgent"/>
|
||||
/// as the handler for all incoming requests, regardless of the <c>agent.name</c> in the request.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Use this overload when hosting a single agent. The provided agent instance is
|
||||
/// registered both as a keyed service and as the default <see cref="AIAgent"/>.
|
||||
/// registered as both a keyed service and the default <see cref="AIAgent"/>.
|
||||
/// This method calls <c>AddResponsesServer()</c> internally.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Example:
|
||||
/// <code>
|
||||
/// builder.Services.AddResponsesServer();
|
||||
/// builder.Services.AddAgentFrameworkHandler(myAgent);
|
||||
/// builder.Services.AddFoundryResponses(myAgent);
|
||||
///
|
||||
/// var app = builder.Build();
|
||||
/// app.MapResponsesServer();
|
||||
/// app.MapFoundryResponses();
|
||||
/// </code>
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <param name="services">The service collection.</param>
|
||||
/// <param name="agent">The agent instance to register.</param>
|
||||
/// <returns>The service collection for chaining.</returns>
|
||||
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<ResponseHandler, AgentFrameworkResponseHandler>();
|
||||
return services;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps the Responses API routes for the agent-framework handler to the endpoint routing pipeline.
|
||||
/// </summary>
|
||||
/// <param name="endpoints">The endpoint route builder.</param>
|
||||
/// <param name="prefix">Optional route prefix (e.g., "/openai/v1"). Default: empty (routes at /responses).</param>
|
||||
/// <returns>The endpoint route builder for chaining.</returns>
|
||||
public static IEndpointRouteBuilder MapFoundryResponses(this IEndpointRouteBuilder endpoints, string prefix = "")
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(endpoints);
|
||||
endpoints.MapResponsesServer(prefix);
|
||||
return endpoints;
|
||||
}
|
||||
}
|
||||
|
||||
+11
-11
@@ -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<ArgumentNullException>(
|
||||
() => 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<AIAgent>();
|
||||
|
||||
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<ArgumentNullException>(
|
||||
() => services.AddAgentFrameworkHandler(null!));
|
||||
() => services.AddFoundryResponses(null!));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user