Skip to content
Open
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
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"aspire.enableSettingsFileCreationPromptOnStartup": false
}
3 changes: 2 additions & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="A2A" Version="0.3.3-preview" />
<PackageVersion Include="A2A" Version="1.0.0-preview2" />
<PackageVersion Include="Microsoft.Agents.AI.A2A" Version="1.3.0-preview.260423.1" />
<PackageVersion Include="CsvHelper" Version="33.1.0" />
<PackageVersion Include="FuzzySharp" Version="2.0.2" />
<PackageVersion Include="Google_GenerativeAI" Version="3.6.3" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

<ItemGroup>
<PackageReference Include="A2A" />
<PackageReference Include="Microsoft.Agents.AI.A2A" />
</ItemGroup>

<ItemGroup>
Expand Down
12 changes: 10 additions & 2 deletions src/Infrastructure/BotSharp.Core.A2A/Functions/A2ADelegationFn.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,12 @@ public A2ADelegationFn(IA2AService a2aService, A2ASettings settings, IConversati

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

var agentId = message.CurrentAgentId;
Expand All @@ -43,6 +44,12 @@ public async Task<bool> Execute(RoleDialogModel message)
}

var conversationId = _stateService.GetConversationId();
if (string.IsNullOrWhiteSpace(conversationId))
{
message.Content = "System Error: Conversation context is unavailable for A2A session continuation.";
message.StopCompletion = true;
return false;
}

try
{
Expand All @@ -54,6 +61,7 @@ public async Task<bool> Execute(RoleDialogModel message)
);

message.Content = responseText;
message.StopCompletion = true;
return true;
}
catch (Exception ex)
Expand Down
59 changes: 36 additions & 23 deletions src/Infrastructure/BotSharp.Core.A2A/Hooks/A2AAgentHook.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
using A2A;
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 Microsoft.Extensions.Logging;
using System.Text.Json;

namespace BotSharp.Core.A2A.Hooks;
Expand All @@ -15,12 +17,14 @@ public class A2AAgentHook : AgentHookBase

private readonly A2ASettings _a2aSettings;
private readonly IA2AService _a2aService;
private readonly ILogger<A2AAgentHook> _logger;

public A2AAgentHook(IServiceProvider services, IA2AService a2aService, A2ASettings a2aSettings, AgentSettings agentSettings)
public A2AAgentHook(IServiceProvider services, IA2AService a2aService, A2ASettings a2aSettings, AgentSettings agentSettings, ILogger<A2AAgentHook> logger)
: base(services, agentSettings)
{
_a2aService = a2aService;
_a2aSettings = a2aSettings;
_logger = logger;
}

public override async Task<string?> OnAgentLoading(string id)
Expand All @@ -45,7 +49,16 @@ public override async Task OnAgentLoaded(Agent agent)
var remoteConfig = _a2aSettings.Agents?.FirstOrDefault(x => x.Id == agent.Id);
if (remoteConfig != null)
{
var agentCard = await _a2aService.GetCapabilitiesAsync(remoteConfig.Endpoint);
AgentCard? agentCard = null;
try
{
agentCard = await _a2aService.GetCapabilitiesAsync(remoteConfig.Endpoint);
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to resolve A2A agent card for endpoint {AgentEndpoint}. Using configured metadata.", remoteConfig.Endpoint);
}

if (agentCard != null)
{
agent.Name = agentCard.Name;
Expand All @@ -54,34 +67,34 @@ public override async Task OnAgentLoaded(Agent agent)
$"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>
var properties = new Dictionary<string, object>
{
{
"user_query",
new
{
"user_query",
new
{
type = "string",
description = "The exact user request or task description to be forwarded."
}
type = "string",
description = "The exact user request or task description to be forwarded."
}
};
}
};

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

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