Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public async Task OnFunctionInvocationAsync(FunctionInvocationContext context, F
Activity.Current.AddTag(OpenTelemetryConstants.GenAiToolArgumentsKey, arguments);
Activity.Current.AddTag(OpenTelemetryConstants.GenAiToolTypeKey, ToolType.Function);
await InvokeWithErrorHandlingAsync(next, context);
Activity.Current.AddTag(OpenTelemetryConstants.GenAiEventContent, GetResult(context));
Activity.Current.AddTag(OpenTelemetryConstants.GenAiToolCallResultKey, GetResult(context));
Activity.Current.AddTag(OpenTelemetryConstants.GenAiToolCallIdKey, context.Function.PluginName);
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ public static Dictionary<string, List<string>> GetGenAiUserAndChoiceMessageConte
private static string? GetEventContentTag(ActivityEvent activityEvent)
{
return activityEvent.Tags?
.FirstOrDefault(tag => tag.Key == OpenTelemetryConstants.GenAiEventContent).Value as string;
.FirstOrDefault(tag => tag.Key == OpenTelemetryConstants.GenAiToolCallResultKey).Value as string;
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GenAiToolCallResultKey (gen_ai.tool.call.result) is tool-call specific, but this helper method is extracting content for user/choice message events. Reusing a tool-result attribute for non-tool events is semantically ambiguous and risks collisions/incorrect analytics. Consider looking for the user/choice event payload under a message-specific key (if one exists in the target schema), or (at minimum) support both keys during migration by checking for the prior key as a fallback so existing instrumentation doesn’t silently stop being parsed.

Copilot uses AI. Check for mistakes.
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ public static BaggageBuilder FromTurnContext(this BaggageBuilder baggageBuilder,

baggageBuilder
.SetCallerBaggage(turnContext)
.SetExecutionTypeBaggage(turnContext)
.SetTargetAgentBaggage(turnContext)
.SetTenantIdBaggage(turnContext)
.SetSourceMetadataBaggage(turnContext)
Expand All @@ -44,18 +43,6 @@ public static BaggageBuilder SetCallerBaggage(this BaggageBuilder baggageBuilder
return baggageBuilder;
}

/// <summary>
/// Sets the execution type baggage value based on caller and recipient agentic status.
/// </summary>
/// <param name="baggageBuilder">The BaggageBuilder instance.</param>
/// <param name="turnContext">The turn context containing activity information.</param>
/// <returns>The updated BaggageBuilder instance.</returns>
public static BaggageBuilder SetExecutionTypeBaggage(this BaggageBuilder baggageBuilder, ITurnContext turnContext)
{
baggageBuilder.SetRange(turnContext.GetExecutionTypePair());
return baggageBuilder;
}

/// <summary>
/// Sets the target agent-related baggage values from the TurnContext.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ public static InvokeAgentScope FromTurnContext(this InvokeAgentScope invokeAgent

invokeAgentScope
.SetCallerTags(turnContext)
.SetExecutionTypeTags(turnContext)
.SetTargetAgentTags(turnContext)
.SetTenantIdTags(turnContext)
.SetSourceMetadataTags(turnContext)
Expand All @@ -45,15 +44,6 @@ public static InvokeAgentScope SetCallerTags(this InvokeAgentScope invokeAgentSc
return invokeAgentScope;
}

/// <summary>
/// Sets the execution type tag based on caller and recipient agentic status.
/// </summary>
public static InvokeAgentScope SetExecutionTypeTags(this InvokeAgentScope invokeAgentScope, ITurnContext turnContext)
{
invokeAgentScope.RecordAttributes(turnContext.GetExecutionTypePair());
return invokeAgentScope;
}

/// <summary>
/// Sets the target agent-related tags from the TurnContext.
/// </summary>
Expand Down
25 changes: 4 additions & 21 deletions src/Observability/Hosting/Extensions/TurnContextExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using Microsoft.Agents.A365.Observability.Runtime.Tracing.Contracts;
using Microsoft.Agents.A365.Observability.Runtime.Tracing.Scopes;
using Microsoft.Agents.Builder;

Expand All @@ -14,7 +12,6 @@ namespace Microsoft.Agents.A365.Observability.Hosting.Extensions
/// </summary>
public static class TurnContextExtensions
{
private const string AgentRole = "agenticUser";
private const string O11ySpanIdKey = "O11ySpanId";
private const string O11yTraceIdKey = "O11yTraceId";

Expand All @@ -23,22 +20,8 @@ public static class TurnContextExtensions
/// </summary>
public static IEnumerable<KeyValuePair<string, object?>> GetCallerBaggagePairs(this ITurnContext turnContext)
{
yield return new KeyValuePair<string, object?>(OpenTelemetryConstants.GenAiCallerIdKey, turnContext.Activity?.From?.Id);
yield return new KeyValuePair<string, object?>(OpenTelemetryConstants.GenAiCallerNameKey, turnContext.Activity?.From?.Name);
yield return new KeyValuePair<string, object?>(OpenTelemetryConstants.GenAiCallerTenantIdKey, turnContext.Activity?.From?.TenantId);
}

/// <summary>
/// Extracts the execution type baggage key-value pair based on caller and recipient agentic status.
/// </summary>
public static IEnumerable<KeyValuePair<string, object?>> GetExecutionTypePair(this ITurnContext turnContext)
{
var isAgenticCaller = turnContext.Activity?.From?.AgenticUserId != null
|| (turnContext.Activity?.From?.Role != null && turnContext.Activity.From.Role.Equals(AgentRole, StringComparison.OrdinalIgnoreCase));
var isAgenticRecipient = turnContext.Activity?.Recipient?.AgenticUserId != null
|| (turnContext.Activity?.Recipient?.Role != null && turnContext.Activity.Recipient.Role.Equals(AgentRole, StringComparison.OrdinalIgnoreCase));
var executionType = isAgenticRecipient && isAgenticCaller ? ExecutionType.Agent2Agent.ToString() : ExecutionType.HumanToAgent.ToString();
yield return new KeyValuePair<string, object?>(OpenTelemetryConstants.GenAiExecutionTypeKey, executionType);
yield return new KeyValuePair<string, object?>(OpenTelemetryConstants.CallerIdKey, turnContext.Activity?.From?.Id);
yield return new KeyValuePair<string, object?>(OpenTelemetryConstants.CallerNameKey, turnContext.Activity?.From?.Name);
}

/// <summary>
Expand Down Expand Up @@ -86,8 +69,8 @@ public static class TurnContextExtensions
/// </summary>
public static IEnumerable<KeyValuePair<string, object?>> GetSourceMetadataBaggagePairs(this ITurnContext turnContext)
{
yield return new KeyValuePair<string, object?>(OpenTelemetryConstants.GenAiChannelNameKey, turnContext.Activity?.ChannelId?.Channel);
yield return new KeyValuePair<string, object?>(OpenTelemetryConstants.GenAiChannelLinkKey, turnContext.Activity?.ChannelId?.SubChannel);
yield return new KeyValuePair<string, object?>(OpenTelemetryConstants.ChannelNameKey, turnContext.Activity?.ChannelId?.Channel);
yield return new KeyValuePair<string, object?>(OpenTelemetryConstants.ChannelLinkKey, turnContext.Activity?.ChannelId?.SubChannel);
}

/// <summary>
Expand Down
22 changes: 2 additions & 20 deletions src/Observability/Hosting/Middleware/OutputLoggingMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,14 @@ public async Task OnTurnAsync(ITurnContext turnContext, NextDelegate next, Cance
var callerDetails = DeriveCallerDetails(turnContext);
var conversationId = turnContext.Activity?.Conversation?.Id;
var sourceMetadata = DeriveSourceMetadata(turnContext);
var executionType = DeriveExecutionType(turnContext);

turnContext.OnSendActivities(CreateSendHandler(
turnContext,
agentDetails,
tenantDetails,
callerDetails,
conversationId,
sourceMetadata,
executionType));
sourceMetadata));

await next(cancellationToken).ConfigureAwait(false);
}
Expand Down Expand Up @@ -130,28 +128,13 @@ public async Task OnTurnAsync(ITurnContext turnContext, NextDelegate next, Cance
description: channelId.SubChannel);
}

private static string? DeriveExecutionType(ITurnContext turnContext)
{
var pairs = turnContext.GetExecutionTypePair();
foreach (var pair in pairs)
{
if (pair.Key == OpenTelemetryConstants.GenAiExecutionTypeKey)
{
return pair.Value?.ToString();
}
}

return null;
}

private static SendActivitiesHandler CreateSendHandler(
ITurnContext turnContext,
AgentDetails agentDetails,
TenantDetails tenantDetails,
CallerDetails? callerDetails,
string? conversationId,
SourceMetadata? sourceMetadata,
string? executionType)
SourceMetadata? sourceMetadata)
{
return async (ctx, activities, nextSend) =>
{
Expand Down Expand Up @@ -188,7 +171,6 @@ private static SendActivitiesHandler CreateSendHandler(

try
{
outputScope.SetTagMaybe(OpenTelemetryConstants.GenAiExecutionTypeKey, executionType);
return await nextSend().ConfigureAwait(false);
}
catch (Exception ex)
Expand Down
56 changes: 7 additions & 49 deletions src/Observability/Runtime/Common/BaggageBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.Agents.A365.Observability.Runtime.Tracing.Contracts;
using Microsoft.Agents.A365.Observability.Runtime.Tracing.Scopes;
using OpenTelemetry;
using System;
Expand All @@ -19,7 +18,6 @@ namespace Microsoft.Agents.A365.Observability.Runtime.Common
/// <para>
/// <b>Certification Requirements:</b> To ensure the agent passes certification, the following properties must be set using their respective methods:
/// <list type="bullet">
/// <item><see cref="OperationSource"/></item>
/// <item><see cref="TenantId"/></item>
/// <item><see cref="ConversationId"/></item>
/// <item><see cref="ChannelName"/></item>
Expand Down Expand Up @@ -60,18 +58,6 @@ public void Dispose()

private readonly Dictionary<string, string?> _pairs = new Dictionary<string, string?>();

/// <summary>
/// Sets the operation source baggage value.
/// </summary>
/// <remarks>
/// This property must be set for the agent to pass certification requirements.
/// </remarks>
public BaggageBuilder OperationSource(OperationSource source)
{
Set(OpenTelemetryConstants.OperationSourceKey, source.ToString());
return this;
}

/// <summary>
/// Sets the tenant ID baggage value.
/// </summary>
Expand Down Expand Up @@ -156,15 +142,6 @@ public BaggageBuilder AgentBlueprintId(string? v)
return this;
}

/// <summary>
/// Sets the agent type baggage value.
/// </summary>
public BaggageBuilder AgentType(AgentType? v)
{
Set(OpenTelemetryConstants.GenAiAgentTypeKey, v?.ToString());
return this;
}

/// <summary>
/// Sets the agent platform ID baggage value.
/// </summary>
Expand All @@ -174,15 +151,6 @@ public BaggageBuilder AgentPlatformId(string? v)
return this;
}

/// <summary>
/// Sets the correlation ID baggage value.
/// </summary>
public BaggageBuilder CorrelationId(string? v)
{
Set(OpenTelemetryConstants.CorrelationIdKey, v);
return this;
}

/// <summary>
/// Sets the caller ID baggage value.
/// </summary>
Expand All @@ -191,7 +159,7 @@ public BaggageBuilder CorrelationId(string? v)
/// </remarks>
public BaggageBuilder CallerId(string? v)
{
Set(OpenTelemetryConstants.GenAiCallerIdKey, v);
Set(OpenTelemetryConstants.CallerIdKey, v);
return this;
}

Expand All @@ -203,7 +171,7 @@ public BaggageBuilder CallerId(string? v)
/// </remarks>
public BaggageBuilder CallerUpn(string? v)
{
Set(OpenTelemetryConstants.GenAiCallerUpnKey, v);
Set(OpenTelemetryConstants.CallerUpnKey, v);
return this;
}

Expand All @@ -215,7 +183,7 @@ public BaggageBuilder CallerUpn(string? v)
/// </remarks>
public BaggageBuilder CallerName(string? v)
{
Set(OpenTelemetryConstants.GenAiCallerNameKey, v);
Set(OpenTelemetryConstants.CallerNameKey, v);
return this;
}

Expand All @@ -224,7 +192,7 @@ public BaggageBuilder CallerName(string? v)
/// </summary>
public BaggageBuilder CallerClientIp(IPAddress v)
{
Set(OpenTelemetryConstants.GenAiCallerClientIpKey, v.ToString());
Set(OpenTelemetryConstants.CallerClientIpKey, v.ToString());
return this;
}

Expand Down Expand Up @@ -257,7 +225,7 @@ public BaggageBuilder ConversationItemLink(string? v)
/// </remarks>
public BaggageBuilder ChannelName(string? v)
{
Set(OpenTelemetryConstants.GenAiChannelNameKey, v);
Set(OpenTelemetryConstants.ChannelNameKey, v);
return this;
}

Expand All @@ -266,7 +234,7 @@ public BaggageBuilder ChannelName(string? v)
/// </summary>
public BaggageBuilder ChannelLink(string? v)
{
Set(OpenTelemetryConstants.GenAiChannelLinkKey, v);
Set(OpenTelemetryConstants.ChannelLinkKey, v);
return this;
}

Expand All @@ -288,15 +256,6 @@ public BaggageBuilder SessionDescription(string? v)
return this;
}

