Skip to content
Closed
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
14 changes: 14 additions & 0 deletions .github/aw/actions-lock.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"entries": {
"actions/github-script@v9.0.0": {
"repo": "actions/github-script",
"version": "v9.0.0",
"sha": "3a2844b7e9c422d3c10d287c895573f7108da1b3"
},
"github/gh-aw-actions/setup@v0.80.9": {
"repo": "github/gh-aw-actions/setup",
"version": "v0.80.9",
"sha": "8c7d04ebf1ece56cd381446125da3e0f6896294a"
}
}
}
2 changes: 1 addition & 1 deletion .github/skills/update-otel-genai-conventions/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ Critical knowledge from past PR reviews that should inform all modes:
- **No CHANGELOGs**: This repository no longer maintains per-library CHANGELOG.md files. Do NOT create or update any CHANGELOG files.
- **Source-generated JSON**: Adding new OTel part types requires: (1) new inner class, (2) `[JsonSerializable]` registration on `OtelContext`, (3) switch case in `SerializeChatMessages()`.
- **LoggerMessage text**: When using `[LoggerMessage]`, the message text should match the OTel event name for console logger readability.
- **No orphan constants**: Never add a constant to `OpenTelemetryConsts.cs` unless the same PR also adds at least one emission site for it. If the convention defines an attribute that no current client populates, classify the change as 🟒 *Constant not yet emitted* and defer the constant β€” do not add it ahead of emission. Verify with `grep -rn NewConstantName src/Libraries/Microsoft.Extensions.AI/` before submitting.
- **No orphan constants**: Never add a constant to `OpenTelemetryConsts.cs` unless the same PR also adds at least one emission site for it. If the convention defines an attribute that no current client populates, classify the change as 🟒 *Constant not yet emitted* and defer the constant β€” do not add it ahead of emission. Verify with `grep -rn NewConstantName src/Libraries/Microsoft.Extensions.AI/` before submitting. This defer rule applies **only** to brand-new attributes/metrics that have no emission site. A change to a convention item the code **already emits** β€” a type/unit change (e.g. `gen_ai.request.top_k` `double` β†’ `int`), a requiredness/scope change, a rename, a sampling-relevance change, or a new well-known value for an already-emitted attribute β€” is actionable and must be applied in the same pass, not deferred. Deferral marks individual constants; it is never a reason to skip the overall update.
- **Area-aware constants**: Pick the nested class in `OpenTelemetryConsts.cs` based on the upstream area: `GenAI.*` for `gen-ai/*`, `MCP.*` for `mcp/*`. Provider-specific attributes (`openai.*`, `anthropic.*`, `aws-bedrock.*`, `azure-ai-inference.*`) generally belong in the **provider package's** constants file, not in `Microsoft.Extensions.AI/OpenTelemetryConsts.cs`. See [references/implementation-patterns.md Β§Area placement guidance](references/implementation-patterns.md#area-placement-guidance).

## Validation
Expand Down
1,623 changes: 1,623 additions & 0 deletions .github/workflows/meai-update-otel-genai.lock.yml

Large diffs are not rendered by default.

429 changes: 429 additions & 0 deletions .github/workflows/meai-update-otel-genai.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,11 @@ public override async IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseA
_ = activity.AddTag(OpenTelemetryConsts.GenAI.Request.TopP, top_p);
}

if (options.Reasoning?.Effort is ReasoningEffort reasoningEffort)
{
_ = activity.AddTag(OpenTelemetryConsts.GenAI.Request.ReasoningLevel, reasoningEffort.ToString().ToLowerInvariant());
}

if (options.ResponseFormat is not null)
{
switch (options.ResponseFormat)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ internal static string SerializeChatMessages(
topLevel.Equals("image", StringComparison.OrdinalIgnoreCase) ? "image" :
topLevel.Equals("audio", StringComparison.OrdinalIgnoreCase) ? "audio" :
topLevel.Equals("video", StringComparison.OrdinalIgnoreCase) ? "video" :
topLevel.Equals("application", StringComparison.OrdinalIgnoreCase) ? "document" :
null;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ public static class Request
public const string Temperature = "gen_ai.request.temperature";
public const string TopK = "gen_ai.request.top_k";
public const string TopP = "gen_ai.request.top_p";
public const string ReasoningLevel = "gen_ai.request.reasoning.level";
}

public static class Response
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ async static IAsyncEnumerable<ChatResponseUpdate> CallbackAsync(
Temperature = 6.0f,
Seed = 42,
StopSequences = ["hello", "world"],
Reasoning = new ReasoningOptions { Effort = ReasoningEffort.High },
AdditionalProperties = new()
{
["service_tier"] = "value1",
Expand Down Expand Up @@ -183,6 +184,7 @@ async static IAsyncEnumerable<ChatResponseUpdate> CallbackAsync(
Assert.Equal(enableSensitiveData ? "value1" : null, activity.GetTagItem("service_tier"));
Assert.Equal(enableSensitiveData ? "value2" : null, activity.GetTagItem("SomethingElse"));
Assert.Equal(42L, activity.GetTagItem("gen_ai.request.seed"));
Assert.Equal("high", activity.GetTagItem("gen_ai.request.reasoning.level"));

Assert.Equal("id123", activity.GetTagItem("gen_ai.response.id"));
Assert.Equal("""["stop"]""", activity.GetTagItem("gen_ai.response.finish_reasons"));
Expand Down Expand Up @@ -445,6 +447,7 @@ async static IAsyncEnumerable<ChatResponseUpdate> 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("cGRmIGRhdGE="), "application/pdf"),
new HostedFileContent("file-xyz789"),
]),
new(ChatRole.Assistant, [new FunctionCallContent("call-456", "SearchFiles")]),
Expand Down Expand Up @@ -492,6 +495,12 @@ async static IAsyncEnumerable<ChatResponseUpdate> CallbackAsync(
"mime_type": "video/mp4",
"modality": "video"
},
{
"type": "blob",
"content": "cGRmIGRhdGE=",
"mime_type": "application/pdf",
"modality": "document"
},
{
"type": "file",
"file_id": "file-xyz789"
Expand Down