// Copyright (c) Microsoft. All rights reserved. using System; using Microsoft.Extensions.AI; using Microsoft.Extensions.DependencyInjection; using Microsoft.Shared.Diagnostics; namespace Microsoft.Agents.AI.Hosting; /// /// Provides extension methods for configuring . /// public static class HostedAgentBuilderExtensions { /// /// Configures the host agent builder to use an in-memory session store for agent session management. /// /// The host agent builder to configure with the in-memory session store. /// The same instance, configured to use an in-memory session store. public static IHostedAgentBuilder WithInMemorySessionStore(this IHostedAgentBuilder builder) { builder.ServiceCollection.AddKeyedSingleton(builder.Name, new InMemoryAgentSessionStore()); return builder; } /// /// Registers the specified agent session store with the host agent builder, enabling session-specific storage for /// agent operations. /// /// The host agent builder to configure with the session store. Cannot be null. /// The agent session store instance to register. Cannot be null. /// The same host agent builder instance, allowing for method chaining. public static IHostedAgentBuilder WithSessionStore(this IHostedAgentBuilder builder, AgentSessionStore store) { builder.ServiceCollection.AddKeyedSingleton(builder.Name, store); return builder; } /// /// Configures the host agent builder to use a custom session store implementation for agent sessions. /// /// The host agent builder to configure. /// A factory function that creates an agent session store instance using the provided service provider and agent /// name. /// The DI service lifetime for the session store registration. Defaults to /// because session stores persist conversation state across requests and are consumed independently of the agent's lifetime. /// The same host agent builder instance, enabling further configuration. public static IHostedAgentBuilder WithSessionStore(this IHostedAgentBuilder builder, Func createAgentSessionStore, ServiceLifetime lifetime = ServiceLifetime.Singleton) { builder.ServiceCollection.AddKeyedService(builder.Name, (sp, key) => { Throw.IfNull(key); var keyString = key as string; Throw.IfNullOrEmpty(keyString); return createAgentSessionStore(sp, keyString) ?? throw new InvalidOperationException($"The agent session store factory did not return a valid {nameof(AgentSessionStore)} instance for key '{keyString}'."); }, lifetime); return builder; } /// /// Adds an AI tool to an agent being configured with the service collection. /// /// The hosted agent builder. /// The AI tool to add to the agent. /// The same instance so that additional calls can be chained. /// Thrown when or is . public static IHostedAgentBuilder WithAITool(this IHostedAgentBuilder builder, AITool tool) { Throw.IfNull(builder); Throw.IfNull(tool); builder.ServiceCollection.AddKeyedSingleton(builder.Name, tool); return builder; } /// /// Adds multiple AI tools to an agent being configured with the service collection. /// /// The hosted agent builder. /// The collection of AI tools to add to the agent. /// The same instance so that additional calls can be chained. /// Thrown when or is . public static IHostedAgentBuilder WithAITools(this IHostedAgentBuilder builder, params AITool[] tools) { Throw.IfNull(builder); Throw.IfNull(tools); foreach (var tool in tools) { builder.WithAITool(tool); } return builder; } /// /// Adds AI tool to an agent being configured with the service collection. /// /// The hosted agent builder. /// A factory function that creates a AI tool using the provided service provider. /// The DI service lifetime for the tool registration. If , the agent's lifetime is used. /// The same instance so that additional calls can be chained. /// Thrown when or is . /// /// Thrown when the effective tool lifetime is shorter than the agent's lifetime, which would cause a captive dependency. /// For example, a singleton agent cannot use scoped or transient tools. /// public static IHostedAgentBuilder WithAITool(this IHostedAgentBuilder builder, Func factory, ServiceLifetime? lifetime = null) { Throw.IfNull(builder); Throw.IfNull(factory); var effectiveLifetime = lifetime ?? builder.Lifetime; ValidateToolLifetime(builder.Lifetime, effectiveLifetime); builder.ServiceCollection.AddKeyedService(builder.Name, (sp, name) => factory(sp), effectiveLifetime); return builder; } /// /// Validates that the tool lifetime is compatible with the agent lifetime. /// A tool's lifetime must be at least as long as the agent's lifetime to prevent captive dependency issues. /// internal static void ValidateToolLifetime(ServiceLifetime agentLifetime, ServiceLifetime toolLifetime) { // ServiceLifetime enum: Singleton=0, Scoped=1, Transient=2 // A higher value means a shorter lifetime. if (toolLifetime > agentLifetime) { throw new InvalidOperationException( $"A tool with lifetime '{toolLifetime}' cannot be registered for an agent with lifetime '{agentLifetime}'. " + "The tool's lifetime must be at least as long as the agent's lifetime to avoid captive dependency issues."); } } }