From f4805b545e79dadb0d5f7ea4cf723d9f66895348 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Sun, 28 Jun 2026 01:35:50 +0000
Subject: [PATCH 1/2] Update OpenTelemetry GenAI conventions to latest
Add document modality detection in OtelMessageSerializer.DeriveModalityFromMediaType
to support the new 'document' modality value added to the GenAI semantic conventions
(open-telemetry/semantic-conventions-genai#142). PDF, Office, and ODF file types
(application/pdf, application/msword, application/vnd.openxmlformats-officedocument.*,
application/vnd.ms-excel, application/vnd.ms-powerpoint,
application/vnd.oasis.opendocument.*, application/rtf, text/rtf) now emit
modality: 'document' in message-part serialization.
Also migrate doc-comment wording in all OpenTelemetry* instrumentation client files
from 'Semantic Conventions for Generative AI systems v1.41' to
'GenAI Semantic Conventions latest' per the new standalone conventions repo.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
.../ChatCompletion/OpenTelemetryChatClient.cs | 2 +-
.../OpenTelemetryImageGenerator.cs | 2 +-
.../Common/OtelMessageSerializer.cs | 15 +++++++++++++++
.../OpenTelemetryEmbeddingGenerator.cs | 2 +-
.../OpenTelemetryRealtimeClientSession.cs | 2 +-
.../OpenTelemetrySpeechToTextClient.cs | 2 +-
.../OpenTelemetryTextToSpeechClient.cs | 2 +-
.../OpenTelemetryChatClientTests.cs | 18 ++++++++++++++++--
8 files changed, 37 insertions(+), 8 deletions(-)
diff --git a/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/OpenTelemetryChatClient.cs b/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/OpenTelemetryChatClient.cs
index 326503bc04d..5906401da1b 100644
--- a/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/OpenTelemetryChatClient.cs
+++ b/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/OpenTelemetryChatClient.cs
@@ -22,7 +22,7 @@ namespace Microsoft.Extensions.AI;
/// Represents a delegating chat client that implements the OpenTelemetry Semantic Conventions for Generative AI systems.
///
-/// This class provides an implementation of the Semantic Conventions for Generative AI systems v1.41, defined at .
+/// This class provides an implementation of the GenAI Semantic Conventions latest, defined at .
/// The specification is still experimental and subject to change; as such, the telemetry output by this client is also subject to change.
///
public sealed partial class OpenTelemetryChatClient : DelegatingChatClient
diff --git a/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/OpenTelemetryImageGenerator.cs b/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/OpenTelemetryImageGenerator.cs
index a20b512c7b0..115ca26c74d 100644
--- a/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/OpenTelemetryImageGenerator.cs
+++ b/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/OpenTelemetryImageGenerator.cs
@@ -20,7 +20,7 @@ namespace Microsoft.Extensions.AI;
/// Represents a delegating image generator that implements the OpenTelemetry Semantic Conventions for Generative AI systems.
///
-/// This class provides an implementation of the Semantic Conventions for Generative AI systems v1.41, defined at .
+/// This class provides an implementation of the GenAI Semantic Conventions latest, defined at .
/// The specification is still experimental and subject to change; as such, the telemetry output by this client is also subject to change.
///
[Experimental(DiagnosticIds.Experiments.AIImageGeneration, UrlFormat = DiagnosticIds.UrlFormat)]
diff --git a/src/Libraries/Microsoft.Extensions.AI/Common/OtelMessageSerializer.cs b/src/Libraries/Microsoft.Extensions.AI/Common/OtelMessageSerializer.cs
index c5916379508..1c62d838dac 100644
--- a/src/Libraries/Microsoft.Extensions.AI/Common/OtelMessageSerializer.cs
+++ b/src/Libraries/Microsoft.Extensions.AI/Common/OtelMessageSerializer.cs
@@ -264,10 +264,12 @@ internal static string SerializeChatMessages(
if (pos >= 0)
{
ReadOnlySpan topLevel = mediaType.AsSpan(0, pos);
+ ReadOnlySpan subtype = mediaType.AsSpan(pos + 1);
return
topLevel.Equals("image", StringComparison.OrdinalIgnoreCase) ? "image" :
topLevel.Equals("audio", StringComparison.OrdinalIgnoreCase) ? "audio" :
topLevel.Equals("video", StringComparison.OrdinalIgnoreCase) ? "video" :
+ IsDocumentMediaType(topLevel, subtype) ? "document" :
null;
}
}
@@ -275,6 +277,19 @@ internal static string SerializeChatMessages(
return null;
}
+ /// Returns when the media type represents a document (PDF, Office, ODF, RTF, …).
+ private static bool IsDocumentMediaType(ReadOnlySpan topLevel, ReadOnlySpan subtype) =>
+ (topLevel.Equals("application", StringComparison.OrdinalIgnoreCase) && (
+ subtype.Equals("pdf", StringComparison.OrdinalIgnoreCase) ||
+ subtype.Equals("msword", StringComparison.OrdinalIgnoreCase) ||
+ subtype.Equals("rtf", StringComparison.OrdinalIgnoreCase) ||
+ subtype.StartsWith("vnd.openxmlformats-officedocument", StringComparison.OrdinalIgnoreCase) ||
+ subtype.StartsWith("vnd.ms-excel", StringComparison.OrdinalIgnoreCase) ||
+ subtype.StartsWith("vnd.ms-powerpoint", StringComparison.OrdinalIgnoreCase) ||
+ subtype.StartsWith("vnd.oasis.opendocument", StringComparison.OrdinalIgnoreCase))) ||
+ (topLevel.Equals("text", StringComparison.OrdinalIgnoreCase) &&
+ subtype.Equals("rtf", StringComparison.OrdinalIgnoreCase));
+
/// Extracts code text from code interpreter inputs.
///
/// Code interpreter inputs typically contain a DataContent with a "text/x-python" or similar
diff --git a/src/Libraries/Microsoft.Extensions.AI/Embeddings/OpenTelemetryEmbeddingGenerator.cs b/src/Libraries/Microsoft.Extensions.AI/Embeddings/OpenTelemetryEmbeddingGenerator.cs
index 090332b255f..78176cb3f25 100644
--- a/src/Libraries/Microsoft.Extensions.AI/Embeddings/OpenTelemetryEmbeddingGenerator.cs
+++ b/src/Libraries/Microsoft.Extensions.AI/Embeddings/OpenTelemetryEmbeddingGenerator.cs
@@ -18,7 +18,7 @@ namespace Microsoft.Extensions.AI;
/// Represents a delegating embedding generator that implements the OpenTelemetry Semantic Conventions for Generative AI systems.
///
-/// This class provides an implementation of the Semantic Conventions for Generative AI systems v1.41, defined at .
+/// This class provides an implementation of the GenAI Semantic Conventions latest, defined at .
/// The specification is still experimental and subject to change; as such, the telemetry output by this client is also subject to change.
///
/// The type of input used to produce embeddings.
diff --git a/src/Libraries/Microsoft.Extensions.AI/Realtime/OpenTelemetryRealtimeClientSession.cs b/src/Libraries/Microsoft.Extensions.AI/Realtime/OpenTelemetryRealtimeClientSession.cs
index a7f78f9e9a6..3d13dd18c40 100644
--- a/src/Libraries/Microsoft.Extensions.AI/Realtime/OpenTelemetryRealtimeClientSession.cs
+++ b/src/Libraries/Microsoft.Extensions.AI/Realtime/OpenTelemetryRealtimeClientSession.cs
@@ -24,7 +24,7 @@ namespace Microsoft.Extensions.AI;
/// Represents a delegating realtime session that follows the OpenTelemetry Semantic Conventions for Generative AI systems where applicable.
///
///
-/// This class follows the patterns of the Semantic Conventions for Generative AI systems v1.41 where applicable, as defined at
+/// This class follows the patterns of the GenAI Semantic Conventions latest where applicable, as defined at
/// , with custom extensions for realtime-specific behavior.
/// The specification does not currently define a realtime operation; a custom operation name is used.
///
diff --git a/src/Libraries/Microsoft.Extensions.AI/SpeechToText/OpenTelemetrySpeechToTextClient.cs b/src/Libraries/Microsoft.Extensions.AI/SpeechToText/OpenTelemetrySpeechToTextClient.cs
index 82ece57f673..6e4cb490f2b 100644
--- a/src/Libraries/Microsoft.Extensions.AI/SpeechToText/OpenTelemetrySpeechToTextClient.cs
+++ b/src/Libraries/Microsoft.Extensions.AI/SpeechToText/OpenTelemetrySpeechToTextClient.cs
@@ -22,7 +22,7 @@ namespace Microsoft.Extensions.AI;
/// Represents a delegating speech-to-text client that implements the OpenTelemetry Semantic Conventions for Generative AI systems.
///
-/// This class provides an implementation of the Semantic Conventions for Generative AI systems v1.41, defined at .
+/// This class provides an implementation of the GenAI Semantic Conventions latest, defined at .
/// The specification is still experimental and subject to change; as such, the telemetry output by this client is also subject to change.
///
[Experimental(DiagnosticIds.Experiments.AISpeechToText, UrlFormat = DiagnosticIds.UrlFormat)]
diff --git a/src/Libraries/Microsoft.Extensions.AI/TextToSpeech/OpenTelemetryTextToSpeechClient.cs b/src/Libraries/Microsoft.Extensions.AI/TextToSpeech/OpenTelemetryTextToSpeechClient.cs
index 3cf4eed611d..feba4c96cc0 100644
--- a/src/Libraries/Microsoft.Extensions.AI/TextToSpeech/OpenTelemetryTextToSpeechClient.cs
+++ b/src/Libraries/Microsoft.Extensions.AI/TextToSpeech/OpenTelemetryTextToSpeechClient.cs
@@ -21,7 +21,7 @@ namespace Microsoft.Extensions.AI;
/// Represents a delegating text-to-speech client that implements the OpenTelemetry Semantic Conventions for Generative AI systems.
///
-/// This class provides an implementation of the Semantic Conventions for Generative AI systems v1.41, defined at .
+/// This class provides an implementation of the GenAI Semantic Conventions latest, defined at .
/// The specification is still experimental and subject to change; as such, the telemetry output by this client is also subject to change.
///
[Experimental(DiagnosticIds.Experiments.AITextToSpeech, UrlFormat = DiagnosticIds.UrlFormat)]
diff --git a/test/Libraries/Microsoft.Extensions.AI.Tests/ChatCompletion/OpenTelemetryChatClientTests.cs b/test/Libraries/Microsoft.Extensions.AI.Tests/ChatCompletion/OpenTelemetryChatClientTests.cs
index 6ef724f7c66..82e73926d58 100644
--- a/test/Libraries/Microsoft.Extensions.AI.Tests/ChatCompletion/OpenTelemetryChatClientTests.cs
+++ b/test/Libraries/Microsoft.Extensions.AI.Tests/ChatCompletion/OpenTelemetryChatClientTests.cs
@@ -445,6 +445,8 @@ async static IAsyncEnumerable CallbackAsync(
new TextReasoningContent("User reasoning"),
new DataContent(Convert.FromBase64String("ZGF0YSBjb250ZW50"), "audio/mp3"),
new UriContent(new Uri("https://example.com/video.mp4"), "video/mp4"),
+ new DataContent(Convert.FromBase64String("dGVzdA=="), "application/pdf"),
+ new UriContent(new Uri("https://example.com/report.docx"), "application/vnd.openxmlformats-officedocument.wordprocessingml.document"),
new HostedFileContent("file-xyz789"),
]),
new(ChatRole.Assistant, [new FunctionCallContent("call-456", "SearchFiles")]),
@@ -493,8 +495,20 @@ async static IAsyncEnumerable CallbackAsync(
"modality": "video"
},
{
- "type": "file",
- "file_id": "file-xyz789"
+ "type": "blob",
+ "content": "dGVzdA==",
+ "mime_type": "application/pdf",
+ "modality": "document"
+ },
+ {
+ "type": "uri",
+ "uri": "https://example.com/report.docx",
+ "mime_type": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
+ "modality": "document"
+ },
+ {
+ "type": "file",
+ "file_id": "file-xyz789"
}
]
},
From 83fe9f1ae2eb5d03acf4f2eb083e94b309adb5ee Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Sun, 28 Jun 2026 01:55:16 +0000
Subject: [PATCH 2/2] Add gen_ai.request.reasoning.level span attribute
(upstream PR #258)
Maps ChatOptions.Reasoning?.Effort to gen_ai.request.reasoning.level string tag
on GenAI inference spans. ReasoningEffort.Low/Medium/High/ExtraHigh emit
'low'/'medium'/'high'/'extra_high' respectively; None is not emitted.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
.../ChatCompletion/OpenTelemetryChatClient.cs | 17 +++++++++++++++++
.../OpenTelemetryConsts.cs | 1 +
.../OpenTelemetryChatClientTests.cs | 2 ++
3 files changed, 20 insertions(+)
diff --git a/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/OpenTelemetryChatClient.cs b/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/OpenTelemetryChatClient.cs
index 5906401da1b..0803b35d464 100644
--- a/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/OpenTelemetryChatClient.cs
+++ b/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/OpenTelemetryChatClient.cs
@@ -353,6 +353,23 @@ public override async IAsyncEnumerable GetStreamingResponseA
_ = activity.AddTag(OpenTelemetryConsts.GenAI.Request.TopP, top_p);
}
+ if (options.Reasoning?.Effort is ReasoningEffort effort)
+ {
+ string? reasoningLevel = effort switch
+ {
+ ReasoningEffort.Low => "low",
+ ReasoningEffort.Medium => "medium",
+ ReasoningEffort.High => "high",
+ ReasoningEffort.ExtraHigh => "extra_high",
+ _ => null,
+ };
+
+ if (reasoningLevel is not null)
+ {
+ _ = activity.AddTag(OpenTelemetryConsts.GenAI.Request.ReasoningLevel, reasoningLevel);
+ }
+ }
+
if (options.ResponseFormat is not null)
{
switch (options.ResponseFormat)
diff --git a/src/Libraries/Microsoft.Extensions.AI/OpenTelemetryConsts.cs b/src/Libraries/Microsoft.Extensions.AI/OpenTelemetryConsts.cs
index 804d0d9f684..f594c7a7dcf 100644
--- a/src/Libraries/Microsoft.Extensions.AI/OpenTelemetryConsts.cs
+++ b/src/Libraries/Microsoft.Extensions.AI/OpenTelemetryConsts.cs
@@ -126,6 +126,7 @@ public static class Request
public const string Model = "gen_ai.request.model";
public const string MaxTokens = "gen_ai.request.max_tokens";
public const string PresencePenalty = "gen_ai.request.presence_penalty";
+ public const string ReasoningLevel = "gen_ai.request.reasoning.level";
public const string Seed = "gen_ai.request.seed";
public const string StopSequences = "gen_ai.request.stop_sequences";
public const string Stream = "gen_ai.request.stream";
diff --git a/test/Libraries/Microsoft.Extensions.AI.Tests/ChatCompletion/OpenTelemetryChatClientTests.cs b/test/Libraries/Microsoft.Extensions.AI.Tests/ChatCompletion/OpenTelemetryChatClientTests.cs
index 82e73926d58..db573f72a35 100644
--- a/test/Libraries/Microsoft.Extensions.AI.Tests/ChatCompletion/OpenTelemetryChatClientTests.cs
+++ b/test/Libraries/Microsoft.Extensions.AI.Tests/ChatCompletion/OpenTelemetryChatClientTests.cs
@@ -127,6 +127,7 @@ async static IAsyncEnumerable CallbackAsync(
TopP = 4.0f,
TopK = 7,
PresencePenalty = 5.0f,
+ Reasoning = new ReasoningOptions { Effort = ReasoningEffort.Low },
ResponseFormat = ChatResponseFormat.Json,
Temperature = 6.0f,
Seed = 42,
@@ -179,6 +180,7 @@ async static IAsyncEnumerable CallbackAsync(
Assert.Equal(6.0f, activity.GetTagItem("gen_ai.request.temperature"));
Assert.Equal(7, activity.GetTagItem("gen_ai.request.top_k"));
Assert.Equal(123, activity.GetTagItem("gen_ai.request.max_tokens"));
+ Assert.Equal("low", activity.GetTagItem("gen_ai.request.reasoning.level"));
Assert.Equal("""["hello", "world"]""", activity.GetTagItem("gen_ai.request.stop_sequences"));
Assert.Equal(enableSensitiveData ? "value1" : null, activity.GetTagItem("service_tier"));
Assert.Equal(enableSensitiveData ? "value2" : null, activity.GetTagItem("SomethingElse"));