update a2a agent to the latest a2a sdk (#5257)

This commit is contained in:
SergeyMenshykh
2026-04-15 11:08:05 +01:00
committed by GitHub
parent 485af07b8c
commit 6173e63f0b
18 changed files with 728 additions and 390 deletions
@@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<TargetFrameworks>net10.0</TargetFrameworks>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
@@ -13,7 +13,6 @@
<PackageReference Include="Azure.AI.OpenAI" />
<PackageReference Include="Azure.Identity" />
<PackageReference Include="Microsoft.Extensions.Hosting" />
<PackageReference Include="System.Net.ServerSentEvents" />
</ItemGroup>
<ItemGroup>
@@ -62,12 +62,10 @@ public static class Program
}
var agentResponse = await hostAgent.Agent!.RunAsync(message, session, cancellationToken: cancellationToken);
foreach (var chatMessage in agentResponse.Messages)
{
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine($"\nAgent: {chatMessage.Text}");
Console.ResetColor();
}
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine($"\nAgent: {agentResponse.Text}");
Console.ResetColor();
}
}
catch (Exception ex)
@@ -13,7 +13,7 @@ namespace A2AServer;
internal static class HostAgentFactory
{
internal static async Task<(AIAgent, AgentCard)> CreateFoundryHostAgentAsync(string agentType, string model, string endpoint, string agentName, IList<AITool>? tools = null)
internal static async Task<(AIAgent, AgentCard)> CreateFoundryHostAgentAsync(string agentType, string model, string endpoint, string agentName, string[] agentUrls, IList<AITool>? tools = null)
{
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
@@ -25,16 +25,16 @@ internal static class HostAgentFactory
AgentCard agentCard = agentType.ToUpperInvariant() switch
{
"INVOICE" => GetInvoiceAgentCard(),
"POLICY" => GetPolicyAgentCard(),
"LOGISTICS" => GetLogisticsAgentCard(),
"INVOICE" => GetInvoiceAgentCard(agentUrls),
"POLICY" => GetPolicyAgentCard(agentUrls),
"LOGISTICS" => GetLogisticsAgentCard(agentUrls),
_ => throw new ArgumentException($"Unsupported agent type: {agentType}"),
};
return new(agent, agentCard);
}
internal static async Task<(AIAgent, AgentCard)> CreateChatCompletionHostAgentAsync(string agentType, string model, string apiKey, string name, string instructions, IList<AITool>? tools = null)
internal static async Task<(AIAgent, AgentCard)> CreateChatCompletionHostAgentAsync(string agentType, string model, string apiKey, string name, string instructions, string[] agentUrls, IList<AITool>? tools = null)
{
AIAgent agent = new OpenAIClient(apiKey)
.GetChatClient(model)
@@ -42,9 +42,9 @@ internal static class HostAgentFactory
AgentCard agentCard = agentType.ToUpperInvariant() switch
{
"INVOICE" => GetInvoiceAgentCard(),
"POLICY" => GetPolicyAgentCard(),
"LOGISTICS" => GetLogisticsAgentCard(),
"INVOICE" => GetInvoiceAgentCard(agentUrls),
"POLICY" => GetPolicyAgentCard(agentUrls),
"LOGISTICS" => GetLogisticsAgentCard(agentUrls),
_ => throw new ArgumentException($"Unsupported agent type: {agentType}"),
};
@@ -52,7 +52,7 @@ internal static class HostAgentFactory
}
#region private
private static AgentCard GetInvoiceAgentCard()
private static AgentCard GetInvoiceAgentCard(string[] agentUrls)
{
var capabilities = new AgentCapabilities()
{
@@ -81,10 +81,11 @@ internal static class HostAgentFactory
DefaultOutputModes = ["text"],
Capabilities = capabilities,
Skills = [invoiceQuery],
SupportedInterfaces = CreateAgentInterfaces(agentUrls)
};
}
private static AgentCard GetPolicyAgentCard()
private static AgentCard GetPolicyAgentCard(string[] agentUrls)
{
var capabilities = new AgentCapabilities()
{
@@ -113,10 +114,11 @@ internal static class HostAgentFactory
DefaultOutputModes = ["text"],
Capabilities = capabilities,
Skills = [policyQuery],
SupportedInterfaces = CreateAgentInterfaces(agentUrls)
};
}
private static AgentCard GetLogisticsAgentCard()
private static AgentCard GetLogisticsAgentCard(string[] agentUrls)
{
var capabilities = new AgentCapabilities()
{
@@ -145,7 +147,18 @@ internal static class HostAgentFactory
DefaultOutputModes = ["text"],
Capabilities = capabilities,
Skills = [logisticsQuery],
SupportedInterfaces = CreateAgentInterfaces(agentUrls)
};
}
private static List<AgentInterface> CreateAgentInterfaces(string[] agentUrls)
{
return agentUrls.Select(url => new AgentInterface
{
Url = url,
ProtocolBinding = "JSONRPC",
ProtocolVersion = "1.0",
}).ToList();
}
#endregion
}
@@ -38,14 +38,15 @@ IConfigurationRoot configuration = new ConfigurationBuilder()
string? apiKey = configuration["OPENAI_API_KEY"];
string model = configuration["OPENAI_CHAT_MODEL_NAME"] ?? "gpt-5.4-mini";
string? endpoint = configuration["AZURE_AI_PROJECT_ENDPOINT"];
string[] agentUrls = (app.Configuration["urls"] ?? "http://localhost:5000").Split(';');
var invoiceQueryPlugin = new InvoiceQuery();
IList<AITool> tools =
[
[
AIFunctionFactory.Create(invoiceQueryPlugin.QueryInvoices),
AIFunctionFactory.Create(invoiceQueryPlugin.QueryByTransactionId),
AIFunctionFactory.Create(invoiceQueryPlugin.QueryByInvoiceId)
];
];
AIAgent hostA2AAgent;
AgentCard hostA2AAgentCard;
@@ -54,9 +55,9 @@ if (!string.IsNullOrEmpty(endpoint) && !string.IsNullOrEmpty(agentName))
{
(hostA2AAgent, hostA2AAgentCard) = agentType.ToUpperInvariant() switch
{
"INVOICE" => await HostAgentFactory.CreateFoundryHostAgentAsync(agentType, model, endpoint, agentName, tools),
"POLICY" => await HostAgentFactory.CreateFoundryHostAgentAsync(agentType, model, endpoint, agentName),
"LOGISTICS" => await HostAgentFactory.CreateFoundryHostAgentAsync(agentType, model, endpoint, agentName),
"INVOICE" => await HostAgentFactory.CreateFoundryHostAgentAsync(agentType, model, endpoint, agentName, agentUrls, tools),
"POLICY" => await HostAgentFactory.CreateFoundryHostAgentAsync(agentType, model, endpoint, agentName, agentUrls),
"LOGISTICS" => await HostAgentFactory.CreateFoundryHostAgentAsync(agentType, model, endpoint, agentName, agentUrls),
_ => throw new ArgumentException($"Unsupported agent type: {agentType}"),
};
}
@@ -68,7 +69,7 @@ else if (!string.IsNullOrEmpty(apiKey))
agentType, model, apiKey, "InvoiceAgent",
"""
You specialize in handling queries related to invoices.
""", tools),
""", agentUrls, tools),
"POLICY" => await HostAgentFactory.CreateChatCompletionHostAgentAsync(
agentType, model, apiKey, "PolicyAgent",
"""
@@ -84,7 +85,7 @@ else if (!string.IsNullOrEmpty(apiKey))
resolution in SAP CRM and notify the customer via email within 2 business days, referencing the
original invoice and the credit memo number. Use the 'Formal Credit Notification' email
template."
"""),
""", agentUrls),
"LOGISTICS" => await HostAgentFactory.CreateChatCompletionHostAgentAsync(
agentType, model, apiKey, "LogisticsAgent",
"""
@@ -95,7 +96,7 @@ else if (!string.IsNullOrEmpty(apiKey))
Shipment number: SHPMT-SAP-001
Item: TSHIRT-RED-L
Quantity: 900
"""),
""", agentUrls),
_ => throw new ArgumentException($"Unsupported agent type: {agentType}"),
};
}
@@ -104,10 +105,9 @@ else
throw new ArgumentException("Either A2AServer:ApiKey or A2AServer:ConnectionString & agentName must be provided");
}
var a2aTaskManager = app.MapA2A(
app.MapA2A(
hostA2AAgent,
path: "/",
agentCard: hostA2AAgentCard,
taskManager => app.MapWellKnownAgentCard(taskManager, "/"));
agentCard: hostA2AAgentCard);
await app.RunAsync();
@@ -43,20 +43,21 @@ internal sealed class A2AAgentClient : AgentClientBase
{
// Convert all messages to A2A parts and create a single message
var parts = messages.ToParts();
var a2aMessage = new AgentMessage
var a2aMessage = new Message
{
MessageId = Guid.NewGuid().ToString("N"),
ContextId = contextId,
Role = MessageRole.User,
Role = Role.User,
Parts = parts
};
var messageSendParams = new MessageSendParams { Message = a2aMessage };
var messageSendParams = new SendMessageRequest { Message = a2aMessage };
var a2aResponse = await a2aClient.SendMessageAsync(messageSendParams, cancellationToken);
// Handle different response types
if (a2aResponse is AgentMessage message)
if (a2aResponse.PayloadCase == SendMessageResponseCase.Message)
{
var message = a2aResponse.Message!;
var responseMessage = message.ToChatMessage();
if (responseMessage is { Contents.Count: > 0 })
{
@@ -67,9 +68,10 @@ internal sealed class A2AAgentClient : AgentClientBase
});
}
}
else if (a2aResponse is AgentTask agentTask)
else if (a2aResponse.PayloadCase == SendMessageResponseCase.Task)
{
// Manually convert AgentTask artifacts to ChatMessages since the extension method is internal
var agentTask = a2aResponse.Task!;
if (agentTask.Artifacts is not null)
{
foreach (var artifact in agentTask.Artifacts)