.NET: Workflow observability (#959)

* Add basic Workflow telemetry

* Add sample

* Add source propagation for executor spans

* Fix tests and address comments

* Fix formatting

* Fix declarative unit tests

* Address comments

* Remove Microsoft.Extensions.AI.Agents.EnableTelemetry

* Formatting

* Formatting

* Formatting

* fix solution

* Address comments

* Add workflow json definition for serialization

* Formmating

* Address comments
This commit is contained in:
Tao Chen
2025-09-30 23:40:04 +00:00
committed by GitHub
parent 2539282d30
commit 042099009f
29 changed files with 598 additions and 72 deletions
@@ -1,36 +1,54 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Threading.Tasks;
using Microsoft.Agents.AI.Workflows.Observability;
namespace Microsoft.Agents.AI.Workflows.Execution;
internal sealed class DirectEdgeRunner(IRunnerContext runContext, DirectEdgeData edgeData) :
EdgeRunner<DirectEdgeData>(runContext, edgeData)
{
public IWorkflowContext WorkflowContext { get; } = runContext.Bind(edgeData.SinkId);
private async ValueTask<Executor> FindRouterAsync(IStepTracer? tracer) => await this.RunContext.EnsureExecutorAsync(this.EdgeData.SinkId, tracer)
.ConfigureAwait(false);
protected internal override async ValueTask<DeliveryMapping?> ChaseEdgeAsync(MessageEnvelope envelope, IStepTracer? stepTracer)
{
using var activity = s_activitySource.StartActivity(ActivityNames.EdgeGroupProcess);
activity?
.SetTag(Tags.EdgeGroupType, nameof(DirectEdgeRunner))
.SetTag(Tags.MessageSourceId, this.EdgeData.SourceId)
.SetTag(Tags.MessageTargetId, this.EdgeData.SinkId);
if (envelope.TargetId is not null && this.EdgeData.SinkId != envelope.TargetId)
{
activity?.SetEdgeRunnerDeliveryStatus(EdgeRunnerDeliveryStatus.DroppedTargetMismatch);
return null;
}
object message = envelope.Message;
if (this.EdgeData.Condition is not null && !this.EdgeData.Condition(message))
try
{
return null;
}
if (this.EdgeData.Condition is not null && !this.EdgeData.Condition(message))
{
activity?.SetEdgeRunnerDeliveryStatus(EdgeRunnerDeliveryStatus.DroppedConditionFalse);
return null;
}
Executor target = await this.FindRouterAsync(stepTracer).ConfigureAwait(false);
if (target.CanHandle(envelope.MessageType))
Executor target = await this.FindRouterAsync(stepTracer).ConfigureAwait(false);
if (target.CanHandle(envelope.MessageType))
{
activity?.SetEdgeRunnerDeliveryStatus(EdgeRunnerDeliveryStatus.Delivered);
return new DeliveryMapping(envelope, target);
}
}
catch (Exception) when (activity is not null)
{
return new DeliveryMapping(envelope, target);
activity.SetEdgeRunnerDeliveryStatus(EdgeRunnerDeliveryStatus.Exception);
throw;
}
activity?.SetEdgeRunnerDeliveryStatus(EdgeRunnerDeliveryStatus.DroppedTypeMismatch);
return null;
}
}