// 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();
}
}