// Copyright (c) Microsoft. All rights reserved. using System; using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using Microsoft.Agents.AI.Workflows.Observability; using Microsoft.Shared.Diagnostics; namespace Microsoft.Agents.AI.Workflows.Execution; internal sealed class ResponseEdgeRunner(IRunnerContext runContext, string executorId, string sinkId) : EdgeRunner(runContext, sinkId) { public static ResponseEdgeRunner ForPort(IRunnerContext runContext, string executorId, RequestPort port) { Throw.IfNull(port); // The port is an request port, so we can use the port's ID as the sink ID. return new ResponseEdgeRunner(runContext, executorId, port.Id); } public string ExecutorId => executorId; protected internal override async ValueTask ChaseEdgeAsync(MessageEnvelope envelope, IStepTracer? stepTracer, CancellationToken cancellationToken) { Debug.Assert(envelope.IsExternal, "Input edges should only be chased from external input"); using var activity = this.StartActivity(); activity? .SetTag(Tags.EdgeGroupType, nameof(ResponseEdgeRunner)) .SetTag(Tags.MessageSourceId, envelope.SourceId) .SetTag(Tags.MessageTargetId, $"{this.ExecutorId}[{this.EdgeData}]"); try { Executor target = await this.FindExecutorAsync(stepTracer).ConfigureAwait(false); Type? runtimeType = await this.GetMessageRuntimeTypeAsync(envelope, stepTracer, cancellationToken).ConfigureAwait(false); if (CanHandle(target, runtimeType)) { activity?.SetEdgeRunnerDeliveryStatus(EdgeRunnerDeliveryStatus.Delivered); return new DeliveryMapping(envelope, target); } activity?.SetEdgeRunnerDeliveryStatus(EdgeRunnerDeliveryStatus.DroppedTypeMismatch); return null; } catch (Exception) when (activity is not null) { activity.SetEdgeRunnerDeliveryStatus(EdgeRunnerDeliveryStatus.Exception); throw; } } private async ValueTask FindExecutorAsync(IStepTracer? tracer) => await this.RunContext.EnsureExecutorAsync(this.ExecutorId, tracer).ConfigureAwait(false); }