From 05fc08fddf5d10ed94718a852dbf995536e2fda5 Mon Sep 17 00:00:00 2001 From: Thanika Reddy Date: Fri, 7 Nov 2025 11:44:26 -0800 Subject: [PATCH 01/11] Add truncation logic --- .../Tracing/Exporters/Agent365ExporterCore.cs | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/src/Observability/Runtime/Tracing/Exporters/Agent365ExporterCore.cs b/src/Observability/Runtime/Tracing/Exporters/Agent365ExporterCore.cs index 4c5c1326..7ade07f8 100644 --- a/src/Observability/Runtime/Tracing/Exporters/Agent365ExporterCore.cs +++ b/src/Observability/Runtime/Tracing/Exporters/Agent365ExporterCore.cs @@ -24,6 +24,74 @@ namespace Microsoft.Agents.A365.Observability.Runtime.Tracing.Exporters public static class Agent365ExporterCore { private const string CorrelationIdHeaderKey = "x-ms-correlation-id"; + private const int MaxActivitySizeBytes = 250 * 1024; + + /// + /// Truncates the largest-to-smallest of the specified activity attributes until the activity's serialized size is under 250 KB. + /// Logs the size of each key/value and each truncation. + /// + /// The activity to check and potentially truncate. + /// The resource for serialization context. + /// Logger for informational messages. + /// True if any truncation occurred, otherwise false. + public static bool TruncateActivityToMaxSize( + Activity activity, + Resource resource, + Action? logInformation = null) + { + if (activity == null) return false; + + // Check initial size + string json = ExportFormatter.FormatSingle(activity, resource); + if (Encoding.UTF8.GetByteCount(json) <= Agent365ExporterCore.MaxActivitySizeBytes) + return false; + + string[] keys = new[] + { + "gen_ai.tool.arguments", + "gen_ai.event.content", + "gen_ai.input.messages", + "gen_ai.agent.invocation_input", + "gen_ai.output.messages", + "gen_ai.agent.invocation_output" + }; + + // Get all key/value sizes and log them + var keySizes = new List<(string Key, int Size, string? Value)>(); + foreach (var key in keys) + { + var value = activity.GetTagItem(key) as string; + int size = !string.IsNullOrEmpty(value) ? Encoding.UTF8.GetByteCount(value) : 0; + keySizes.Add((key, size, value)); + logInformation?.Invoke($"Activity '{activity.DisplayName}': Key '{key}' size = {size} bytes."); + } + + // Sort keys by size descending + var sorted = keySizes + .Where(k => !string.IsNullOrEmpty(k.Value) && k.Size > 0) + .OrderByDescending(k => k.Size) + .ToList(); + + bool truncated = false; + + foreach (var (key, size, _) in sorted) + { + activity.SetTag(key, "TRUNCATED"); + logInformation?.Invoke( + $"Truncated '{key}' in activity '{activity.DisplayName}' to reduce size. Previous size: {size} bytes."); + + // Re-check size after each truncation + json = ExportFormatter.FormatSingle(activity, resource); + if (Encoding.UTF8.GetByteCount(json) <= Agent365ExporterCore.MaxActivitySizeBytes) + { + truncated = true; + break; + } + truncated = true; + } + + return truncated; + } /// /// Partitions a batch of activities by tenant and agent identity. @@ -110,6 +178,13 @@ public static async Task ExportBatchCoreAsync( foreach (var g in groups) { var (tenantId, agentId, activities) = g; + + // Truncate activities if needed before serialization + foreach (var activity in activities) + { + Agent365ExporterCore.TruncateActivityToMaxSize(activity, resource, logInformation); + } + var json = ExportFormatter.FormatMany(activities, resource); using var content = new StringContent(json, Encoding.UTF8, "application/json"); From a87a2bc367dfc184a03210ad6b65bf04836b05bd Mon Sep 17 00:00:00 2001 From: Thanika Reddy Date: Fri, 7 Nov 2025 11:57:55 -0800 Subject: [PATCH 02/11] Unit tests. --- .../Tracing/Exporters/Agent365ExporterCore.cs | 5 +- .../Exporters/Agent365ExporterTests.cs | 91 ++++++++++++++++++- 2 files changed, 92 insertions(+), 4 deletions(-) diff --git a/src/Observability/Runtime/Tracing/Exporters/Agent365ExporterCore.cs b/src/Observability/Runtime/Tracing/Exporters/Agent365ExporterCore.cs index 7ade07f8..571b2751 100644 --- a/src/Observability/Runtime/Tracing/Exporters/Agent365ExporterCore.cs +++ b/src/Observability/Runtime/Tracing/Exporters/Agent365ExporterCore.cs @@ -63,7 +63,7 @@ public static bool TruncateActivityToMaxSize( var value = activity.GetTagItem(key) as string; int size = !string.IsNullOrEmpty(value) ? Encoding.UTF8.GetByteCount(value) : 0; keySizes.Add((key, size, value)); - logInformation?.Invoke($"Activity '{activity.DisplayName}': Key '{key}' size = {size} bytes."); + logInformation?.Invoke($"Activity '{activity.DisplayName}': Key '{key}' size = {size / 1024} KB."); } // Sort keys by size descending @@ -77,8 +77,7 @@ public static bool TruncateActivityToMaxSize( foreach (var (key, size, _) in sorted) { activity.SetTag(key, "TRUNCATED"); - logInformation?.Invoke( - $"Truncated '{key}' in activity '{activity.DisplayName}' to reduce size. Previous size: {size} bytes."); + logInformation?.Invoke($"Truncated '{key}' in activity '{activity.DisplayName}' to reduce size. Previous size: {size / 1024} KB."); // Re-check size after each truncation json = ExportFormatter.FormatSingle(activity, resource); diff --git a/src/Tests/Microsoft.Agents.A365.Observability.Runtime.Tests/Tracing/Exporters/Agent365ExporterTests.cs b/src/Tests/Microsoft.Agents.A365.Observability.Runtime.Tests/Tracing/Exporters/Agent365ExporterTests.cs index dce5c055..c8d0ac84 100644 --- a/src/Tests/Microsoft.Agents.A365.Observability.Runtime.Tests/Tracing/Exporters/Agent365ExporterTests.cs +++ b/src/Tests/Microsoft.Agents.A365.Observability.Runtime.Tests/Tracing/Exporters/Agent365ExporterTests.cs @@ -2,7 +2,6 @@ using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Agents.A365.Observability.Runtime.Tracing.Exporters; using Microsoft.Agents.A365.Observability.Runtime.Tracing.Scopes; -using Microsoft.VisualStudio.TestTools.UnitTesting; using OpenTelemetry; using OpenTelemetry.Resources; using System.Diagnostics; @@ -1044,4 +1043,94 @@ public void Export_StandardEndpoint_WithDifferentClusterCategories_ProcessesCorr } #endregion + + #region TruncateActivityToMaxSize Tests + + [TestMethod] + public void TruncateActivityToMaxSize_DoesNothing_WhenUnderLimit() + { + // Arrange + using var activity = CreateActivity("tenant-1", "agent-1"); + activity.SetTag("gen_ai.tool.arguments", new string('a', 1024)); // 1KB + var resource = ResourceBuilder.CreateEmpty().Build(); + var logs = new List(); + + // Act + var result = Agent365ExporterCore.TruncateActivityToMaxSize(activity, resource, logs.Add); + + // Assert + result.Should().BeFalse(); + activity.GetTagItem("gen_ai.tool.arguments").Should().BeOfType().Which.Should().NotBe("TRUNCATED"); + } + + [TestMethod] + public void TruncateActivityToMaxSize_TruncatesSingleLargeKey() + { + // Arrange + using var activity = CreateActivity("tenant-1", "agent-1"); + // 300KB value + activity.SetTag("gen_ai.tool.arguments", new string('b', 300 * 1024)); + var resource = ResourceBuilder.CreateEmpty().Build(); + var logs = new List(); + + // Act + var result = Agent365ExporterCore.TruncateActivityToMaxSize(activity, resource, logs.Add); + + // Assert + result.Should().BeTrue(); + activity.GetTagItem("gen_ai.tool.arguments").Should().Be("TRUNCATED"); + logs.Should().Contain(l => l.Contains("Key 'gen_ai.tool.arguments' size = ")); + logs.Should().Contain(l => l.Contains("Truncated 'gen_ai.tool.arguments'")); + } + + [TestMethod] + public void TruncateActivityToMaxSize_TruncatesMultipleKeys_LargestFirst() + { + // Arrange + using var activity = CreateActivity("tenant-1", "agent-1"); + // 200KB and 100KB values, total > 250KB + activity.SetTag("gen_ai.tool.arguments", new string('c', 200 * 1024)); + activity.SetTag("gen_ai.event.content", new string('d', 100 * 1024)); + var resource = ResourceBuilder.CreateEmpty().Build(); + var logs = new List(); + + // Act + var result = Agent365ExporterCore.TruncateActivityToMaxSize(activity, resource, logs.Add); + + // Assert + result.Should().BeTrue(); + // The largest should be truncated first + activity.GetTagItem("gen_ai.tool.arguments").Should().Be("TRUNCATED"); + // If still over, the next largest is truncated + // (depends on serialization overhead, so check at least one is truncated) + logs.Should().Contain(l => l.Contains("Truncated 'gen_ai.tool.arguments'")); + logs.Should().Contain(l => l.Contains("Key 'gen_ai.tool.arguments' size = ")); + logs.Should().Contain(l => l.Contains("Key 'gen_ai.event.content' size = ")); + } + + [TestMethod] + public void TruncateActivityToMaxSize_LogsAllKeySizes() + { + // Arrange + using var activity = CreateActivity("tenant-1", "agent-1"); + activity.SetTag("gen_ai.tool.arguments", new string('x', 100 * 1024)); + activity.SetTag("gen_ai.event.content", new string('y', 125 * 1024)); + activity.SetTag("gen_ai.input.messages", new string('z', 75 * 1024)); + var resource = ResourceBuilder.CreateEmpty().Build(); + var logs = new List(); + + // Act + Agent365ExporterCore.TruncateActivityToMaxSize(activity, resource, logs.Add); + + // Assert + logs.Should().Contain(l => l.Contains("Key 'gen_ai.tool.arguments' size = 100")); + logs.Should().Contain(l => l.Contains("Key 'gen_ai.event.content' size = 125")); + logs.Should().Contain(l => l.Contains("Key 'gen_ai.input.messages' size = 75")); + logs.Should().Contain(l => l.Contains("Key 'gen_ai.agent.invocation_input' size = 0")); + logs.Should().Contain(l => l.Contains("Key 'gen_ai.output.messages' size = 0")); + logs.Should().Contain(l => l.Contains("Key 'gen_ai.agent.invocation_output' size = 0")); + } + + #endregion + } \ No newline at end of file From 4327dce6a448f5e3c016d242762d9be243146b97 Mon Sep 17 00:00:00 2001 From: Thanika Reddy Date: Fri, 7 Nov 2025 12:29:41 -0800 Subject: [PATCH 03/11] Integration test. --- .../Agent365ExporterE2ETests.cs | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/src/Tests/Microsoft.Agents.A365.Observability.Runtime.IntegrationTests/Agent365ExporterE2ETests.cs b/src/Tests/Microsoft.Agents.A365.Observability.Runtime.IntegrationTests/Agent365ExporterE2ETests.cs index fe8e4d28..2a368285 100644 --- a/src/Tests/Microsoft.Agents.A365.Observability.Runtime.IntegrationTests/Agent365ExporterE2ETests.cs +++ b/src/Tests/Microsoft.Agents.A365.Observability.Runtime.IntegrationTests/Agent365ExporterE2ETests.cs @@ -345,6 +345,102 @@ public async Task AddTracing_NestedScopes_AllExporterRequestsReceived() allOperationNames.Should().Contain(new[] { "invoke_agent", "execute_tool", InferenceOperationType.Chat.ToString() }, "All three nested scopes should be exported, even if batched in fewer requests."); } + [TestMethod] + public async Task Exporter_Truncates_Scope() + { + // Arrange + this.SetupExporterTest(); + this._receivedRequest = false; + this._receivedContent = null; + + // Create a sample text file >250KB and base64 encode it + var tempFile = Path.GetTempFileName(); + var fileBytes = new byte[300 * 1024]; // 300KB + new Random(42).NextBytes(fileBytes); + await File.WriteAllBytesAsync(tempFile, fileBytes); + var base64 = Convert.ToBase64String(await File.ReadAllBytesAsync(tempFile)); + File.Delete(tempFile); + + var agentDetails = new AgentDetails( + agentId: Guid.NewGuid().ToString(), + agentName: "Test Agent", + agentDescription: "Agent for truncation test.", + agentAUID: Guid.NewGuid().ToString(), + agentUPN: "testagent@contoso.com", + agentBlueprintId: Guid.NewGuid().ToString(), + tenantId: Guid.NewGuid().ToString()); + var tenantDetails = new TenantDetails(Guid.NewGuid()); + var endpoint = new Uri("https://test-endpoint"); + var invokeAgentDetails = new InvokeAgentDetails(endpoint, agentDetails); + var request = new Request( + content: "Test request content", + executionType: ExecutionType.HumanToAgent, + sourceMetadata: new SourceMetadata("test", "test-id")); + + var toolCallDetails = new ToolCallDetails( + toolName: "LargeFileTool", + arguments: base64, + toolCallId: "call-123", + description: "Test tool with large file content", + toolType: "file-upload", + endpoint: endpoint); + + // Act: Start nested scopes + using (var agentScope = InvokeAgentScope.Start(invokeAgentDetails, tenantDetails, request)) + { + agentScope.RecordInputMessages(new[] { "Agent input" }); + agentScope.RecordOutputMessages(new[] { "Agent output" }); + using (var toolScope = ExecuteToolScope.Start(toolCallDetails, agentDetails, tenantDetails)) + { + toolScope.RecordResponse("Tool response"); + } + } + + // Wait for export + var timeout = TimeSpan.FromSeconds(10); + var start = DateTime.UtcNow; + while (!this._receivedRequest && DateTime.UtcNow - start < timeout) + { + await Task.Delay(500).ConfigureAwait(false); + } + + this._receivedRequest.Should().BeTrue("Exporter should make the expected HTTP request."); + this._receivedContent.Should().NotBeNull("Exporter should send a request body."); + + // Assert: Find both activities in the exported payload + using var doc = JsonDocument.Parse(this._receivedContent!); + var root = doc.RootElement; + var spans = root + .GetProperty("resourceSpans")[0] + .GetProperty("scopeSpans")[0] + .GetProperty("spans") + .EnumerateArray(); + + bool foundInvokeAgent = false; + bool foundExecuteTool = false; + foreach (var span in spans) + { + var attrs = span.GetProperty("attributes"); + var opName = this.GetAttribute(attrs, "gen_ai.operation.name"); + if (opName == "invoke_agent") + { + foundInvokeAgent = true; + // Should NOT be truncated + var input = this.GetAttribute(attrs, "gen_ai.input.messages"); + input.Should().Be("Agent input"); + } + if (opName == "execute_tool") + { + foundExecuteTool = true; + // Should be truncated + var args = this.GetAttribute(attrs, "gen_ai.tool.arguments"); + args.Should().Be("TRUNCATED"); + } + } + foundInvokeAgent.Should().BeTrue(); + foundExecuteTool.Should().BeTrue(); + } + private class TestHttpMessageHandler : HttpMessageHandler { private Func _handler; From ec8dceb9cf6bdd7a35165614c7629e08fd561d4f Mon Sep 17 00:00:00 2001 From: Thanika Reddy Date: Fri, 7 Nov 2025 12:35:57 -0800 Subject: [PATCH 04/11] Use constants. --- .../Tracing/Exporters/Agent365ExporterCore.cs | 12 ++++++------ .../Runtime/Tracing/Scopes/OpenTelemetryConstants.cs | 2 ++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Observability/Runtime/Tracing/Exporters/Agent365ExporterCore.cs b/src/Observability/Runtime/Tracing/Exporters/Agent365ExporterCore.cs index 571b2751..e74ca975 100644 --- a/src/Observability/Runtime/Tracing/Exporters/Agent365ExporterCore.cs +++ b/src/Observability/Runtime/Tracing/Exporters/Agent365ExporterCore.cs @@ -48,12 +48,12 @@ public static bool TruncateActivityToMaxSize( string[] keys = new[] { - "gen_ai.tool.arguments", - "gen_ai.event.content", - "gen_ai.input.messages", - "gen_ai.agent.invocation_input", - "gen_ai.output.messages", - "gen_ai.agent.invocation_output" + OpenTelemetryConstants.GenAiToolArgumentsKey, + OpenTelemetryConstants.GenAiEventContent, + OpenTelemetryConstants.GenAiInputMessagesKey, + OpenTelemetryConstants.GenAiInvocationInputKey, + OpenTelemetryConstants.GenAiOutputMessagesKey, + OpenTelemetryConstants.GenAiInvocationOutputKey }; // Get all key/value sizes and log them diff --git a/src/Observability/Runtime/Tracing/Scopes/OpenTelemetryConstants.cs b/src/Observability/Runtime/Tracing/Scopes/OpenTelemetryConstants.cs index eaa8f8fd..4c293960 100644 --- a/src/Observability/Runtime/Tracing/Scopes/OpenTelemetryConstants.cs +++ b/src/Observability/Runtime/Tracing/Scopes/OpenTelemetryConstants.cs @@ -35,6 +35,8 @@ public static class OpenTelemetryConstants public const string GenAiProviderNameKey = "gen_ai.provider.name"; public const string GenAiInputMessagesKey = "gen_ai.input.messages"; public const string GenAiOutputMessagesKey = "gen_ai.output.messages"; + public const string GenAiInvocationInputKey = "gen_ai.agent.invocation_input"; + public const string GenAiInvocationOutputKey = "gen_ai.agent.invocation_output"; [DataContract] public enum OperationNames From d13dca5d6e7c3046aea4e8faef35b8f72632ac13 Mon Sep 17 00:00:00 2001 From: Thanika Reddy Date: Fri, 7 Nov 2025 13:16:26 -0800 Subject: [PATCH 05/11] Unnecessary variable. --- .../Runtime/Tracing/Exporters/Agent365ExporterCore.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Observability/Runtime/Tracing/Exporters/Agent365ExporterCore.cs b/src/Observability/Runtime/Tracing/Exporters/Agent365ExporterCore.cs index e74ca975..937fc640 100644 --- a/src/Observability/Runtime/Tracing/Exporters/Agent365ExporterCore.cs +++ b/src/Observability/Runtime/Tracing/Exporters/Agent365ExporterCore.cs @@ -72,8 +72,6 @@ public static bool TruncateActivityToMaxSize( .OrderByDescending(k => k.Size) .ToList(); - bool truncated = false; - foreach (var (key, size, _) in sorted) { activity.SetTag(key, "TRUNCATED"); @@ -83,13 +81,11 @@ public static bool TruncateActivityToMaxSize( json = ExportFormatter.FormatSingle(activity, resource); if (Encoding.UTF8.GetByteCount(json) <= Agent365ExporterCore.MaxActivitySizeBytes) { - truncated = true; break; } - truncated = true; } - return truncated; + return true; } /// From 77b6b4ca5f862035608d93c3c227b38fb74cce9e Mon Sep 17 00:00:00 2001 From: Thanika Reddy Date: Fri, 21 Nov 2025 09:21:18 -0800 Subject: [PATCH 06/11] Move out to static readonly and rename to LargePayloadAttributeKeys. --- .../Tracing/Exporters/Agent365ExporterCore.cs | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/Observability/Runtime/Tracing/Exporters/Agent365ExporterCore.cs b/src/Observability/Runtime/Tracing/Exporters/Agent365ExporterCore.cs index 937fc640..74565df1 100644 --- a/src/Observability/Runtime/Tracing/Exporters/Agent365ExporterCore.cs +++ b/src/Observability/Runtime/Tracing/Exporters/Agent365ExporterCore.cs @@ -25,6 +25,15 @@ public static class Agent365ExporterCore { private const string CorrelationIdHeaderKey = "x-ms-correlation-id"; private const int MaxActivitySizeBytes = 250 * 1024; + private static readonly string[] LargePayloadAttributeKeys = new[] + { + OpenTelemetryConstants.GenAiToolArgumentsKey, + OpenTelemetryConstants.GenAiEventContent, + OpenTelemetryConstants.GenAiInputMessagesKey, + OpenTelemetryConstants.GenAiInvocationInputKey, + OpenTelemetryConstants.GenAiOutputMessagesKey, + OpenTelemetryConstants.GenAiInvocationOutputKey + }; /// /// Truncates the largest-to-smallest of the specified activity attributes until the activity's serialized size is under 250 KB. @@ -46,19 +55,9 @@ public static bool TruncateActivityToMaxSize( if (Encoding.UTF8.GetByteCount(json) <= Agent365ExporterCore.MaxActivitySizeBytes) return false; - string[] keys = new[] - { - OpenTelemetryConstants.GenAiToolArgumentsKey, - OpenTelemetryConstants.GenAiEventContent, - OpenTelemetryConstants.GenAiInputMessagesKey, - OpenTelemetryConstants.GenAiInvocationInputKey, - OpenTelemetryConstants.GenAiOutputMessagesKey, - OpenTelemetryConstants.GenAiInvocationOutputKey - }; - // Get all key/value sizes and log them var keySizes = new List<(string Key, int Size, string? Value)>(); - foreach (var key in keys) + foreach (var key in Agent365ExporterCore.LargePayloadAttributeKeys) { var value = activity.GetTagItem(key) as string; int size = !string.IsNullOrEmpty(value) ? Encoding.UTF8.GetByteCount(value) : 0; From 1b8d4f30daa1d9151407836c8d863c6df3b85d7a Mon Sep 17 00:00:00 2001 From: Thanika Reddy Date: Fri, 21 Nov 2025 10:06:54 -0800 Subject: [PATCH 07/11] Restore original Agent365ExporterCore. Move truncation to ExportFormatter. --- .../Runtime/Common/ExportFormatter.cs | 64 +++++++++++++++-- .../Tracing/Exporters/Agent365ExporterCore.cs | 69 ------------------- 2 files changed, 60 insertions(+), 73 deletions(-) diff --git a/src/Observability/Runtime/Common/ExportFormatter.cs b/src/Observability/Runtime/Common/ExportFormatter.cs index c11020b0..2ea65123 100644 --- a/src/Observability/Runtime/Common/ExportFormatter.cs +++ b/src/Observability/Runtime/Common/ExportFormatter.cs @@ -2,14 +2,16 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // ------------------------------------------------------------------------------ +using Microsoft.Agents.A365.Observability.Runtime.DTOs; +using Microsoft.Agents.A365.Observability.Runtime.Tracing.Scopes; using OpenTelemetry.Resources; using System; -using System.Linq; using System.Collections.Generic; using System.Diagnostics; +using System.Linq; +using System.Text; using System.Text.Json; using System.Text.Json.Serialization; -using Microsoft.Agents.A365.Observability.Runtime.DTOs; namespace Microsoft.Agents.A365.Observability.Runtime.Common { @@ -18,13 +20,25 @@ namespace Microsoft.Agents.A365.Observability.Runtime.Common /// public class ExportFormatter { + private const int MaxSpanSizeBytes = 250 * 1024; + private static readonly string[] LargePayloadAttributeKeys = new[] + { + OpenTelemetryConstants.GenAiToolArgumentsKey, + OpenTelemetryConstants.GenAiEventContent, + OpenTelemetryConstants.GenAiInputMessagesKey, + OpenTelemetryConstants.GenAiInvocationInputKey, + OpenTelemetryConstants.GenAiOutputMessagesKey, + OpenTelemetryConstants.GenAiInvocationOutputKey + }; + /// /// Formats a collection of Activity spans into an OTLP JSON payload compatible with the Agent 365 Observability ingestion service. /// /// The collection of Activity spans to be formatted into the OTLP payload. /// The OpenTelemetry resource associated with the spans, containing resource attributes. + /// An optional logger for informational messages. /// A JSON string representing the OTLP payload for the provided activities and resource. - public static string FormatMany(IEnumerable activities, Resource resource) + public static string FormatMany(IEnumerable activities, Resource resource, Action? logInformation = null) { var resourceAttributes = GetResourceAttributes(resource); var serviceName = GetServiceName(resource); @@ -40,7 +54,7 @@ public static string FormatMany(IEnumerable activities, Resource resou spans = new List(); scopeMap[key] = spans; } - spans.Add(BuildOtlpSpan(activity)); + spans.Add(BuildOtlpSpanWithTruncation(activity: activity, logInformation: logInformation)); } var scopeSpans = new List(scopeMap.Count); @@ -148,6 +162,48 @@ public static string FormatLogData(IDictionary data) return resource.Attributes.FirstOrDefault(a => a.Key == "service.version").Value?.ToString(); } + private static OtlpSpan BuildOtlpSpanWithTruncation(Activity activity, Action? logInformation = null) + { + var span = BuildOtlpSpan(activity); + + // Gather key sizes + var keySizes = new List<(string Key, int Size, string? Value)>(); + foreach (var key in ExportFormatter.LargePayloadAttributeKeys) + { + if (span.Attributes != null && span.Attributes.TryGetValue(key, out var valueObj)) + { + var value = valueObj as string; + int size = !string.IsNullOrEmpty(value) ? Encoding.UTF8.GetByteCount(value) : 0; + keySizes.Add((key, size, value)); + logInformation?.Invoke($"Activity '{activity.DisplayName}': Key '{key}' size = {size / 1024} KB."); + } + } + + // Sort keys by size descending + var sorted = keySizes + .Where(k => !string.IsNullOrEmpty(k.Value) && k.Size > 0) + .OrderByDescending(k => k.Size) + .ToList(); + + foreach (var (key, size, _) in sorted) + { + if (span.Attributes != null) + { + span.Attributes[key] = "TRUNCATED"; + logInformation?.Invoke($"Truncated '{key}' in activity '{activity.DisplayName}' to reduce size. Previous size: {size / 1024} KB."); + } + + // Re-check size after each truncation + var json = SerializePayload(span); + if (Encoding.UTF8.GetByteCount(json) <= ExportFormatter.MaxSpanSizeBytes) + { + break; + } + } + + return span; + } + private static OtlpSpan BuildOtlpSpan(Activity activity) { return new OtlpSpan diff --git a/src/Observability/Runtime/Tracing/Exporters/Agent365ExporterCore.cs b/src/Observability/Runtime/Tracing/Exporters/Agent365ExporterCore.cs index 74565df1..4c5c1326 100644 --- a/src/Observability/Runtime/Tracing/Exporters/Agent365ExporterCore.cs +++ b/src/Observability/Runtime/Tracing/Exporters/Agent365ExporterCore.cs @@ -24,68 +24,6 @@ namespace Microsoft.Agents.A365.Observability.Runtime.Tracing.Exporters public static class Agent365ExporterCore { private const string CorrelationIdHeaderKey = "x-ms-correlation-id"; - private const int MaxActivitySizeBytes = 250 * 1024; - private static readonly string[] LargePayloadAttributeKeys = new[] - { - OpenTelemetryConstants.GenAiToolArgumentsKey, - OpenTelemetryConstants.GenAiEventContent, - OpenTelemetryConstants.GenAiInputMessagesKey, - OpenTelemetryConstants.GenAiInvocationInputKey, - OpenTelemetryConstants.GenAiOutputMessagesKey, - OpenTelemetryConstants.GenAiInvocationOutputKey - }; - - /// - /// Truncates the largest-to-smallest of the specified activity attributes until the activity's serialized size is under 250 KB. - /// Logs the size of each key/value and each truncation. - /// - /// The activity to check and potentially truncate. - /// The resource for serialization context. - /// Logger for informational messages. - /// True if any truncation occurred, otherwise false. - public static bool TruncateActivityToMaxSize( - Activity activity, - Resource resource, - Action? logInformation = null) - { - if (activity == null) return false; - - // Check initial size - string json = ExportFormatter.FormatSingle(activity, resource); - if (Encoding.UTF8.GetByteCount(json) <= Agent365ExporterCore.MaxActivitySizeBytes) - return false; - - // Get all key/value sizes and log them - var keySizes = new List<(string Key, int Size, string? Value)>(); - foreach (var key in Agent365ExporterCore.LargePayloadAttributeKeys) - { - var value = activity.GetTagItem(key) as string; - int size = !string.IsNullOrEmpty(value) ? Encoding.UTF8.GetByteCount(value) : 0; - keySizes.Add((key, size, value)); - logInformation?.Invoke($"Activity '{activity.DisplayName}': Key '{key}' size = {size / 1024} KB."); - } - - // Sort keys by size descending - var sorted = keySizes - .Where(k => !string.IsNullOrEmpty(k.Value) && k.Size > 0) - .OrderByDescending(k => k.Size) - .ToList(); - - foreach (var (key, size, _) in sorted) - { - activity.SetTag(key, "TRUNCATED"); - logInformation?.Invoke($"Truncated '{key}' in activity '{activity.DisplayName}' to reduce size. Previous size: {size / 1024} KB."); - - // Re-check size after each truncation - json = ExportFormatter.FormatSingle(activity, resource); - if (Encoding.UTF8.GetByteCount(json) <= Agent365ExporterCore.MaxActivitySizeBytes) - { - break; - } - } - - return true; - } /// /// Partitions a batch of activities by tenant and agent identity. @@ -172,13 +110,6 @@ public static async Task ExportBatchCoreAsync( foreach (var g in groups) { var (tenantId, agentId, activities) = g; - - // Truncate activities if needed before serialization - foreach (var activity in activities) - { - Agent365ExporterCore.TruncateActivityToMaxSize(activity, resource, logInformation); - } - var json = ExportFormatter.FormatMany(activities, resource); using var content = new StringContent(json, Encoding.UTF8, "application/json"); From f26d145230b907463d8126eafa450d6c3cb67309 Mon Sep 17 00:00:00 2001 From: Thanika Reddy Date: Fri, 21 Nov 2025 10:37:01 -0800 Subject: [PATCH 08/11] Attempt truncation only if size exceeded. Move tests to ExportFormatter --- .../Runtime/Common/ExportFormatter.cs | 5 +- .../Tracing/Exporters/Agent365ExporterCore.cs | 2 +- .../Agent365ExporterE2ETests.cs | 4 +- .../Common/ExportFormatterTests.cs | 98 +++++++++++++++++++ .../Exporters/Agent365ExporterTests.cs | 90 ----------------- 5 files changed, 105 insertions(+), 94 deletions(-) diff --git a/src/Observability/Runtime/Common/ExportFormatter.cs b/src/Observability/Runtime/Common/ExportFormatter.cs index 2ea65123..73519e4f 100644 --- a/src/Observability/Runtime/Common/ExportFormatter.cs +++ b/src/Observability/Runtime/Common/ExportFormatter.cs @@ -2,7 +2,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // ------------------------------------------------------------------------------ -using Microsoft.Agents.A365.Observability.Runtime.DTOs; using Microsoft.Agents.A365.Observability.Runtime.Tracing.Scopes; using OpenTelemetry.Resources; using System; @@ -166,6 +165,10 @@ private static OtlpSpan BuildOtlpSpanWithTruncation(Activity activity, Action(); foreach (var key in ExportFormatter.LargePayloadAttributeKeys) diff --git a/src/Observability/Runtime/Tracing/Exporters/Agent365ExporterCore.cs b/src/Observability/Runtime/Tracing/Exporters/Agent365ExporterCore.cs index 4c5c1326..06552f36 100644 --- a/src/Observability/Runtime/Tracing/Exporters/Agent365ExporterCore.cs +++ b/src/Observability/Runtime/Tracing/Exporters/Agent365ExporterCore.cs @@ -110,7 +110,7 @@ public static async Task ExportBatchCoreAsync( foreach (var g in groups) { var (tenantId, agentId, activities) = g; - var json = ExportFormatter.FormatMany(activities, resource); + var json = ExportFormatter.FormatMany(activities, resource, logInformation); using var content = new StringContent(json, Encoding.UTF8, "application/json"); var ppapiDiscovery = new PowerPlatformApiDiscovery(options.ClusterCategory); diff --git a/src/Tests/Microsoft.Agents.A365.Observability.Runtime.IntegrationTests/Agent365ExporterE2ETests.cs b/src/Tests/Microsoft.Agents.A365.Observability.Runtime.IntegrationTests/Agent365ExporterE2ETests.cs index 7ef94141..2244c1c7 100644 --- a/src/Tests/Microsoft.Agents.A365.Observability.Runtime.IntegrationTests/Agent365ExporterE2ETests.cs +++ b/src/Tests/Microsoft.Agents.A365.Observability.Runtime.IntegrationTests/Agent365ExporterE2ETests.cs @@ -372,11 +372,11 @@ public async Task Exporter_Truncates_Scope() tenantId: Guid.NewGuid().ToString()); var tenantDetails = new TenantDetails(Guid.NewGuid()); var endpoint = new Uri("https://test-endpoint"); - var invokeAgentDetails = new InvokeAgentDetails(endpoint, agentDetails); + var invokeAgentDetails = new InvokeAgentDetails(details: agentDetails, endpoint: endpoint); var request = new Request( content: "Test request content", executionType: ExecutionType.HumanToAgent, - sourceMetadata: new SourceMetadata("test", "test-id")); + sourceMetadata: new SourceMetadata(name: "test", id: "test-id")); var toolCallDetails = new ToolCallDetails( toolName: "LargeFileTool", diff --git a/src/Tests/Microsoft.Agents.A365.Observability.Runtime.Tests/Common/ExportFormatterTests.cs b/src/Tests/Microsoft.Agents.A365.Observability.Runtime.Tests/Common/ExportFormatterTests.cs index fecb3a35..d5ccb84b 100644 --- a/src/Tests/Microsoft.Agents.A365.Observability.Runtime.Tests/Common/ExportFormatterTests.cs +++ b/src/Tests/Microsoft.Agents.A365.Observability.Runtime.Tests/Common/ExportFormatterTests.cs @@ -483,4 +483,102 @@ public void FormatLogData_WithMissingOptionalFields_ProducesDefaults() var attrs = root.GetProperty("Attributes"); attrs.GetProperty("key").GetString().Should().Be("val"); } + + #region ExportFormatter FormatMany Truncation Tests + + [TestMethod] + public void FormatMany_DoesNothing_WhenUnderLimit() + { + // Arrange + using var activity = CreateActivity("tenant-1", "agent-1"); + activity.SetTag("gen_ai.tool.arguments", new string('a', 1024)); // 1KB + var resource = ResourceBuilder.CreateEmpty().Build(); + var logs = new List(); + + // Act + var json = ExportFormatter.FormatMany(new[] { activity }, resource, logs.Add); + + // Assert + var doc = JsonDocument.Parse(json); + var resourceSpans = doc.RootElement.GetProperty("resourceSpans"); + var scopeSpans = resourceSpans[0].GetProperty("scopeSpans"); + var span = scopeSpans[0].GetProperty("spans")[0]; + span.GetProperty("attributes").GetProperty("gen_ai.tool.arguments").GetString().Should().NotBe("TRUNCATED"); + logs.Should().NotContain(l => l.Contains("Truncated")); + } + + [TestMethod] + public void FormatMany_TruncatesSingleLargeKey() + { + // Arrange + using var activity = CreateActivity("tenant-1", "agent-1"); + activity.SetTag("gen_ai.tool.arguments", new string('b', 300 * 1024)); // 300KB + var resource = ResourceBuilder.CreateEmpty().Build(); + var logs = new List(); + + // Act + var json = ExportFormatter.FormatMany(new[] { activity }, resource, logs.Add); + + // Assert + var doc = JsonDocument.Parse(json); + var resourceSpans = doc.RootElement.GetProperty("resourceSpans"); + var scopeSpans = resourceSpans[0].GetProperty("scopeSpans"); + var span = scopeSpans[0].GetProperty("spans")[0]; + span.GetProperty("attributes").GetProperty("gen_ai.tool.arguments").GetString().Should().Be("TRUNCATED"); + logs.Should().Contain(l => l.Contains("Key 'gen_ai.tool.arguments' size = ")); + logs.Should().Contain(l => l.Contains("Truncated 'gen_ai.tool.arguments'")); + } + + [TestMethod] + public void FormatMany_TruncatesMultipleKeys_LargestFirst() + { + // Arrange + using var activity = CreateActivity("tenant-1", "agent-1"); + activity.SetTag("gen_ai.tool.arguments", new string('c', 200 * 1024)); + activity.SetTag("gen_ai.event.content", new string('d', 100 * 1024)); + var resource = ResourceBuilder.CreateEmpty().Build(); + var logs = new List(); + + // Act + var json = ExportFormatter.FormatMany(new[] { activity }, resource, logs.Add); + + // Assert + var doc = JsonDocument.Parse(json); + var resourceSpans = doc.RootElement.GetProperty("resourceSpans"); + var scopeSpans = resourceSpans[0].GetProperty("scopeSpans"); + var span = scopeSpans[0].GetProperty("spans")[0]; + var attr = span.GetProperty("attributes"); + attr.GetProperty("gen_ai.tool.arguments").GetString().Should().Be("TRUNCATED"); + logs.Should().Contain(l => l.Contains("Truncated 'gen_ai.tool.arguments'")); + logs.Should().Contain(l => l.Contains("Key 'gen_ai.tool.arguments' size = ")); + logs.Should().Contain(l => l.Contains("Key 'gen_ai.event.content' size = ")); + } + + [TestMethod] + public void FormatMany_LogsAllKeySizes() + { + // Arrange + using var activity = CreateActivity("tenant-1", "agent-1"); + activity.SetTag("gen_ai.tool.arguments", new string('x', 100 * 1024)); + activity.SetTag("gen_ai.event.content", new string('y', 125 * 1024)); + activity.SetTag("gen_ai.input.messages", new string('z', 75 * 1024)); + activity.SetTag("gen_ai.agent.invocation_input", new string('z', 0)); + activity.SetTag("gen_ai.agent.invocation_output", new string('z', 0)); + activity.SetTag("gen_ai.output.messages", new string('z', 0)); + var resource = ResourceBuilder.CreateEmpty().Build(); + var logs = new List(); + + // Act + ExportFormatter.FormatMany(new[] { activity }, resource, logs.Add); + + // Assert + logs.Should().Contain(l => l.Contains("Key 'gen_ai.tool.arguments' size = 100")); + logs.Should().Contain(l => l.Contains("Key 'gen_ai.event.content' size = 125")); + logs.Should().Contain(l => l.Contains("Key 'gen_ai.input.messages' size = 75")); + logs.Should().Contain(l => l.Contains("Key 'gen_ai.agent.invocation_input' size = 0")); + logs.Should().Contain(l => l.Contains("Key 'gen_ai.agent.invocation_output' size = 0")); + logs.Should().Contain(l => l.Contains("Key 'gen_ai.output.messages' size = 0")); + } + + #endregion } \ No newline at end of file diff --git a/src/Tests/Microsoft.Agents.A365.Observability.Runtime.Tests/Tracing/Exporters/Agent365ExporterTests.cs b/src/Tests/Microsoft.Agents.A365.Observability.Runtime.Tests/Tracing/Exporters/Agent365ExporterTests.cs index fecc8448..5b98090f 100644 --- a/src/Tests/Microsoft.Agents.A365.Observability.Runtime.Tests/Tracing/Exporters/Agent365ExporterTests.cs +++ b/src/Tests/Microsoft.Agents.A365.Observability.Runtime.Tests/Tracing/Exporters/Agent365ExporterTests.cs @@ -1012,94 +1012,4 @@ public void Export_StandardEndpoint_WithDifferentClusterCategories_ProcessesCorr } #endregion - - #region TruncateActivityToMaxSize Tests - - [TestMethod] - public void TruncateActivityToMaxSize_DoesNothing_WhenUnderLimit() - { - // Arrange - using var activity = CreateActivity("tenant-1", "agent-1"); - activity.SetTag("gen_ai.tool.arguments", new string('a', 1024)); // 1KB - var resource = ResourceBuilder.CreateEmpty().Build(); - var logs = new List(); - - // Act - var result = Agent365ExporterCore.TruncateActivityToMaxSize(activity, resource, logs.Add); - - // Assert - result.Should().BeFalse(); - activity.GetTagItem("gen_ai.tool.arguments").Should().BeOfType().Which.Should().NotBe("TRUNCATED"); - } - - [TestMethod] - public void TruncateActivityToMaxSize_TruncatesSingleLargeKey() - { - // Arrange - using var activity = CreateActivity("tenant-1", "agent-1"); - // 300KB value - activity.SetTag("gen_ai.tool.arguments", new string('b', 300 * 1024)); - var resource = ResourceBuilder.CreateEmpty().Build(); - var logs = new List(); - - // Act - var result = Agent365ExporterCore.TruncateActivityToMaxSize(activity, resource, logs.Add); - - // Assert - result.Should().BeTrue(); - activity.GetTagItem("gen_ai.tool.arguments").Should().Be("TRUNCATED"); - logs.Should().Contain(l => l.Contains("Key 'gen_ai.tool.arguments' size = ")); - logs.Should().Contain(l => l.Contains("Truncated 'gen_ai.tool.arguments'")); - } - - [TestMethod] - public void TruncateActivityToMaxSize_TruncatesMultipleKeys_LargestFirst() - { - // Arrange - using var activity = CreateActivity("tenant-1", "agent-1"); - // 200KB and 100KB values, total > 250KB - activity.SetTag("gen_ai.tool.arguments", new string('c', 200 * 1024)); - activity.SetTag("gen_ai.event.content", new string('d', 100 * 1024)); - var resource = ResourceBuilder.CreateEmpty().Build(); - var logs = new List(); - - // Act - var result = Agent365ExporterCore.TruncateActivityToMaxSize(activity, resource, logs.Add); - - // Assert - result.Should().BeTrue(); - // The largest should be truncated first - activity.GetTagItem("gen_ai.tool.arguments").Should().Be("TRUNCATED"); - // If still over, the next largest is truncated - // (depends on serialization overhead, so check at least one is truncated) - logs.Should().Contain(l => l.Contains("Truncated 'gen_ai.tool.arguments'")); - logs.Should().Contain(l => l.Contains("Key 'gen_ai.tool.arguments' size = ")); - logs.Should().Contain(l => l.Contains("Key 'gen_ai.event.content' size = ")); - } - - [TestMethod] - public void TruncateActivityToMaxSize_LogsAllKeySizes() - { - // Arrange - using var activity = CreateActivity("tenant-1", "agent-1"); - activity.SetTag("gen_ai.tool.arguments", new string('x', 100 * 1024)); - activity.SetTag("gen_ai.event.content", new string('y', 125 * 1024)); - activity.SetTag("gen_ai.input.messages", new string('z', 75 * 1024)); - var resource = ResourceBuilder.CreateEmpty().Build(); - var logs = new List(); - - // Act - Agent365ExporterCore.TruncateActivityToMaxSize(activity, resource, logs.Add); - - // Assert - logs.Should().Contain(l => l.Contains("Key 'gen_ai.tool.arguments' size = 100")); - logs.Should().Contain(l => l.Contains("Key 'gen_ai.event.content' size = 125")); - logs.Should().Contain(l => l.Contains("Key 'gen_ai.input.messages' size = 75")); - logs.Should().Contain(l => l.Contains("Key 'gen_ai.agent.invocation_input' size = 0")); - logs.Should().Contain(l => l.Contains("Key 'gen_ai.output.messages' size = 0")); - logs.Should().Contain(l => l.Contains("Key 'gen_ai.agent.invocation_output' size = 0")); - } - - #endregion - } \ No newline at end of file From 576127f4e2a90f6321de5ca89efb446796a0e13e Mon Sep 17 00:00:00 2001 From: Thanika Reddy Date: Fri, 21 Nov 2025 10:53:47 -0800 Subject: [PATCH 09/11] Return early if attributes null --- src/Observability/Runtime/Common/ExportFormatter.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Observability/Runtime/Common/ExportFormatter.cs b/src/Observability/Runtime/Common/ExportFormatter.cs index 73519e4f..38bffe87 100644 --- a/src/Observability/Runtime/Common/ExportFormatter.cs +++ b/src/Observability/Runtime/Common/ExportFormatter.cs @@ -165,6 +165,9 @@ private static OtlpSpan BuildOtlpSpanWithTruncation(Activity activity, Action(); foreach (var key in ExportFormatter.LargePayloadAttributeKeys) { - if (span.Attributes != null && span.Attributes.TryGetValue(key, out var valueObj)) + if (span.Attributes.TryGetValue(key, out var valueObj)) { var value = valueObj as string; int size = !string.IsNullOrEmpty(value) ? Encoding.UTF8.GetByteCount(value) : 0; @@ -190,11 +193,8 @@ private static OtlpSpan BuildOtlpSpanWithTruncation(Activity activity, Action Date: Mon, 24 Nov 2025 09:32:18 -0800 Subject: [PATCH 10/11] Move auto-instrumenttaion constants to new file. --- .../Runtime/Common/ExportFormatter.cs | 4 ++-- .../Scopes/AutoInstrumentationConstants.cs | 20 +++++++++++++++++++ .../Tracing/Scopes/OpenTelemetryConstants.cs | 2 -- 3 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 src/Observability/Runtime/Tracing/Scopes/AutoInstrumentationConstants.cs diff --git a/src/Observability/Runtime/Common/ExportFormatter.cs b/src/Observability/Runtime/Common/ExportFormatter.cs index 38bffe87..622b80fc 100644 --- a/src/Observability/Runtime/Common/ExportFormatter.cs +++ b/src/Observability/Runtime/Common/ExportFormatter.cs @@ -25,9 +25,9 @@ public class ExportFormatter OpenTelemetryConstants.GenAiToolArgumentsKey, OpenTelemetryConstants.GenAiEventContent, OpenTelemetryConstants.GenAiInputMessagesKey, - OpenTelemetryConstants.GenAiInvocationInputKey, OpenTelemetryConstants.GenAiOutputMessagesKey, - OpenTelemetryConstants.GenAiInvocationOutputKey + AutoInstrumentationConstants.GenAiInvocationInputKey, + AutoInstrumentationConstants.GenAiInvocationOutputKey }; /// diff --git a/src/Observability/Runtime/Tracing/Scopes/AutoInstrumentationConstants.cs b/src/Observability/Runtime/Tracing/Scopes/AutoInstrumentationConstants.cs new file mode 100644 index 00000000..80b7f8c4 --- /dev/null +++ b/src/Observability/Runtime/Tracing/Scopes/AutoInstrumentationConstants.cs @@ -0,0 +1,20 @@ +// ------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// ------------------------------------------------------------------------------ + +namespace Microsoft.Agents.A365.Observability.Runtime.Tracing.Scopes +{ + /// + /// Constants used for auto-instrumentation. + /// + public static class AutoInstrumentationConstants + { + /// The key for the input to a GenAI agent invocation. + /// Set by the Semantic Kernel OpenTelemetry integration for agent invocations. + public const string GenAiInvocationInputKey = "gen_ai.agent.invocation_input"; + + /// The key for the output of a GenAI agent invocation. + /// Set by the Semantic Kernel OpenTelemetry integration for agent invocations. + public const string GenAiInvocationOutputKey = "gen_ai.agent.invocation_output"; + } +} diff --git a/src/Observability/Runtime/Tracing/Scopes/OpenTelemetryConstants.cs b/src/Observability/Runtime/Tracing/Scopes/OpenTelemetryConstants.cs index 5430b3f8..62cfee5d 100644 --- a/src/Observability/Runtime/Tracing/Scopes/OpenTelemetryConstants.cs +++ b/src/Observability/Runtime/Tracing/Scopes/OpenTelemetryConstants.cs @@ -36,8 +36,6 @@ public static class OpenTelemetryConstants public const string GenAiProviderNameKey = "gen_ai.provider.name"; public const string GenAiInputMessagesKey = "gen_ai.input.messages"; public const string GenAiOutputMessagesKey = "gen_ai.output.messages"; - public const string GenAiInvocationInputKey = "gen_ai.agent.invocation_input"; - public const string GenAiInvocationOutputKey = "gen_ai.agent.invocation_output"; [DataContract] public enum OperationNames From e4d71e262db26cfe6bd8ad40d4b18ccc360dd5ef Mon Sep 17 00:00:00 2001 From: threddy <126917135+threddy@users.noreply.github.com> Date: Mon, 24 Nov 2025 09:39:49 -0800 Subject: [PATCH 11/11] Update activity agent identifier in test --- .../Common/ExportFormatterTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Tests/Microsoft.Agents.A365.Observability.Runtime.Tests/Common/ExportFormatterTests.cs b/src/Tests/Microsoft.Agents.A365.Observability.Runtime.Tests/Common/ExportFormatterTests.cs index d5ccb84b..f3e35d08 100644 --- a/src/Tests/Microsoft.Agents.A365.Observability.Runtime.Tests/Common/ExportFormatterTests.cs +++ b/src/Tests/Microsoft.Agents.A365.Observability.Runtime.Tests/Common/ExportFormatterTests.cs @@ -511,7 +511,7 @@ public void FormatMany_DoesNothing_WhenUnderLimit() public void FormatMany_TruncatesSingleLargeKey() { // Arrange - using var activity = CreateActivity("tenant-1", "agent-1"); + using var activity = CreateActivity("tenant-1", "agent-2"); activity.SetTag("gen_ai.tool.arguments", new string('b', 300 * 1024)); // 300KB var resource = ResourceBuilder.CreateEmpty().Build(); var logs = new List(); @@ -581,4 +581,4 @@ public void FormatMany_LogsAllKeySizes() } #endregion -} \ No newline at end of file +}