mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
0086d38f58
* refactor: Normalize Run/RunStreaming with AIAgent * refactor: Clarify Session vs. Run -level concepts * Rename RunId to SessionId to better match Run/Session terminology in AIAgent * [BREAKING]: Will break existing checkpointed sessions in CosmosDb due to field rename * refactor: Rename and simplify interface around getting typed data out of ExternalRequest/Response * Also adds hints around using value types in PortableValue * refactor: Rename AddFanInEdge to AddFanInBarrierEdge This will prevent a breaking change later when we introduce a programmable FanIn edge, analogous to the FanOut edge's EdgeSelector. The goal, in the long run is to support a number of different FanIn scenarios, with naive FanIn (no barrier) by default, similar to FanOut. * refactor: AsAgent(this Workflow, ...) => AsAIAgent(...) * misc - part1: SwitchBuilder internal --------- Co-authored-by: Dmytro Struk <13853051+dmytrostruk@users.noreply.github.com>
130 lines
5.3 KiB
C#
130 lines
5.3 KiB
C#
// Copyright (c) Microsoft. All rights reserved.
|
|
|
|
using System;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace Microsoft.Agents.AI.Workflows;
|
|
|
|
/// <summary>
|
|
/// Represents the binding information for a workflow executor, including its identifier, factory method, type, and
|
|
/// optional raw value.
|
|
/// </summary>
|
|
/// <param name="Id">The unique identifier for the executor in the workflow.</param>
|
|
/// <param name="FactoryAsync">A factory function that creates an instance of the executor. The function accepts two string parameters and returns
|
|
/// a ValueTask containing the created Executor instance.</param>
|
|
/// <param name="ExecutorType">The type of the executor. Must be a type derived from Executor.</param>
|
|
/// <param name="RawValue">An optional raw value associated with the binding.</param>
|
|
public abstract record class ExecutorBinding(string Id, Func<string, ValueTask<Executor>>? FactoryAsync, Type ExecutorType, object? RawValue = null)
|
|
: IIdentified,
|
|
IEquatable<IIdentified>,
|
|
IEquatable<string>
|
|
{
|
|
/// <summary>
|
|
/// Gets a value indicating whether the binding is a placeholder (i.e., does not have a factory method defined).
|
|
/// </summary>
|
|
[MemberNotNullWhen(false, nameof(FactoryAsync))]
|
|
public bool IsPlaceholder => this.FactoryAsync == null;
|
|
|
|
/// <summary>
|
|
/// Gets a value whether the executor created from this binding is a shared instance across all runs.
|
|
/// </summary>
|
|
public abstract bool IsSharedInstance { get; }
|
|
|
|
/// <summary>
|
|
/// Gets a value whether instances of the executor created from this binding can be used in concurrent runs
|
|
/// from the same <see cref="Workflow"/> instance.
|
|
/// </summary>
|
|
public abstract bool SupportsConcurrentSharedExecution { get; }
|
|
|
|
/// <summary>
|
|
/// Gets a value whether instances of the executor created from this binding can be reset between subsequent
|
|
/// runs from the same <see cref="Workflow"/> instance. This value is not relevant for executors that <see
|
|
/// cref="SupportsConcurrentSharedExecution"/>.
|
|
/// </summary>
|
|
public abstract bool SupportsResetting { get; }
|
|
|
|
/// <inheritdoc/>
|
|
public override string ToString() => $"{this.Id}:{(this.IsPlaceholder ? ":<unbound>" : this.ExecutorType.Name)}";
|
|
|
|
private Executor CheckId(Executor executor)
|
|
{
|
|
if (executor.Id != this.Id)
|
|
{
|
|
throw new InvalidOperationException(
|
|
$"Executor ID mismatch: expected '{this.Id}', but got '{executor.Id}'.");
|
|
}
|
|
|
|
return executor;
|
|
}
|
|
|
|
internal async ValueTask<Executor> CreateInstanceAsync(string sessionId)
|
|
=> !this.IsPlaceholder
|
|
? this.CheckId(await this.FactoryAsync(sessionId).ConfigureAwait(false))
|
|
: throw new InvalidOperationException(
|
|
$"Cannot create executor with ID '{this.Id}': Binding ({this.GetType().Name}) is a placeholder.");
|
|
|
|
/// <inheritdoc/>
|
|
public virtual bool Equals(ExecutorBinding? other) =>
|
|
other is not null && other.Id == this.Id;
|
|
|
|
/// <inheritdoc/>
|
|
public bool Equals(IIdentified? other) =>
|
|
other is not null && other.Id == this.Id;
|
|
|
|
/// <inheritdoc/>
|
|
public bool Equals(string? other) =>
|
|
other is not null && other == this.Id;
|
|
|
|
internal ValueTask<bool> TryResetAsync()
|
|
{
|
|
// Non-shared instances do not need resetting
|
|
if (!this.IsSharedInstance)
|
|
{
|
|
return new(true);
|
|
}
|
|
|
|
// If the executor supports concurrent use, then resetting is a no-op.
|
|
if (!this.SupportsResetting)
|
|
{
|
|
return new(false);
|
|
}
|
|
|
|
return this.ResetCoreAsync();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Resets the executor's shared resources to their initial state. Must be overridden by bindings that support
|
|
/// resetting.
|
|
/// </summary>
|
|
/// <exception cref="InvalidOperationException"></exception>
|
|
protected virtual ValueTask<bool> ResetCoreAsync() => throw new InvalidOperationException("ExecutorBindings that support resetting must override ResetCoreAsync()");
|
|
|
|
/// <inheritdoc/>
|
|
public override int GetHashCode() => this.Id.GetHashCode();
|
|
|
|
/// <summary>
|
|
/// Defines an implicit conversion from an Executor to a <see cref="ExecutorBinding"/>.
|
|
/// </summary>
|
|
/// <param name="executor">The Executor instance to convert.</param>
|
|
public static implicit operator ExecutorBinding(Executor executor) => executor.BindExecutor();
|
|
|
|
/// <summary>
|
|
/// Defines an implicit conversion from a string identifier to an <see cref="ExecutorPlaceholder"/>.
|
|
/// </summary>
|
|
/// <param name="id">The string identifier to convert to a placeholder.</param>
|
|
public static implicit operator ExecutorBinding(string id) => new ExecutorPlaceholder(id);
|
|
|
|
/// <summary>
|
|
/// Defines an implicit conversion from a <see cref="RequestPort "/>to an <see cref="ExecutorBinding"/>.
|
|
/// </summary>
|
|
/// <param name="port">The RequestPort instance to convert.</param>
|
|
public static implicit operator ExecutorBinding(RequestPort port) => port.BindAsExecutor();
|
|
|
|
/// <summary>
|
|
/// Defines an implicit conversion from an <see cref="AIAgent"/> to an <see cref="ExecutorBinding"/> instance.
|
|
/// </summary>
|
|
/// <param name="agent"></param>
|
|
public static implicit operator ExecutorBinding(AIAgent agent) => agent.BindAsExecutor();
|
|
}
|