mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
.NET: Add A2AAgentOptions and align A2AAgent constructors with ChatClientAgent pattern (#5954)
* .NET: Add A2AAgentOptions and align A2AAgent constructors with ChatClientAgent pattern Adds a new A2AAgentOptions class (Id, Name, Description, Clone) and an options-based constructor on A2AAgent, mirroring ChatClientAgent/ChatClientAgentOptions. The existing parameter-based constructor is preserved for backward compatibility and now delegates to the options-based one. Extension methods are extended with options-based overloads: - A2AClientExtensions.AsAIAgent(IA2AClient, A2AAgentOptions, ...) - A2AAgentCardExtensions.AsAIAgent(AgentCard, A2AAgentOptions, ...) - A2ACardResolverExtensions.GetAIAgentAsync(A2ACardResolver, A2AAgentOptions, ...) For card-based creation, user-supplied options override values from the agent card; Name and Description fall back to card values when not set. Options are cloned when stored on the agent to prevent post-construction mutation, matching the ChatClientAgent pattern. Resolves #5870. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address PR review comments - Add Throw.IfNull(client) in A2AClientExtensions.AsAIAgent - Add Throw.IfNull(card) in A2AAgentCardExtensions.AsAIAgent - Clarify httpClient docs in A2ACardResolverExtensions.GetAIAgentAsync: it applies to the created A2A client, not to card discovery - Rename test methods from GetAIAgent_* to AsAIAgent_* to match the API under test Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
co-authored by
Copilot
parent
f390595188
commit
dd1e615dad
@@ -28,9 +28,7 @@ public sealed class A2AAgent : AIAgent
|
||||
private static readonly AIAgentMetadata s_agentMetadata = new("a2a");
|
||||
|
||||
private readonly IA2AClient _a2aClient;
|
||||
private readonly string? _id;
|
||||
private readonly string? _name;
|
||||
private readonly string? _description;
|
||||
private readonly A2AAgentOptions _agentOptions;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
/// <summary>
|
||||
@@ -38,17 +36,37 @@ public sealed class A2AAgent : AIAgent
|
||||
/// </summary>
|
||||
/// <param name="a2aClient">The A2A client to use for interacting with A2A agents.</param>
|
||||
/// <param name="id">The unique identifier for the agent.</param>
|
||||
/// <param name="name">The the name of the agent.</param>
|
||||
/// <param name="name">The name of the agent.</param>
|
||||
/// <param name="description">The description of the agent.</param>
|
||||
/// <param name="loggerFactory">Optional logger factory to use for logging.</param>
|
||||
public A2AAgent(IA2AClient a2aClient, string? id = null, string? name = null, string? description = null, ILoggerFactory? loggerFactory = null)
|
||||
: this(
|
||||
a2aClient,
|
||||
new A2AAgentOptions
|
||||
{
|
||||
Id = id,
|
||||
Name = name,
|
||||
Description = description
|
||||
},
|
||||
loggerFactory)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="A2AAgent"/> class.
|
||||
/// </summary>
|
||||
/// <param name="a2aClient">The A2A client to use for interacting with A2A agents.</param>
|
||||
/// <param name="options">
|
||||
/// Configuration options that control the agent's identity, including its identifier, name, and description.
|
||||
/// </param>
|
||||
/// <param name="loggerFactory">Optional logger factory to use for logging.</param>
|
||||
public A2AAgent(IA2AClient a2aClient, A2AAgentOptions options, ILoggerFactory? loggerFactory = null)
|
||||
{
|
||||
_ = Throw.IfNull(a2aClient);
|
||||
_ = Throw.IfNull(options);
|
||||
|
||||
this._a2aClient = a2aClient;
|
||||
this._id = id;
|
||||
this._name = name;
|
||||
this._description = description;
|
||||
this._agentOptions = options.Clone();
|
||||
this._logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger<A2AAgent>();
|
||||
}
|
||||
|
||||
@@ -216,13 +234,13 @@ public sealed class A2AAgent : AIAgent
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override string? IdCore => this._id;
|
||||
protected override string? IdCore => this._agentOptions.Id;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string? Name => this._name;
|
||||
public override string? Name => this._agentOptions.Name;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string? Description => this._description;
|
||||
public override string? Description => this._agentOptions.Description;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override object? GetService(Type serviceType, object? serviceKey = null)
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
namespace Microsoft.Agents.AI.A2A;
|
||||
|
||||
/// <summary>
|
||||
/// Represents configuration options for an <see cref="A2AAgent"/>, including its identifier, name, and description.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This class is used to encapsulate information about an A2A agent, such as its unique
|
||||
/// identifier, display name, and a descriptive summary. It provides an alternative to passing
|
||||
/// these values as individual constructor parameters.
|
||||
/// </remarks>
|
||||
public sealed class A2AAgentOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the agent id.
|
||||
/// </summary>
|
||||
public string? Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the agent name.
|
||||
/// </summary>
|
||||
public string? Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the agent description.
|
||||
/// </summary>
|
||||
public string? Description { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="A2AAgentOptions"/> with the same values as this instance.
|
||||
/// </summary>
|
||||
public A2AAgentOptions Clone()
|
||||
=> new()
|
||||
{
|
||||
Id = this.Id,
|
||||
Name = this.Name,
|
||||
Description = this.Description
|
||||
};
|
||||
}
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
using System.Net.Http;
|
||||
using Microsoft.Agents.AI;
|
||||
using Microsoft.Agents.AI.A2A;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Shared.Diagnostics;
|
||||
|
||||
namespace A2A;
|
||||
|
||||
@@ -36,4 +38,39 @@ public static class A2AAgentCardExtensions
|
||||
|
||||
return a2aClient.AsAIAgent(name: card.Name, description: card.Description, loggerFactory: loggerFactory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves an instance of <see cref="AIAgent"/> for an existing A2A agent.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method can be used to access A2A agents that support the
|
||||
/// <see href="https://github.com/a2aproject/A2A/blob/main/docs/topics/agent-discovery.md#2-curated-registries-catalog-based-discovery">Curated Registries (Catalog-Based Discovery)</see>
|
||||
/// discovery mechanism. When <paramref name="agentOptions"/> is provided, any non-null values override
|
||||
/// the corresponding values from the <see cref="AgentCard"/>.
|
||||
/// </remarks>
|
||||
/// <param name="card">The <see cref="AgentCard" /> to use for the agent creation.</param>
|
||||
/// <param name="agentOptions">
|
||||
/// Configuration options that control the agent's identity. When provided, non-null values override the
|
||||
/// corresponding values from the agent card.
|
||||
/// </param>
|
||||
/// <param name="httpClient">The <see cref="HttpClient"/> to use for HTTP requests.</param>
|
||||
/// <param name="clientOptions">
|
||||
/// Optional <see cref="A2AClientOptions"/> controlling protocol binding preference.
|
||||
/// When not provided, defaults to preferring HTTP+JSON first, with JSON-RPC as fallback.
|
||||
/// </param>
|
||||
/// <param name="loggerFactory">The logger factory for enabling logging within the agent.</param>
|
||||
/// <returns>An <see cref="AIAgent"/> instance backed by the A2A agent.</returns>
|
||||
public static AIAgent AsAIAgent(this AgentCard card, A2AAgentOptions agentOptions, HttpClient? httpClient = null, A2AClientOptions? clientOptions = null, ILoggerFactory? loggerFactory = null)
|
||||
{
|
||||
_ = Throw.IfNull(card);
|
||||
_ = Throw.IfNull(agentOptions);
|
||||
|
||||
var a2aClient = A2AClientFactory.Create(card, httpClient, clientOptions);
|
||||
|
||||
var mergedOptions = agentOptions.Clone();
|
||||
mergedOptions.Name ??= card.Name;
|
||||
mergedOptions.Description ??= card.Description;
|
||||
|
||||
return a2aClient.AsAIAgent(mergedOptions, loggerFactory);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Threading.Tasks;
|
||||
using Microsoft.Agents.AI;
|
||||
using Microsoft.Agents.AI.A2A;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Shared.Diagnostics;
|
||||
|
||||
namespace A2A;
|
||||
|
||||
@@ -48,4 +49,39 @@ public static class A2ACardResolverExtensions
|
||||
|
||||
return agentCard.AsAIAgent(httpClient, options, loggerFactory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves an instance of <see cref="AIAgent"/> for an existing A2A agent.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method can be used to access A2A agents that support the
|
||||
/// <see href="https://github.com/a2aproject/A2A/blob/main/docs/topics/agent-discovery.md#1-well-known-uri">Well-Known URI</see>
|
||||
/// discovery mechanism. When <paramref name="agentOptions"/> is provided, any non-null values override
|
||||
/// the corresponding values from the resolved <see cref="AgentCard"/>.
|
||||
/// </remarks>
|
||||
/// <param name="resolver">The <see cref="A2ACardResolver" /> to use for the agent creation.</param>
|
||||
/// <param name="agentOptions">
|
||||
/// Configuration options that control the agent's identity. When provided, non-null values override the
|
||||
/// corresponding values from the resolved agent card.
|
||||
/// </param>
|
||||
/// <param name="httpClient">
|
||||
/// The <see cref="HttpClient"/> to use for HTTP requests made by the created A2A client.
|
||||
/// This is not used for fetching the agent card; the resolver uses its own configured client for that.
|
||||
/// </param>
|
||||
/// <param name="clientOptions">
|
||||
/// Optional <see cref="A2AClientOptions"/> controlling protocol binding preference.
|
||||
/// When not provided, defaults to preferring HTTP+JSON first, with JSON-RPC as fallback.
|
||||
/// </param>
|
||||
/// <param name="loggerFactory">The logger factory for enabling logging within the agent.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
|
||||
/// <returns>An <see cref="AIAgent"/> instance backed by the A2A agent.</returns>
|
||||
public static async Task<AIAgent> GetAIAgentAsync(this A2ACardResolver resolver, A2AAgentOptions agentOptions, HttpClient? httpClient = null, A2AClientOptions? clientOptions = null, ILoggerFactory? loggerFactory = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
_ = Throw.IfNull(agentOptions);
|
||||
|
||||
// Obtain the agent card from the resolver.
|
||||
var agentCard = await resolver.GetAgentCardAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
return agentCard.AsAIAgent(agentOptions, httpClient, clientOptions, loggerFactory);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
using Microsoft.Agents.AI;
|
||||
using Microsoft.Agents.AI.A2A;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Shared.Diagnostics;
|
||||
|
||||
namespace A2A;
|
||||
|
||||
@@ -31,10 +32,32 @@ public static class A2AClientExtensions
|
||||
/// </remarks>
|
||||
/// <param name="client">The <see cref="IA2AClient" /> to use for the agent.</param>
|
||||
/// <param name="id">The unique identifier for the agent.</param>
|
||||
/// <param name="name">The the name of the agent.</param>
|
||||
/// <param name="name">The name of the agent.</param>
|
||||
/// <param name="description">The description of the agent.</param>
|
||||
/// <param name="loggerFactory">Optional logger factory for enabling logging within the agent.</param>
|
||||
/// <returns>An <see cref="AIAgent"/> instance backed by the A2A agent.</returns>
|
||||
public static AIAgent AsAIAgent(this IA2AClient client, string? id = null, string? name = null, string? description = null, ILoggerFactory? loggerFactory = null) =>
|
||||
new A2AAgent(client, id, name, description, loggerFactory);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves an instance of <see cref="AIAgent"/> for an existing A2A agent.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method can be used to access A2A agents that support the
|
||||
/// <see href="https://github.com/a2aproject/A2A/blob/main/docs/topics/agent-discovery.md#3-direct-configuration--private-discovery">Direct Configuration / Private Discovery</see>
|
||||
/// discovery mechanism.
|
||||
/// </remarks>
|
||||
/// <param name="client">The <see cref="IA2AClient" /> to use for the agent.</param>
|
||||
/// <param name="options">
|
||||
/// Configuration options that control the agent's identity, including its identifier, name, and description.
|
||||
/// </param>
|
||||
/// <param name="loggerFactory">Optional logger factory for enabling logging within the agent.</param>
|
||||
/// <returns>An <see cref="AIAgent"/> instance backed by the A2A agent.</returns>
|
||||
public static AIAgent AsAIAgent(this IA2AClient client, A2AAgentOptions options, ILoggerFactory? loggerFactory = null)
|
||||
{
|
||||
_ = Throw.IfNull(client);
|
||||
_ = Throw.IfNull(options);
|
||||
|
||||
return new A2AAgent(client, options, loggerFactory);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user