diff --git a/docs/decisions/0003-agent-opentelemetry-instrumentation.md b/docs/decisions/0003-agent-opentelemetry-instrumentation.md
index 9b76eee411..863387b5cb 100644
--- a/docs/decisions/0003-agent-opentelemetry-instrumentation.md
+++ b/docs/decisions/0003-agent-opentelemetry-instrumentation.md
@@ -139,14 +139,6 @@ using var telemetryAgent = baseAgent.WithOpenTelemetry();
var response = await telemetryAgent.RunAsync(messages);
```
-### Integration with AppContext Switch
-
-The implementation integrates with the standard .NET telemetry enablement pattern:
-
-```csharp
-AppContext.SetSwitch("Microsoft.Extensions.AI.Agents.EnableTelemetry", true);
-```
-
### Relationship to Microsoft.Extensions.AI
This implementation follows the exact patterns established by Microsoft.Extensions.AI's OpenTelemetry instrumentation, ensuring consistency across the AI ecosystem and leveraging proven patterns for telemetry integration.
diff --git a/dotnet/Directory.Packages.props b/dotnet/Directory.Packages.props
index bc0b0dd0db..558b25a90e 100644
--- a/dotnet/Directory.Packages.props
+++ b/dotnet/Directory.Packages.props
@@ -24,6 +24,7 @@
+
diff --git a/dotnet/agent-framework-dotnet.slnx b/dotnet/agent-framework-dotnet.slnx
index 246768b367..4d44098cd0 100644
--- a/dotnet/agent-framework-dotnet.slnx
+++ b/dotnet/agent-framework-dotnet.slnx
@@ -107,6 +107,9 @@
+
+
+
diff --git a/dotnet/samples/GettingStarted/Workflows/Observability/AspireDashboard/AspireDashboard.csproj b/dotnet/samples/GettingStarted/Workflows/Observability/AspireDashboard/AspireDashboard.csproj
new file mode 100644
index 0000000000..807bc89323
--- /dev/null
+++ b/dotnet/samples/GettingStarted/Workflows/Observability/AspireDashboard/AspireDashboard.csproj
@@ -0,0 +1,22 @@
+
+
+
+ Exe
+ net9.0
+
+ enable
+ disable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dotnet/samples/GettingStarted/Workflows/Observability/AspireDashboard/Program.cs b/dotnet/samples/GettingStarted/Workflows/Observability/AspireDashboard/Program.cs
new file mode 100644
index 0000000000..18d6259cc9
--- /dev/null
+++ b/dotnet/samples/GettingStarted/Workflows/Observability/AspireDashboard/Program.cs
@@ -0,0 +1,101 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System;
+using System.Diagnostics;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.Agents.AI.Workflows;
+using Microsoft.Agents.AI.Workflows.Reflection;
+using OpenTelemetry;
+using OpenTelemetry.Logs;
+using OpenTelemetry.Metrics;
+using OpenTelemetry.Resources;
+using OpenTelemetry.Trace;
+
+namespace WorkflowObservabilitySample;
+
+///
+/// This sample shows how to enable observability in a workflow and send the traces
+/// to be visualized in Aspire Dashboard.
+///
+/// In this example, we create a simple text processing pipeline that:
+/// 1. Takes input text and converts it to uppercase using an UppercaseExecutor
+/// 2. Takes the uppercase text and reverses it using a ReverseTextExecutor
+///
+/// The executors are connected sequentially, so data flows from one to the next in order.
+/// For input "Hello, World!", the workflow produces "!DLROW ,OLLEH".
+///
+public static class Program
+{
+ private const string SourceName = "Workflow.Sample";
+ private static readonly ActivitySource s_activitySource = new(SourceName);
+
+ private static async Task Main()
+ {
+ // Configure OpenTelemetry for Aspire dashboard
+ var otlpEndpoint = Environment.GetEnvironmentVariable("OTLP_ENDPOINT") ?? "http://localhost:4317";
+
+ var resourceBuilder = ResourceBuilder
+ .CreateDefault()
+ .AddService("WorkflowSample");
+
+ using var traceProvider = Sdk.CreateTracerProviderBuilder()
+ .SetResourceBuilder(resourceBuilder)
+ .AddSource("Microsoft.Agents.AI.Workflows*")
+ .AddSource(SourceName)
+ .AddOtlpExporter(options => options.Endpoint = new Uri(otlpEndpoint))
+ .Build();
+
+ // Start a root activity for the application
+ using var activity = s_activitySource.StartActivity("main");
+ Console.WriteLine($"Operation/Trace ID: {Activity.Current?.TraceId}");
+
+ // Create the executors
+ UppercaseExecutor uppercase = new();
+ ReverseTextExecutor reverse = new();
+
+ // Build the workflow by connecting executors sequentially
+ var workflow = new WorkflowBuilder(uppercase)
+ .AddEdge(uppercase, reverse)
+ .Build();
+
+ // Execute the workflow with input data
+ Run run = await InProcessExecution.RunAsync(workflow, "Hello, World!");
+ foreach (WorkflowEvent evt in run.NewEvents)
+ {
+ if (evt is ExecutorCompletedEvent executorComplete)
+ {
+ Console.WriteLine($"{executorComplete.ExecutorId}: {executorComplete.Data}");
+ }
+ }
+ }
+}
+
+///
+/// First executor: converts input text to uppercase.
+///
+internal sealed class UppercaseExecutor() : ReflectingExecutor("UppercaseExecutor"), IMessageHandler
+{
+ ///
+ /// Processes the input message by converting it to uppercase.
+ ///
+ /// The input text to convert
+ /// Workflow context for accessing workflow services and adding events
+ /// The input text converted to uppercase
+ public async ValueTask HandleAsync(string message, IWorkflowContext context) =>
+ message.ToUpperInvariant(); // The return value will be sent as a message along an edge to subsequent executors
+}
+
+///
+/// Second executor: reverses the input text and completes the workflow.
+///
+internal sealed class ReverseTextExecutor() : ReflectingExecutor("ReverseTextExecutor"), IMessageHandler
+{
+ ///
+ /// Processes the input message by reversing the text.
+ ///
+ /// The input text to reverse
+ /// Workflow context for accessing workflow services and adding events
+ /// The input text reversed
+ public async ValueTask HandleAsync(string message, IWorkflowContext context) => new string(message.Reverse().ToArray());
+}
diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/Interpreter/DeclarativeWorkflowContext.cs b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/Interpreter/DeclarativeWorkflowContext.cs
index cabec25607..3b01032467 100644
--- a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/Interpreter/DeclarativeWorkflowContext.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/Interpreter/DeclarativeWorkflowContext.cs
@@ -29,6 +29,7 @@ internal sealed class DeclarativeWorkflowContext : IWorkflowContext
private IWorkflowContext Source { get; }
public WorkflowFormulaState State { get; }
+ public IReadOnlyDictionary? TraceContext => this.Source.TraceContext;
///
public ValueTask AddEventAsync(WorkflowEvent workflowEvent) => this.Source.AddEventAsync(workflowEvent);
diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows/Execution/DirectEdgeRunner.cs b/dotnet/src/Microsoft.Agents.AI.Workflows/Execution/DirectEdgeRunner.cs
index 61c7ad7b66..ee303c500b 100644
--- a/dotnet/src/Microsoft.Agents.AI.Workflows/Execution/DirectEdgeRunner.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Workflows/Execution/DirectEdgeRunner.cs
@@ -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(runContext, edgeData)
{
- public IWorkflowContext WorkflowContext { get; } = runContext.Bind(edgeData.SinkId);
-
private async ValueTask FindRouterAsync(IStepTracer? tracer) => await this.RunContext.EnsureExecutorAsync(this.EdgeData.SinkId, tracer)
.ConfigureAwait(false);
protected internal override async ValueTask 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;
- }
-
- Executor target = await this.FindRouterAsync(stepTracer).ConfigureAwait(false);
- if (target.CanHandle(envelope.MessageType))
- {
- return new DeliveryMapping(envelope, target);
+ 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))
+ {
+ activity?.SetEdgeRunnerDeliveryStatus(EdgeRunnerDeliveryStatus.Delivered);
+ return new DeliveryMapping(envelope, target);
+ }
+ }
+ catch (Exception) when (activity is not null)
+ {
+ activity.SetEdgeRunnerDeliveryStatus(EdgeRunnerDeliveryStatus.Exception);
+ throw;
}
+ activity?.SetEdgeRunnerDeliveryStatus(EdgeRunnerDeliveryStatus.DroppedTypeMismatch);
return null;
}
}
diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows/Execution/EdgeRunner.cs b/dotnet/src/Microsoft.Agents.AI.Workflows/Execution/EdgeRunner.cs
index 31bb01da1e..d71fa539b3 100644
--- a/dotnet/src/Microsoft.Agents.AI.Workflows/Execution/EdgeRunner.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Workflows/Execution/EdgeRunner.cs
@@ -1,5 +1,6 @@
// Copyright (c) Microsoft. All rights reserved.
+using System.Diagnostics;
using System.Threading.Tasks;
using Microsoft.Shared.Diagnostics;
@@ -13,6 +14,9 @@ internal interface IStatefulEdgeRunner
internal abstract class EdgeRunner
{
+ protected static readonly string s_namespace = typeof(EdgeRunner).Namespace!;
+ protected static readonly ActivitySource s_activitySource = new(s_namespace);
+
// TODO: Can this be sync?
protected internal abstract ValueTask ChaseEdgeAsync(MessageEnvelope envelope, IStepTracer? stepTracer);
}
diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows/Execution/FanInEdgeRunner.cs b/dotnet/src/Microsoft.Agents.AI.Workflows/Execution/FanInEdgeRunner.cs
index b79599b7d1..02c0252af3 100644
--- a/dotnet/src/Microsoft.Agents.AI.Workflows/Execution/FanInEdgeRunner.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Workflows/Execution/FanInEdgeRunner.cs
@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
+using Microsoft.Agents.AI.Workflows.Observability;
namespace Microsoft.Agents.AI.Workflows.Execution;
@@ -18,8 +19,14 @@ internal sealed class FanInEdgeRunner(IRunnerContext runContext, FanInEdgeData e
{
Debug.Assert(!envelope.IsExternal, "FanIn edges should never be chased from external input");
+ using var activity = s_activitySource.StartActivity(ActivityNames.EdgeGroupProcess);
+ activity?
+ .SetTag(Tags.EdgeGroupType, nameof(FanInEdgeRunner))
+ .SetTag(Tags.MessageTargetId, this.EdgeData.SinkId);
+
if (envelope.TargetId is not null && this.EdgeData.SinkId != envelope.TargetId)
{
+ activity?.SetEdgeRunnerDeliveryStatus(EdgeRunnerDeliveryStatus.DroppedTargetMismatch);
return null;
}
@@ -28,14 +35,30 @@ internal sealed class FanInEdgeRunner(IRunnerContext runContext, FanInEdgeData e
if (releasedMessages is null)
{
// Not ready to process yet.
+ activity?.SetEdgeRunnerDeliveryStatus(EdgeRunnerDeliveryStatus.Buffered);
return null;
}
- // TODO: Filter messages based on accepted input types?
- Executor target = await this.RunContext.EnsureExecutorAsync(this.EdgeData.SinkId, stepTracer)
- .ConfigureAwait(false);
+ try
+ {
+ // TODO: Filter messages based on accepted input types?
+ Executor target = await this.RunContext.EnsureExecutorAsync(this.EdgeData.SinkId, stepTracer)
+ .ConfigureAwait(false);
+ // Materialize the filtered list via ToList() to avoid multiple enumerations
+ var finalReleasedMessages = releasedMessages.Where(envelope => target.CanHandle(envelope.MessageType)).ToList();
+ if (finalReleasedMessages.Count == 0)
+ {
+ activity?.SetEdgeRunnerDeliveryStatus(EdgeRunnerDeliveryStatus.DroppedTypeMismatch);
+ return null;
+ }
- return new DeliveryMapping(releasedMessages.Where(envelope => target.CanHandle(envelope.MessageType)), target);
+ return new DeliveryMapping(finalReleasedMessages, target);
+ }
+ catch (Exception) when (activity is not null)
+ {
+ activity.SetEdgeRunnerDeliveryStatus(EdgeRunnerDeliveryStatus.Exception);
+ throw;
+ }
}
public ValueTask ExportStateAsync()
diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows/Execution/FanOutEdgeRunner.cs b/dotnet/src/Microsoft.Agents.AI.Workflows/Execution/FanOutEdgeRunner.cs
index 30729c1c0d..aa6133955d 100644
--- a/dotnet/src/Microsoft.Agents.AI.Workflows/Execution/FanOutEdgeRunner.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Workflows/Execution/FanOutEdgeRunner.cs
@@ -1,40 +1,61 @@
// Copyright (c) Microsoft. All rights reserved.
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
+using Microsoft.Agents.AI.Workflows.Observability;
namespace Microsoft.Agents.AI.Workflows.Execution;
internal sealed class FanOutEdgeRunner(IRunnerContext runContext, FanOutEdgeData edgeData) :
EdgeRunner(runContext, edgeData)
{
- private Dictionary BoundContexts { get; }
- = edgeData.SinkIds.ToDictionary(
- sinkId => sinkId,
- runContext.Bind);
-
protected internal override async ValueTask ChaseEdgeAsync(MessageEnvelope envelope, IStepTracer? stepTracer)
{
+ using var activity = s_activitySource.StartActivity(ActivityNames.EdgeGroupProcess);
+ activity?
+ .SetTag(Tags.EdgeGroupType, nameof(FanOutEdgeRunner))
+ .SetTag(Tags.MessageSourceId, this.EdgeData.SourceId);
+
object message = envelope.Message;
- IEnumerable targetIds =
- this.EdgeData.EdgeAssigner is null
- ? this.EdgeData.SinkIds
- : this.EdgeData.EdgeAssigner(message, this.BoundContexts.Count)
- .Select(i => this.EdgeData.SinkIds[i]);
- Executor[] result = await Task.WhenAll(targetIds.Where(IsValidTarget)
- .Select(tid => this.RunContext.EnsureExecutorAsync(tid, stepTracer)
- .AsTask()))
- .ConfigureAwait(false);
-
- if (result.Length == 0)
+ try
{
- return null;
- }
+ IEnumerable targetIds =
+ this.EdgeData.EdgeAssigner is null
+ ? this.EdgeData.SinkIds
+ : this.EdgeData.EdgeAssigner(message, this.EdgeData.SinkIds.Count)
+ .Select(i => this.EdgeData.SinkIds[i]);
- IEnumerable validTargets = result.Where(t => t.CanHandle(envelope.MessageType));
- return new DeliveryMapping(envelope, validTargets);
+ Executor[] result = await Task.WhenAll(targetIds.Where(IsValidTarget)
+ .Select(tid => this.RunContext.EnsureExecutorAsync(tid, stepTracer)
+ .AsTask()))
+ .ConfigureAwait(false);
+
+ if (result.Length == 0)
+ {
+ activity?.SetEdgeRunnerDeliveryStatus(EdgeRunnerDeliveryStatus.DroppedTargetMismatch);
+ return null;
+ }
+
+ IEnumerable validTargets = result.Where(t => t.CanHandle(envelope.MessageType));
+
+ if (!validTargets.Any())
+ {
+ activity?.SetEdgeRunnerDeliveryStatus(EdgeRunnerDeliveryStatus.DroppedTypeMismatch);
+ return null;
+ }
+
+ activity?.SetEdgeRunnerDeliveryStatus(EdgeRunnerDeliveryStatus.Delivered);
+
+ return new DeliveryMapping(envelope, validTargets);
+ }
+ catch (Exception) when (activity is not null)
+ {
+ activity.SetEdgeRunnerDeliveryStatus(EdgeRunnerDeliveryStatus.Exception);
+ throw;
+ }
bool IsValidTarget(string targetId)
{
diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows/Execution/IRunnerContext.cs b/dotnet/src/Microsoft.Agents.AI.Workflows/Execution/IRunnerContext.cs
index be427d7702..fab24f4085 100644
--- a/dotnet/src/Microsoft.Agents.AI.Workflows/Execution/IRunnerContext.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Workflows/Execution/IRunnerContext.cs
@@ -1,5 +1,6 @@
// Copyright (c) Microsoft. All rights reserved.
+using System.Collections.Generic;
using System.Threading.Tasks;
namespace Microsoft.Agents.AI.Workflows.Execution;
@@ -10,6 +11,6 @@ internal interface IRunnerContext : IExternalRequestSink
ValueTask SendMessageAsync(string sourceId, object message, string? targetId = null);
ValueTask AdvanceAsync();
- IWorkflowContext Bind(string executorId);
+ IWorkflowContext Bind(string executorId, Dictionary? traceContext = null);
ValueTask EnsureExecutorAsync(string executorId, IStepTracer? tracer);
}
diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows/Execution/ISuperStepRunner.cs b/dotnet/src/Microsoft.Agents.AI.Workflows/Execution/ISuperStepRunner.cs
index 6c317aa32d..7b892e9468 100644
--- a/dotnet/src/Microsoft.Agents.AI.Workflows/Execution/ISuperStepRunner.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Workflows/Execution/ISuperStepRunner.cs
@@ -10,6 +10,8 @@ internal interface ISuperStepRunner
{
string RunId { get; }
+ string StartExecutorId { get; }
+
bool HasUnservicedRequests { get; }
bool HasUnprocessedMessages { get; }
diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows/Execution/InputEdgeRunner.cs b/dotnet/src/Microsoft.Agents.AI.Workflows/Execution/InputEdgeRunner.cs
index 54a27baded..1174ac7ea2 100644
--- a/dotnet/src/Microsoft.Agents.AI.Workflows/Execution/InputEdgeRunner.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Workflows/Execution/InputEdgeRunner.cs
@@ -1,7 +1,9 @@
// Copyright (c) Microsoft. All rights reserved.
+using System;
using System.Diagnostics;
using System.Threading.Tasks;
+using Microsoft.Agents.AI.Workflows.Observability;
using Microsoft.Shared.Diagnostics;
namespace Microsoft.Agents.AI.Workflows.Execution;
@@ -9,8 +11,6 @@ namespace Microsoft.Agents.AI.Workflows.Execution;
internal sealed class InputEdgeRunner(IRunnerContext runContext, string sinkId)
: EdgeRunner(runContext, sinkId)
{
- public IWorkflowContext WorkflowContext { get; } = runContext.Bind(sinkId);
-
public static InputEdgeRunner ForPort(IRunnerContext runContext, InputPort port)
{
Throw.IfNull(port);
@@ -22,13 +22,30 @@ internal sealed class InputEdgeRunner(IRunnerContext runContext, string sinkId)
protected internal override async ValueTask ChaseEdgeAsync(MessageEnvelope envelope, IStepTracer? stepTracer)
{
Debug.Assert(envelope.IsExternal, "Input edges should only be chased from external input");
- Executor target = await this.FindExecutorAsync(stepTracer).ConfigureAwait(false);
- if (target.CanHandle(envelope.MessageType))
- {
- return new DeliveryMapping(envelope, target);
- }
- return null;
+ using var activity = s_activitySource.StartActivity(ActivityNames.EdgeGroupProcess);
+ activity?
+ .SetTag(Tags.EdgeGroupType, nameof(InputEdgeRunner))
+ .SetTag(Tags.MessageSourceId, envelope.SourceId)
+ .SetTag(Tags.MessageTargetId, this.EdgeData);
+
+ try
+ {
+ Executor target = await this.FindExecutorAsync(stepTracer).ConfigureAwait(false);
+ if (target.CanHandle(envelope.MessageType))
+ {
+ 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.EdgeData, tracer).ConfigureAwait(false);
diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows/Execution/MessageEnvelope.cs b/dotnet/src/Microsoft.Agents.AI.Workflows/Execution/MessageEnvelope.cs
index 420175c943..d9e5665ec5 100644
--- a/dotnet/src/Microsoft.Agents.AI.Workflows/Execution/MessageEnvelope.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Workflows/Execution/MessageEnvelope.cs
@@ -1,25 +1,37 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
+using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Agents.AI.Workflows.Checkpointing;
namespace Microsoft.Agents.AI.Workflows.Execution;
-internal sealed class MessageEnvelope(object message, ExecutorIdentity source, TypeId? declaredType = null, string? targetId = null)
+internal sealed class MessageEnvelope(
+ object message,
+ ExecutorIdentity source,
+ TypeId? declaredType = null,
+ string? targetId = null,
+ Dictionary? traceContext = null)
{
public TypeId MessageType => declaredType ?? new(message.GetType());
public object Message => message;
public ExecutorIdentity Source => source;
public string? TargetId => targetId;
+ public Dictionary? TraceContext => traceContext;
+
[MemberNotNullWhen(false, nameof(SourceId))]
public bool IsExternal => this.Source == ExecutorIdentity.None;
public string? SourceId => this.Source.Id;
- internal MessageEnvelope(object message, ExecutorIdentity source, Type declaredType, string? targetId = null)
- : this(message, source, new TypeId(declaredType), targetId)
+ internal MessageEnvelope(
+ object message,
+ ExecutorIdentity source,
+ Type declaredType,
+ string? targetId = null,
+ Dictionary? traceContext = null) : this(message, source, new TypeId(declaredType), targetId, traceContext)
{
if (!declaredType.IsInstanceOfType(message))
{
diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows/Executor.cs b/dotnet/src/Microsoft.Agents.AI.Workflows/Executor.cs
index 65bc6e8732..e273b4b8a0 100644
--- a/dotnet/src/Microsoft.Agents.AI.Workflows/Executor.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Workflows/Executor.cs
@@ -8,6 +8,7 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.Agents.AI.Workflows.Checkpointing;
using Microsoft.Agents.AI.Workflows.Execution;
+using Microsoft.Agents.AI.Workflows.Observability;
using Microsoft.Agents.AI.Workflows.Reflection;
namespace Microsoft.Agents.AI.Workflows;
@@ -25,6 +26,9 @@ public abstract class Executor : IIdentified
private readonly ExecutorOptions _options;
+ private static readonly string s_namespace = typeof(Executor).Namespace!;
+ private static readonly ActivitySource s_activitySource = new(s_namespace);
+
///
/// Initialize the executor with a unique identifier
///
@@ -88,6 +92,12 @@ public abstract class Executor : IIdentified
/// An exception is generated while handling the message.
public async ValueTask