Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions BotSharp.sln
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Plugin.MMPEmbeddin
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Plugin.Membase", "src\Plugins\BotSharp.Plugin.Membase\BotSharp.Plugin.Membase.csproj", "{13223C71-9EAC-9835-28ED-5A4833E6F915}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MultiTenancy", "MultiTenancy", "{7C64208C-8D11-4E17-A3E9-14D7910763EB}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Core.A2A", "src\Infrastructure\BotSharp.Core.A2A\BotSharp.Core.A2A.csproj", "{E8D01281-D52A-BFF4-33DB-E35D91754272}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Plugin.MultiTenancy", "src\Plugins\BotSharp.Plugin.MultiTenancy\BotSharp.Plugin.MultiTenancy.csproj", "{9BC8DF43-88D1-4C57-A678-AC0153BDF4EB}"
EndProject
Expand Down Expand Up @@ -653,6 +653,14 @@ Global
{13223C71-9EAC-9835-28ED-5A4833E6F915}.Release|Any CPU.Build.0 = Release|Any CPU
{13223C71-9EAC-9835-28ED-5A4833E6F915}.Release|x64.ActiveCfg = Release|Any CPU
{13223C71-9EAC-9835-28ED-5A4833E6F915}.Release|x64.Build.0 = Release|Any CPU
{E8D01281-D52A-BFF4-33DB-E35D91754272}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E8D01281-D52A-BFF4-33DB-E35D91754272}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E8D01281-D52A-BFF4-33DB-E35D91754272}.Debug|x64.ActiveCfg = Debug|Any CPU
{E8D01281-D52A-BFF4-33DB-E35D91754272}.Debug|x64.Build.0 = Debug|Any CPU
{E8D01281-D52A-BFF4-33DB-E35D91754272}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E8D01281-D52A-BFF4-33DB-E35D91754272}.Release|Any CPU.Build.0 = Release|Any CPU
{E8D01281-D52A-BFF4-33DB-E35D91754272}.Release|x64.ActiveCfg = Release|Any CPU
{E8D01281-D52A-BFF4-33DB-E35D91754272}.Release|x64.Build.0 = Release|Any CPU
{9BC8DF43-88D1-4C57-A678-AC0153BDF4EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9BC8DF43-88D1-4C57-A678-AC0153BDF4EB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9BC8DF43-88D1-4C57-A678-AC0153BDF4EB}.Debug|x64.ActiveCfg = Debug|Any CPU
Expand Down Expand Up @@ -735,7 +743,7 @@ Global
{E7C243B9-E751-B3B4-8F16-95C76CA90D31} = {51AFE054-AE99-497D-A593-69BAEFB5106F}
{394B858B-9C26-B977-A2DA-8CC7BE5914CB} = {4F346DCE-087F-4368-AF88-EE9C720D0E69}
{13223C71-9EAC-9835-28ED-5A4833E6F915} = {53E7CD86-0D19-40D9-A0FA-AB4613837E89}
{7C64208C-8D11-4E17-A3E9-14D7910763EB} = {2635EC9B-2E5F-4313-AC21-0B847F31F36C}
{E8D01281-D52A-BFF4-33DB-E35D91754272} = {E29DC6C4-5E57-48C5-BCB0-6B8F84782749}
{9BC8DF43-88D1-4C57-A678-AC0153BDF4EB} = {7C64208C-8D11-4E17-A3E9-14D7910763EB}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
Expand Down
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="A2A" Version="0.3.3-preview" />
<PackageVersion Include="CsvHelper" Version="33.1.0" />
<PackageVersion Include="FuzzySharp" Version="2.0.2" />
<PackageVersion Include="Google_GenerativeAI" Version="3.4.1" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,10 @@ public static class AgentType
/// Agent that cannot use external tools
/// </summary>
public const string Static = "static";

/// <summary>
/// A2A remote agent for Microsoft Agent Framework integration
/// </summary>
public const string A2ARemote = "a2a-remote";
}

36 changes: 36 additions & 0 deletions src/Infrastructure/BotSharp.Core.A2A/A2APlugin.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using BotSharp.Abstraction.Agents;
using BotSharp.Abstraction.Conversations;
using BotSharp.Abstraction.Functions;
using BotSharp.Abstraction.Plugins;
using BotSharp.Abstraction.Settings;
using BotSharp.Core.A2A.Functions;
using BotSharp.Core.A2A.Hooks;
using BotSharp.Core.A2A.Services;
using BotSharp.Core.A2A.Settings;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace BotSharp.Core.A2A;

