// Copyright (c) Microsoft. All rights reserved. using System; using System.Threading.Tasks; namespace Microsoft.Agents.AI.Workflows; /// /// Provides methods for creating instances. /// public static class Configured { /// /// Creates a instance from an existing subject instance. /// /// /// The subject instance. If the subject implements , its ID will be used /// and checked against the provided ID (if any). /// /// /// A unique identifier for the configured subject. This is required if the subject does not implement /// /// /// /// The raw representation of the subject instance. /// /// public static Configured FromInstance(TSubject subject, string? id = null, object? raw = null) { if (subject is IIdentified identified) { if (id is not null && identified.Id != id) { throw new ArgumentException($"Provided ID '{id}' does not match subject's ID '{identified.Id}'.", nameof(id)); } return new Configured((_, __) => new(subject), id: identified.Id, raw: raw ?? subject); } if (id is null) { throw new ArgumentNullException(nameof(id), "ID must be provided when the subject does not implement IIdentified."); } return new Configured((_, __) => new(subject), id, raw: raw ?? subject); } } /// /// A representation of a preconfigured, lazy-instantiatable instance of . /// /// The type of the preconfigured subject. /// A factory to intantiate the subject when desired. /// The unique identifier for the configured subject. /// public class Configured(Func> factoryAsync, string id, object? raw = null) { /// /// Gets the raw representation of the configured object, if any. /// public object? Raw => raw; /// /// Gets the configured identifier for the subject. /// public string Id => id; /// /// Gets the factory function to create an instance of given a . /// public Func> FactoryAsync => factoryAsync; /// /// The configuration for this configured instance. /// public Config Configuration => new(this.Id); /// /// Gets a "partially" applied factory function that only requires no parameters to create an instance of /// with the provided instance. /// internal Func> BoundFactoryAsync => (runId) => this.FactoryAsync(this.Configuration, runId); } /// /// A representation of a preconfigured, lazy-instantiatable instance of . /// /// The type of the preconfigured subject. /// The type of configuration options for the preconfigured subject. /// A factory to intantiate the subject when desired. /// The unique identifier for the configured subject. /// Additional configuration options for the subject. /// public class Configured(Func, string, ValueTask> factoryAsync, string id, TOptions? options = default, object? raw = null) { /// /// The raw representation of the configured object, if any. /// public object? Raw => raw; /// /// Gets the configured identifier for the subject. /// public string Id => id; /// /// Gets the options associated with this instance. /// public TOptions? Options => options; /// /// Gets the factory function to create an instance of given a . /// public Func, string, ValueTask> FactoryAsync => factoryAsync; /// /// The configuration for this configured instance. /// public Config Configuration => new(this.Id, this.Options); /// /// Gets a "partially" applied factory function that only requires no parameters to create an instance of /// with the provided instance. /// internal Func> BoundFactoryAsync => (runId) => this.CreateValidatingMemoizedFactory()(this.Configuration, runId); private Func> CreateValidatingMemoizedFactory() { return FactoryAsync; async ValueTask FactoryAsync(Config configuration, string runId) { if (this.Id != configuration.Id) { throw new InvalidOperationException($"Requested instance ID '{configuration.Id}' does not match configured ID '{this.Id}'."); } TSubject subject = await this.FactoryAsync(this.Configuration, runId).ConfigureAwait(false); if (this.Id is not null && subject is IIdentified identified && identified.Id != this.Id) { throw new InvalidOperationException($"Created instance ID '{identified.Id}' does not match configured ID '{this.Id}'."); } return subject; } } /// /// Memoizes and erases the typed configuration options for the subject. /// public Configured Memoize() => new(this.CreateValidatingMemoizedFactory(), this.Id); }