// Copyright (c) Microsoft. All rights reserved. using System.ClientModel.Primitives; using System.Collections.Generic; using System.Reflection; using System.Threading.Tasks; namespace Microsoft.Agents.AI.Foundry; /// /// Framework-wide pipeline policy that appends the agent-framework-dotnet/{version} /// segment to outgoing User-Agent headers, mirroring the /// agent-framework-python/{version} contract used by every Python provider package. /// /// /// /// The segment value is computed once from the Microsoft.Agents.AI.Foundry assembly's /// . The policy is idempotent on retries: if /// the segment is already present in the User-Agent header, the policy does not append /// it again. /// /// /// The policy is registered by FoundryChatClient on the underlying chat client's /// OpenAIRequestPolicies hook so every outbound Foundry call carries the segment. The /// policy is currently colocated with the Foundry package; it is expected to migrate to a /// framework-wide location (such as Microsoft.Agents.AI) once another provider package /// adopts the same User-Agent contract. /// /// internal sealed class AgentFrameworkUserAgentPolicy : PipelinePolicy { /// Gets the singleton policy instance. public static AgentFrameworkUserAgentPolicy Instance { get; } = new AgentFrameworkUserAgentPolicy(); private static readonly string s_segmentValue = CreateSegmentValue(); public override void Process(PipelineMessage message, IReadOnlyList pipeline, int currentIndex) { AppendHeader(message); ProcessNext(message, pipeline, currentIndex); } public override async ValueTask ProcessAsync(PipelineMessage message, IReadOnlyList pipeline, int currentIndex) { AppendHeader(message); await ProcessNextAsync(message, pipeline, currentIndex).ConfigureAwait(false); } private static void AppendHeader(PipelineMessage message) { if (message.Request.Headers.TryGetValue("User-Agent", out var existing) && !string.IsNullOrEmpty(existing)) { // Guard against double-append on retries or when the policy // is registered on multiple pipeline positions. if (existing!.Contains(s_segmentValue)) { return; } message.Request.Headers.Set("User-Agent", $"{existing} {s_segmentValue}"); } else { message.Request.Headers.Set("User-Agent", s_segmentValue); } } private static string CreateSegmentValue() { const string Name = "agent-framework-dotnet"; if (typeof(AgentFrameworkUserAgentPolicy).Assembly.GetCustomAttribute()?.InformationalVersion is string version) { int pos = version.IndexOf('+'); if (pos >= 0) { version = version.Substring(0, pos); } if (version.Length > 0) { return $"{Name}/{version}"; } } return Name; } }