mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
* Foundry.Hosting.UnitTests: extract project from Foundry.UnitTests Move all Hosting/* tests, three toolbox TestData JSONs, and the FakeAuthenticationTokenProvider/HttpHandlerAssert/TestDataUtil helpers (trimmed to toolbox getters) into a new Microsoft.Agents.AI.Foundry.Hosting.UnitTests project. Add it to the slnx and grant the new assembly InternalsVisibleTo from Microsoft.Agents.AI.Foundry and Microsoft.Agents.AI.Foundry.Hosting. * Foundry.Hosting.UnitTests: align namespaces to assembly name Rename namespaces from Microsoft.Agents.AI.Foundry.UnitTests(.Hosting) to Microsoft.Agents.AI.Foundry.Hosting.UnitTests across all moved tests, the duplicated helpers, and the trimmed TestDataUtil. Also fixes the prior namespace inconsistency in FoundryToolboxTests. * Foundry.Hosting.UnitTests: split WorkflowIntegrationTests by SUT Replace the WorkflowIntegrationTests file (an IT-named file inside a UT project) with two SUT-focused files plus a shared test-doubles file: - AgentFrameworkResponseHandlerWorkflowTests.cs - the 5 handler-driven tests that exercise AgentFrameworkResponseHandler with a real workflow agent. - OutputConverterWorkflowTests.cs - the 5 OutputConverter tests driven by hand-crafted update sequences mirroring real workflow patterns. - WorkflowTestAgents.cs - StreamingTextAgent and ThrowingStreamingAgent extracted as internal types used by both files. * Foundry.UnitTests: trim Hosting-related conditionals and dead testdata Now that Hosting tests live in their own project: - drop the Compile Remove guard for the Hosting subfolder, - drop the .NETCoreApp-only PackageReferences (Azure.AI.AgentServer.Responses, Microsoft.AspNetCore.TestHost, OpenTelemetry, OpenTelemetry.Exporter.InMemory), - drop the conditional ProjectReference to Microsoft.Agents.AI.Foundry.Hosting, - delete the three Toolbox JSON files and the matching Toolbox getters in TestDataUtil. * Foundry.Hosting.UnitTests: drop redundant 'using Microsoft.Agents.AI.Foundry.Hosting' The new project namespace is Microsoft.Agents.AI.Foundry.Hosting.UnitTests, which already brings the parent Microsoft.Agents.AI.Foundry.Hosting namespace into scope. The explicit using statement is therefore redundant (IDE0005). Caught by 'dotnet format --verify-no-changes' running on Linux against the .NET 10 SDK. * Foundry.Hosting: drop InternalsVisibleTo to Foundry.UnitTests The non-hosting Foundry.UnitTests project no longer holds any Hosting tests after the split, so it doesn't need access to internal types in Microsoft.Agents.AI.Foundry.Hosting. Only Microsoft.Agents.AI.Foundry.Hosting.UnitTests needs it. * Foundry.Hosting: rename DelegatingResponsesClient to UserAgentResponsesClient Address westey-m's review feedback on PR #5453: `Delegating*` is conventionally reserved for inheritable base classes (mirroring `DelegatingHandler`) where consumers override one or two members. This polyfill is sealed and only injects the User-Agent supplement, so the new name reflects its actual purpose. Renamed via `git mv` to preserve history: * `src/Microsoft.Agents.AI.Foundry.Hosting/DelegatingResponsesClient.cs` to `UserAgentResponsesClient.cs` * `tests/Microsoft.Agents.AI.Foundry.Hosting.UnitTests/DelegatingResponsesClientTests.cs` to `UserAgentResponsesClientTests.cs` Class, constructor, and all references updated across: * `src/.../UserAgentResponsesClient.cs` (class + constructor + internal log message) * `src/.../ServiceCollectionExtensions.cs` (cref + type check + instantiation) * `src/.../HostedAgentUserAgentPolicy.cs` (cref) * `tests/Foundry.UnitTests/RequestOptionsExtensionsTests.cs` (comment) * `tests/Foundry.Hosting.UnitTests/UserAgentResponsesClientTests.cs` (class + cref + instantiations)
85 lines
3.0 KiB
C#
85 lines
3.0 KiB
C#
// 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.Hosting;
|
|
|
|
/// <summary>
|
|
/// Pipeline policy that appends the hosted-agent <c>User-Agent</c> segment
|
|
/// (e.g. <c>"foundry-hosting/agent-framework-dotnet/{version}"</c>) to outgoing requests.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// The supplement value is computed once from the Microsoft.Agents.AI.Foundry.Hosting
|
|
/// assembly's informational version. The policy is idempotent on retries: if the segment
|
|
/// is already present in the <c>User-Agent</c> header, the policy does not append it again.
|
|
/// </para>
|
|
/// <para>
|
|
/// This policy is added at request time (per-call <see cref="PipelinePosition"/>)
|
|
/// by <see cref="UserAgentResponsesClient"/> when invoking the wrapped
|
|
/// <see cref="OpenAI.Responses.ResponsesClient"/>. It is only registered when an agent is
|
|
/// resolved by the Foundry hosting layer.
|
|
/// </para>
|
|
/// </remarks>
|
|
internal sealed class HostedAgentUserAgentPolicy : PipelinePolicy
|
|
{
|
|
public static HostedAgentUserAgentPolicy Instance { get; } = new HostedAgentUserAgentPolicy();
|
|
|
|
private static readonly string s_supplementValue = CreateSupplementValue();
|
|
|
|
public override void Process(PipelineMessage message, IReadOnlyList<PipelinePolicy> pipeline, int currentIndex)
|
|
{
|
|
AppendHeader(message);
|
|
ProcessNext(message, pipeline, currentIndex);
|
|
}
|
|
|
|
public override async ValueTask ProcessAsync(PipelineMessage message, IReadOnlyList<PipelinePolicy> 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_supplementValue))
|
|
{
|
|
return;
|
|
}
|
|
|
|
message.Request.Headers.Set("User-Agent", $"{existing} {s_supplementValue}");
|
|
}
|
|
else
|
|
{
|
|
message.Request.Headers.Set("User-Agent", s_supplementValue);
|
|
}
|
|
}
|
|
|
|
private static string CreateSupplementValue()
|
|
{
|
|
const string Name = "foundry-hosting/agent-framework-dotnet";
|
|
|
|
if (typeof(HostedAgentUserAgentPolicy).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.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;
|
|
}
|
|
}
|