mirror of
https://github.com/davidkaya/aryx.git
synced 2026-08-06 19:58:43 +02:00
fix: project copilot tool results into workflows
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -185,7 +185,7 @@ public sealed class CopilotAgentBundleTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConvertToolRequestsToFunctionCalls_SkipsNonHandoffToolCalls()
|
||||
public void ConvertToolRequestsToFunctionCalls_MapsNonHandoffToolCalls()
|
||||
{
|
||||
AssistantMessageDataToolRequestsItem[] toolRequests =
|
||||
{
|
||||
@@ -197,9 +197,99 @@ public sealed class CopilotAgentBundleTests
|
||||
|
||||
IReadOnlyList<FunctionCallContent> result = AryxCopilotAgent.ConvertToolRequestsToFunctionCalls(toolRequests);
|
||||
|
||||
FunctionCallContent single = Assert.Single(result);
|
||||
Assert.Equal("call-003", single.CallId);
|
||||
Assert.Equal("handoff_to_reviewer", single.Name);
|
||||
Assert.Collection(
|
||||
result,
|
||||
functionCall =>
|
||||
{
|
||||
Assert.Equal("call-001", functionCall.CallId);
|
||||
Assert.Equal("ask_user", functionCall.Name);
|
||||
},
|
||||
functionCall =>
|
||||
{
|
||||
Assert.Equal("call-002", functionCall.CallId);
|
||||
Assert.Equal("web_fetch", functionCall.Name);
|
||||
},
|
||||
functionCall =>
|
||||
{
|
||||
Assert.Equal("call-003", functionCall.CallId);
|
||||
Assert.Equal("handoff_to_reviewer", functionCall.Name);
|
||||
},
|
||||
functionCall =>
|
||||
{
|
||||
Assert.Equal("call-004", functionCall.CallId);
|
||||
Assert.Equal("grep", functionCall.Name);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryCreateToolResultContent_UsesSdkResultContentForNonHandoffTools()
|
||||
{
|
||||
ToolExecutionCompleteEvent toolExecutionComplete = new()
|
||||
{
|
||||
Data = new ToolExecutionCompleteData
|
||||
{
|
||||
ToolCallId = "call-123",
|
||||
Success = true,
|
||||
Result = new ToolExecutionCompleteDataResult
|
||||
{
|
||||
Content = "Search complete.",
|
||||
DetailedContent = "Search complete with extra context.",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
FunctionResultContent? toolResult = AryxCopilotAgent.TryCreateToolResultContent(toolExecutionComplete, "rg");
|
||||
|
||||
Assert.NotNull(toolResult);
|
||||
Assert.Equal("call-123", toolResult.CallId);
|
||||
Assert.Equal("Search complete.", Assert.IsType<string>(toolResult.Result));
|
||||
Assert.Same(toolExecutionComplete, toolResult.RawRepresentation);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryCreateToolResultContent_UsesSdkErrorMessageForFailedTools()
|
||||
{
|
||||
ToolExecutionCompleteEvent toolExecutionComplete = new()
|
||||
{
|
||||
Data = new ToolExecutionCompleteData
|
||||
{
|
||||
ToolCallId = "call-456",
|
||||
Success = false,
|
||||
Error = new ToolExecutionCompleteDataError
|
||||
{
|
||||
Message = "Permission denied.",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
FunctionResultContent? toolResult = AryxCopilotAgent.TryCreateToolResultContent(toolExecutionComplete, "view");
|
||||
|
||||
Assert.NotNull(toolResult);
|
||||
Assert.Equal("call-456", toolResult.CallId);
|
||||
Assert.Equal("Permission denied.", Assert.IsType<string>(toolResult.Result));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryCreateToolResultContent_SkipsHandoffTools()
|
||||
{
|
||||
ToolExecutionCompleteEvent toolExecutionComplete = new()
|
||||
{
|
||||
Data = new ToolExecutionCompleteData
|
||||
{
|
||||
ToolCallId = "call-789",
|
||||
Success = true,
|
||||
Result = new ToolExecutionCompleteDataResult
|
||||
{
|
||||
Content = "Transferred.",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
FunctionResultContent? toolResult = AryxCopilotAgent.TryCreateToolResultContent(
|
||||
toolExecutionComplete,
|
||||
"handoff_to_reviewer");
|
||||
|
||||
Assert.Null(toolResult);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -60,7 +60,7 @@ public sealed class CopilotTurnExecutionStateTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ObserveSessionEvent_ToolExecutionStart_TracksToolNameByCallId()
|
||||
public void ObserveSessionEvent_ToolExecutionStart_TracksToolNameByCallIdAndQueuesToolActivity()
|
||||
{
|
||||
RunTurnCommandDto command = CreateCommand();
|
||||
CopilotTurnExecutionState state = new(command);
|
||||
@@ -68,22 +68,32 @@ public sealed class CopilotTurnExecutionStateTests
|
||||
state.ObserveSessionEvent(
|
||||
command.Pattern.Agents[0],
|
||||
SessionEvent.FromJson(
|
||||
"""
|
||||
{
|
||||
"type": "tool.execution_start",
|
||||
"data": {
|
||||
"toolCallId": "tool-call-1",
|
||||
"toolName": "view"
|
||||
},
|
||||
"id": "33333333-3333-3333-3333-333333333333",
|
||||
"timestamp": "2026-03-27T00:00:00Z"
|
||||
}
|
||||
"""));
|
||||
"""{"type":"tool.execution_start","data":{"toolCallId":"tool-call-1","toolName":"view"},"id":"33333333-3333-3333-3333-333333333333","timestamp":"2026-03-27T00:00:00Z"}"""));
|
||||
|
||||
AgentActivityEventDto toolActivity = Assert.Single(state.DrainPendingEvents().OfType<AgentActivityEventDto>());
|
||||
Assert.Equal("tool-calling", toolActivity.ActivityType);
|
||||
Assert.Equal("view", toolActivity.ToolName);
|
||||
Assert.Equal("tool-call-1", toolActivity.ToolCallId);
|
||||
Assert.True(state.ToolNamesByCallId.TryGetValue("tool-call-1", out string? toolName));
|
||||
Assert.Equal("view", toolName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ObserveSessionEvent_ToolExecutionStart_DoesNotQueueToolActivityForHandoffTools()
|
||||
{
|
||||
RunTurnCommandDto command = CreateCommand();
|
||||
CopilotTurnExecutionState state = new(command);
|
||||
|
||||
state.ObserveSessionEvent(
|
||||
command.Pattern.Agents[0],
|
||||
SessionEvent.FromJson(
|
||||
"""{"type":"tool.execution_start","data":{"toolCallId":"tool-call-1","toolName":"handoff_to_specialist"},"id":"1ce9d1dc-68f1-4df5-9728-f97017233279","timestamp":"2026-03-27T00:00:00Z"}"""));
|
||||
|
||||
Assert.Empty(state.DrainPendingEvents().OfType<AgentActivityEventDto>());
|
||||
Assert.True(state.ToolNamesByCallId.TryGetValue("tool-call-1", out string? toolName));
|
||||
Assert.Equal("handoff_to_specialist", toolName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ObserveSessionEvent_AssistantMessageWithToolRequests_QueuesMessageReclassifiedEvent()
|
||||
{
|
||||
@@ -178,6 +188,10 @@ public sealed class CopilotTurnExecutionStateTests
|
||||
|
||||
IReadOnlyList<SidecarEventDto> pending = state.DrainPendingEvents();
|
||||
|
||||
AgentActivityEventDto[] toolActivities = [.. pending.OfType<AgentActivityEventDto>().Where(activity => activity.ActivityType == "tool-calling")];
|
||||
Assert.Equal(2, toolActivities.Length);
|
||||
Assert.Contains(toolActivities, activity => activity.ToolCallId == "tool-call-1" && activity.ToolName == "rg");
|
||||
Assert.Contains(toolActivities, activity => activity.ToolCallId == "tool-call-2" && activity.ToolName == "view");
|
||||
MessageReclassifiedEventDto reclassified = Assert.Single(pending.OfType<MessageReclassifiedEventDto>());
|
||||
Assert.Equal("msg-3", reclassified.MessageId);
|
||||
Assert.True(state.ToolNamesByCallId.TryGetValue("tool-call-1", out string? firstToolName));
|
||||
|
||||
@@ -86,6 +86,26 @@ public sealed class WorkflowRequestInfoInterpreterTests
|
||||
Assert.Empty(toolNamesByCallId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryCreateActivityFromRequest_SkipsDuplicateTrackedToolCallIds()
|
||||
{
|
||||
ConcurrentDictionary<string, string> toolNamesByCallId = new(StringComparer.Ordinal)
|
||||
{
|
||||
["call-1"] = "view",
|
||||
};
|
||||
RequestInfoEvent requestInfo = CreateRequestInfoEvent(
|
||||
new FunctionCallContent("call-1", "view", new Dictionary<string, object?>()));
|
||||
|
||||
AgentActivityEventDto? activity = WorkflowRequestInfoInterpreter.TryCreateActivityFromRequest(
|
||||
CreateSingleAgentCommand(),
|
||||
requestInfo,
|
||||
new AgentIdentity("agent-1", "Primary"),
|
||||
toolNamesByCallId);
|
||||
|
||||
Assert.Null(activity);
|
||||
Assert.Equal("view", toolNamesByCallId["call-1"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryCreateActivityFromRequest_ReturnsHandoffActivityForKnownTargets()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user