/// <summary>
/// Sets the hiring manager ID baggage value.
/// </summary>
public BaggageBuilder HiringManagerId(string? v)
{
Set(OpenTelemetryConstants.HiringManagerIdKey, v);
return this;
}

/// <summary>
/// Applies the collected baggage to the current context.
/// </summary>
Expand All @@ -317,11 +276,10 @@ public IDisposable Build()
/// <summary>
/// Convenience: begin a request baggage scope with common fields.
/// </summary>
public static IDisposable SetRequestContext(string? tenantId, string? agentId, string? correlationId = null)
public static IDisposable SetRequestContext(string? tenantId, string? agentId)
=> new BaggageBuilder()
.TenantId(tenantId)
.AgentId(agentId)
.CorrelationId(correlationId)
.Build();

/// <summary>
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing SetRequestContext to remove the correlationId parameter is a source/binary breaking change for any external callers. If this API is public, consider keeping the old overload (possibly marked [Obsolete]) and routing it to the new implementation, or providing a compatibility shim so downstream apps can upgrade without immediate code changes.

Suggested change
/// <summary>
/// <summary>
/// Convenience overload preserved for backward compatibility. The correlationId
/// parameter is ignored; use <see cref="SetRequestContext(string?, string?)"/> instead.
/// </summary>
[Obsolete("Use SetRequestContext(string? tenantId, string? agentId) instead.")]
public static IDisposable SetRequestContext(string? tenantId, string? agentId, string? correlationId)
=> SetRequestContext(tenantId, agentId);
/// <summary>

Copilot uses AI. Check for mistakes.
Expand Down
2 changes: 1 addition & 1 deletion src/Observability/Runtime/Common/ExportFormatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public class ExportFormatter
private static readonly string[] LargePayloadAttributeKeys = new[]
{
OpenTelemetryConstants.GenAiToolArgumentsKey,
OpenTelemetryConstants.GenAiEventContent,
OpenTelemetryConstants.GenAiToolCallResultKey,
OpenTelemetryConstants.GenAiInputMessagesKey,
OpenTelemetryConstants.GenAiOutputMessagesKey,
AutoInstrumentationConstants.GenAiInvocationInputKey,
Expand Down
Loading