diff --git a/dotnet/Directory.Packages.props b/dotnet/Directory.Packages.props
index c48afe4ebf..27f1c0821e 100644
--- a/dotnet/Directory.Packages.props
+++ b/dotnet/Directory.Packages.props
@@ -13,6 +13,7 @@
+
diff --git a/dotnet/agent-framework-dotnet.slnx b/dotnet/agent-framework-dotnet.slnx
index e33215905e..a38787036d 100644
--- a/dotnet/agent-framework-dotnet.slnx
+++ b/dotnet/agent-framework-dotnet.slnx
@@ -115,7 +115,6 @@
-
@@ -129,9 +128,6 @@
-
-
-
diff --git a/dotnet/samples/GettingStarted/AgentSample.cs b/dotnet/samples/GettingStarted/AgentSample.cs
index d518607691..0be5aa1551 100644
--- a/dotnet/samples/GettingStarted/AgentSample.cs
+++ b/dotnet/samples/GettingStarted/AgentSample.cs
@@ -159,11 +159,8 @@ public class AgentSample(ITestOutputHelper output) : BaseSample(output)
private async Task AzureAIAgentsPersistentAgentCleanUpAsync(ChatClientAgent agent, AgentThread? thread, CancellationToken cancellationToken)
{
- var persistentAgentsClient = agent.ChatClient.GetService();
- if (persistentAgentsClient is null)
- {
+ var persistentAgentsClient = agent.ChatClient.GetService() ??
throw new InvalidOperationException("The provided chat client is not a Persistent Agents Chat Client");
- }
await persistentAgentsClient.Administration.DeleteAgentAsync(agent.Id, cancellationToken);
diff --git a/dotnet/samples/GettingStarted/Orchestration/ConcurrentOrchestration_Intro.cs b/dotnet/samples/GettingStarted/Orchestration/ConcurrentOrchestration_Intro.cs
index 1c8acfc101..1ab57d9e6b 100644
--- a/dotnet/samples/GettingStarted/Orchestration/ConcurrentOrchestration_Intro.cs
+++ b/dotnet/samples/GettingStarted/Orchestration/ConcurrentOrchestration_Intro.cs
@@ -1,9 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.Agents.Orchestration;
-using Microsoft.Agents.Orchestration.Concurrent;
using Microsoft.Extensions.AI.Agents;
-using Microsoft.Extensions.AI.Agents.Runtime.InProcess;
namespace Orchestration;
@@ -42,20 +40,14 @@ public class ConcurrentOrchestration_Intro(ITestOutputHelper output) : Orchestra
StreamingResponseCallback = streamedResponse ? monitor.StreamingResultCallback : null,
};
- // Start the runtime
- await using InProcessRuntime runtime = new();
- await runtime.StartAsync();
-
// Run the orchestration
string input = "What is temperature?";
Console.WriteLine($"\n# INPUT: {input}\n");
- OrchestrationResult result = await orchestration.InvokeAsync(input, runtime);
+ OrchestrationResult result = await orchestration.InvokeAsync(input);
- string[] output = await result.GetValueAsync(TimeSpan.FromSeconds(ResultTimeoutInSeconds));
+ string[] output = await result;
Console.WriteLine($"\n# RESULT:\n{string.Join("\n\n", output.Select(text => $"{text}"))}");
- await runtime.RunUntilIdleAsync();
-
this.DisplayHistory(monitor.History);
}
}
diff --git a/dotnet/samples/GettingStarted/Orchestration/ConcurrentOrchestration_With_StructuredOutput.cs b/dotnet/samples/GettingStarted/Orchestration/ConcurrentOrchestration_With_StructuredOutput.cs
index f62c9e2a9e..1b6ce726e2 100644
--- a/dotnet/samples/GettingStarted/Orchestration/ConcurrentOrchestration_With_StructuredOutput.cs
+++ b/dotnet/samples/GettingStarted/Orchestration/ConcurrentOrchestration_With_StructuredOutput.cs
@@ -2,10 +2,7 @@
using System.Text.Json;
using Microsoft.Agents.Orchestration;
-using Microsoft.Agents.Orchestration.Concurrent;
-using Microsoft.Agents.Orchestration.Transforms;
using Microsoft.Extensions.AI.Agents;
-using Microsoft.Extensions.AI.Agents.Runtime.InProcess;
using Microsoft.Shared.Samples;
namespace Orchestration;
@@ -43,20 +40,14 @@ public class ConcurrentOrchestration_With_StructuredOutput(ITestOutputHelper out
ResultTransform = outputTransform.TransformAsync,
};
- // Start the runtime
- await using InProcessRuntime runtime = new();
- await runtime.StartAsync();
-
// Run the orchestration
const string resourceId = "Hamlet_full_play_summary.txt";
string input = Resources.Read(resourceId);
Console.WriteLine($"\n# INPUT: @{resourceId}\n");
- OrchestrationResult result = await orchestration.InvokeAsync(input, runtime);
+ OrchestrationResult result = await orchestration.InvokeAsync(input);
- Analysis output = await result.GetValueAsync(TimeSpan.FromSeconds(ResultTimeoutInSeconds * 2));
+ Analysis output = await result;
Console.WriteLine($"\n# RESULT:\n{JsonSerializer.Serialize(output, s_options)}");
-
- await runtime.RunUntilIdleAsync();
}
#pragma warning disable CA1812 // Avoid uninstantiated internal classes
diff --git a/dotnet/samples/GettingStarted/Orchestration/GroupChatOrchestration_Intro.cs b/dotnet/samples/GettingStarted/Orchestration/GroupChatOrchestration_Intro.cs
index 920ebec115..9a47dc7293 100644
--- a/dotnet/samples/GettingStarted/Orchestration/GroupChatOrchestration_Intro.cs
+++ b/dotnet/samples/GettingStarted/Orchestration/GroupChatOrchestration_Intro.cs
@@ -1,9 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.Agents.Orchestration;
-using Microsoft.Agents.Orchestration.GroupChat;
using Microsoft.Extensions.AI.Agents;
-using Microsoft.Extensions.AI.Agents.Runtime.InProcess;
namespace Orchestration;
@@ -68,17 +66,10 @@ public class GroupChatOrchestration_Intro(ITestOutputHelper output) : Orchestrat
StreamingResponseCallback = streamedResponse ? monitor.StreamingResultCallback : null,
};
- // Start the runtime
- await using InProcessRuntime runtime = new();
- await runtime.StartAsync();
-
string input = "Create a slogon for a new eletric SUV that is affordable and fun to drive.";
Console.WriteLine($"\n# INPUT: {input}\n");
- OrchestrationResult result = await orchestration.InvokeAsync(input, runtime);
- string text = await result.GetValueAsync(TimeSpan.FromSeconds(ResultTimeoutInSeconds * 3));
- Console.WriteLine($"\n# RESULT: {text}");
-
- await runtime.RunUntilIdleAsync();
+ OrchestrationResult result = await orchestration.InvokeAsync(input);
+ Console.WriteLine($"\n# RESULT: {await result}");
this.DisplayHistory(monitor.History);
}
diff --git a/dotnet/samples/GettingStarted/Orchestration/GroupChatOrchestration_With_AIManager.cs b/dotnet/samples/GettingStarted/Orchestration/GroupChatOrchestration_With_AIManager.cs
index b26b37b721..42f4844a92 100644
--- a/dotnet/samples/GettingStarted/Orchestration/GroupChatOrchestration_With_AIManager.cs
+++ b/dotnet/samples/GettingStarted/Orchestration/GroupChatOrchestration_With_AIManager.cs
@@ -1,10 +1,8 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.Agents.Orchestration;
-using Microsoft.Agents.Orchestration.GroupChat;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.AI.Agents;
-using Microsoft.Extensions.AI.Agents.Runtime.InProcess;
namespace Orchestration;
@@ -132,17 +130,10 @@ public class GroupChatOrchestration_With_AIManager(ITestOutputHelper output) : O
ResponseCallback = monitor.ResponseCallback,
};
- // Start the runtime
- await using InProcessRuntime runtime = new();
- await runtime.StartAsync();
-
// Run the orchestration
Console.WriteLine($"\n# INPUT: {topic}\n");
- OrchestrationResult result = await orchestration.InvokeAsync(topic, runtime);
- string text = await result.GetValueAsync(TimeSpan.FromSeconds(ResultTimeoutInSeconds * 3));
- Console.WriteLine($"\n# RESULT: {text}");
-
- await runtime.RunUntilIdleAsync();
+ OrchestrationResult result = await orchestration.InvokeAsync(topic);
+ Console.WriteLine($"\n# RESULT: {await result}");
this.DisplayHistory(monitor.History);
}
diff --git a/dotnet/samples/GettingStarted/Orchestration/GroupChatOrchestration_With_HumanInTheLoop.cs b/dotnet/samples/GettingStarted/Orchestration/GroupChatOrchestration_With_HumanInTheLoop.cs
index 39e6a7c4da..9e6e6286f5 100644
--- a/dotnet/samples/GettingStarted/Orchestration/GroupChatOrchestration_With_HumanInTheLoop.cs
+++ b/dotnet/samples/GettingStarted/Orchestration/GroupChatOrchestration_With_HumanInTheLoop.cs
@@ -1,10 +1,8 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.Agents.Orchestration;
-using Microsoft.Agents.Orchestration.GroupChat;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.AI.Agents;
-using Microsoft.Extensions.AI.Agents.Runtime.InProcess;
namespace Orchestration;
@@ -68,18 +66,11 @@ public class GroupChatOrchestration_With_HumanInTheLoop(ITestOutputHelper output
ResponseCallback = monitor.ResponseCallback,
};
- // Start the runtime
- await using InProcessRuntime runtime = new();
- await runtime.StartAsync();
-
// Run the orchestration
string input = "Create a slogon for a new eletric SUV that is affordable and fun to drive.";
Console.WriteLine($"\n# INPUT: {input}\n");
- OrchestrationResult result = await orchestration.InvokeAsync(input, runtime);
- string text = await result.GetValueAsync(TimeSpan.FromSeconds(ResultTimeoutInSeconds * 3));
- Console.WriteLine($"\n# RESULT: {text}");
-
- await runtime.RunUntilIdleAsync();
+ OrchestrationResult result = await orchestration.InvokeAsync(input);
+ Console.WriteLine($"\n# RESULT: {await result}");
this.DisplayHistory(monitor.History);
}
@@ -97,23 +88,12 @@ public class GroupChatOrchestration_With_HumanInTheLoop(ITestOutputHelper output
{
string? lastAgent = history.LastOrDefault()?.AuthorName;
- GroupChatManagerResult result;
+ GroupChatManagerResult result =
+ lastAgent is null ? new(false) { Reason = "No agents have spoken yet." } :
+ lastAgent is "Reviewer" ? new(true) { Reason = "User input is needed after the reviewer's message." } :
+ new(false) { Reason = "User input is not needed until the reviewer's message." };
- if (lastAgent is null)
- {
- result = new GroupChatManagerResult(false) { Reason = "No agents have spoken yet." };
- }
-
- if (lastAgent == "Reviewer")
- {
- result = new GroupChatManagerResult(true) { Reason = "User input is needed after the reviewer's message." };
- }
- else
- {
- result = new GroupChatManagerResult(false) { Reason = "User input is not needed until the reviewer's message." };
- }
-
- return new ValueTask>(result);
+ return new(result);
}
}
}
diff --git a/dotnet/samples/GettingStarted/Orchestration/HandoffOrchestration_Intro.cs b/dotnet/samples/GettingStarted/Orchestration/HandoffOrchestration_Intro.cs
index 641a6b5b72..cee35fa613 100644
--- a/dotnet/samples/GettingStarted/Orchestration/HandoffOrchestration_Intro.cs
+++ b/dotnet/samples/GettingStarted/Orchestration/HandoffOrchestration_Intro.cs
@@ -1,10 +1,8 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.Agents.Orchestration;
-using Microsoft.Agents.Orchestration.Handoff;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.AI.Agents;
-using Microsoft.Extensions.AI.Agents.Runtime.InProcess;
namespace Orchestration;
@@ -65,11 +63,7 @@ public class HandoffOrchestration_Intro(ITestOutputHelper output) : Orchestratio
.Add(triageAgent, statusAgent, returnAgent, refundAgent)
.Add(statusAgent, triageAgent, "Transfer to this agent if the issue is not status related")
.Add(returnAgent, triageAgent, "Transfer to this agent if the issue is not return related")
- .Add(refundAgent, triageAgent, "Transfer to this agent if the issue is not refund related"),
- triageAgent,
- statusAgent,
- returnAgent,
- refundAgent)
+ .Add(refundAgent, triageAgent, "Transfer to this agent if the issue is not refund related"))
{
InteractiveCallback = () =>
{
@@ -84,18 +78,11 @@ public class HandoffOrchestration_Intro(ITestOutputHelper output) : Orchestratio
StreamingResponseCallback = streamedResponse ? monitor.StreamingResultCallback : null,
};
- // Start the runtime
- await using InProcessRuntime runtime = new();
- await runtime.StartAsync();
-
// Run the orchestration
Console.WriteLine($"\n# INPUT:\n{task}\n");
- OrchestrationResult result = await orchestration.InvokeAsync(task, runtime);
+ OrchestrationResult result = await orchestration.InvokeAsync(task);
- string text = await result.GetValueAsync(TimeSpan.FromSeconds(300));
- Console.WriteLine($"\n# RESULT: {text}");
-
- await runtime.RunUntilIdleAsync();
+ Console.WriteLine($"\n# RESULT: {await result}");
this.DisplayHistory(monitor.History);
}
diff --git a/dotnet/samples/GettingStarted/Orchestration/HandoffOrchestration_With_StructuredInput.cs b/dotnet/samples/GettingStarted/Orchestration/HandoffOrchestration_With_StructuredInput.cs
index 454a0fcb53..4778570fcb 100644
--- a/dotnet/samples/GettingStarted/Orchestration/HandoffOrchestration_With_StructuredInput.cs
+++ b/dotnet/samples/GettingStarted/Orchestration/HandoffOrchestration_With_StructuredInput.cs
@@ -2,10 +2,8 @@
using System.Text.Json.Serialization;
using Microsoft.Agents.Orchestration;
-using Microsoft.Agents.Orchestration.Handoff;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.AI.Agents;
-using Microsoft.Extensions.AI.Agents.Runtime.InProcess;
namespace Orchestration;
@@ -49,10 +47,7 @@ public class HandoffOrchestration_With_StructuredInput(ITestOutputHelper output)
HandoffOrchestration orchestration =
new(OrchestrationHandoffs
.StartWith(triageAgent)
- .Add(triageAgent, dotnetAgent, pythonAgent),
- triageAgent,
- pythonAgent,
- dotnetAgent)
+ .Add(triageAgent, dotnetAgent, pythonAgent))
{
LoggerFactory = this.LoggerFactory,
ResponseCallback = monitor.ResponseCallback,
@@ -83,18 +78,11 @@ public class HandoffOrchestration_With_StructuredInput(ITestOutputHelper output)
Labels = []
};
- // Start the runtime
- await using InProcessRuntime runtime = new();
- await runtime.StartAsync();
-
// Run the orchestration
Console.WriteLine($"\n# INPUT:\n{input.Id}: {input.Title}\n");
- OrchestrationResult result = await orchestration.InvokeAsync(input, runtime);
- string text = await result.GetValueAsync(TimeSpan.FromSeconds(ResultTimeoutInSeconds));
- Console.WriteLine($"\n# RESULT: {text}");
+ OrchestrationResult result = await orchestration.InvokeAsync(input);
+ Console.WriteLine($"\n# RESULT: {await result}");
Console.WriteLine($"\n# LABELS: {string.Join(",", githubPlugin.Labels["12345"])}\n");
-
- await runtime.RunUntilIdleAsync();
}
private sealed class GithubIssue
diff --git a/dotnet/samples/GettingStarted/Orchestration/SequentialOrchestration_Intro.cs b/dotnet/samples/GettingStarted/Orchestration/SequentialOrchestration_Intro.cs
index 44e0771af4..579aecf96f 100644
--- a/dotnet/samples/GettingStarted/Orchestration/SequentialOrchestration_Intro.cs
+++ b/dotnet/samples/GettingStarted/Orchestration/SequentialOrchestration_Intro.cs
@@ -1,9 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.Agents.Orchestration;
-using Microsoft.Agents.Orchestration.Sequential;
using Microsoft.Extensions.AI.Agents;
-using Microsoft.Extensions.AI.Agents.Runtime.InProcess;
namespace Orchestration;
@@ -64,18 +62,11 @@ public class SequentialOrchestration_Intro(ITestOutputHelper output) : Orchestra
StreamingResponseCallback = streamedResponse ? monitor.StreamingResultCallback : null,
};
- // Start the runtime
- await using InProcessRuntime runtime = new();
- await runtime.StartAsync();
-
// Run the orchestration
string input = "An eco-friendly stainless steel water bottle that keeps drinks cold for 24 hours";
Console.WriteLine($"\n# INPUT: {input}\n");
- OrchestrationResult result = await orchestration.InvokeAsync(input, runtime);
- string text = await result.GetValueAsync(TimeSpan.FromSeconds(ResultTimeoutInSeconds));
- Console.WriteLine($"\n# RESULT: {text}");
-
- await runtime.RunUntilIdleAsync();
+ OrchestrationResult result = await orchestration.InvokeAsync(input);
+ Console.WriteLine($"\n# RESULT: {await result}");
this.DisplayHistory(monitor.History);
}
diff --git a/dotnet/samples/GettingStarted/Orchestration/SequentialOrchestration_With_Cancellation.cs b/dotnet/samples/GettingStarted/Orchestration/SequentialOrchestration_With_Cancellation.cs
index a4394dac46..6b93c8590b 100644
--- a/dotnet/samples/GettingStarted/Orchestration/SequentialOrchestration_With_Cancellation.cs
+++ b/dotnet/samples/GettingStarted/Orchestration/SequentialOrchestration_With_Cancellation.cs
@@ -1,9 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.Agents.Orchestration;
-using Microsoft.Agents.Orchestration.Sequential;
using Microsoft.Extensions.AI.Agents;
-using Microsoft.Extensions.AI.Agents.Runtime.InProcess;
namespace Orchestration;
@@ -26,29 +24,22 @@ public class SequentialOrchestration_With_Cancellation(ITestOutputHelper output)
// Define the orchestration
SequentialOrchestration orchestration = new(agent) { LoggerFactory = this.LoggerFactory };
- // Start the runtime
- await using InProcessRuntime runtime = new();
- await runtime.StartAsync();
-
// Run the orchestration
string input = "42";
Console.WriteLine($"\n# INPUT: {input}\n");
- OrchestrationResult result = await orchestration.InvokeAsync(input, runtime);
+ OrchestrationResult result = await orchestration.InvokeAsync(input);
result.Cancel();
await Task.Delay(TimeSpan.FromSeconds(3));
try
{
- string text = await result.GetValueAsync(TimeSpan.FromSeconds(ResultTimeoutInSeconds));
- Console.WriteLine($"\n# RESULT: {text}");
+ Console.WriteLine($"\n# RESULT: {await result}");
}
- catch (AggregateException exception)
+ catch (TimeoutException exception)
{
- Console.WriteLine($"\n# CANCELLED: {exception.InnerException?.Message}");
+ Console.WriteLine($"\n# CANCELED: {exception.Message}");
}
-
- await runtime.RunUntilIdleAsync();
}
}
diff --git a/dotnet/src/LegacySupport/IsExternalInit/IsExternalInit.cs b/dotnet/src/LegacySupport/IsExternalInit/IsExternalInit.cs
index b340f84592..e86a0cd0e7 100644
--- a/dotnet/src/LegacySupport/IsExternalInit/IsExternalInit.cs
+++ b/dotnet/src/LegacySupport/IsExternalInit/IsExternalInit.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Microsoft. All rights reserved.
+// Copyright (c) Microsoft. All rights reserved.
/* This enables support for C# 9/10 records on older frameworks */
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/AgentActor.cs b/dotnet/src/Microsoft.Agents.Orchestration/AgentActor.cs
index 925221ed78..b3bdbccdd3 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/AgentActor.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/AgentActor.cs
@@ -25,12 +25,7 @@ public abstract class AgentActor : OrchestrationActor
/// An .
/// The logger to use for the actor
protected AgentActor(ActorId id, IAgentRuntime runtime, OrchestrationContext context, Agent agent, ILogger? logger = null)
- : base(
- id,
- runtime,
- context,
- agent.Description,
- logger)
+ : base(id, runtime, context, agent.Description, logger)
{
this.Agent = agent;
this.Thread = this.Agent.GetNewThread();
@@ -55,102 +50,75 @@ public abstract class AgentActor : OrchestrationActor
}
///
- /// Invokes the agent for a regular (not streamed) response.
+ /// Invokes the agent for a non-streamed responses.
///
/// The messages to send.
/// The options for running the agent.
/// A cancellation token for the operation.
/// A task that represents the asynchronous operation.
///
- /// Override this method to customize the invocation of the agent.
+ /// This method is not intended to be called directly; instead, use .
+ /// This method exists to be overridden in derived classes in order to customize the invocation of the agent by .
///
- protected virtual Task InvokeAsync(
- IReadOnlyCollection messages,
- AgentRunOptions options,
- CancellationToken cancellationToken = default) =>
- this.Agent.RunAsync(
- [.. messages],
- this.Thread,
- options,
- cancellationToken);
+ protected virtual Task InvokeCoreAsync(
+ IReadOnlyCollection messages, AgentRunOptions? options, CancellationToken cancellationToken) =>
+ this.Agent.RunAsync([.. messages], this.Thread, options, cancellationToken);
///
- /// Invokes the agent for a streamed response.
+ /// Invokes the agent for a streamed responses.
///
/// The messages to send.
/// The options for running the agent.
/// A cancellation token for the operation.
/// A task that represents the asynchronous operation.
///
- /// Override this method to customize the invocation of the agent.
+ /// This method is not intended to be called directly; instead, use .
+ /// This method exists to be overridden in derived classes in order to customize the invocation of the agent by .
///
- protected virtual IAsyncEnumerable InvokeStreamingAsync(IReadOnlyCollection messages, AgentRunOptions options, CancellationToken cancellationToken) =>
- this.Agent.RunStreamingAsync(
- messages,
- this.Thread,
- options,
- cancellationToken);
+ protected virtual IAsyncEnumerable InvokeStreamingCoreAsync(
+ IReadOnlyCollection messages, AgentRunOptions? options, CancellationToken cancellationToken) =>
+ this.Agent.RunStreamingAsync(messages, this.Thread, options, cancellationToken);
///
- /// Invokes the agent with a single chat message.
- /// This method sets the message role to and delegates to the overload accepting multiple messages.
- ///
- /// The chat message content to send.
- /// A cancellation token that can be used to cancel the operation.
- /// A task that returns the response .
- protected ValueTask InvokeAsync(ChatMessage input, CancellationToken cancellationToken) =>
- this.InvokeAsync([input], cancellationToken);
-
- ///
- /// Invokes the agent with input messages and respond with both streamed and regular messages.
+ /// Runs the agent with input messages and respond with both streamed and regular messages.
///
/// The list of chat messages to send.
/// A cancellation token that can be used to cancel the operation.
/// A task that returns the response .
- protected async ValueTask InvokeAsync(IEnumerable input, CancellationToken cancellationToken)
+ protected async ValueTask RunAsync(IEnumerable input, CancellationToken cancellationToken)
{
- this.Context.Cancellation.ThrowIfCancellationRequested();
+ using CancellationTokenSource combined = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, this.Context.CancellationToken);
+ cancellationToken = combined.Token;
- AgentRunOptions options = new();
-
- if (this.Context.StreamingResponseCallback == null)
+ // Utilize streaming iff a streaming callback is provided; otherwise, use the non-streaming API.
+ AgentRunResponse response;
+ if (this.Context.StreamingResponseCallback is { } streamingCallback)
{
- // No need to utilize streaming if no callback is provided
- AgentRunResponse response = await this.InvokeAsync([.. input], options, cancellationToken).ConfigureAwait(false);
+ // For streaming, enumerate all the updates, invoking the callback for each, and storing them all.
+ // Then convert them all into a single response instance.
+ List updates = [];
- if (this.Context.ResponseCallback is not null)
+ await foreach (AgentRunResponseUpdate update in this.InvokeStreamingCoreAsync([.. input], options: null, cancellationToken).WithCancellation(this.Context.CancellationToken).ConfigureAwait(false))
{
- await this.Context.ResponseCallback.Invoke(response.Messages).ConfigureAwait(false);
+ updates.Add(update);
+ await streamingCallback(update).ConfigureAwait(false);
}
- return response.Messages.Last();
+ response = updates.ToAgentRunResponse();
}
-
- IAsyncEnumerable streamedResponses = this.InvokeStreamingAsync([.. input], options, cancellationToken);
- AgentRunResponseUpdate? lastStreamedResponse = null;
- List updates = [];
- await foreach (AgentRunResponseUpdate streamedResponse in streamedResponses.ConfigureAwait(false))
+ else
{
- this.Context.Cancellation.ThrowIfCancellationRequested();
-
- await HandleStreamedMessage(lastStreamedResponse, isFinal: false).ConfigureAwait(false);
-
- lastStreamedResponse = streamedResponse;
+ // For non-streaming, just invoke the non-streaming method and get back the response.
+ response = await this.InvokeCoreAsync([.. input], options: null, cancellationToken).ConfigureAwait(false);
}
- return updates.ToAgentRunResponse().Messages.Last();
-
- async ValueTask HandleStreamedMessage(AgentRunResponseUpdate? streamedResponse, bool isFinal)
+ // Regardless of whether we invoked streaming callbacks for individual updates, invoke the non-streaming callback with the final response instance.
+ // This can be used as an indication of completeness if someone otherwise only cares about the streaming updates.
+ if (this.Context.ResponseCallback is { } responseCallback)
{
- if (this.Context.StreamingResponseCallback != null && streamedResponse != null)
- {
- await this.Context.StreamingResponseCallback.Invoke(streamedResponse, isFinal).ConfigureAwait(false);
- }
-
- if (streamedResponse != null)
- {
- updates.Add(streamedResponse);
- }
+ await responseCallback.Invoke(response.Messages).ConfigureAwait(false);
}
+
+ return response.Messages.LastOrDefault() ?? new();
}
}
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/AgentOrchestration.RequestActor.cs b/dotnet/src/Microsoft.Agents.Orchestration/AgentOrchestration.RequestActor.cs
index 70cbb60d90..7a03982f97 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/AgentOrchestration.RequestActor.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/AgentOrchestration.RequestActor.cs
@@ -4,7 +4,6 @@ using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
-using Microsoft.Agents.Orchestration.Transforms;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.AI.Agents.Runtime;
using Microsoft.Extensions.Logging;
@@ -18,7 +17,7 @@ public abstract partial class AgentOrchestration
///
private sealed class RequestActor : OrchestrationActor
{
- private readonly OrchestrationInputTransform _transform;
+ private readonly Func>> _transform;
private readonly Func, ValueTask> _action;
private readonly TaskCompletionSource _completionSource;
@@ -36,7 +35,7 @@ public abstract partial class AgentOrchestration
ActorId id,
IAgentRuntime runtime,
OrchestrationContext context,
- OrchestrationInputTransform transform,
+ Func>> transform,
TaskCompletionSource completionSource,
Func, ValueTask> action,
ILogger? logger = null)
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/AgentOrchestration.ResultActor.cs b/dotnet/src/Microsoft.Agents.Orchestration/AgentOrchestration.ResultActor.cs
index c92d35f6bd..a593c47a58 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/AgentOrchestration.ResultActor.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/AgentOrchestration.ResultActor.cs
@@ -4,7 +4,6 @@ using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
-using Microsoft.Agents.Orchestration.Transforms;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.AI.Agents.Runtime;
using Microsoft.Extensions.Logging;
@@ -19,8 +18,8 @@ public abstract partial class AgentOrchestration
private sealed class ResultActor : OrchestrationActor
{
private readonly TaskCompletionSource _completionSource;
- private readonly OrchestrationResultTransform _transformResult;
- private readonly OrchestrationOutputTransform _transform;
+ private readonly Func> _transformResult;
+ private readonly Func, CancellationToken, ValueTask> _transform;
///
/// Initializes a new instance of the class.
@@ -36,8 +35,8 @@ public abstract partial class AgentOrchestration
ActorId id,
IAgentRuntime runtime,
OrchestrationContext context,
- OrchestrationResultTransform transformResult,
- OrchestrationOutputTransform transformOutput,
+ Func> transformResult,
+ Func, CancellationToken, ValueTask> transformOutput,
TaskCompletionSource completionSource,
ILogger>? logger = null)
: base(id, runtime, context, $"{id.Type}_Actor", logger)
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/AgentOrchestration.cs b/dotnet/src/Microsoft.Agents.Orchestration/AgentOrchestration.cs
index 8f32787357..8e3dd2424f 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/AgentOrchestration.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/AgentOrchestration.cs
@@ -2,33 +2,20 @@
using System;
using System.Collections.Generic;
-using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using Microsoft.Agents.Orchestration.Extensions;
-using Microsoft.Agents.Orchestration.Transforms;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.AI.Agents;
using Microsoft.Extensions.AI.Agents.Runtime;
+using Microsoft.Extensions.AI.Agents.Runtime.InProcess;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Shared.Diagnostics;
+#pragma warning disable CA2000 // Dispose objects before losing scope
+
namespace Microsoft.Agents.Orchestration;
-///
-/// Called for every response is produced by any agent.
-///
-/// The agent response
-public delegate ValueTask OrchestrationResponseCallback(IEnumerable response);
-
-///
-/// Called to expose the streamed response produced by any agent.
-///
-/// The agent response
-/// Indicates if streamed content is final chunk of the message.
-public delegate ValueTask OrchestrationStreamingCallback(AgentRunResponseUpdate response, bool isFinal);
-
///
/// Called when human interaction is requested.
///
@@ -47,9 +34,17 @@ public abstract partial class AgentOrchestration
/// Specifies the member agents or orchestrations participating in this orchestration.
protected AgentOrchestration(params Agent[] members)
{
+ _ = Throw.IfNull(members);
+
// Capture orchestration root name without generic parameters for use in
// agent type and topic formatting as well as logging.
- this.OrchestrationLabel = this.GetType().Name.Split('`').First();
+ string name = this.GetType().Name;
+ int pos = name.IndexOf('`');
+ if (pos > 0)
+ {
+ name = name.Substring(0, pos);
+ }
+ this.OrchestrationLabel = name;
this.Members = members;
}
@@ -72,22 +67,22 @@ public abstract partial class AgentOrchestration
///
/// Transforms the orchestration input into a source input suitable for processing.
///
- public OrchestrationInputTransform InputTransform { get; init; } = DefaultTransforms.FromInput;
+ public Func>> InputTransform { get; init; } = DefaultTransforms.FromInput;
///
/// Transforms the processed result into the final output form.
///
- public OrchestrationOutputTransform ResultTransform { get; init; } = DefaultTransforms.ToOutput;
+ public Func, CancellationToken, ValueTask> ResultTransform { get; init; } = DefaultTransforms.ToOutput;
///
/// Optional callback that is invoked for every agent response.
///
- public OrchestrationResponseCallback? ResponseCallback { get; init; }
+ public Func, ValueTask>? ResponseCallback { get; init; }
///
- /// Optional callback that is invoked for every agent response.
+ /// Optional callback that is invoked for every agent update.
///
- public OrchestrationStreamingCallback? StreamingResponseCallback { get; init; }
+ public Func? StreamingResponseCallback { get; init; }
///
/// Gets the list of member targets involved in the orchestration.
@@ -108,14 +103,17 @@ public abstract partial class AgentOrchestration
/// A cancellation token that can be used to cancel the operation.
public async ValueTask> InvokeAsync(
TInput input,
- IAgentRuntime runtime,
+ IAgentRuntime? runtime = null,
CancellationToken cancellationToken = default)
{
Throw.IfNull(input, nameof(input));
- TopicId topic = new($"{this.OrchestrationLabel}_{Guid.NewGuid().ToString().Replace("-", string.Empty)}");
+ cancellationToken.ThrowIfCancellationRequested();
+
+ TopicId topic = new($"{this.OrchestrationLabel}_{Guid.NewGuid():N}");
CancellationTokenSource orchestrationCancelSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
+ cancellationToken = orchestrationCancelSource.Token;
OrchestrationContext context =
new(this.OrchestrationLabel,
@@ -129,9 +127,10 @@ public abstract partial class AgentOrchestration
TaskCompletionSource completion = new();
- ActorType orchestrationType = await this.RegisterAsync(runtime, context, completion, handoff: null).ConfigureAwait(false);
+ InProcessRuntime? temporaryRuntime = null;
+ runtime ??= temporaryRuntime = InProcessRuntime.StartNew();
- cancellationToken.ThrowIfCancellationRequested();
+ ActorType orchestrationType = await this.RegisterAsync(runtime, context, completion, handoff: null).ConfigureAwait(false);
logger.LogOrchestrationInvoke(this.OrchestrationLabel, topic);
@@ -139,7 +138,7 @@ public abstract partial class AgentOrchestration
logger.LogOrchestrationYield(this.OrchestrationLabel, topic);
- return new OrchestrationResult(context, completion, orchestrationCancelSource, logger);
+ return new OrchestrationResult(context, completion, orchestrationCancelSource, logger, temporaryRuntime);
}
///
@@ -191,24 +190,22 @@ public abstract partial class AgentOrchestration
ActorType orchestrationEntry =
await runtime.RegisterOrchestrationAgentAsync(
this.FormatAgentType(context.Topic, "Boot"),
- (agentId, runtime) =>
- {
- RequestActor actor =
- new(agentId,
- runtime,
- context,
- this.InputTransform,
- completion,
- StartAsync,
- context.LoggerFactory.CreateLogger());
- return new ValueTask(actor);
- }).ConfigureAwait(false);
+ (agentId, runtime) =>
+ {
+ RequestActor actor =
+ new(agentId,
+ runtime,
+ context,
+ this.InputTransform,
+ completion,
+ input => this.StartAsync(runtime, context.Topic, input, entryAgent),
+ context.LoggerFactory.CreateLogger());
+ return new ValueTask(actor);
+ }).ConfigureAwait(false);
logger.LogOrchestrationRegistrationDone(context.Orchestration, context.Topic);
return orchestrationEntry;
-
- ValueTask StartAsync(IEnumerable input) => this.StartAsync(runtime, context.Topic, input, entryAgent);
}
///
@@ -219,12 +216,12 @@ public abstract partial class AgentOrchestration
IAgentRuntime runtime,
OrchestrationContext context,
TaskCompletionSource completion,
- OrchestrationOutputTransform outputTransform)
+ Func, CancellationToken, ValueTask> outputTransform)
{
///
/// Register the final result type.
///
- public async ValueTask RegisterResultTypeAsync(OrchestrationResultTransform resultTransform)
+ public async ValueTask RegisterResultTypeAsync(Func> resultTransform)
{
// Register actor for final result
ActorType registeredType =
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Logging/AgentOrchestrationLogMessages.cs b/dotnet/src/Microsoft.Agents.Orchestration/AgentOrchestrationLogMessages.cs
similarity index 100%
rename from dotnet/src/Microsoft.Agents.Orchestration/Logging/AgentOrchestrationLogMessages.cs
rename to dotnet/src/Microsoft.Agents.Orchestration/AgentOrchestrationLogMessages.cs
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Extensions/RuntimeExtensions.cs b/dotnet/src/Microsoft.Agents.Orchestration/AgentRuntimeExtensions.cs
similarity index 75%
rename from dotnet/src/Microsoft.Agents.Orchestration/Extensions/RuntimeExtensions.cs
rename to dotnet/src/Microsoft.Agents.Orchestration/AgentRuntimeExtensions.cs
index 94400a6746..9ae4fcf822 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/Extensions/RuntimeExtensions.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/AgentRuntimeExtensions.cs
@@ -3,22 +3,19 @@
using System;
using System.Threading;
using System.Threading.Tasks;
-using Microsoft.Extensions.AI.Agents.Runtime;
-namespace Microsoft.Agents.Orchestration.Extensions;
+namespace Microsoft.Extensions.AI.Agents.Runtime;
///
/// Extension methods for .
///
-public static class RuntimeExtensions
+internal static class AgentRuntimeExtensions
{
///
/// Sends a message to the specified agent.
///
- public static async ValueTask PublishMessageAsync(this IAgentRuntime runtime, object message, ActorType agentType, CancellationToken cancellationToken = default)
- {
- await runtime.PublishMessageAsync(message, new TopicId(agentType.Name), sender: null, messageId: null, cancellationToken).ConfigureAwait(false);
- }
+ public static ValueTask PublishMessageAsync(this IAgentRuntime runtime, object message, ActorType agentType, CancellationToken cancellationToken = default) =>
+ runtime.PublishMessageAsync(message, new TopicId(agentType.Name), sender: null, messageId: null, cancellationToken);
///
/// Registers an agent factory for the specified agent type and associates it with the runtime.
@@ -42,10 +39,8 @@ public static class RuntimeExtensions
///
/// The runtime for managing the subscription.
/// The agent type to subscribe.
- public static async Task SubscribeAsync(this IAgentRuntime runtime, ActorType agentType)
- {
- await runtime.AddSubscriptionAsync(new TypeSubscription(agentType.Name, agentType)).ConfigureAwait(false);
- }
+ public static Task SubscribeAsync(this IAgentRuntime runtime, ActorType agentType) =>
+ runtime.AddSubscriptionAsync(new TypeSubscription(agentType.Name, agentType)).AsTask();
///
/// Subscribes the specified agent type to the provided topics.
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Concurrent/ConcurrentActor.cs b/dotnet/src/Microsoft.Agents.Orchestration/Concurrent/ConcurrentActor.cs
index 5a1a10e7b1..077c3670c9 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/Concurrent/ConcurrentActor.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/Concurrent/ConcurrentActor.cs
@@ -7,7 +7,7 @@ using Microsoft.Extensions.AI.Agents;
using Microsoft.Extensions.AI.Agents.Runtime;
using Microsoft.Extensions.Logging;
-namespace Microsoft.Agents.Orchestration.Concurrent;
+namespace Microsoft.Agents.Orchestration;
///
/// An used with the .
@@ -37,10 +37,10 @@ internal sealed class ConcurrentActor : AgentActor
{
this.Logger.LogConcurrentAgentInvoke(this.Id);
- ChatMessage response = await this.InvokeAsync(item.Messages, cancellationToken).ConfigureAwait(false);
+ ChatMessage response = await this.RunAsync(item.Messages, cancellationToken).ConfigureAwait(false);
this.Logger.LogConcurrentAgentResult(this.Id, response.Text);
- await this.PublishMessageAsync(response.AsResultMessage(), this._handoffActor, cancellationToken).ConfigureAwait(false);
+ await this.PublishMessageAsync(new ConcurrentMessages.Result(response), this._handoffActor, cancellationToken).ConfigureAwait(false);
}
}
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Concurrent/ConcurrentMessages.cs b/dotnet/src/Microsoft.Agents.Orchestration/Concurrent/ConcurrentMessages.cs
index 947bfbf7be..c095f8f9c1 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/Concurrent/ConcurrentMessages.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/Concurrent/ConcurrentMessages.cs
@@ -3,52 +3,20 @@
using System.Collections.Generic;
using Microsoft.Extensions.AI;
-namespace Microsoft.Agents.Orchestration.Concurrent;
+namespace Microsoft.Agents.Orchestration;
///
/// Common messages used by the .
///
internal static class ConcurrentMessages
{
- ///
- /// An empty message instance as a default.
- ///
- public static readonly ChatMessage Empty = new();
-
///
/// The input task for a .
///
- public sealed class Request
- {
- ///
- /// The request input.
- ///
- public IList Messages { get; init; } = [];
- }
+ public sealed record Request(IList Messages);
///
/// A result from a .
///
- public sealed class Result
- {
- ///
- /// The result message.
- ///
- public ChatMessage Message { get; init; } = Empty;
- }
-
- ///
- /// Extension method to convert a to a .
- ///
- public static Result AsResultMessage(this string text, ChatRole? role = null) => new() { Message = new ChatMessage(role ?? ChatRole.Assistant, text) };
-
- ///
- /// Extension method to convert a to a .
- ///
- public static Result AsResultMessage(this ChatMessage message) => new() { Message = message };
-
- ///
- /// Extension method to convert a collection of to a .
- ///
- public static Request AsInputMessage(this IEnumerable messages) => new() { Messages = [.. messages] };
+ public sealed record Result(ChatMessage Message);
}
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Concurrent/ConcurrentOrchestration.String.cs b/dotnet/src/Microsoft.Agents.Orchestration/Concurrent/ConcurrentOrchestration.String.cs
index 4f82f6309f..8e7e57bb31 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/Concurrent/ConcurrentOrchestration.String.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/Concurrent/ConcurrentOrchestration.String.cs
@@ -5,7 +5,7 @@ using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.AI.Agents;
-namespace Microsoft.Agents.Orchestration.Concurrent;
+namespace Microsoft.Agents.Orchestration;
///
/// An orchestration that broadcasts the input message to each agent.
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Concurrent/ConcurrentOrchestration.cs b/dotnet/src/Microsoft.Agents.Orchestration/Concurrent/ConcurrentOrchestration.cs
index 6e9e19d0a1..9a2c0d86f6 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/Concurrent/ConcurrentOrchestration.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/Concurrent/ConcurrentOrchestration.cs
@@ -3,13 +3,12 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
-using Microsoft.Agents.Orchestration.Extensions;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.AI.Agents;
using Microsoft.Extensions.AI.Agents.Runtime;
using Microsoft.Extensions.Logging;
-namespace Microsoft.Agents.Orchestration.Concurrent;
+namespace Microsoft.Agents.Orchestration;
///
/// An orchestration that broadcasts the input message to each agent.
@@ -32,7 +31,7 @@ public class ConcurrentOrchestration
///
protected override ValueTask StartAsync(IAgentRuntime runtime, TopicId topic, IEnumerable input, ActorType? entryAgent)
{
- return runtime.PublishMessageAsync(input.AsInputMessage(), topic);
+ return runtime.PublishMessageAsync(new ConcurrentMessages.Request([.. input]), topic);
}
///
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Logging/ConcurrentOrchestrationLogMessages.cs b/dotnet/src/Microsoft.Agents.Orchestration/Concurrent/ConcurrentOrchestrationLogMessages.cs
similarity index 96%
rename from dotnet/src/Microsoft.Agents.Orchestration/Logging/ConcurrentOrchestrationLogMessages.cs
rename to dotnet/src/Microsoft.Agents.Orchestration/Concurrent/ConcurrentOrchestrationLogMessages.cs
index eb76da967b..d74eea7965 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/Logging/ConcurrentOrchestrationLogMessages.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/Concurrent/ConcurrentOrchestrationLogMessages.cs
@@ -1,7 +1,6 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Diagnostics.CodeAnalysis;
-using Microsoft.Agents.Orchestration.Concurrent;
using Microsoft.Extensions.AI.Agents.Runtime;
using Microsoft.Extensions.Logging;
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Concurrent/ConcurrentResultActor.cs b/dotnet/src/Microsoft.Agents.Orchestration/Concurrent/ConcurrentResultActor.cs
index db5bbcb573..c8741501a4 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/Concurrent/ConcurrentResultActor.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/Concurrent/ConcurrentResultActor.cs
@@ -6,7 +6,7 @@ using System.Threading.Tasks;
using Microsoft.Extensions.AI.Agents.Runtime;
using Microsoft.Extensions.Logging;
-namespace Microsoft.Agents.Orchestration.Concurrent;
+namespace Microsoft.Agents.Orchestration;
///
/// Actor for capturing each message.
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/DefaultTransforms.cs b/dotnet/src/Microsoft.Agents.Orchestration/DefaultTransforms.cs
new file mode 100644
index 0000000000..9b24dcb31b
--- /dev/null
+++ b/dotnet/src/Microsoft.Agents.Orchestration/DefaultTransforms.cs
@@ -0,0 +1,73 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System;
+using System.Collections.Generic;
+using System.Text.Json;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.AI;
+
+namespace Microsoft.Agents.Orchestration;
+
+internal static class DefaultTransforms
+{
+ public static ValueTask> FromInput(TInput input, CancellationToken cancellationToken = default) =>
+ new(input switch
+ {
+ IEnumerable messages => messages,
+ ChatMessage message => [message],
+ string text => [new ChatMessage(ChatRole.User, text)],
+ _ => [new ChatMessage(ChatRole.User, JsonSerializer.Serialize(input))]
+ });
+
+ public static ValueTask ToOutput(IList result, CancellationToken cancellationToken = default)
+ {
+ bool isSingleResult = result.Count == 1;
+
+ TOutput output =
+ GetDefaultOutput() ??
+ GetObjectOutput() ??
+ throw new InvalidOperationException($"Unable to transform output to {typeof(TOutput)}.");
+
+ return new ValueTask(output);
+
+ TOutput? GetObjectOutput()
+ {
+ if (isSingleResult)
+ {
+ try
+ {
+ return JsonSerializer.Deserialize(result[0].Text);
+ }
+ catch (JsonException)
+ {
+ }
+ }
+
+ return default;
+ }
+
+ TOutput? GetDefaultOutput()
+ {
+ if (typeof(TOutput).IsInstanceOfType(result))
+ {
+ return (TOutput)(object)result;
+ }
+
+ if (isSingleResult)
+ {
+ if (typeof(ChatMessage).IsAssignableFrom(typeof(TOutput)))
+ {
+ return (TOutput)(object)result[0];
+ }
+
+ if (typeof(string) == typeof(TOutput))
+ {
+ return (TOutput)(object)(result[0].Text ?? string.Empty);
+ }
+ }
+
+ return default;
+ }
+ }
+}
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatAgentActor.cs b/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatAgentActor.cs
index c71d8dc398..458fcea9eb 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatAgentActor.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatAgentActor.cs
@@ -8,7 +8,7 @@ using Microsoft.Extensions.AI.Agents;
using Microsoft.Extensions.AI.Agents.Runtime;
using Microsoft.Extensions.Logging;
-namespace Microsoft.Agents.Orchestration.GroupChat;
+namespace Microsoft.Agents.Orchestration;
///
/// An used with the .
@@ -30,32 +30,20 @@ internal sealed class GroupChatAgentActor : AgentActor
{
this._cache = [];
- this.RegisterMessageHandler(this.HandleAsync);
- this.RegisterMessageHandler(this.HandleAsync);
+ this.RegisterMessageHandler((item, ctx) => this._cache.AddRange(item.Messages));
+ this.RegisterMessageHandler((item, ctx) => this.ResetThread());
this.RegisterMessageHandler(this.HandleAsync);
}
- private ValueTask HandleAsync(GroupChatMessages.Group item, MessageContext messageContext, CancellationToken cancellationToken)
- {
- this._cache.AddRange(item.Messages);
- return default;
- }
-
- private ValueTask HandleAsync(GroupChatMessages.Reset item, MessageContext messageContext, CancellationToken cancellationToken)
- {
- this.ResetThread();
- return default;
- }
-
private async ValueTask HandleAsync(GroupChatMessages.Speak item, MessageContext messageContext, CancellationToken cancellationToken)
{
this.Logger.LogChatAgentInvoke(this.Id);
- ChatMessage response = await this.InvokeAsync(this._cache, cancellationToken).ConfigureAwait(false);
+ ChatMessage response = await this.RunAsync(this._cache, cancellationToken).ConfigureAwait(false);
this.Logger.LogChatAgentResult(this.Id, response.Text);
this._cache.Clear();
- await this.PublishMessageAsync(response.AsGroupMessage(), this.Context.Topic, cancellationToken: cancellationToken).ConfigureAwait(false);
+ await this.PublishMessageAsync(new GroupChatMessages.Group([response]), this.Context.Topic, cancellationToken: cancellationToken).ConfigureAwait(false);
}
}
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatManager.cs b/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatManager.cs
index de6999d928..8063475e8c 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatManager.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatManager.cs
@@ -5,7 +5,7 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.AI;
-namespace Microsoft.Agents.Orchestration.GroupChat;
+namespace Microsoft.Agents.Orchestration;
///
/// Represents the result of a group chat manager operation, including a value and a reason.
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatManagerActor.cs b/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatManagerActor.cs
index 69eb38972b..15a995f51d 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatManagerActor.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatManagerActor.cs
@@ -7,7 +7,7 @@ using Microsoft.Extensions.AI;
using Microsoft.Extensions.AI.Agents.Runtime;
using Microsoft.Extensions.Logging;
-namespace Microsoft.Agents.Orchestration.GroupChat;
+namespace Microsoft.Agents.Orchestration;
///
/// An used to manage a .
@@ -52,7 +52,7 @@ internal sealed class GroupChatManagerActor : OrchestrationActor
this._chat.AddRange(item.Messages);
- await this.PublishMessageAsync(item.Messages.AsGroupMessage(), this.Context.Topic, cancellationToken: cancellationToken).ConfigureAwait(false);
+ await this.PublishMessageAsync(new GroupChatMessages.Group(item.Messages), this.Context.Topic, cancellationToken: cancellationToken).ConfigureAwait(false);
await this.ManageAsync(messageContext, cancellationToken).ConfigureAwait(false);
}
@@ -78,7 +78,7 @@ internal sealed class GroupChatManagerActor : OrchestrationActor
ChatMessage input = await this._manager.InteractiveCallback.Invoke().ConfigureAwait(false);
this.Logger.LogChatManagerUserInput(this.Id, input.Text);
this._chat.Add(input);
- await this.PublishMessageAsync(input.AsGroupMessage(), this.Context.Topic, cancellationToken: cancellationToken).ConfigureAwait(false);
+ await this.PublishMessageAsync(new GroupChatMessages.Group([input]), this.Context.Topic, cancellationToken: cancellationToken).ConfigureAwait(false);
}
}
@@ -88,7 +88,7 @@ internal sealed class GroupChatManagerActor : OrchestrationActor
{
GroupChatManagerResult filterResult = await this._manager.FilterResults(this._chat, cancellationToken).ConfigureAwait(false);
this.Logger.LogChatManagerResult(this.Id, filterResult.Value, filterResult.Reason);
- await this.PublishMessageAsync(filterResult.Value.AsResultMessage(), this._orchestrationType, cancellationToken).ConfigureAwait(false);
+ await this.PublishMessageAsync(new GroupChatMessages.Result(new(ChatRole.Assistant, filterResult.Value)), this._orchestrationType, cancellationToken).ConfigureAwait(false);
return;
}
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatMessages.cs b/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatMessages.cs
index f4bc53e9ac..a70236e70b 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatMessages.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatMessages.cs
@@ -3,28 +3,17 @@
using System.Collections.Generic;
using Microsoft.Extensions.AI;
-namespace Microsoft.Agents.Orchestration.GroupChat;
+namespace Microsoft.Agents.Orchestration;
///
/// Common messages used for agent chat patterns.
///
-public static class GroupChatMessages
+internal static class GroupChatMessages
{
- ///
- /// An empty message instance as a default.
- ///
- internal static readonly ChatMessage Empty = new();
-
///
/// Broadcast a message to all .
///
- public sealed class Group
- {
- ///
- /// The chat message being broadcast.
- ///
- public IEnumerable Messages { get; init; } = [];
- }
+ public sealed record Group(IEnumerable Messages);
///
/// Reset/clear the conversation history for all .
@@ -34,13 +23,7 @@ public static class GroupChatMessages
///
/// The final result.
///
- public sealed class Result
- {
- ///
- /// The chat response message.
- ///
- public ChatMessage Message { get; init; } = Empty;
- }
+ public sealed record Result(ChatMessage Message);
///
/// Signal a to respond.
@@ -50,36 +33,11 @@ public static class GroupChatMessages
///
/// The input task.
///
- public sealed class InputTask
+ public sealed record InputTask(IEnumerable Messages)
{
///
- /// A task that does not require any action.
+ /// Gets an input task that does not require any action.
///
- public static readonly InputTask None = new();
-
- ///
- /// The input that defines the task goal.
- ///
- public IEnumerable Messages { get; init; } = [];
+ public static InputTask None { get; } = new([]);
}
-
- ///
- /// Extension method to convert a to a .
- ///
- public static Group AsGroupMessage(this ChatMessage message) => new() { Messages = [message] };
-
- ///
- /// Extension method to convert a to a .
- ///
- public static Group AsGroupMessage(this IEnumerable messages) => new() { Messages = messages };
-
- ///
- /// Extension method to convert a to a .
- ///
- public static InputTask AsInputTaskMessage(this IEnumerable messages) => new() { Messages = messages };
-
- ///
- /// Extension method to convert a to a .
- ///
- public static Result AsResultMessage(this string text) => new() { Message = new(ChatRole.Assistant, text) };
}
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatOrchestration.String.cs b/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatOrchestration.String.cs
index f1a17142ee..2392d7251c 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatOrchestration.String.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatOrchestration.String.cs
@@ -2,7 +2,7 @@
using Microsoft.Extensions.AI.Agents;
-namespace Microsoft.Agents.Orchestration.GroupChat;
+namespace Microsoft.Agents.Orchestration;
///
/// An orchestration that broadcasts the input message to each agent.
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatOrchestration.cs b/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatOrchestration.cs
index e921eb7362..f93fee7bbb 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatOrchestration.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatOrchestration.cs
@@ -3,14 +3,13 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
-using Microsoft.Agents.Orchestration.Extensions;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.AI.Agents;
using Microsoft.Extensions.AI.Agents.Runtime;
using Microsoft.Extensions.Logging;
using Microsoft.Shared.Diagnostics;
-namespace Microsoft.Agents.Orchestration.GroupChat;
+namespace Microsoft.Agents.Orchestration;
///
/// An orchestration that coordinates a group-chat.
@@ -42,7 +41,7 @@ public class GroupChatOrchestration :
{
throw new ArgumentException("Entry agent is not defined.", nameof(entryAgent));
}
- return runtime.PublishMessageAsync(input.AsInputTaskMessage(), entryAgent.Value);
+ return runtime.PublishMessageAsync(new GroupChatMessages.InputTask(input), entryAgent.Value);
}
///
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Logging/GroupChatOrchestrationLogMessages.cs b/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatOrchestrationLogMessages.cs
similarity index 98%
rename from dotnet/src/Microsoft.Agents.Orchestration/Logging/GroupChatOrchestrationLogMessages.cs
rename to dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatOrchestrationLogMessages.cs
index 6f06c39dee..83446d778f 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/Logging/GroupChatOrchestrationLogMessages.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatOrchestrationLogMessages.cs
@@ -1,7 +1,6 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Diagnostics.CodeAnalysis;
-using Microsoft.Agents.Orchestration.GroupChat;
using Microsoft.Extensions.AI.Agents.Runtime;
using Microsoft.Extensions.Logging;
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatTeam.cs b/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatTeam.cs
index 9409c5561a..fcf844a18b 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatTeam.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatTeam.cs
@@ -4,29 +4,22 @@ using System;
using System.Collections.Generic;
using System.Linq;
-namespace Microsoft.Agents.Orchestration.GroupChat;
+namespace Microsoft.Agents.Orchestration;
///
/// Describes a team of agents participating in a group chat.
///
-public class GroupChatTeam : Dictionary;
-
-///
-/// Extensions for .
-///
-public static class ChatGroupExtensions
+public sealed class GroupChatTeam : Dictionary
{
///
/// Format the names of the agents in the team as a comma delimimted list.
///
- /// The agent team
/// A comma delimimted list of agent name.
- public static string FormatNames(this GroupChatTeam team) => string.Join(",", team.Select(t => t.Key));
+ public string FormatNames() => string.Join(",", this.Select(t => t.Key));
///
/// Format the names and descriptions of the agents in the team as a markdown list.
///
- /// The agent team
/// A markdown list of agent names and descriptions.
- public static string FormatList(this GroupChatTeam team) => string.Join(Environment.NewLine, team.Select(t => $"- {t.Key}: {t.Value.Description}"));
+ public string FormatList() => string.Join(Environment.NewLine, this.Select(t => $"- {t.Key}: {t.Value.Description}"));
}
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/RoundRobinGroupChatManager.cs b/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/RoundRobinGroupChatManager.cs
index cb65c6c7e9..96170bf341 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/RoundRobinGroupChatManager.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/RoundRobinGroupChatManager.cs
@@ -6,7 +6,7 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.AI;
-namespace Microsoft.Agents.Orchestration.GroupChat;
+namespace Microsoft.Agents.Orchestration;
///
/// A that selects agents in a round-robin fashion.
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Handoff/HandoffActor.cs b/dotnet/src/Microsoft.Agents.Orchestration/Handoff/HandoffActor.cs
index 8d699e9bda..da7fca1f42 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/Handoff/HandoffActor.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/Handoff/HandoffActor.cs
@@ -9,7 +9,7 @@ using Microsoft.Extensions.AI.Agents;
using Microsoft.Extensions.AI.Agents.Runtime;
using Microsoft.Extensions.Logging;
-namespace Microsoft.Agents.Orchestration.Handoff;
+namespace Microsoft.Agents.Orchestration;
///
/// An actor used with the .
@@ -54,48 +54,35 @@ internal sealed class HandoffActor : AgentActor
ToolMode = ChatToolMode.Auto
};
- this.RegisterMessageHandler(this.HandleAsync);
+ this.RegisterMessageHandler(this.Handle);
this.RegisterMessageHandler(this.HandleAsync);
- this.RegisterMessageHandler(this.HandleAsync);
+ this.RegisterMessageHandler(this.Handle);
}
///
- protected override Task InvokeAsync(
- IReadOnlyCollection messages,
- AgentRunOptions options,
- CancellationToken cancellationToken = default) =>
- this._chatAgent.RunAsync(
- [.. messages],
- this.Thread,
- options,
- this._options,
- cancellationToken);
+ protected override Task InvokeCoreAsync(
+ IReadOnlyCollection messages, AgentRunOptions? options, CancellationToken cancellationToken) =>
+ this._chatAgent.RunAsync([.. messages], this.Thread, options, this._options, cancellationToken);
///
- protected override IAsyncEnumerable InvokeStreamingAsync(IReadOnlyCollection messages, AgentRunOptions options, CancellationToken cancellationToken) =>
- this._chatAgent.RunStreamingAsync(
- messages,
- this.Thread,
- options,
- this._options,
- cancellationToken);
+ protected override IAsyncEnumerable InvokeStreamingCoreAsync(
+ IReadOnlyCollection messages, AgentRunOptions? options, CancellationToken cancellationToken) =>
+ this._chatAgent.RunStreamingAsync(messages, this.Thread, options, this._options, cancellationToken);
///
/// Gets or sets the callback to be invoked for interactive input.
///
public OrchestrationInteractiveCallback? InteractiveCallback { get; init; }
- private ValueTask HandleAsync(HandoffMessages.InputTask item, MessageContext messageContext, CancellationToken cancellationToken)
+ private void Handle(HandoffMessages.InputTask item, MessageContext messageContext)
{
this._taskSummary = null;
this._cache.AddRange(item.Messages);
- return default;
}
- private ValueTask HandleAsync(HandoffMessages.Response item, MessageContext messageContext, CancellationToken cancellationToken)
+ private void Handle(HandoffMessages.Response item, MessageContext messageContext)
{
this._cache.Add(item.Message);
- return default;
}
private async ValueTask HandleAsync(HandoffMessages.Request item, MessageContext messageContext, CancellationToken cancellationToken)
@@ -109,7 +96,7 @@ internal sealed class HandoffActor : AgentActor
ChatMessage response;
try
{
- response = await this.InvokeAsync(this._cache, cancellationToken).ConfigureAwait(false);
+ response = await this.RunAsync(this._cache, cancellationToken).ConfigureAwait(false);
}
catch (Exception exception)
{
@@ -126,7 +113,7 @@ internal sealed class HandoffActor : AgentActor
// Since we don't want to publish that message, so we only publish if the response is an ASSISTANT message.
if (response.Role == ChatRole.Assistant)
{
- await this.PublishMessageAsync(new HandoffMessages.Response { Message = response }, this.Context.Topic, messageId: null, cancellationToken).ConfigureAwait(false);
+ await this.PublishMessageAsync(new HandoffMessages.Response(response), this.Context.Topic, messageId: null, cancellationToken).ConfigureAwait(false);
}
if (this._handoffAgent != null)
@@ -141,7 +128,7 @@ internal sealed class HandoffActor : AgentActor
if (this.InteractiveCallback != null && this._taskSummary == null)
{
ChatMessage input = await this.InteractiveCallback().ConfigureAwait(false);
- await this.PublishMessageAsync(new HandoffMessages.Response { Message = input }, this.Context.Topic, messageId: null, cancellationToken).ConfigureAwait(false);
+ await this.PublishMessageAsync(new HandoffMessages.Response(input), this.Context.Topic, messageId: null, cancellationToken).ConfigureAwait(false);
this._cache.Add(input);
continue;
}
@@ -187,7 +174,7 @@ internal sealed class HandoffActor : AgentActor
{
this.Logger.LogHandoffSummary(this.Id, summary);
this._taskSummary = summary;
- await this.PublishMessageAsync(new HandoffMessages.Result { Message = new ChatMessage(ChatRole.Assistant, summary) }, this._resultHandoff, cancellationToken).ConfigureAwait(false);
+ await this.PublishMessageAsync(new HandoffMessages.Result(new(ChatRole.Assistant, summary)), this._resultHandoff, cancellationToken).ConfigureAwait(false);
if (FunctionInvokingChatClient.CurrentContext is not null)
{
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Handoff/HandoffMessages.cs b/dotnet/src/Microsoft.Agents.Orchestration/Handoff/HandoffMessages.cs
index a4764871c1..a4b68b3bb0 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/Handoff/HandoffMessages.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/Handoff/HandoffMessages.cs
@@ -3,39 +3,22 @@
using System.Collections.Generic;
using Microsoft.Extensions.AI;
-namespace Microsoft.Agents.Orchestration.Handoff;
+namespace Microsoft.Agents.Orchestration;
///
/// A message that describes the input task and captures results for a .
///
internal static class HandoffMessages
{
- ///
- /// An empty message instance as a default.
- ///
- internal static readonly ChatMessage Empty = new();
-
///
/// The input message.
///
- public sealed class InputTask
- {
- ///
- /// The orchestration input messages.
- ///
- public IList Messages { get; init; } = [];
- }
+ public sealed record InputTask(IList Messages);
///
/// The final result.
///
- public sealed class Result
- {
- ///
- /// The orchestration result message.
- ///
- public ChatMessage Message { get; init; } = Empty;
- }
+ public sealed record Result(ChatMessage Message);
///
/// Signals the handoff to another agent.
@@ -45,21 +28,5 @@ internal static class HandoffMessages
///
/// Broadcast an agent response to all actors in the orchestration.
///
- public sealed class Response
- {
- ///
- /// The chat response message.
- ///
- public ChatMessage Message { get; init; } = Empty;
- }
-
- ///
- /// Extension method to convert a to a .
- ///
- public static InputTask AsInputTaskMessage(this IEnumerable messages) => new() { Messages = [.. messages] };
-
- ///
- /// Extension method to convert a to a .
- ///
- public static Result AsResultMessage(this ChatMessage message) => new() { Message = message };
+ public sealed record Response(ChatMessage Message);
}
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Handoff/HandoffOrchestration.String.cs b/dotnet/src/Microsoft.Agents.Orchestration/Handoff/HandoffOrchestration.String.cs
index bd698778f9..203b9cdb7c 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/Handoff/HandoffOrchestration.String.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/Handoff/HandoffOrchestration.String.cs
@@ -2,7 +2,7 @@
using Microsoft.Extensions.AI.Agents;
-namespace Microsoft.Agents.Orchestration.Handoff;
+namespace Microsoft.Agents.Orchestration;
///
/// An orchestration that passes the input message to the first agent, and
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Handoff/HandoffOrchestration.cs b/dotnet/src/Microsoft.Agents.Orchestration/Handoff/HandoffOrchestration.cs
index b90063aa81..bbeff667e1 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/Handoff/HandoffOrchestration.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/Handoff/HandoffOrchestration.cs
@@ -4,13 +4,12 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
-using Microsoft.Agents.Orchestration.Extensions;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.AI.Agents;
using Microsoft.Extensions.AI.Agents.Runtime;
using Microsoft.Extensions.Logging;
-namespace Microsoft.Agents.Orchestration.Handoff;
+namespace Microsoft.Agents.Orchestration;
///
/// An orchestration that provides the input message to the first agent
@@ -24,17 +23,21 @@ public class HandoffOrchestration : AgentOrchestration class.
///
/// Defines the handoff connections for each agent.
- /// The agents participating in the orchestration.
- public HandoffOrchestration(OrchestrationHandoffs handoffs, params Agent[] agents)
- : base(agents)
+ /// Additional agents participating in the orchestration that weren't passed to .
+ public HandoffOrchestration(OrchestrationHandoffs handoffs, params Agent[] agents) : base(
+ agents is { Length: 0 } ? handoffs.Agents.ToArray() :
+ handoffs.Agents is { Count: 0 } ? agents :
+ handoffs.Agents.Concat(agents).Distinct().ToArray())
{
// Create list of distinct agent names
- HashSet agentNames = new(agents.Select(a => a.Name ?? a.Id), StringComparer.Ordinal)
+ HashSet agentNames = new(base.Members.Select(a => a.Name ?? a.Id), StringComparer.Ordinal)
{
handoffs.FirstAgentName
};
+
// Extract names from handoffs that don't align with a member agent.
string[] badNames = [.. handoffs.Keys.Concat(handoffs.Values.SelectMany(h => h.Keys)).Where(name => !agentNames.Contains(name))];
+
// Fail fast if invalid names are present.
if (badNames.Length > 0)
{
@@ -56,7 +59,7 @@ public class HandoffOrchestration : AgentOrchestration
/// Defines the handoff relationships for a given agent.
@@ -38,7 +38,9 @@ public sealed class OrchestrationHandoffs : Dictionary
/// The first agent to be invoked (prior to any handoff).
public OrchestrationHandoffs(Agent firstAgent)
: this(firstAgent.Name ?? firstAgent.Id)
- { }
+ {
+ this.Agents.Add(firstAgent);
+ }
///
/// Initializes a new instance of the class with no handoff relationships.
@@ -62,26 +64,19 @@ public sealed class OrchestrationHandoffs : Dictionary
/// The source agent.
/// The updated instance.
public static OrchestrationHandoffs StartWith(Agent source) => new(source);
-}
-///
-/// Extension methods for building and modifying relationships.
-///
-public static class OrchestrationHandoffsExtensions
-{
///
/// Adds handoff relationships from a source agent to one or more target agents.
/// Each target agent's name or ID is mapped to its description.
///
- /// The orchestration handoffs collection to update.
/// The source agent.
/// The target agents to add as handoff targets for the source agent.
/// The updated instance.
- public static OrchestrationHandoffs Add(this OrchestrationHandoffs handoffs, Agent source, params Agent[] targets)
+ public OrchestrationHandoffs Add(Agent source, params Agent[] targets)
{
string key = source.Name ?? source.Id;
- AgentHandoffs agentHandoffs = handoffs.GetAgentHandoffs(key);
+ AgentHandoffs agentHandoffs = this.GetAgentHandoffs(key);
foreach (Agent target in targets)
{
@@ -90,60 +85,61 @@ public static class OrchestrationHandoffsExtensions
throw new InvalidOperationException($"The provided target agent with Id '{target.Id}' has no description or name, and no handoff description has been provided. At least one of these are required to register a handoff so that the appropriate target agent can be chosen.");
}
+ this.Agents.Add(target);
agentHandoffs[target.Name ?? target.Id] = target.Description ?? target.Name!;
}
- return handoffs;
+ this.Agents.Add(source);
+
+ return this;
}
///
/// Adds a handoff relationship from a source agent to a target agent with a custom description.
///
- /// The orchestration handoffs collection to update.
/// The source agent.
/// The target agent.
/// The handoff description.
/// The updated instance.
- public static OrchestrationHandoffs Add(this OrchestrationHandoffs handoffs, Agent source, Agent target, string description)
- => handoffs.Add(source.Name ?? source.Id, target.Name ?? target.Id, description);
+ public OrchestrationHandoffs Add(Agent source, Agent target, string description)
+ => this.Add(source.Name ?? source.Id, target.Name ?? target.Id, description);
///
/// Adds a handoff relationship from a source agent to a target agent name/ID with a custom description.
///
- /// The orchestration handoffs collection to update.
/// The source agent.
/// The target agent's name or ID.
/// The handoff description.
/// The updated instance.
- public static OrchestrationHandoffs Add(this OrchestrationHandoffs handoffs, Agent source, string targetName, string description)
- => handoffs.Add(source.Name ?? source.Id, targetName, description);
+ public OrchestrationHandoffs Add(Agent source, string targetName, string description)
+ => this.Add(source.Name ?? source.Id, targetName, description);
///
/// Adds a handoff relationship from a source agent name/ID to a target agent name/ID with a custom description.
///
- /// The orchestration handoffs collection to update.
/// The source agent's name or ID.
/// The target agent's name or ID.
/// The handoff description.
/// The updated instance.
- public static OrchestrationHandoffs Add(this OrchestrationHandoffs handoffs, string sourceName, string targetName, string description)
+ public OrchestrationHandoffs Add(string sourceName, string targetName, string description)
{
- AgentHandoffs agentHandoffs = handoffs.GetAgentHandoffs(sourceName);
+ AgentHandoffs agentHandoffs = this.GetAgentHandoffs(sourceName);
agentHandoffs[targetName] = description;
- return handoffs;
+ return this;
}
- private static AgentHandoffs GetAgentHandoffs(this OrchestrationHandoffs handoffs, string key)
+ private AgentHandoffs GetAgentHandoffs(string key)
{
- if (!handoffs.TryGetValue(key, out AgentHandoffs? agentHandoffs))
+ if (!this.TryGetValue(key, out AgentHandoffs? agentHandoffs))
{
- agentHandoffs = [];
- handoffs[key] = agentHandoffs;
+ this[key] = agentHandoffs = [];
}
return agentHandoffs;
}
+
+ internal HashSet Agents { get; } = [];
}
///
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Logging/OrchestrationResultLogMessages.cs b/dotnet/src/Microsoft.Agents.Orchestration/Logging/OrchestrationResultLogMessages.cs
deleted file mode 100644
index 98760093b7..0000000000
--- a/dotnet/src/Microsoft.Agents.Orchestration/Logging/OrchestrationResultLogMessages.cs
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using System.Diagnostics.CodeAnalysis;
-using Microsoft.Extensions.AI.Agents.Runtime;
-using Microsoft.Extensions.Logging;
-
-namespace Microsoft.Agents.Orchestration;
-
-///
-/// Extensions for logging .
-///
-///
-/// This extension uses the to
-/// generate logging code at compile time to achieve optimized code.
-///
-[ExcludeFromCodeCoverage]
-internal static partial class OrchestrationResultLogMessages
-{
- ///
- /// Logs awaiting the orchestration.
- ///
- [LoggerMessage(
- Level = LogLevel.Trace,
- Message = "AWAIT {Orchestration}: {Topic}")]
- public static partial void LogOrchestrationResultAwait(
- this ILogger logger,
- string orchestration,
- TopicId topic);
-
- ///
- /// Logs timeout while awaiting the orchestration.
- ///
- [LoggerMessage(
- Level = LogLevel.Error,
- Message = "TIMEOUT {Orchestration}: {Topic}")]
- public static partial void LogOrchestrationResultTimeout(
- this ILogger logger,
- string orchestration,
- TopicId topic);
-
- ///
- /// Logs cancelled the orchestration.
- ///
- [LoggerMessage(
- Level = LogLevel.Error,
- Message = "CANCELLED {Orchestration}: {Topic}")]
- public static partial void LogOrchestrationResultCancelled(
- this ILogger logger,
- string orchestration,
- TopicId topic);
-
- ///
- /// Logs the awaited the orchestration has completed.
- ///
- [LoggerMessage(
- Level = LogLevel.Trace,
- Message = "COMPLETE {Orchestration}: {Topic}")]
- public static partial void LogOrchestrationResultComplete(
- this ILogger logger,
- string orchestration,
- TopicId topic);
-}
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Marker.cs b/dotnet/src/Microsoft.Agents.Orchestration/Marker.cs
deleted file mode 100644
index 061b173c01..0000000000
--- a/dotnet/src/Microsoft.Agents.Orchestration/Marker.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-#if !NET5_0_OR_GREATER
-using System.ComponentModel;
-
-namespace System.Runtime.CompilerServices;
-
-[EditorBrowsable(EditorBrowsableState.Never)]
-internal static class IsExternalInit { }
-#endif
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Microsoft.Agents.Orchestration.csproj b/dotnet/src/Microsoft.Agents.Orchestration/Microsoft.Agents.Orchestration.csproj
index 3be18dcea3..78878572cc 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/Microsoft.Agents.Orchestration.csproj
+++ b/dotnet/src/Microsoft.Agents.Orchestration/Microsoft.Agents.Orchestration.csproj
@@ -10,6 +10,7 @@
true
+ true
@@ -23,7 +24,6 @@
-
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/OrchestrationActor.cs b/dotnet/src/Microsoft.Agents.Orchestration/OrchestrationActor.cs
index 165a83f766..651cd10016 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/OrchestrationActor.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/OrchestrationActor.cs
@@ -33,11 +33,9 @@ public abstract class OrchestrationActor : RuntimeActor
/// The recipient agent's type.
/// A token used to cancel the operation if needed.
/// The agent identifier, if it exists.
- protected async ValueTask PublishMessageAsync(
+ protected ValueTask PublishMessageAsync(
object message,
ActorType agentType,
- CancellationToken cancellationToken = default)
- {
- await base.PublishMessageAsync(message, new TopicId(agentType.Name), messageId: null, cancellationToken).ConfigureAwait(false);
- }
+ CancellationToken cancellationToken = default) =>
+ base.PublishMessageAsync(message, new TopicId(agentType.Name), messageId: null, cancellationToken);
}
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/OrchestrationContext.cs b/dotnet/src/Microsoft.Agents.Orchestration/OrchestrationContext.cs
index 3f6a439fd8..84e4e318a5 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/OrchestrationContext.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/OrchestrationContext.cs
@@ -1,6 +1,11 @@
// Copyright (c) Microsoft. All rights reserved.
+using System;
+using System.Collections.Generic;
using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.AI;
+using Microsoft.Extensions.AI.Agents;
using Microsoft.Extensions.AI.Agents.Runtime;
using Microsoft.Extensions.Logging;
@@ -14,17 +19,17 @@ public sealed class OrchestrationContext
internal OrchestrationContext(
string orchestration,
TopicId topic,
- OrchestrationResponseCallback? responseCallback,
- OrchestrationStreamingCallback? streamingCallback,
+ Func, ValueTask>? responseCallback,
+ Func? streamingCallback,
ILoggerFactory loggerFactory,
- CancellationToken cancellation)
+ CancellationToken cancellationToken)
{
this.Orchestration = orchestration;
this.Topic = topic;
this.ResponseCallback = responseCallback;
this.StreamingResponseCallback = streamingCallback;
this.LoggerFactory = loggerFactory;
- this.Cancellation = cancellation;
+ this.CancellationToken = cancellationToken;
}
///
@@ -43,7 +48,7 @@ public sealed class OrchestrationContext
///
/// Gets the cancellation token that can be used to observe cancellation requests for the orchestration.
///
- public CancellationToken Cancellation { get; }
+ public CancellationToken CancellationToken { get; }
///
/// Gets the associated logger factory for creating loggers within the orchestration context.
@@ -53,10 +58,10 @@ public sealed class OrchestrationContext
///
/// Optional callback that is invoked for every agent response.
///
- public OrchestrationResponseCallback? ResponseCallback { get; }
+ public Func, ValueTask>? ResponseCallback { get; }
///
/// Optional callback that is invoked for every agent response.
///
- public OrchestrationStreamingCallback? StreamingResponseCallback { get; }
+ public Func? StreamingResponseCallback { get; }
}
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/OrchestrationResult.cs b/dotnet/src/Microsoft.Agents.Orchestration/OrchestrationResult.cs
index c5de6b2e8d..c898a0c9c2 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/OrchestrationResult.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/OrchestrationResult.cs
@@ -1,6 +1,8 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.AI.Agents.Runtime;
@@ -13,31 +15,39 @@ namespace Microsoft.Agents.Orchestration;
/// This class encapsulates the asynchronous completion of an orchestration process.
///
/// The type of the value produced by the orchestration.
-public sealed class OrchestrationResult : IDisposable
+public sealed partial class OrchestrationResult : IAsyncDisposable
{
private readonly OrchestrationContext _context;
private readonly CancellationTokenSource _cancelSource;
private readonly TaskCompletionSource _completion;
private readonly ILogger _logger;
+ private readonly IAsyncDisposable? _additionalDisposable;
private bool _isDisposed;
- internal OrchestrationResult(OrchestrationContext context, TaskCompletionSource completion, CancellationTokenSource orchestrationCancelSource, ILogger logger)
+ internal OrchestrationResult(OrchestrationContext context, TaskCompletionSource completion, CancellationTokenSource orchestrationCancelSource, ILogger logger, IAsyncDisposable? additionalDisposable = null)
{
this._cancelSource = orchestrationCancelSource;
this._context = context;
this._completion = completion;
this._logger = logger;
+ this._additionalDisposable = additionalDisposable;
}
///
/// Releases all resources used by the instance.
///
- public void Dispose()
+ public async ValueTask DisposeAsync()
{
if (!this._isDisposed)
{
- this._cancelSource.Dispose();
this._isDisposed = true;
+
+ this._cancelSource.Dispose();
+
+ if (this._additionalDisposable is { } ad)
+ {
+ await ad.DisposeAsync().ConfigureAwait(false);
+ }
}
}
@@ -52,42 +62,9 @@ public sealed class OrchestrationResult : IDisposable
public TopicId Topic => this._context.Topic;
///
- /// Asynchronously retrieves the orchestration result value.
- /// If a timeout is specified, the method will throw a
- /// if the orchestration does not complete within the allotted time.
+ /// Gets a task that represents the completion of the orchestration result.
///
- /// An optional representing the maximum wait duration.
- /// A cancellation token that can be used to cancel the operation.
- /// A representing the result of the orchestration.
- /// Thrown if this instance has been disposed.
- /// Thrown if the orchestration does not complete within the specified timeout period.
- public async ValueTask GetValueAsync(TimeSpan? timeout = null, CancellationToken cancellationToken = default)
- {
-#if NET
- ObjectDisposedException.ThrowIf(this._isDisposed, this);
-#else
- if (this._isDisposed)
- {
- throw new ObjectDisposedException(this.GetType().Name);
- }
-#endif
-
- this._logger.LogOrchestrationResultAwait(this.Orchestration, this.Topic);
-
- if (timeout.HasValue)
- {
- Task[] tasks = [this._completion.Task];
- if (!Task.WaitAll(tasks, timeout.Value))
- {
- this._logger.LogOrchestrationResultTimeout(this.Orchestration, this.Topic);
- throw new TimeoutException($"Orchestration did not complete within the allowed duration ({timeout}).");
- }
- }
-
- this._logger.LogOrchestrationResultComplete(this.Orchestration, this.Topic);
-
- return await this._completion.Task.ConfigureAwait(false);
- }
+ public Task Task => this._completion.Task;
///
/// Cancel the orchestration associated with this result.
@@ -108,8 +85,21 @@ public sealed class OrchestrationResult : IDisposable
}
#endif
- this._logger.LogOrchestrationResultCancelled(this.Orchestration, this.Topic);
+ this.LogOrchestrationResultCanceled(this.Orchestration, this.Topic);
this._cancelSource.Cancel();
- this._completion.SetCanceled();
}
+
+ /// Enable directly awaiting an by using 's awaiter.
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public TaskAwaiter GetAwaiter() => this.Task.GetAwaiter();
+
+ ///
+ /// Logs canceled the orchestration.
+ ///
+ [LoggerMessage(
+ Level = LogLevel.Error,
+ Message = "CANCELED {Orchestration}: {Topic}")]
+ private partial void LogOrchestrationResultCanceled(
+ string orchestration,
+ TopicId topic);
}
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Sequential/SequentialActor.cs b/dotnet/src/Microsoft.Agents.Orchestration/Sequential/SequentialActor.cs
index 6be2d40fdc..9d5be2c91b 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/Sequential/SequentialActor.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/Sequential/SequentialActor.cs
@@ -8,7 +8,7 @@ using Microsoft.Extensions.AI.Agents;
using Microsoft.Extensions.AI.Agents.Runtime;
using Microsoft.Extensions.Logging;
-namespace Microsoft.Agents.Orchestration.Sequential;
+namespace Microsoft.Agents.Orchestration;
///
/// An actor used with the .
@@ -48,10 +48,10 @@ internal sealed class SequentialActor : AgentActor
this.Logger.LogSequentialAgentInvoke(this.Id);
- ChatMessage response = await this.InvokeAsync(input, cancellationToken).ConfigureAwait(false);
+ ChatMessage response = await this.RunAsync(input, cancellationToken).ConfigureAwait(false);
this.Logger.LogSequentialAgentResult(this.Id, response.Text);
- await this.PublishMessageAsync(response.AsResponseMessage(), this._nextAgent, cancellationToken: cancellationToken).ConfigureAwait(false);
+ await this.PublishMessageAsync(new SequentialMessages.Response(response), this._nextAgent, cancellationToken: cancellationToken).ConfigureAwait(false);
}
}
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Sequential/SequentialMessages.cs b/dotnet/src/Microsoft.Agents.Orchestration/Sequential/SequentialMessages.cs
index ec9fae3a0c..632e511222 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/Sequential/SequentialMessages.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/Sequential/SequentialMessages.cs
@@ -3,58 +3,20 @@
using System.Collections.Generic;
using Microsoft.Extensions.AI;
-namespace Microsoft.Agents.Orchestration.Sequential;
+namespace Microsoft.Agents.Orchestration;
///
/// A message that describes the input task and captures results for a .
///
internal static class SequentialMessages
{
- ///
- /// An empty message instance as a default.
- ///
- public static readonly ChatMessage Empty = new();
-
///
/// Represents a request containing a sequence of chat messages to be processed by the sequential orchestration.
///
- public sealed class Request
- {
- ///
- /// The request input.
- ///
- public IList Messages { get; init; } = [];
- }
+ public sealed record Request(IList Messages);
///
/// Represents a response containing the result message from the sequential orchestration.
///
- public sealed class Response
- {
- ///
- /// The response message.
- ///
- public ChatMessage Message { get; init; } = Empty;
- }
-
- ///
- /// Extension method to convert a to a .
- ///
- /// The chat message to include in the request.
- /// A containing the provided messages.
- public static Request AsRequestMessage(this ChatMessage message) => new() { Messages = [message] };
-
- ///
- /// Extension method to convert a collection of to a .
- ///
- /// The collection of chat messages to include in the request.
- /// A containing the provided messages.
- public static Request AsRequestMessage(this IEnumerable messages) => new() { Messages = [.. messages] };
-
- ///
- /// Extension method to convert a to a .
- ///
- /// The chat message to include in the response.
- /// A containing the provided message.
- public static Response AsResponseMessage(this ChatMessage message) => new() { Message = message };
+ public sealed record Response(ChatMessage Message);
}
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Sequential/SequentialOrchestration.String.cs b/dotnet/src/Microsoft.Agents.Orchestration/Sequential/SequentialOrchestration.String.cs
index a44a2c372f..53b1077d4e 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/Sequential/SequentialOrchestration.String.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/Sequential/SequentialOrchestration.String.cs
@@ -2,7 +2,7 @@
using Microsoft.Extensions.AI.Agents;
-namespace Microsoft.Agents.Orchestration.Sequential;
+namespace Microsoft.Agents.Orchestration;
///
/// An orchestration that passes the input message to the first agent, and
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Sequential/SequentialOrchestration.cs b/dotnet/src/Microsoft.Agents.Orchestration/Sequential/SequentialOrchestration.cs
index c8dba0116e..26ec06f814 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/Sequential/SequentialOrchestration.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/Sequential/SequentialOrchestration.cs
@@ -3,13 +3,12 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
-using Microsoft.Agents.Orchestration.Extensions;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.AI.Agents;
using Microsoft.Extensions.AI.Agents.Runtime;
using Microsoft.Extensions.Logging;
-namespace Microsoft.Agents.Orchestration.Sequential;
+namespace Microsoft.Agents.Orchestration;
///
/// An orchestration that provides the input message to the first agent
@@ -33,7 +32,7 @@ public class SequentialOrchestration : AgentOrchestration
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Logging/SequentialOrchestrationLogMessages.cs b/dotnet/src/Microsoft.Agents.Orchestration/Sequential/SequentialOrchestrationLogMessages.cs
similarity index 95%
rename from dotnet/src/Microsoft.Agents.Orchestration/Logging/SequentialOrchestrationLogMessages.cs
rename to dotnet/src/Microsoft.Agents.Orchestration/Sequential/SequentialOrchestrationLogMessages.cs
index 93d28eef8a..6d647a7519 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/Logging/SequentialOrchestrationLogMessages.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/Sequential/SequentialOrchestrationLogMessages.cs
@@ -1,7 +1,6 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Diagnostics.CodeAnalysis;
-using Microsoft.Agents.Orchestration.Sequential;
using Microsoft.Extensions.AI.Agents.Runtime;
using Microsoft.Extensions.Logging;
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Transforms/DefaultTransforms.cs b/dotnet/src/Microsoft.Agents.Orchestration/Transforms/DefaultTransforms.cs
deleted file mode 100644
index b9e082f4bf..0000000000
--- a/dotnet/src/Microsoft.Agents.Orchestration/Transforms/DefaultTransforms.cs
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using System;
-using System.Collections.Generic;
-using System.Text.Json;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.Extensions.AI;
-
-namespace Microsoft.Agents.Orchestration.Transforms;
-
-internal static class DefaultTransforms
-{
- public static ValueTask> FromInput(TInput input, CancellationToken cancellationToken = default)
- {
- return new ValueTask>(TransformInput());
-
- IEnumerable TransformInput() =>
- input switch
- {
- IEnumerable messages => messages,
- ChatMessage message => [message],
- string text => [new ChatMessage(ChatRole.User, text)],
- _ => [new ChatMessage(ChatRole.User, JsonSerializer.Serialize(input))]
- };
- }
-
- public static ValueTask ToOutput(IList result, CancellationToken cancellationToken = default)
- {
- bool isSingleResult = result.Count == 1;
-
- TOutput output =
- GetDefaultOutput() ??
- GetObjectOutput() ??
- throw new InvalidOperationException($"Unable to transform output to {typeof(TOutput)}.");
-
- return new ValueTask(output);
-
- TOutput? GetObjectOutput()
- {
- if (!isSingleResult)
- {
- return default;
- }
-
- try
- {
- return JsonSerializer.Deserialize(result[0].Text);
- }
- catch (JsonException)
- {
- return default;
- }
- }
-
- TOutput? GetDefaultOutput()
- {
- object? output = null;
- if (typeof(TOutput).IsAssignableFrom(result.GetType()))
- {
- output = (object)result;
- }
- else if (isSingleResult && typeof(ChatMessage).IsAssignableFrom(typeof(TOutput)))
- {
- output = (object)result[0];
- }
- else if (isSingleResult && typeof(string) == typeof(TOutput))
- {
- output = result[0].Text ?? string.Empty;
- }
-
- return (TOutput?)output;
- }
- }
-}
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Transforms/OrchestrationTransforms.cs b/dotnet/src/Microsoft.Agents.Orchestration/Transforms/OrchestrationTransforms.cs
deleted file mode 100644
index 6a1dfe9f45..0000000000
--- a/dotnet/src/Microsoft.Agents.Orchestration/Transforms/OrchestrationTransforms.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.Extensions.AI;
-
-namespace Microsoft.Agents.Orchestration.Transforms;
-
-///
-/// Delegate for transforming an input of type into a collection of .
-/// This is typically used to convert user or system input into a format suitable for chat orchestration.
-///
-/// The input object to transform.
-/// A cancellation token that can be used to cancel the operation.
-/// A containing an enumerable of representing the transformed input.
-public delegate ValueTask> OrchestrationInputTransform(TInput input, CancellationToken cancellationToken = default);
-
-///
-/// Delegate for transforming a into an output of type .
-/// This is typically used to convert a chat response into a desired output format.
-///
-/// The result messages to transform.
-/// A cancellation token that can be used to cancel the operation.
-/// A containing the transformed output of type .
-public delegate ValueTask OrchestrationOutputTransform(IList result, CancellationToken cancellationToken = default);
-
-///
-/// Delegate for transforming the internal result message for an orchestration into a .
-///
-/// The result message type
-/// The result messages
-/// The orchestration result as a .
-public delegate IList OrchestrationResultTransform(TResult result);
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Transforms/StructuredOutputTransform.cs b/dotnet/src/Microsoft.Agents.Orchestration/Transforms/StructuredOutputTransform.cs
index cda2788285..52c7fb08a0 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/Transforms/StructuredOutputTransform.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/Transforms/StructuredOutputTransform.cs
@@ -7,7 +7,7 @@ using System.Threading.Tasks;
using Microsoft.Extensions.AI;
using Microsoft.Shared.Diagnostics;
-namespace Microsoft.Agents.Orchestration.Transforms;
+namespace Microsoft.Agents.Orchestration;
///
/// Populates the target result type into a structured output.
diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/ActorMetadata.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/ActorMetadata.cs
index 317907deaa..04ba72acb7 100644
--- a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/ActorMetadata.cs
+++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/ActorMetadata.cs
@@ -22,11 +22,6 @@ public readonly struct ActorMetadata : IEquatable
throw new ArgumentException("Invalid actor key.", nameof(key));
}
- if (description is null)
- {
- throw new ArgumentNullException(nameof(description));
- }
-
this.Type = type;
this.Key = key;
this.Description = description;
diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/AgentRuntimeExtensions.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/AgentRuntimeExtensions.cs
new file mode 100644
index 0000000000..a517da7881
--- /dev/null
+++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/AgentRuntimeExtensions.cs
@@ -0,0 +1,71 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Shared.Diagnostics;
+
+namespace Microsoft.Extensions.AI.Agents.Runtime;
+
+///
+/// Provides extension methods for the agent runtime.
+///
+public static class AgentRuntimeExtensions
+{
+ ///
+ /// Retrieves an actor by its type.
+ ///
+ /// The agent runtime.
+ /// The type of the actor.
+ /// An optional key to specify variations of the actor. Defaults to "default".
+ /// If true, the actor is fetched lazily.
+ /// A token to cancel the operation if needed.
+ /// A task representing the asynchronous operation, returning the actor's ID.
+ public static ValueTask GetActorAsync(this IAgentRuntime agentRuntime, ActorType actorType, string? key = null, bool lazy = true, CancellationToken cancellationToken = default)
+ {
+ Throw.IfNull(agentRuntime);
+
+ return agentRuntime.GetActorAsync(actorType.Name, key, lazy, cancellationToken);
+ }
+
+ ///
+ /// Retrieves an actor by its string representation.
+ ///
+ /// The agent runtime.
+ /// The string representation of the actor.
+ /// An optional key to specify variations of the actor. Defaults to "default".
+ /// If true, the actor is fetched lazily.
+ /// A token to cancel the operation if needed.
+ /// A task representing the asynchronous operation, returning the actor's ID.
+ public static ValueTask GetActorAsync(this IAgentRuntime agentRuntime, string actor, string? key = null, bool lazy = true, CancellationToken cancellationToken = default)
+ {
+ Throw.IfNull(agentRuntime);
+
+ return agentRuntime.GetActorAsync(new ActorId(actor, key ?? "default"), lazy, cancellationToken);
+ }
+
+ ///
+ /// Registers an actor factory with the runtime, associating it with a specific actor type.
+ ///
+ /// The type of actor created by the factory.
+ /// The agent runtime.
+ /// The actor type to associate with the factory.
+ /// A function that asynchronously creates the actor instance.
+ /// A token to cancel the operation if needed.
+ /// A task representing the asynchronous operation, returning the registered actor type.
+ public static ValueTask RegisterActorFactoryAsync(
+ this IAgentRuntime agentRuntime,
+ ActorType type,
+ Func> factoryFunc,
+ CancellationToken cancellationToken = default)
+ where TActor : IRuntimeActor
+ {
+ Throw.IfNull(agentRuntime);
+ Throw.IfNull(factoryFunc);
+
+ return agentRuntime.RegisterActorFactoryAsync(
+ type,
+ async ValueTask (actorId, runtime) => await factoryFunc(actorId, runtime).ConfigureAwait(false),
+ cancellationToken);
+ }
+}
diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/IAgentRuntime.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/IAgentRuntime.cs
index 4dcafd8b40..feb9261c37 100644
--- a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/IAgentRuntime.cs
+++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/IAgentRuntime.cs
@@ -47,26 +47,6 @@ public interface IAgentRuntime : ISaveState
/// A task representing the asynchronous operation, returning the actor's ID.
ValueTask GetActorAsync(ActorId actorId, bool lazy = true, CancellationToken cancellationToken = default);
- ///
- /// Retrieves an actor by its type.
- ///
- /// The type of the actor.
- /// An optional key to specify variations of the actor. Defaults to "default".
- /// If true, the actor is fetched lazily.
- /// A token to cancel the operation if needed.
- /// A task representing the asynchronous operation, returning the actor's ID.
- ValueTask GetActorAsync(ActorType actorType, string key = "default", bool lazy = true, CancellationToken cancellationToken = default);
-
- ///
- /// Retrieves an actor by its string representation.
- ///
- /// The string representation of the actor.
- /// An optional key to specify variations of the actor. Defaults to "default".
- /// If true, the actor is fetched lazily.
- /// A token to cancel the operation if needed.
- /// A task representing the asynchronous operation, returning the actor's ID.
- ValueTask GetActorAsync(string actor, string key = "default", bool lazy = true, CancellationToken cancellationToken = default);
-
///
/// Saves the state of an actor.
/// The result must be JSON serializable.
diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/IRuntimeActor.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/IRuntimeActor.cs
index d94871f644..6bba64fa4b 100644
--- a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/IRuntimeActor.cs
+++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/IRuntimeActor.cs
@@ -32,6 +32,6 @@ public interface IRuntimeActor : ISaveState
/// A task representing the asynchronous operation, returning a response to the message.
/// The response can be null if no reply is necessary.
///
- /// Thrown if the message was cancelled.
+ /// Thrown if the message was canceled.
ValueTask