mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
32e7ff00b5
* feat: Add support for Workflow-as-Executor * Fixes routing of 'object' compile-typed variables to properly take in type information * Fixes a concurrency issue in StepTracer * fix: Make Subworkflow ExternalRequests work properly * fix: Threading and Concurrency fixes; prep for OffThread Mode * refactor: Remove dead code around OffStreamRunEventStream Currently not used, and will be replaced with a rewrite when brought back, so having it in the change is not valuable. * ci: Work around issues with dotnet-format not properly analyzing the source * fix: Fix the logic of AsyncCoordinator and AsyncBarrier * Prevent individual wait cancellations from canceling the entire barrier * Propagate information about whether the wait was completed or cancelled, and whether any waiters were present when released * fix: Remove superfluous acces to .Keys in InProcStepTracer * refactor: Clean up AsyncCoordinator's use of AsyncBarrier
167 lines
7.1 KiB
C#
167 lines
7.1 KiB
C#
// Copyright (c) Microsoft. All rights reserved.
|
|
|
|
using System;
|
|
using System.Diagnostics;
|
|
using System.Linq;
|
|
using System.Linq.Expressions;
|
|
using Microsoft.Agents.AI.Workflows.Checkpointing;
|
|
using Microsoft.Agents.AI.Workflows.Execution;
|
|
using Microsoft.Extensions.AI;
|
|
|
|
namespace Microsoft.Agents.AI.Workflows.UnitTests;
|
|
|
|
internal static partial class ValidationExtensions
|
|
{
|
|
public static Expression<Func<EdgeConnection, bool>> CreateValidator(this EdgeConnection prototype)
|
|
{
|
|
return actual => actual.SourceIds.Count == prototype.SourceIds.Count &&
|
|
actual.SinkIds.Count == prototype.SinkIds.Count &&
|
|
prototype.SourceIds.SequenceEqual(actual.SourceIds) &&
|
|
prototype.SinkIds.SequenceEqual(actual.SinkIds);
|
|
}
|
|
|
|
public static Expression<Func<TypeId, bool>> CreateValidator(this TypeId? prototype)
|
|
{
|
|
return actual => (prototype == null && actual == null)
|
|
|| (prototype != null && actual != null
|
|
&& actual.AssemblyName == prototype.AssemblyName
|
|
&& actual.TypeName == prototype.TypeName);
|
|
}
|
|
|
|
public static Expression<Func<ExecutorInfo, bool>> CreateValidator(this ExecutorInfo prototype)
|
|
{
|
|
return actual => actual.ExecutorId == prototype.ExecutorId &&
|
|
// Rely on the TypeId test to probe TypeId serialization - just validate that we got a functional TypeId
|
|
actual.ExecutorType.Equals(prototype.ExecutorType);
|
|
}
|
|
|
|
public static Expression<Func<RequestPortInfo, bool>> CreatePortInfoValidator(this RequestPort prototype)
|
|
{
|
|
return actual => actual.PortId == prototype.Id &&
|
|
// Rely on the TypeId test to probe TypeId serialization - just validate that we got a functional TypeId
|
|
actual.RequestType.IsMatch(prototype.Request) &&
|
|
actual.ResponseType.IsMatch(prototype.Response);
|
|
}
|
|
|
|
public static Expression<Func<DirectEdgeInfo, bool>> CreateValidator(this DirectEdgeInfo prototype)
|
|
{
|
|
return actual => actual.Connection == prototype.Connection &&
|
|
actual.HasCondition == prototype.HasCondition;
|
|
}
|
|
|
|
public static Expression<Func<FanOutEdgeInfo, bool>> CreateValidator(this FanOutEdgeInfo prototype)
|
|
{
|
|
return actual => actual.Connection == prototype.Connection &&
|
|
actual.HasAssigner == prototype.HasAssigner;
|
|
}
|
|
|
|
public static Expression<Func<FanInEdgeInfo, bool>> CreateValidator(this FanInEdgeInfo prototype)
|
|
{
|
|
return actual => actual.Connection == prototype.Connection;
|
|
}
|
|
|
|
public static Expression<Func<EdgeInfo, bool>> CreatePolyValidator(this EdgeInfo prototype)
|
|
{
|
|
switch (prototype.Kind)
|
|
{
|
|
case EdgeKind.Direct:
|
|
{
|
|
var innerValidatorExpr = CreateValidator((DirectEdgeInfo)prototype);
|
|
|
|
// Check that incoming is of the correct type, and if so, chain to the body
|
|
Debug.Assert(innerValidatorExpr.Parameters.Count == 1, "Validator is of unexpected arity");
|
|
|
|
return CreateValidatorExpression(innerValidatorExpr);
|
|
}
|
|
case EdgeKind.FanOut:
|
|
{
|
|
var innerValidatorExpr = CreateValidator((FanOutEdgeInfo)prototype);
|
|
|
|
// Check that incoming is of the correct type, and if so, chain to the body
|
|
Debug.Assert(innerValidatorExpr.Parameters.Count == 1, "Validator is of unexpected arity");
|
|
|
|
return CreateValidatorExpression(innerValidatorExpr);
|
|
}
|
|
case EdgeKind.FanIn:
|
|
{
|
|
var innerValidatorExpr = CreateValidator((FanInEdgeInfo)prototype);
|
|
|
|
// Check that incoming is of the correct type, and if so, chain to the body
|
|
Debug.Assert(innerValidatorExpr.Parameters.Count == 1, "Validator is of unexpected arity");
|
|
|
|
return CreateValidatorExpression(innerValidatorExpr);
|
|
}
|
|
default:
|
|
throw new NotSupportedException($"Unsupported edge type: {prototype.Kind}");
|
|
}
|
|
|
|
Expression<Func<EdgeInfo, bool>> CreateValidatorExpression<TInner>(Expression<Func<TInner, bool>> innerValidator)
|
|
where TInner : EdgeInfo
|
|
{
|
|
var innerParam = innerValidator.Parameters[0];
|
|
var innerBody = innerValidator.Body;
|
|
|
|
var outerParam = Expression.Parameter(typeof(EdgeInfo), "actual");
|
|
var convertExpr = Expression.Convert(outerParam, typeof(TInner));
|
|
|
|
ExpressionVisitor visitor = new SubstitutionVisitor(innerParam, convertExpr);
|
|
Expression innerValidatorExpr = visitor.Visit(innerBody);
|
|
|
|
BinaryExpression bodyExpression = Expression.AndAlso(
|
|
Expression.AndAlso(
|
|
Expression.Equal(
|
|
Expression.Property(outerParam, nameof(EdgeInfo.Kind)),
|
|
Expression.Constant(prototype.Kind)
|
|
),
|
|
Expression.TypeIs(outerParam, typeof(TInner))
|
|
),
|
|
innerValidatorExpr
|
|
);
|
|
|
|
return Expression.Lambda<Func<EdgeInfo, bool>>(
|
|
bodyExpression,
|
|
outerParam);
|
|
}
|
|
}
|
|
|
|
public static Expression<Func<ScopeId, bool>> CreateValidator(this ScopeId prototype)
|
|
{
|
|
return actual => actual.ExecutorId == prototype.ExecutorId &&
|
|
actual.ScopeName == prototype.ScopeName;
|
|
}
|
|
|
|
public static Expression<Func<ScopeKey, bool>> CreateValidator(this ScopeKey prototype)
|
|
{
|
|
return actual => actual.Key == prototype.Key &&
|
|
actual.ScopeId.ScopeName == prototype.ScopeId.ScopeName &&
|
|
actual.ScopeId.ExecutorId == prototype.ScopeId.ExecutorId;
|
|
}
|
|
|
|
public static Expression<Func<ExecutorIdentity, bool>> CreateValidator(this ExecutorIdentity prototype)
|
|
{
|
|
return actual => actual.Id == prototype.Id;
|
|
}
|
|
|
|
public static Expression<Func<ExternalRequest, bool>> CreateValidator(this ExternalRequest prototype)
|
|
{
|
|
return actual => actual.RequestId == prototype.RequestId &&
|
|
actual.PortInfo == prototype.PortInfo &&
|
|
actual.Data == prototype.Data;
|
|
}
|
|
|
|
public static Expression<Func<ExternalResponse, bool>> CreateValidator(this ExternalResponse prototype)
|
|
{
|
|
return actual => actual.RequestId == prototype.RequestId &&
|
|
actual.Data == prototype.Data;
|
|
}
|
|
|
|
public static Expression<Func<ChatMessage, bool>> CreateValidatorCheckingText(this ChatMessage prototype)
|
|
{
|
|
return actual => actual.Role == prototype.Role &&
|
|
actual.AuthorName == prototype.AuthorName &&
|
|
actual.CreatedAt == prototype.CreatedAt &&
|
|
actual.MessageId == prototype.MessageId &&
|
|
actual.Text == prototype.Text;
|
|
}
|
|
}
|