public class A2APlugin : IBotSharpPlugin
{

public string Id => "058cdf87-fcf3-eda9-915a-565c04bc9f56";

public string Name => "A2A Protocol Integration";

public string Description => "Enables seamless integration with external agents via the Agent-to-Agent (A2A) protocol.";

public void RegisterDI(IServiceCollection services, IConfiguration config)
{
services.AddScoped(provider =>
{
var settingService = provider.GetRequiredService<ISettingService>();
return settingService.Bind<A2ASettings>("A2AIntegration");
});

services.AddScoped<IA2AService, A2AService>();
services.AddScoped<IAgentHook, A2AAgentHook>();
services.AddScoped<IFunctionCallback, A2ADelegationFn>();
}
}
21 changes: 21 additions & 0 deletions src/Infrastructure/BotSharp.Core.A2A/BotSharp.Core.A2A.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>$(TargetFramework)</TargetFramework>
<LangVersion>$(LangVersion)</LangVersion>
<VersionPrefix>$(BotSharpVersion)</VersionPrefix>
<GeneratePackageOnBuild>$(GeneratePackageOnBuild)</GeneratePackageOnBuild>
<OutputPath>$(SolutionDir)packages</OutputPath>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="A2A" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\Infrastructure\BotSharp.Abstraction\BotSharp.Abstraction.csproj" />
</ItemGroup>

</Project>
65 changes: 65 additions & 0 deletions src/Infrastructure/BotSharp.Core.A2A/Functions/A2ADelegationFn.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using BotSharp.Abstraction.Conversations;
using BotSharp.Abstraction.Conversations.Models;
using BotSharp.Abstraction.Functions;
using BotSharp.Core.A2A.Services;
using BotSharp.Core.A2A.Settings;
using System.Text.Json;

namespace BotSharp.Core.A2A.Functions;

public class A2ADelegationFn : IFunctionCallback
{
public string Name => "delegate_to_a2a";
public string Indication => "Connecting to external agent network...";

private readonly IA2AService _a2aService;
private readonly A2ASettings _settings;
private readonly IConversationStateService _stateService;

public A2ADelegationFn(IA2AService a2aService, A2ASettings settings, IConversationStateService stateService)
{
_a2aService = a2aService;
_settings = settings;
_stateService = stateService;
}

public async Task<bool> Execute(RoleDialogModel message)
{
var args = JsonSerializer.Deserialize<JsonElement>(message.FunctionArgs);
string queryText = string.Empty;
if (args.TryGetProperty("user_query", out var queryProp))
{
queryText = queryProp.GetString();
}

var agentId = message.CurrentAgentId;
var agentConfig = _settings.Agents.FirstOrDefault(x => x.Id == agentId);

if (agentConfig == null)
{
message.Content = "System Error: Remote agent configuration not found.";
message.StopCompletion = true;
return false;
}

var conversationId = _stateService.GetConversationId();

try
{
var responseText = await _a2aService.SendMessageAsync(
agentConfig.Endpoint,
queryText,
conversationId,
CancellationToken.None
);

message.Content = responseText;
return true;
}
catch (Exception ex)
{
message.Content = $"Communication failure with external agent: {ex.Message}";
return false;
}
}
}
85 changes: 85 additions & 0 deletions src/Infrastructure/BotSharp.Core.A2A/Hooks/A2AAgentHook.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
using BotSharp.Abstraction.Agents;
using BotSharp.Abstraction.Agents.Enums;
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Agents.Settings;
using BotSharp.Abstraction.Functions.Models;
using BotSharp.Core.A2A.Services;
using BotSharp.Core.A2A.Settings;
using System.Text.Json;

namespace BotSharp.Core.A2A.Hooks;

public class A2AAgentHook : AgentHookBase
{
public override string SelfId => string.Empty;

private readonly A2ASettings _settings;
private readonly IA2AService _iA2AService;

public A2AAgentHook(IServiceProvider services, IA2AService a2AService, A2ASettings settings)
: base(services, new AgentSettings())
{
_iA2AService = a2AService;
_settings = settings;
}

public override bool OnAgentLoading(ref string id)
{
var agentId = id;
var remoteConfig = _settings.Agents.FirstOrDefault(x => x.Id == agentId);
if (remoteConfig != null)
{
return true;
}
return base.OnAgentLoading(ref id);
}

public override void OnAgentLoaded(Agent agent)
{
// Check if this is an A2A remote agent
if (agent.Type != AgentType.A2ARemote)
{
return;
}

var remoteConfig = _settings.Agents.FirstOrDefault(x => x.Id == agent.Id);
if (remoteConfig != null)
{
var agentCard = _iA2AService.GetCapabilitiesAsync(remoteConfig.Endpoint).GetAwaiter().GetResult();
agent.Name = agentCard.Name;
agent.Description = agentCard.Description;
agent.Instruction = $"You are a proxy interface for an external intelligent service named '{agentCard.Name}'. " +
$"Your ONLY goal is to forward the user's request verbatim to the external service. " +
$"You must use the function 'delegate_to_a2a' to communicate with it. " +
$"Do not attempt to answer the question yourself.";

var properties = new Dictionary<string, object>
{
{
"user_query",
new
{
type = "string",
description = "The exact user request or task description to be forwarded."
}
}
};

var propertiesJson = JsonSerializer.Serialize(properties);
var propertiesDocument = JsonDocument.Parse(propertiesJson);

agent.Functions.Add(new FunctionDef
{
Name = "delegate_to_a2a",
Description = $"Delegates the task to the external {remoteConfig.Name} via A2A protocol.",
Parameters = new FunctionParametersDef()
{
Type = "object",
Properties = propertiesDocument,
Required = new List<string> { "user_query" }
}
});
}
base.OnAgentLoaded(agent);
}
}
Loading
Loading