chore: import upstream snapshot with attribution
CodeQL / Analyze (csharp) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:21:23 +08:00
commit b957a53def
5423 changed files with 863745 additions and 0 deletions
@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<UserSecretsId>5ee045b0-aea3-4f08-8d31-32d1a6f8fed0</UserSecretsId>
<NoWarn>$(NoWarn);CS1591;VSTHRD111;CA2007;SKEXP0110</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="A2A.AspNetCore" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\src\Agents\A2A\Agents.A2A.csproj" />
<ProjectReference Include="..\..\..\..\src\Agents\AzureAI\Agents.AzureAI.csproj" />
<ProjectReference Include="..\..\..\..\src\Agents\Core\Agents.Core.csproj" />
<ProjectReference Include="..\..\..\..\src\Connectors\Connectors.OpenAI\Connectors.OpenAI.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,85 @@
### Each A2A agent is available at a different host address
@hostInvoice = http://localhost:5000
@hostPolicy = http://localhost:5001
@hostLogistics = http://localhost:5002
### Query agent card for the invoice agent
GET {{hostInvoice}}/.well-known/agent-card.json
### Send a message to the invoice agent
POST {{hostInvoice}}
Content-Type: application/json
{
"id": "1",
"jsonrpc": "2.0",
"method": "message/send",
"params": {
"id": "12345",
"message": {
"kind": "message",
"role": "user",
"messageId": "msg_1",
"parts": [
{
"kind": "text",
"text": "Show me all invoices for Contoso?"
}
]
}
}
}
### Query agent card for the policy agent
GET {{hostPolicy}}/.well-known/agent-card.json
### Send a message to the policy agent
POST {{hostPolicy}}
Content-Type: application/json
{
"id": "1",
"jsonrpc": "2.0",
"method": "message/send",
"params": {
"id": "12345",
"message": {
"kind": "message",
"role": "user",
"messageId": "msg_1",
"parts": [
{
"kind": "text",
"text": "What is the policy for short shipments?"
}
]
}
}
}
### Query agent card for the logistics agent
GET {{hostLogistics}}/.well-known/agent-card.json
### Send a message to the logistics agent
POST {{hostLogistics}}
Content-Type: application/json
{
"id": "1",
"jsonrpc": "2.0",
"method": "message/send",
"params": {
"id": "12345",
"message": {
"kind": "message",
"role": "user",
"messageId": "msg_1",
"parts": [
{
"kind": "text",
"text": "What is the status for SHPMT-SAP-001?"
}
]
}
}
}
@@ -0,0 +1,162 @@
// Copyright (c) Microsoft. All rights reserved.
using A2A;
using Azure.AI.Agents.Persistent;
using Azure.Identity;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Agents;
using Microsoft.SemanticKernel.Agents.A2A;
using Microsoft.SemanticKernel.Agents.AzureAI;
namespace A2AServer;
internal static class HostAgentFactory
{
internal static async Task<A2AHostAgent> CreateFoundryHostAgentAsync(string agentType, string modelId, string endpoint, string assistantId, IEnumerable<KernelPlugin>? plugins = null)
{
var agentsClient = new PersistentAgentsClient(endpoint, new AzureCliCredential());
PersistentAgent definition = await agentsClient.Administration.GetAgentAsync(assistantId);
var agent = new AzureAIAgent(definition, agentsClient, plugins);
AgentCard agentCard = agentType.ToUpperInvariant() switch
{
"INVOICE" => GetInvoiceAgentCard(),
"POLICY" => GetPolicyAgentCard(),
"LOGISTICS" => GetLogisticsAgentCard(),
_ => throw new ArgumentException($"Unsupported agent type: {agentType}"),
};
return new A2AHostAgent(agent, agentCard);
}
internal static async Task<A2AHostAgent> CreateChatCompletionHostAgentAsync(string agentType, string modelId, string apiKey, string name, string instructions, IEnumerable<KernelPlugin>? plugins = null)
{
var builder = Kernel.CreateBuilder();
builder.AddOpenAIChatCompletion(modelId, apiKey);
if (plugins is not null)
{
foreach (var plugin in plugins)
{
builder.Plugins.Add(plugin);
}
}
var kernel = builder.Build();
var agent = new ChatCompletionAgent()
{
Kernel = kernel,
Name = name,
Instructions = instructions,
Arguments = new KernelArguments(new PromptExecutionSettings() { FunctionChoiceBehavior = FunctionChoiceBehavior.Auto() }),
};
AgentCard agentCard = agentType.ToUpperInvariant() switch
{
"INVOICE" => GetInvoiceAgentCard(),
"POLICY" => GetPolicyAgentCard(),
"LOGISTICS" => GetLogisticsAgentCard(),
_ => throw new ArgumentException($"Unsupported agent type: {agentType}"),
};
return new A2AHostAgent(agent, agentCard);
}
#region private
private static AgentCard GetInvoiceAgentCard()
{
var capabilities = new AgentCapabilities()
{
Streaming = false,
PushNotifications = false,
};
var invoiceQuery = new AgentSkill()
{
Id = "id_invoice_agent",
Name = "InvoiceQuery",
Description = "Handles requests relating to invoices.",
Tags = ["invoice", "semantic-kernel"],
Examples =
[
"List the latest invoices for Contoso.",
],
};
return new()
{
Name = "InvoiceAgent",
Description = "Handles requests relating to invoices.",
Version = "1.0.0",
DefaultInputModes = ["text"],
DefaultOutputModes = ["text"],
Capabilities = capabilities,
Skills = [invoiceQuery],
};
}
private static AgentCard GetPolicyAgentCard()
{
var capabilities = new AgentCapabilities()
{
Streaming = false,
PushNotifications = false,
};
var invoiceQuery = new AgentSkill()
{
Id = "id_policy_agent",
Name = "PolicyAgent",
Description = "Handles requests relating to policies and customer communications.",
Tags = ["policy", "semantic-kernel"],
Examples =
[
"What is the policy for short shipments?",
],
};
return new AgentCard()
{
Name = "PolicyAgent",
Description = "Handles requests relating to policies and customer communications.",
Version = "1.0.0",
DefaultInputModes = ["text"],
DefaultOutputModes = ["text"],
Capabilities = capabilities,
Skills = [invoiceQuery],
};
}
private static AgentCard GetLogisticsAgentCard()
{
var capabilities = new AgentCapabilities()
{
Streaming = false,
PushNotifications = false,
};
var invoiceQuery = new AgentSkill()
{
Id = "id_invoice_agent",
Name = "LogisticsQuery",
Description = "Handles requests relating to logistics.",
Tags = ["logistics", "semantic-kernel"],
Examples =
[
"What is the status for SHPMT-SAP-001",
],
};
return new AgentCard()
{
Name = "LogisticsAgent",
Description = "Handles requests relating to logistics.",
Version = "1.0.0",
DefaultInputModes = ["text"],
DefaultOutputModes = ["text"],
Capabilities = capabilities,
Skills = [invoiceQuery],
};
}
#endregion
}
@@ -0,0 +1,177 @@
// Copyright (c) Microsoft. All rights reserved.
using System.ComponentModel;
using Microsoft.SemanticKernel;
namespace A2A;
/// <summary>
/// A simple invoice plugin that returns mock data.
/// </summary>
public class Product
{
public string Name { get; set; }
public int Quantity { get; set; }
public decimal Price { get; set; } // Price per unit
public Product(string name, int quantity, decimal price)
{
this.Name = name;
this.Quantity = quantity;
this.Price = price;
}
public decimal TotalPrice()
{
return this.Quantity * this.Price; // Total price for this product
}
}
public class Invoice
{
public string TransactionId { get; set; }
public string InvoiceId { get; set; }
public string CompanyName { get; set; }
public DateTime InvoiceDate { get; set; }
public List<Product> Products { get; set; } // List of products
public Invoice(string transactionId, string invoiceId, string companyName, DateTime invoiceDate, List<Product> products)
{
this.TransactionId = transactionId;
this.InvoiceId = invoiceId;
this.CompanyName = companyName;
this.InvoiceDate = invoiceDate;
this.Products = products;
}
public decimal TotalInvoicePrice()
{
return this.Products.Sum(product => product.TotalPrice()); // Total price of all products in the invoice
}
}
public class InvoiceQueryPlugin
{
private readonly List<Invoice> _invoices;
private static readonly Random s_random = new();
public InvoiceQueryPlugin()
{
// Extended mock data with quantities and prices
this._invoices =
[
new("TICKET-XYZ987", "INV789", "Contoso", GetRandomDateWithinLastTwoMonths(),
[
new("T-Shirts", 150, 10.00m),
new("Hats", 200, 15.00m),
new("Glasses", 300, 5.00m)
]),
new("TICKET-XYZ111", "INV111", "XStore", GetRandomDateWithinLastTwoMonths(),
[
new("T-Shirts", 2500, 12.00m),
new("Hats", 1500, 8.00m),
new("Glasses", 200, 20.00m)
]),
new("TICKET-XYZ222", "INV222", "Cymbal Direct", GetRandomDateWithinLastTwoMonths(),
[
new("T-Shirts", 1200, 14.00m),
new("Hats", 800, 7.00m),
new("Glasses", 500, 25.00m)
]),
new("TICKET-XYZ333", "INV333", "Contoso", GetRandomDateWithinLastTwoMonths(),
[
new("T-Shirts", 400, 11.00m),
new("Hats", 600, 15.00m),
new("Glasses", 700, 5.00m)
]),
new("TICKET-XYZ444", "INV444", "XStore", GetRandomDateWithinLastTwoMonths(),
[
new("T-Shirts", 800, 10.00m),
new("Hats", 500, 18.00m),
new("Glasses", 300, 22.00m)
]),
new("TICKET-XYZ555", "INV555", "Cymbal Direct", GetRandomDateWithinLastTwoMonths(),
[
new("T-Shirts", 1100, 9.00m),
new("Hats", 900, 12.00m),
new("Glasses", 1200, 15.00m)
]),
new("TICKET-XYZ666", "INV666", "Contoso", GetRandomDateWithinLastTwoMonths(),
[
new("T-Shirts", 2500, 8.00m),
new("Hats", 1200, 10.00m),
new("Glasses", 1000, 6.00m)
]),
new("TICKET-XYZ777", "INV777", "XStore", GetRandomDateWithinLastTwoMonths(),
[
new("T-Shirts", 1900, 13.00m),
new("Hats", 1300, 16.00m),
new("Glasses", 800, 19.00m)
]),
new("TICKET-XYZ888", "INV888", "Cymbal Direct", GetRandomDateWithinLastTwoMonths(),
[
new("T-Shirts", 2200, 11.00m),
new("Hats", 1700, 8.50m),
new("Glasses", 600, 21.00m)
]),
new("TICKET-XYZ999", "INV999", "Contoso", GetRandomDateWithinLastTwoMonths(),
[
new("T-Shirts", 1400, 10.50m),
new("Hats", 1100, 9.00m),
new("Glasses", 950, 12.00m)
])
];
}
public static DateTime GetRandomDateWithinLastTwoMonths()
{
// Get the current date and time
DateTime endDate = DateTime.Now;
// Calculate the start date, which is two months before the current date
DateTime startDate = endDate.AddMonths(-2);
// Generate a random number of days between 0 and the total number of days in the range
int totalDays = (endDate - startDate).Days;
int randomDays = s_random.Next(0, totalDays + 1); // +1 to include the end date
// Return the random date
return startDate.AddDays(randomDays);
}
[KernelFunction]
[Description("Retrieves invoices for the specified company and optionally within the specified time range")]
public IEnumerable<Invoice> QueryInvoices(string companyName, DateTime? startDate = null, DateTime? endDate = null)
{
var query = this._invoices.Where(i => i.CompanyName.Equals(companyName, StringComparison.OrdinalIgnoreCase));
if (startDate.HasValue)
{
query = query.Where(i => i.InvoiceDate >= startDate.Value);
}
if (endDate.HasValue)
{
query = query.Where(i => i.InvoiceDate <= endDate.Value);
}
return query.ToList();
}
[KernelFunction]
[Description("Retrieves invoice using the transaction id")]
public IEnumerable<Invoice> QueryByTransactionId(string transactionId)
{
var query = this._invoices.Where(i => i.TransactionId.Equals(transactionId, StringComparison.OrdinalIgnoreCase));
return query.ToList();
}
[KernelFunction]
[Description("Retrieves invoice using the invoice id")]
public IEnumerable<Invoice> QueryByInvoiceId(string invoiceId)
{
var query = this._invoices.Where(i => i.InvoiceId.Equals(invoiceId, StringComparison.OrdinalIgnoreCase));
return query.ToList();
}
}
@@ -0,0 +1,102 @@
// Copyright (c) Microsoft. All rights reserved.
using A2A;
using A2A.AspNetCore;
using A2AServer;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Agents.A2A;
string agentId = string.Empty;
string agentType = string.Empty;
for (var i = 0; i < args.Length; i++)
{
if (args[i].StartsWith("--agentId", StringComparison.InvariantCultureIgnoreCase) && i + 1 < args.Length)
{
agentId = args[++i];
}
else if (args[i].StartsWith("--agentType", StringComparison.InvariantCultureIgnoreCase) && i + 1 < args.Length)
{
agentType = args[++i];
}
}
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpClient().AddLogging();
var app = builder.Build();
var httpClient = app.Services.GetRequiredService<IHttpClientFactory>().CreateClient();
var logger = app.Logger;
IConfigurationRoot configuration = new ConfigurationBuilder()
.AddEnvironmentVariables()
.AddUserSecrets<Program>()
.Build();
string? apiKey = configuration["A2AServer:ApiKey"];
string? endpoint = configuration["A2AServer:Endpoint"];
string modelId = configuration["A2AServer:ModelId"] ?? "gpt-4o-mini";
IEnumerable<KernelPlugin> invoicePlugins = [KernelPluginFactory.CreateFromType<InvoiceQueryPlugin>()];
A2AHostAgent? hostAgent = null;
if (!string.IsNullOrEmpty(endpoint) && !string.IsNullOrEmpty(agentId))
{
hostAgent = agentType.ToUpperInvariant() switch
{
"INVOICE" => await HostAgentFactory.CreateFoundryHostAgentAsync(agentType, modelId, endpoint, agentId, invoicePlugins),
"POLICY" => await HostAgentFactory.CreateFoundryHostAgentAsync(agentType, modelId, endpoint, agentId),
"LOGISTICS" => await HostAgentFactory.CreateFoundryHostAgentAsync(agentType, modelId, endpoint, agentId),
_ => throw new ArgumentException($"Unsupported agent type: {agentType}"),
};
}
else if (!string.IsNullOrEmpty(apiKey))
{
hostAgent = agentType.ToUpperInvariant() switch
{
"INVOICE" => await HostAgentFactory.CreateChatCompletionHostAgentAsync(
agentType, modelId, apiKey, "InvoiceAgent",
"""
You specialize in handling queries related to invoices.
""", invoicePlugins),
"POLICY" => await HostAgentFactory.CreateChatCompletionHostAgentAsync(
agentType, modelId, apiKey, "PolicyAgent",
"""
You specialize in handling queries related to policies and customer communications.
Always reply with exactly this text:
Policy: Short Shipment Dispute Handling Policy V2.1
Summary: "For short shipments reported by customers, first verify internal shipment records
(SAP) and physical logistics scan data (BigQuery). If discrepancy is confirmed and logistics data
shows fewer items packed than invoiced, issue a credit for the missing items. Document the
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."
""", invoicePlugins),
"LOGISTICS" => await HostAgentFactory.CreateChatCompletionHostAgentAsync(
agentType, modelId, apiKey, "LogisticsAgent",
"""
You specialize in handling queries related to logistics.
Always reply with exactly:
Shipment number: SHPMT-SAP-001
Item: TSHIRT-RED-L
Quantity: 900
""", invoicePlugins),
_ => throw new ArgumentException($"Unsupported agent type: {agentType}"),
};
}
else
{
throw new ArgumentException("Either A2AServer:ApiKey or A2AServer:ConnectionString & agentId must be provided");
}
app.MapA2A(hostAgent!.TaskManager!, "/");
app.MapWellKnownAgentCard(hostAgent!.TaskManager!, "/");
await app.RunAsync();