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
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,33 @@ await this._sharedStateRef.InvokeWithStateAsync(
sharedState.Conversation.AddMessages(incomingMessages);
}

newConversationBookmark = sharedState.Conversation.AddMessages(result.Response.Messages);
if (result.IsHandoffRequested)
{
Comment thread
lokitoth marked this conversation as resolved.
int preHandoffMessageCount = result.Response.Messages.Count - 1;
newConversationBookmark = sharedState.Conversation.AddMessages(result.Response.Messages.Take(preHandoffMessageCount));

// The following message contains the Handoff FunctionCallResult which should be added to the conversation history with
// the caveat that we need to get it back next time _this_ agent is invoked because we need to feed the FunctionCallResult
// back to the agent. So ignore the bookmark update.
ChatMessage handoffCallResultMessage = result.Response.Messages[preHandoffMessageCount];
Comment thread
lokitoth marked this conversation as resolved.

if (handoffCallResultMessage.Role != ChatRole.Tool)
{
throw new InvalidOperationException("The last message in a handoff response must be a Tool message containing the Handoff FunctionCallResult.");
}

if (handoffCallResultMessage.Contents.Count != 1 ||
handoffCallResultMessage.Contents[0] is not FunctionResultContent)
{
throw new InvalidOperationException("The Tool message in a handoff response must contain exactly one content item of type FunctionResultContent.");
Comment thread
lokitoth marked this conversation as resolved.
}

_ = sharedState.Conversation.AddMessage(handoffCallResultMessage);
}
else
{
newConversationBookmark = sharedState.Conversation.AddMessages(result.Response.Messages);
}

return new ValueTask();
},
Expand Down Expand Up @@ -376,39 +402,28 @@ private async ValueTask<AgentInvocationResult> InvokeAgentAsync(IEnumerable<Chat
List<AgentResponseUpdate> updates = [];
List<FunctionCallContent> candidateRequests = [];

await this.InvokeWithStateAsync(
async (state, ctx, ct) =>
{
this._session ??= await this._agent.CreateSessionAsync(ct).ConfigureAwait(false);

IAsyncEnumerable<AgentResponseUpdate> agentStream =
this._agent.RunStreamingAsync(messages,
this._session,
options: this._agentOptions,
cancellationToken: ct);
this._session ??= await this._agent.CreateSessionAsync(cancellationToken).ConfigureAwait(false);

await foreach (AgentResponseUpdate update in agentStream.ConfigureAwait(false))
{
await AddUpdateAsync(update, ct).ConfigureAwait(false);
IAsyncEnumerable<AgentResponseUpdate> agentStream =
this._agent.RunStreamingAsync(messages, this._session, this._agentOptions, cancellationToken);

collector.ProcessAgentResponseUpdate(update, CollectHandoffRequestsFilter);
await foreach (AgentResponseUpdate update in agentStream.ConfigureAwait(false))
{
await AddUpdateAsync(update, cancellationToken).ConfigureAwait(false);
Comment thread
lokitoth marked this conversation as resolved.

bool CollectHandoffRequestsFilter(FunctionCallContent candidateHandoffRequest)
{
bool isHandoffRequest = this._handoffFunctionNames.Contains(candidateHandoffRequest.Name);
if (isHandoffRequest)
{
candidateRequests.Add(candidateHandoffRequest);
}
collector.ProcessAgentResponseUpdate(update, CollectHandoffRequestsFilter);

return !isHandoffRequest;
}
bool CollectHandoffRequestsFilter(FunctionCallContent candidateHandoffRequest)
{
bool isHandoffRequest = this._handoffFunctionNames.Contains(candidateHandoffRequest.Name);
if (isHandoffRequest)
{
candidateRequests.Add(candidateHandoffRequest);
}

return state;
},
context,
cancellationToken: cancellationToken).ConfigureAwait(false);
return !isHandoffRequest;
}
}

if (candidateRequests.Count > 1)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,22 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.Extensions.AI;

namespace Microsoft.Agents.AI.Workflows.Specialized.Magentic;

internal sealed class StreamingToolCallResultPairMatcher
{
private enum CallType
internal enum CallType
{
Function,
McpServerTool
}

private record CallSummaryKey(CallType Type, string CallId);

private struct ToolCallSummary(CallType callType, string callId, string name)
internal struct ToolCallSummary(CallType callType, string callId, string name)
{
public CallType CallType => callType;

Expand All @@ -28,6 +29,12 @@ private struct ToolCallSummary(CallType callType, string callId, string name)

private readonly Dictionary<CallSummaryKey, ToolCallSummary> _callSummaries = new();

public bool HasUnmatchedCalls => this._callSummaries.Count > 0;

public IEnumerable<ToolCallSummary> UnmatchedCalls => this.HasUnmatchedCalls
? this._callSummaries.Values.ToList()
: [];
Comment thread
lokitoth marked this conversation as resolved.

private void Collect(CallType callType, string callId, string name, string callContentTypeName, string resultContentTypeName)
{
CallSummaryKey key = new(callType, callId);
Expand Down
Loading
Loading