// Copyright (c) Microsoft. All rights reserved. using System; using System.Linq; using Microsoft.Extensions.AI; using Microsoft.Extensions.DependencyInjection; using Microsoft.Shared.Diagnostics; namespace Microsoft.Agents.AI.Hosting; /// /// Provides extension methods for configuring AI agents in a service collection. /// public static class AgentHostingServiceCollectionExtensions { /// /// Adds an AI agent to the service collection using only a name and instructions, resolving the chat client from dependency injection. /// /// The service collection to configure. /// The name of the agent. /// The instructions for the agent. /// The same instance so that additional calls can be chained. /// Thrown when or is . public static IHostedAgentBuilder AddAIAgent(this IServiceCollection services, string name, string? instructions) { Throw.IfNull(services); Throw.IfNullOrEmpty(name); return services.AddAIAgent(name, (sp, key) => { var chatClient = sp.GetRequiredService(); var tools = sp.GetKeyedServices(name).ToList(); return new ChatClientAgent(chatClient, instructions, key, tools: tools); }); } /// /// Adds an AI agent to the service collection with a provided chat client instance. /// /// The service collection to configure. /// The name of the agent. /// The instructions for the agent. /// The chat client which the agent will use for inference. /// The same instance so that additional calls can be chained. /// Thrown when or is . public static IHostedAgentBuilder AddAIAgent(this IServiceCollection services, string name, string? instructions, IChatClient chatClient) { Throw.IfNull(services); Throw.IfNullOrEmpty(name); return services.AddAIAgent(name, (sp, key) => { var tools = sp.GetKeyedServices(name).ToList(); return new ChatClientAgent(chatClient, instructions, key, tools: tools); }); } /// /// Adds an AI agent to the service collection using a chat client resolved by an optional keyed service. /// /// The service collection 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 , a non-keyed service will be resolved. /// The same instance so that additional calls can be chained. /// Thrown when or is . public static IHostedAgentBuilder AddAIAgent(this IServiceCollection services, string name, string? instructions, object? chatClientServiceKey) { Throw.IfNull(services); Throw.IfNullOrEmpty(name); return services.AddAIAgent(name, (sp, key) => { var chatClient = chatClientServiceKey is null ? sp.GetRequiredService() : sp.GetRequiredKeyedService(chatClientServiceKey); var tools = sp.GetKeyedServices(name).ToList(); return new ChatClientAgent(chatClient, instructions, key, tools: tools); }); } /// /// Adds an AI agent to the service collection using a chat client (optionally keyed) and a description. /// /// The service collection 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 , a non-keyed service will be resolved. /// The same instance so that additional calls can be chained. /// Thrown when or is . public static IHostedAgentBuilder AddAIAgent(this IServiceCollection services, string name, string? instructions, string? description, object? chatClientServiceKey) { Throw.IfNull(services); Throw.IfNullOrEmpty(name); return services.AddAIAgent(name, (sp, key) => { var chatClient = chatClientServiceKey is null ? sp.GetRequiredService() : sp.GetRequiredKeyedService(chatClientServiceKey); var tools = sp.GetKeyedServices(name).ToList(); return new ChatClientAgent(chatClient, instructions: instructions, name: key, description: description, tools: tools); }); } /// /// Adds an AI agent to the service collection using a custom factory delegate. /// /// The service collection 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 same instance so that additional calls can be chained. /// Thrown when , , or is . /// Thrown when the agent factory delegate returns or an agent whose does not match . public static IHostedAgentBuilder AddAIAgent(this IServiceCollection services, string name, Func createAgentDelegate) { Throw.IfNull(services); Throw.IfNull(name); Throw.IfNull(createAgentDelegate); 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; }); return new HostedAgentBuilder(name, services); } }