Skip to content

Commit dd6ae07

Browse files
authored
Merge pull request #1262 from SciSharp/a2a
feat: a2a support
2 parents ca39b60 + d1eb06f commit dd6ae07

File tree

16 files changed

+467
-5
lines changed

16 files changed

+467
-5
lines changed

BotSharp.sln

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Plugin.MMPEmbeddin
153153
EndProject
154154
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Plugin.Membase", "src\Plugins\BotSharp.Plugin.Membase\BotSharp.Plugin.Membase.csproj", "{13223C71-9EAC-9835-28ED-5A4833E6F915}"
155155
EndProject
156-
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MultiTenancy", "MultiTenancy", "{7C64208C-8D11-4E17-A3E9-14D7910763EB}"
156+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Core.A2A", "src\Infrastructure\BotSharp.Core.A2A\BotSharp.Core.A2A.csproj", "{E8D01281-D52A-BFF4-33DB-E35D91754272}"
157157
EndProject
158158
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Plugin.MultiTenancy", "src\Plugins\BotSharp.Plugin.MultiTenancy\BotSharp.Plugin.MultiTenancy.csproj", "{9BC8DF43-88D1-4C57-A678-AC0153BDF4EB}"
159159
EndProject
@@ -653,6 +653,14 @@ Global
653653
{13223C71-9EAC-9835-28ED-5A4833E6F915}.Release|Any CPU.Build.0 = Release|Any CPU
654654
{13223C71-9EAC-9835-28ED-5A4833E6F915}.Release|x64.ActiveCfg = Release|Any CPU
655655
{13223C71-9EAC-9835-28ED-5A4833E6F915}.Release|x64.Build.0 = Release|Any CPU
656+
{E8D01281-D52A-BFF4-33DB-E35D91754272}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
657+
{E8D01281-D52A-BFF4-33DB-E35D91754272}.Debug|Any CPU.Build.0 = Debug|Any CPU
658+
{E8D01281-D52A-BFF4-33DB-E35D91754272}.Debug|x64.ActiveCfg = Debug|Any CPU
659+
{E8D01281-D52A-BFF4-33DB-E35D91754272}.Debug|x64.Build.0 = Debug|Any CPU
660+
{E8D01281-D52A-BFF4-33DB-E35D91754272}.Release|Any CPU.ActiveCfg = Release|Any CPU
661+
{E8D01281-D52A-BFF4-33DB-E35D91754272}.Release|Any CPU.Build.0 = Release|Any CPU
662+
{E8D01281-D52A-BFF4-33DB-E35D91754272}.Release|x64.ActiveCfg = Release|Any CPU
663+
{E8D01281-D52A-BFF4-33DB-E35D91754272}.Release|x64.Build.0 = Release|Any CPU
656664
{9BC8DF43-88D1-4C57-A678-AC0153BDF4EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
657665
{9BC8DF43-88D1-4C57-A678-AC0153BDF4EB}.Debug|Any CPU.Build.0 = Debug|Any CPU
658666
{9BC8DF43-88D1-4C57-A678-AC0153BDF4EB}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -735,7 +743,7 @@ Global
735743
{E7C243B9-E751-B3B4-8F16-95C76CA90D31} = {51AFE054-AE99-497D-A593-69BAEFB5106F}
736744
{394B858B-9C26-B977-A2DA-8CC7BE5914CB} = {4F346DCE-087F-4368-AF88-EE9C720D0E69}
737745
{13223C71-9EAC-9835-28ED-5A4833E6F915} = {53E7CD86-0D19-40D9-A0FA-AB4613837E89}
738-
{7C64208C-8D11-4E17-A3E9-14D7910763EB} = {2635EC9B-2E5F-4313-AC21-0B847F31F36C}
746+
{E8D01281-D52A-BFF4-33DB-E35D91754272} = {E29DC6C4-5E57-48C5-BCB0-6B8F84782749}
739747
{9BC8DF43-88D1-4C57-A678-AC0153BDF4EB} = {7C64208C-8D11-4E17-A3E9-14D7910763EB}
740748
EndGlobalSection
741749
GlobalSection(ExtensibilityGlobals) = postSolution

Directory.Packages.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
44
</PropertyGroup>
55
<ItemGroup>
6+
<PackageVersion Include="A2A" Version="0.3.3-preview" />
67
<PackageVersion Include="CsvHelper" Version="33.1.0" />
78
<PackageVersion Include="FuzzySharp" Version="2.0.2" />
89
<PackageVersion Include="Google_GenerativeAI" Version="3.4.1" />

src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentType.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,10 @@ public static class AgentType
2323
/// Agent that cannot use external tools
2424
/// </summary>
2525
public const string Static = "static";
26+
27+
/// <summary>
28+
/// A2A remote agent for Microsoft Agent Framework integration
29+
/// </summary>
30+
public const string A2ARemote = "a2a-remote";
2631
}
2732

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using BotSharp.Abstraction.Agents;
2+
using BotSharp.Abstraction.Conversations;
3+
using BotSharp.Abstraction.Functions;
4+
using BotSharp.Abstraction.Plugins;
5+
using BotSharp.Abstraction.Settings;
6+
using BotSharp.Core.A2A.Functions;
7+
using BotSharp.Core.A2A.Hooks;
8+
using BotSharp.Core.A2A.Services;
9+
using BotSharp.Core.A2A.Settings;
10+
using Microsoft.Extensions.Configuration;
11+
using Microsoft.Extensions.DependencyInjection;
12+
13+
namespace BotSharp.Core.A2A;
14+
15+
public class A2APlugin : IBotSharpPlugin
16+
{
17+
18+
public string Id => "058cdf87-fcf3-eda9-915a-565c04bc9f56";
19+
20+
public string Name => "A2A Protocol Integration";
21+
22+
public string Description => "Enables seamless integration with external agents via the Agent-to-Agent (A2A) protocol.";
23+
24+
public void RegisterDI(IServiceCollection services, IConfiguration config)
25+
{
26+
services.AddScoped(provider =>
27+
{
28+
var settingService = provider.GetRequiredService<ISettingService>();
29+
return settingService.Bind<A2ASettings>("A2AIntegration");
30+
});
31+
32+
services.AddScoped<IA2AService, A2AService>();
33+
services.AddScoped<IAgentHook, A2AAgentHook>();
34+
services.AddScoped<IFunctionCallback, A2ADelegationFn>();
35+
}
36+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>$(TargetFramework)</TargetFramework>
5+
<LangVersion>$(LangVersion)</LangVersion>
6+
<VersionPrefix>$(BotSharpVersion)</VersionPrefix>
7+
<GeneratePackageOnBuild>$(GeneratePackageOnBuild)</GeneratePackageOnBuild>
8+
<OutputPath>$(SolutionDir)packages</OutputPath>
9+
<ImplicitUsings>enable</ImplicitUsings>
10+
<Nullable>enable</Nullable>
11+
</PropertyGroup>
12+
13+
<ItemGroup>
14+
<PackageReference Include="A2A" />
15+
</ItemGroup>
16+
17+
<ItemGroup>
18+
<ProjectReference Include="..\..\Infrastructure\BotSharp.Abstraction\BotSharp.Abstraction.csproj" />
19+
</ItemGroup>
20+
21+
</Project>
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
using BotSharp.Abstraction.Conversations;
2+
using BotSharp.Abstraction.Conversations.Models;
3+
using BotSharp.Abstraction.Functions;
4+
using BotSharp.Core.A2A.Services;
5+
using BotSharp.Core.A2A.Settings;
6+
using System.Text.Json;
7+
8+
namespace BotSharp.Core.A2A.Functions;
9+
10+
public class A2ADelegationFn : IFunctionCallback
11+
{
12+
public string Name => "delegate_to_a2a";
13+
public string Indication => "Connecting to external agent network...";
14+
15+
private readonly IA2AService _a2aService;
16+
private readonly A2ASettings _settings;
17+
private readonly IConversationStateService _stateService;
18+
19+
public A2ADelegationFn(IA2AService a2aService, A2ASettings settings, IConversationStateService stateService)
20+
{
21+
_a2aService = a2aService;
22+
_settings = settings;
23+
_stateService = stateService;
24+
}
25+
26+
public async Task<bool> Execute(RoleDialogModel message)
27+
{
28+
var args = JsonSerializer.Deserialize<JsonElement>(message.FunctionArgs);
29+
string queryText = string.Empty;
30+
if (args.TryGetProperty("user_query", out var queryProp))
31+
{
32+
queryText = queryProp.GetString();
33+
}
34+
35+
var agentId = message.CurrentAgentId;
36+
var agentConfig = _settings.Agents.FirstOrDefault(x => x.Id == agentId);
37+
38+
if (agentConfig == null)
39+
{
40+
message.Content = "System Error: Remote agent configuration not found.";
41+
message.StopCompletion = true;
42+
return false;
43+
}
44+
45+
var conversationId = _stateService.GetConversationId();
46+
47+
try
48+
{
49+
var responseText = await _a2aService.SendMessageAsync(
50+
agentConfig.Endpoint,
51+
queryText,
52+
conversationId,
53+
CancellationToken.None
54+
);
55+
56+
message.Content = responseText;
57+
return true;
58+
}
59+
catch (Exception ex)
60+
{
61+
message.Content = $"Communication failure with external agent: {ex.Message}";
62+
return false;
63+
}
64+
}
65+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
using BotSharp.Abstraction.Agents;
2+
using BotSharp.Abstraction.Agents.Enums;
3+
using BotSharp.Abstraction.Agents.Models;
4+
using BotSharp.Abstraction.Agents.Settings;
5+
using BotSharp.Abstraction.Functions.Models;
6+
using BotSharp.Core.A2A.Services;
7+
using BotSharp.Core.A2A.Settings;
8+
using System.Text.Json;
9+
10+
namespace BotSharp.Core.A2A.Hooks;
11+
12+
public class A2AAgentHook : AgentHookBase
13+
{
14+
public override string SelfId => string.Empty;
15+
16+
private readonly A2ASettings _settings;
17+
private readonly IA2AService _iA2AService;
18+
19+
public A2AAgentHook(IServiceProvider services, IA2AService a2AService, A2ASettings settings)
20+
: base(services, new AgentSettings())
21+
{
22+
_iA2AService = a2AService;
23+
_settings = settings;
24+
}
25+
26+
public override bool OnAgentLoading(ref string id)
27+
{
28+
var agentId = id;
29+
var remoteConfig = _settings.Agents.FirstOrDefault(x => x.Id == agentId);
30+
if (remoteConfig != null)
31+
{
32+
return true;
33+
}
34+
return base.OnAgentLoading(ref id);
35+
}
36+
37+
public override void OnAgentLoaded(Agent agent)
38+
{
39+
// Check if this is an A2A remote agent
40+
if (agent.Type != AgentType.A2ARemote)
41+
{
42+
return;
43+
}
44+
45+
var remoteConfig = _settings.Agents.FirstOrDefault(x => x.Id == agent.Id);
46+
if (remoteConfig != null)
47+
{
48+
var agentCard = _iA2AService.GetCapabilitiesAsync(remoteConfig.Endpoint).GetAwaiter().GetResult();
49+
agent.Name = agentCard.Name;
50+
agent.Description = agentCard.Description;
51+
agent.Instruction = $"You are a proxy interface for an external intelligent service named '{agentCard.Name}'. " +
52+
$"Your ONLY goal is to forward the user's request verbatim to the external service. " +
53+
$"You must use the function 'delegate_to_a2a' to communicate with it. " +
54+
$"Do not attempt to answer the question yourself.";
55+
56+
var properties = new Dictionary<string, object>
57+
{
58+
{
59+
"user_query",
60+
new
61+
{
62+
type = "string",
63+
description = "The exact user request or task description to be forwarded."
64+
}
65+
}
66+
};
67+
68+
var propertiesJson = JsonSerializer.Serialize(properties);
69+
var propertiesDocument = JsonDocument.Parse(propertiesJson);
70+
71+
agent.Functions.Add(new FunctionDef
72+
{
73+
Name = "delegate_to_a2a",
74+
Description = $"Delegates the task to the external {remoteConfig.Name} via A2A protocol.",
75+
Parameters = new FunctionParametersDef()
76+
{
77+
Type = "object",
78+
Properties = propertiesDocument,
79+
Required = new List<string> { "user_query" }
80+
}
81+
});
82+
}
83+
base.OnAgentLoaded(agent);
84+
}
85+
}

0 commit comments

Comments
 (0)