// Copyright (c) Microsoft. All rights reserved. using System; using System.Linq; using Microsoft.Extensions.AI; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Shared.Diagnostics; namespace Microsoft.Agents.AI.Hosting; /// /// Provides extension methods for configuring AI agents in a host application builder. /// public static class HostApplicationBuilderAgentExtensions { /// /// Adds an AI agent to the host application builder with the specified name and instructions. /// /// The host application builder to configure. /// The name of the agent. /// The instructions for the agent. /// The configured host application builder. /// Thrown when , , or is null. public static IHostApplicationBuilder AddAIAgent(this IHostApplicationBuilder builder, string name, string? instructions) { Throw.IfNull(builder); Throw.IfNullOrEmpty(name); return builder.AddAIAgent(name, instructions, chatClientServiceKey: null); } /// /// Adds an AI agent to the host application builder with the specified name, instructions, and chat client key. /// /// The host application builder to configure. /// The name of the agent. /// The instructions for the agent. /// The chat client which the agent will use for inference. /// The configured host application builder. /// Thrown when , , or is null. public static IHostApplicationBuilder AddAIAgent(this IHostApplicationBuilder builder, string name, string? instructions, IChatClient chatClient) { Throw.IfNull(builder); Throw.IfNullOrEmpty(name); return builder.AddAIAgent(name, (sp, key) => new ChatClientAgent(chatClient, instructions, key)); } /// /// Adds an AI agent to the host application builder with the specified name, instructions, and chat client key. /// /// The host application builder to configure. /// The name of the agent. /// The instructions for the agent. /// A description of the agent. /// The key to use when resolving the chat client from the service provider. If null, a non-keyed service will be resolved. /// The configured host application builder. /// Thrown when , , or is null. public static IHostApplicationBuilder AddAIAgent(this IHostApplicationBuilder builder, string name, string? instructions, string? description, object? chatClientServiceKey) { Throw.IfNull(builder); Throw.IfNullOrEmpty(name); return builder.AddAIAgent(name, (sp, key) => { var chatClient = chatClientServiceKey is null ? sp.GetRequiredService() : sp.GetRequiredKeyedService(chatClientServiceKey); return new ChatClientAgent(chatClient, instructions: instructions, name: key, description: description); }); } /// /// Adds an AI agent to the host application builder with the specified name, instructions, and chat client key. /// /// The host application builder to configure. /// The name of the agent. /// The instructions for the agent. /// The key to use when resolving the chat client from the service provider. If null, a non-keyed service will be resolved. /// The configured host application builder. /// Thrown when , , or is null. public static IHostApplicationBuilder AddAIAgent(this IHostApplicationBuilder builder, string name, string? instructions, object? chatClientServiceKey) { Throw.IfNull(builder); Throw.IfNullOrEmpty(name); return builder.AddAIAgent(name, (sp, key) => { var chatClient = chatClientServiceKey is null ? sp.GetRequiredService() : sp.GetRequiredKeyedService(chatClientServiceKey); return new ChatClientAgent(chatClient, instructions, key); }); } /// /// Adds an AI agent to the host application builder using a custom factory delegate. /// /// The host application builder to configure. /// The name of the agent. /// A factory delegate that creates the AI agent instance. The delegate receives the service provider and agent key as parameters. /// The configured host application builder. /// Thrown when , , or is null. /// Thrown when the agent factory delegate returns null or an invalid AI agent instance. public static IHostApplicationBuilder AddAIAgent(this IHostApplicationBuilder builder, string name, Func createAgentDelegate) { Throw.IfNull(builder); Throw.IfNull(name); Throw.IfNull(createAgentDelegate); builder.Services.AddKeyedSingleton(name, (sp, key) => { Throw.IfNull(key); var keyString = key as string; Throw.IfNullOrEmpty(keyString); var agent = createAgentDelegate(sp, keyString) ?? throw new InvalidOperationException($"The agent factory did not return a valid {nameof(AIAgent)} instance for key '{keyString}'."); if (!string.Equals(agent.Name, keyString, StringComparison.Ordinal)) { throw new InvalidOperationException($"The agent factory returned an agent with name '{agent.Name}', but the expected name is '{keyString}'."); } return agent; }); // Register the agent by name for discovery. var agentHostBuilder = GetAgentRegistry(builder); agentHostBuilder.AgentNames.Add(name); return builder; } private static LocalAgentRegistry GetAgentRegistry(IHostApplicationBuilder builder) { var descriptor = builder.Services.FirstOrDefault(s => !s.IsKeyedService && s.ServiceType.Equals(typeof(LocalAgentRegistry))); if (descriptor?.ImplementationInstance is not LocalAgentRegistry instance) { instance = new LocalAgentRegistry(); ConfigureHostBuilder(builder, instance); } return instance; } private static void ConfigureHostBuilder(IHostApplicationBuilder builder, LocalAgentRegistry agentHostBuilderContext) { builder.Services.Add(ServiceDescriptor.Singleton(agentHostBuilderContext)); builder.Services.AddSingleton(); } }