Skip to content
Merged
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## Unreleased

- Filter extra attributes dynamically instead of against a static reserved-key list: `BaseDataBuilder.AddExtraAttributes` now skips any key already set by the builder on the span (`!attributes.ContainsKey(key)`), removing the hard-coded `ReservedAttributeKeys` set so newly added builder attributes are protected automatically ([#107](https://github.com/microsoft/opentelemetry-distro-dotnet/pull/107))

## 1.0.6 - 2026-07-01

- Make the `invoke_agent` span compliant with OpenTelemetry GenAI semantic conventions v1.42: add request/response GenAI attributes (`gen_ai.request.*`, `gen_ai.data_source.id`, `gen_ai.output.type`, `gen_ai.system_instructions`, `gen_ai.response.finish_reasons`, `gen_ai.usage.*`) via reusable `GenAiRequestParameters`/`GenAiResponseParameters`, and emit `gen_ai.provider.name` on spans that carry agent details (e.g. `invoke_agent`, inference). Token usage is now emitted as integers and `gen_ai.response.finish_reasons` as a string array across all spans ([#120](https://github.com/microsoft/opentelemetry-distro-dotnet/pull/120))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,65 +31,6 @@ protected static T ApplyStatus(T data, Exception? error)
return data;
}

// Reserved attribute keys managed by specific builder methods; extra attributes must NOT override these.
private static readonly HashSet<string> ReservedAttributeKeys = new HashSet<string>(StringComparer.Ordinal)
{
OpenTelemetryConstants.GenAiInputMessagesKey,
OpenTelemetryConstants.GenAiOutputMessagesKey,
OpenTelemetryConstants.GenAiAgentIdKey,
OpenTelemetryConstants.GenAiAgentNameKey,
OpenTelemetryConstants.GenAiAgentDescriptionKey,
OpenTelemetryConstants.GenAiAgentVersionKey,
OpenTelemetryConstants.AgentAUIDKey,
OpenTelemetryConstants.AgentEmailKey,
OpenTelemetryConstants.AgentBlueprintIdKey,
OpenTelemetryConstants.AgentPlatformIdKey,
OpenTelemetryConstants.TenantIdKey,
OpenTelemetryConstants.GenAiProviderNameKey,
OpenTelemetryConstants.ServerAddressKey,
OpenTelemetryConstants.ServerPortKey,
OpenTelemetryConstants.ChannelNameKey,
OpenTelemetryConstants.ChannelLinkKey,
OpenTelemetryConstants.UserIdKey,
OpenTelemetryConstants.UserEmailKey,
OpenTelemetryConstants.UserNameKey,
OpenTelemetryConstants.CallerAgentNameKey,
OpenTelemetryConstants.CallerAgentIdKey,
OpenTelemetryConstants.CallerAgentBlueprintIdKey,
OpenTelemetryConstants.CallerAgentAUIDKey,
OpenTelemetryConstants.CallerAgentEmailKey,
OpenTelemetryConstants.CallerAgentPlatformIdKey,
OpenTelemetryConstants.CallerAgentVersionKey,
OpenTelemetryConstants.CallerClientIpKey,
OpenTelemetryConstants.GenAiConversationIdKey,
OpenTelemetryConstants.SessionIdKey,
OpenTelemetryConstants.GenAiToolNameKey,
OpenTelemetryConstants.GenAiToolArgumentsKey,
OpenTelemetryConstants.GenAiToolCallIdKey,
OpenTelemetryConstants.GenAiToolDescriptionKey,
OpenTelemetryConstants.GenAiToolTypeKey,
OpenTelemetryConstants.GenAiToolCallResultKey,
OpenTelemetryConstants.GenAiOperationNameKey,
OpenTelemetryConstants.GenAiRequestModelKey,
OpenTelemetryConstants.GenAiUsageInputTokensKey,
OpenTelemetryConstants.GenAiUsageOutputTokensKey,
OpenTelemetryConstants.GenAiResponseFinishReasonsKey,
OpenTelemetryConstants.GenAiAgentThoughtProcessKey,
OpenTelemetryConstants.GenAiDataSourceIdKey,
OpenTelemetryConstants.GenAiOutputTypeKey,
OpenTelemetryConstants.GenAiRequestChoiceCountKey,
OpenTelemetryConstants.GenAiRequestSeedKey,
OpenTelemetryConstants.GenAiRequestFrequencyPenaltyKey,
OpenTelemetryConstants.GenAiRequestMaxTokensKey,
OpenTelemetryConstants.GenAiRequestPresencePenaltyKey,
OpenTelemetryConstants.GenAiRequestStopSequencesKey,
OpenTelemetryConstants.GenAiRequestTemperatureKey,
OpenTelemetryConstants.GenAiRequestTopPKey,
OpenTelemetryConstants.GenAiSystemInstructionsKey,
OpenTelemetryConstants.GenAiUsageCacheCreationInputTokensKey,
OpenTelemetryConstants.GenAiUsageCacheReadInputTokensKey
};

/// <summary>
/// Adds attributes for input messages.
/// </summary>
Expand Down Expand Up @@ -266,15 +207,16 @@ protected static void AddIfNotNull(IDictionary<string, object?> attributes, stri
}

/// <summary>
/// Adds extra attributes to the attributes dictionary while ignoring reserved keys.
/// Adds extra attributes to the attributes dictionary.
/// Extra attributes cannot override keys already set by the builder on this span.
/// </summary>
protected static void AddExtraAttributes(IDictionary<string, object?> attributes, IDictionary<string, object?>? extraAttributes)
{
if (extraAttributes == null) return;

foreach (var kvp in extraAttributes)
{
if ((kvp.Value != null && !ReservedAttributeKeys.Contains(kvp.Key)))
if (kvp.Value != null && !attributes.ContainsKey(kvp.Key))
{
attributes[kvp.Key] = kvp.Value;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,31 @@ public void Build_IgnoresNullValues_InExtraAttributes()
telemetry.Attributes.Should().ContainKey("custom.good").WhoseValue.Should().Be("ok");
}

[TestMethod]
public void Build_AllowsRequestModel_ViaExtraAttributes_WhenNotSetByBuilder()
{
// Arrange
var endpoint = new Uri("https://example.com");
var agentDetails = new AgentDetails("agent-model", "ModelAgent");
var scopeDetails = new InvokeAgentScopeDetails(endpoint: endpoint);
var conversationId = "conv-model";
var extras = new Dictionary<string, object?>
{
{OpenTelemetryConstants.GenAiRequestModelKey, "gpt-4o"},
};

// Act
var telemetry = InvokeAgentDataBuilder.Build(
scopeDetails,
agentDetails,
conversationId,
extraAttributes: extras);

// Assert - gen_ai.request.model is allowed because InvokeAgentDataBuilder does not set it
telemetry.Attributes.Should().ContainKey(OpenTelemetryConstants.GenAiRequestModelKey)
.WhoseValue.Should().Be("gpt-4o");
}

[TestMethod]
public void Build_WithAgentPlatformId_SetsExpectedAttributes()
{
Expand Down
Loading