mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
e3875f2c91
* .NET: DevUI: add configurable access controls for the DevUI HTTP surface * .NET: DevUI: address review and fix dotnet format - Restore parameterless AddDevUI overloads for binary compatibility on IServiceCollection and IHostApplicationBuilder. - Keep /meta outside the auth-filtered group so the frontend can discover whether a bearer token is required before prompting for one. Surface the actual requirement via MetaResponse.auth_required. - Invoke DevUIOptions.ConfigureEndpoints before mapping protected endpoints so RouteGroupBuilder conventions (RequireAuthorization, rate limiting) reliably apply. - Treat a null RemoteIpAddress as non-loopback in DevUIAuthFilter; tests now set IPAddress.Loopback explicitly when exercising the loopback path. - Add a DEVUI_AUTH_TOKEN env-var fallback test and a /meta-public test. - Fix dotnet format: add UTF-8 BOM to new files, simplify a cref in DevUIOptions, and drop an unused using in the new test. * .NET: DevUI: add missing authRequired param XML tag * .NET: DevUI tests: set loopback/AllowRemoteAccess for null-RemoteIp default DevUIIntegrationTests use the default TestServer which leaves RemoteIpAddress null. With the new conservative loopback default those tests now hit 403; set AllowRemoteAccess on the option since those tests are not exercising access control. Also add the missing SimulateRemoteIp call in the wrong-bearer test. * .NET: DevUI tests: capture DEVUI_AUTH_TOKEN before parallel tests can see it The env-var test was leaking DEVUI_AUTH_TOKEN into parallel DevUIIntegrationTests, intermittently causing their requests to be rejected as 401. Eagerly resolve the singleton DevUIAuthFilter so its constructor captures the token, then restore the env var before any HTTP requests run.
80 lines
3.0 KiB
C#
80 lines
3.0 KiB
C#
// Copyright (c) Microsoft. All rights reserved.
|
|
|
|
using Microsoft.Agents.AI;
|
|
using Microsoft.Agents.AI.DevUI;
|
|
using Microsoft.Agents.AI.Workflows;
|
|
using Microsoft.Shared.Diagnostics;
|
|
|
|
namespace Microsoft.Extensions.DependencyInjection;
|
|
|
|
/// <summary>
|
|
/// Extension methods for <see cref="IServiceCollection"/> to configure DevUI.
|
|
/// </summary>
|
|
public static class MicrosoftAgentAIDevUIServiceCollectionsExtensions
|
|
{
|
|
/// <summary>
|
|
/// Adds services required for DevUI integration.
|
|
/// </summary>
|
|
/// <param name="services">The <see cref="IServiceCollection"/> to configure.</param>
|
|
/// <returns>The <see cref="IServiceCollection"/> for method chaining.</returns>
|
|
public static IServiceCollection AddDevUI(this IServiceCollection services)
|
|
=> AddDevUI(services, configure: null);
|
|
|
|
/// <summary>
|
|
/// Adds services required for DevUI integration.
|
|
/// </summary>
|
|
/// <param name="services">The <see cref="IServiceCollection"/> to configure.</param>
|
|
/// <param name="configure">Optional callback used to configure <see cref="DevUIOptions"/>.</param>
|
|
/// <returns>The <see cref="IServiceCollection"/> for method chaining.</returns>
|
|
public static IServiceCollection AddDevUI(this IServiceCollection services, Action<DevUIOptions>? configure)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(services);
|
|
|
|
var optionsBuilder = services.AddOptions<DevUIOptions>();
|
|
if (configure is not null)
|
|
{
|
|
optionsBuilder.Configure(configure);
|
|
}
|
|
|
|
services.AddSingleton<DevUIAuthFilter>();
|
|
|
|
// a factory that tries to construct an AIAgent from Workflow,
|
|
// even if workflow was not explicitly registered as an AIAgent.
|
|
|
|
#pragma warning disable IDE0001 // Simplify Names
|
|
services.AddKeyedSingleton<AIAgent>(KeyedService.AnyKey, (sp, key) =>
|
|
{
|
|
var keyAsStr = key as string;
|
|
Throw.IfNullOrEmpty(keyAsStr);
|
|
|
|
var workflow = sp.GetKeyedService<Workflow>(keyAsStr);
|
|
if (workflow is not null)
|
|
{
|
|
return workflow.AsAIAgent(name: workflow.Name);
|
|
}
|
|
|
|
// another thing we can do is resolve a non-keyed workflow.
|
|
// however, we can't rely on anything than key to be equal to the workflow.Name.
|
|
// so we try: if we fail, we return null.
|
|
workflow = sp.GetService<Workflow>();
|
|
if (workflow is not null && workflow.Name?.Equals(keyAsStr, StringComparison.Ordinal) == true)
|
|
{
|
|
return workflow.AsAIAgent(name: workflow.Name);
|
|
}
|
|
|
|
// and it's possible to lookup at the default-registered AIAgent
|
|
// with the condition of same name as the key.
|
|
var agent = sp.GetService<AIAgent>();
|
|
if (agent is not null && agent.Name?.Equals(keyAsStr, StringComparison.Ordinal) == true)
|
|
{
|
|
return agent;
|
|
}
|
|
|
|
return null!;
|
|
});
|
|
#pragma warning restore IDE0001 // Simplify Names
|
|
|
|
return services;
|
|
}
|
|
}
|