refactor: simplify workflow request interpretation

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
David Kaya
2026-03-24 21:29:35 +01:00
co-authored by Copilot
parent 38ebe5bdfa
commit 8b2e7cd2c0
4 changed files with 71 additions and 18 deletions
@@ -0,0 +1,5 @@
using System.Diagnostics.CodeAnalysis;
[assembly: Experimental(
"MEAI001",
UrlFormat = "https://aka.ms/dotnet-extensions-warnings/{0}")]
@@ -1,6 +1,7 @@
using System.Collections.Concurrent; using System.Collections.Concurrent;
using Eryx.AgentHost.Contracts; using Eryx.AgentHost.Contracts;
using Microsoft.Agents.AI.Workflows; using Microsoft.Agents.AI.Workflows;
using Microsoft.Extensions.AI;
namespace Eryx.AgentHost.Services; namespace Eryx.AgentHost.Services;
@@ -8,14 +9,6 @@ internal static class WorkflowRequestInfoInterpreter
{ {
private static readonly Type? HandoffTargetType = LoadType( private static readonly Type? HandoffTargetType = LoadType(
"Microsoft.Agents.AI.Workflows.Specialized.HandoffTarget, Microsoft.Agents.AI.Workflows"); "Microsoft.Agents.AI.Workflows.Specialized.HandoffTarget, Microsoft.Agents.AI.Workflows");
private static readonly Type? FunctionCallContentType = LoadType(
"Microsoft.Extensions.AI.FunctionCallContent, Microsoft.Extensions.AI.Abstractions");
private static readonly Type? McpServerToolCallContentType = LoadType(
"Microsoft.Extensions.AI.McpServerToolCallContent, Microsoft.Extensions.AI.Abstractions");
private static readonly Type? CodeInterpreterToolCallContentType = LoadType(
"Microsoft.Extensions.AI.CodeInterpreterToolCallContent, Microsoft.Extensions.AI.Abstractions");
private static readonly Type? ImageGenerationToolCallContentType = LoadType(
"Microsoft.Extensions.AI.ImageGenerationToolCallContent, Microsoft.Extensions.AI.Abstractions");
public static AgentActivityEventDto? TryCreateActivityFromRequest( public static AgentActivityEventDto? TryCreateActivityFromRequest(
RunTurnCommandDto command, RunTurnCommandDto command,
@@ -85,30 +78,53 @@ internal static class WorkflowRequestInfoInterpreter
out string toolName, out string toolName,
out string? toolCallId) out string? toolCallId)
{ {
if (TryReadPortableValue(requestInfo.Request.Data, FunctionCallContentType, out object? functionCall)) if (TryGetStableToolRequestInfo(requestInfo.Request.Data, out toolName, out toolCallId))
{ {
toolName = GetStringProperty(functionCall, "Name") ?? "function";
toolCallId = NormalizeOptionalString(GetStringProperty(functionCall, "CallId"));
return true; return true;
} }
if (TryReadPortableValue(requestInfo.Request.Data, McpServerToolCallContentType, out object? mcpToolCall)) return TryGetEvaluationToolRequestInfo(requestInfo.Request.Data, out toolName, out toolCallId);
}
private static bool TryGetStableToolRequestInfo(
PortableValue requestData,
out string toolName,
out string? toolCallId)
{
if (requestData.Is<FunctionCallContent>(out FunctionCallContent? functionCall))
{ {
toolName = GetStringProperty(mcpToolCall, "ToolName") toolName = NormalizeOptionalString(functionCall.Name) ?? "function";
?? GetStringProperty(mcpToolCall, "ServerName") toolCallId = NormalizeOptionalString(functionCall.CallId);
return true;
}
toolName = string.Empty;
toolCallId = null;
return false;
}
private static bool TryGetEvaluationToolRequestInfo(
PortableValue requestData,
out string toolName,
out string? toolCallId)
{
if (requestData.Is<McpServerToolCallContent>(out McpServerToolCallContent? mcpToolCall))
{
toolName = NormalizeOptionalString(mcpToolCall.ToolName)
?? NormalizeOptionalString(mcpToolCall.ServerName)
?? string.Empty; ?? string.Empty;
toolCallId = NormalizeOptionalString(GetStringProperty(mcpToolCall, "CallId")); toolCallId = NormalizeOptionalString(mcpToolCall.CallId);
return !string.IsNullOrWhiteSpace(toolName); return !string.IsNullOrWhiteSpace(toolName);
} }
if (TryReadPortableValue(requestInfo.Request.Data, CodeInterpreterToolCallContentType, out object? codeInterpreterToolCall)) if (requestData.Is<CodeInterpreterToolCallContent>(out CodeInterpreterToolCallContent? codeInterpreterToolCall))
{ {
toolName = "code interpreter"; toolName = "code interpreter";
toolCallId = NormalizeOptionalString(GetStringProperty(codeInterpreterToolCall, "CallId")); toolCallId = NormalizeOptionalString(codeInterpreterToolCall.CallId);
return true; return true;
} }
if (TryReadPortableValue(requestInfo.Request.Data, ImageGenerationToolCallContentType, out _)) if (requestData.Is<ImageGenerationToolCallContent>())
{ {
toolName = "image generation"; toolName = "image generation";
toolCallId = null; toolCallId = null;
@@ -0,0 +1,5 @@
using System.Diagnostics.CodeAnalysis;
[assembly: Experimental(
"MEAI001",
UrlFormat = "https://aka.ms/dotnet-extensions-warnings/{0}")]
@@ -31,6 +31,25 @@ public sealed class WorkflowRequestInfoInterpreterTests
Assert.Equal("view", toolNamesByCallId["call-1"]); Assert.Equal("view", toolNamesByCallId["call-1"]);
} }
[Fact]
public void TryCreateActivityFromRequest_MapsMcpToolCalls()
{
ConcurrentDictionary<string, string> toolNamesByCallId = new(StringComparer.Ordinal);
RequestInfoEvent requestInfo = CreateRequestInfoEvent(
CreateMcpToolCall("call-1", "git.status", "Git MCP"));
AgentActivityEventDto? activity = WorkflowRequestInfoInterpreter.TryCreateActivityFromRequest(
CreateSingleAgentCommand(),
requestInfo,
new AgentIdentity("agent-1", "Primary"),
toolNamesByCallId);
Assert.NotNull(activity);
Assert.Equal("tool-calling", activity.ActivityType);
Assert.Equal("git.status", activity.ToolName);
Assert.Equal("git.status", toolNamesByCallId["call-1"]);
}
[Fact] [Fact]
public void TryCreateActivityFromRequest_MapsCodeInterpreterCallsToSyntheticToolName() public void TryCreateActivityFromRequest_MapsCodeInterpreterCallsToSyntheticToolName()
{ {
@@ -159,6 +178,14 @@ public sealed class WorkflowRequestInfoInterpreterTests
return instance; return instance;
} }
private static object CreateMcpToolCall(string callId, string toolName, string serverName)
{
Type type = Type.GetType(
"Microsoft.Extensions.AI.McpServerToolCallContent, Microsoft.Extensions.AI.Abstractions",
throwOnError: true)!;
return Activator.CreateInstance(type, callId, toolName, serverName)!;
}
private static object CreateImageGenerationToolCall() private static object CreateImageGenerationToolCall()
{ {
Type type = Type.GetType( Type type = Type.GetType(