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