// 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;
}
}