diff --git a/dotnet/src/LegacySupport/TrimAttributes/DynamicallyAccessedMemberTypes.cs b/dotnet/src/LegacySupport/TrimAttributes/DynamicallyAccessedMemberTypes.cs
index 15f7505691..c8663103a5 100644
--- a/dotnet/src/LegacySupport/TrimAttributes/DynamicallyAccessedMemberTypes.cs
+++ b/dotnet/src/LegacySupport/TrimAttributes/DynamicallyAccessedMemberTypes.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Microsoft. All rights reserved.
+// Copyright (c) Microsoft. All rights reserved.
namespace System.Diagnostics.CodeAnalysis;
diff --git a/dotnet/src/LegacySupport/TrimAttributes/DynamicallyAccessedMembersAttribute.cs b/dotnet/src/LegacySupport/TrimAttributes/DynamicallyAccessedMembersAttribute.cs
index 0698067c11..fc8eae8910 100644
--- a/dotnet/src/LegacySupport/TrimAttributes/DynamicallyAccessedMembersAttribute.cs
+++ b/dotnet/src/LegacySupport/TrimAttributes/DynamicallyAccessedMembersAttribute.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Microsoft. All rights reserved.
+// Copyright (c) Microsoft. All rights reserved.
namespace System.Diagnostics.CodeAnalysis;
@@ -39,7 +39,7 @@ internal sealed class DynamicallyAccessedMembersAttribute : Attribute
/// The types of members dynamically accessed.
public DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes memberTypes)
{
- MemberTypes = memberTypes;
+ this.MemberTypes = memberTypes;
}
///
diff --git a/dotnet/src/LegacySupport/TrimAttributes/RequiresDynamicCodeAttribute.cs b/dotnet/src/LegacySupport/TrimAttributes/RequiresDynamicCodeAttribute.cs
index b593fc1761..d609038d56 100644
--- a/dotnet/src/LegacySupport/TrimAttributes/RequiresDynamicCodeAttribute.cs
+++ b/dotnet/src/LegacySupport/TrimAttributes/RequiresDynamicCodeAttribute.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Microsoft. All rights reserved.
+// Copyright (c) Microsoft. All rights reserved.
namespace System.Diagnostics.CodeAnalysis;
@@ -22,7 +22,7 @@ internal sealed class RequiresDynamicCodeAttribute : Attribute
///
public RequiresDynamicCodeAttribute(string message)
{
- Message = message;
+ this.Message = message;
}
///
diff --git a/dotnet/src/LegacySupport/TrimAttributes/RequiresUnreferencedCodeAttribute.cs b/dotnet/src/LegacySupport/TrimAttributes/RequiresUnreferencedCodeAttribute.cs
index 8b116ff2bc..3464586054 100644
--- a/dotnet/src/LegacySupport/TrimAttributes/RequiresUnreferencedCodeAttribute.cs
+++ b/dotnet/src/LegacySupport/TrimAttributes/RequiresUnreferencedCodeAttribute.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Microsoft. All rights reserved.
+// Copyright (c) Microsoft. All rights reserved.
namespace System.Diagnostics.CodeAnalysis;
@@ -23,7 +23,7 @@ internal sealed class RequiresUnreferencedCodeAttribute : Attribute
///
public RequiresUnreferencedCodeAttribute(string message)
{
- Message = message;
+ this.Message = message;
}
///
diff --git a/dotnet/src/LegacySupport/TrimAttributes/UnconditionalSuppressMessageAttribute.cs b/dotnet/src/LegacySupport/TrimAttributes/UnconditionalSuppressMessageAttribute.cs
index 2896a7497f..4e1b380fc2 100644
--- a/dotnet/src/LegacySupport/TrimAttributes/UnconditionalSuppressMessageAttribute.cs
+++ b/dotnet/src/LegacySupport/TrimAttributes/UnconditionalSuppressMessageAttribute.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Microsoft. All rights reserved.
+// Copyright (c) Microsoft. All rights reserved.
namespace System.Diagnostics.CodeAnalysis;
@@ -23,8 +23,8 @@ internal sealed class UnconditionalSuppressMessageAttribute : Attribute
/// The identifier of the analysis rule the attribute applies to.
public UnconditionalSuppressMessageAttribute(string category, string checkId)
{
- Category = category;
- CheckId = checkId;
+ this.Category = category;
+ this.CheckId = checkId;
}
///
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/AgentActor.cs b/dotnet/src/Microsoft.Agents.Orchestration/AgentActor.cs
index df8903840e..fb83e266d3 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/AgentActor.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/AgentActor.cs
@@ -25,7 +25,7 @@ public abstract class AgentActor : OrchestrationActor
/// The orchestration context.
/// An .
/// The logger to use for the actor
- protected AgentActor(AgentId id, IAgentRuntime runtime, OrchestrationContext context, Agent agent, ILogger? logger = null)
+ protected AgentActor(ActorId id, IAgentRuntime runtime, OrchestrationContext context, Agent agent, ILogger? logger = null)
: base(
id,
runtime,
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/AgentOrchestration.RequestActor.cs b/dotnet/src/Microsoft.Agents.Orchestration/AgentOrchestration.RequestActor.cs
index 651ea3959b..70cbb60d90 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/AgentOrchestration.RequestActor.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/AgentOrchestration.RequestActor.cs
@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
+using System.Threading;
using System.Threading.Tasks;
using Microsoft.Agents.Orchestration.Transforms;
using Microsoft.Extensions.AI;
@@ -15,7 +16,7 @@ public abstract partial class AgentOrchestration
///
/// Actor responsible for receiving final message and transforming it into the output type.
///
- private sealed class RequestActor : OrchestrationActor, IHandle
+ private sealed class RequestActor : OrchestrationActor
{
private readonly OrchestrationInputTransform _transform;
private readonly Func, ValueTask> _action;
@@ -32,7 +33,7 @@ public abstract partial class AgentOrchestration
/// An asynchronous function that processes the resulting source.
/// The logger to use for the actor
public RequestActor(
- AgentId id,
+ ActorId id,
IAgentRuntime runtime,
OrchestrationContext context,
OrchestrationInputTransform transform,
@@ -44,6 +45,8 @@ public abstract partial class AgentOrchestration
this._transform = transform;
this._action = action;
this._completionSource = completionSource;
+
+ this.RegisterMessageHandler(this.HandleAsync);
}
///
@@ -51,13 +54,14 @@ public abstract partial class AgentOrchestration
///
/// The input message of type TInput.
/// The context of the message, providing additional details.
+ /// A token to cancel the operation if needed.
/// A ValueTask representing the asynchronous operation.
- public async ValueTask HandleAsync(TInput item, MessageContext messageContext)
+ private async ValueTask HandleAsync(TInput item, MessageContext messageContext, CancellationToken cancellationToken)
{
this.Logger.LogOrchestrationRequestInvoke(this.Context.Orchestration, this.Id);
try
{
- IEnumerable input = await this._transform.Invoke(item).ConfigureAwait(false);
+ IEnumerable input = await this._transform.Invoke(item, cancellationToken).ConfigureAwait(false);
Task task = this._action.Invoke(input).AsTask();
this.Logger.LogOrchestrationStart(this.Context.Orchestration, this.Id);
await task.ConfigureAwait(false);
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/AgentOrchestration.ResultActor.cs b/dotnet/src/Microsoft.Agents.Orchestration/AgentOrchestration.ResultActor.cs
index edc2e79950..c92d35f6bd 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/AgentOrchestration.ResultActor.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/AgentOrchestration.ResultActor.cs
@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
+using System.Threading;
using System.Threading.Tasks;
using Microsoft.Agents.Orchestration.Transforms;
using Microsoft.Extensions.AI;
@@ -15,7 +16,7 @@ public abstract partial class AgentOrchestration
///
/// Actor responsible for receiving the resultant message, transforming it, and handling further orchestration.
///
- private sealed class ResultActor : OrchestrationActor, IHandle
+ private sealed class ResultActor : OrchestrationActor
{
private readonly TaskCompletionSource _completionSource;
private readonly OrchestrationResultTransform _transformResult;
@@ -32,7 +33,7 @@ public abstract partial class AgentOrchestration
/// Optional TaskCompletionSource to signal orchestration completion.
/// The logger to use for the actor
public ResultActor(
- AgentId id,
+ ActorId id,
IAgentRuntime runtime,
OrchestrationContext context,
OrchestrationResultTransform transformResult,
@@ -44,6 +45,8 @@ public abstract partial class AgentOrchestration
this._completionSource = completionSource;
this._transformResult = transformResult;
this._transform = transformOutput;
+
+ this.RegisterMessageHandler(this.HandleAsync);
}
///
@@ -53,8 +56,9 @@ public abstract partial class AgentOrchestration
///
/// The result item to process.
/// The context associated with the message.
+ /// A token to cancel the operation if needed.
/// A ValueTask representing asynchronous operation.
- public async ValueTask HandleAsync(TResult item, MessageContext messageContext)
+ private async ValueTask HandleAsync(TResult item, MessageContext messageContext, CancellationToken cancellationToken)
{
this.Logger.LogOrchestrationResultInvoke(this.Context.Orchestration, this.Id);
@@ -63,7 +67,7 @@ public abstract partial class AgentOrchestration
if (!this._completionSource.Task.IsCompleted)
{
IList result = this._transformResult.Invoke(item);
- TOutput output = await this._transform.Invoke(result).ConfigureAwait(false);
+ TOutput output = await this._transform.Invoke(result, cancellationToken).ConfigureAwait(false);
this._completionSource.TrySetResult(output);
}
}
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/AgentOrchestration.cs b/dotnet/src/Microsoft.Agents.Orchestration/AgentOrchestration.cs
index 8df9416205..c95961469c 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/AgentOrchestration.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/AgentOrchestration.cs
@@ -129,7 +129,7 @@ public abstract partial class AgentOrchestration
TaskCompletionSource completion = new();
- AgentType orchestrationType = await this.RegisterAsync(runtime, context, completion, handoff: null).ConfigureAwait(false);
+ ActorType orchestrationType = await this.RegisterAsync(runtime, context, completion, handoff: null).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
@@ -149,7 +149,7 @@ public abstract partial class AgentOrchestration
/// The unique identifier for the orchestration session.
/// The input to be transformed and processed.
/// The initial agent type used for starting the orchestration.
- protected abstract ValueTask StartAsync(IAgentRuntime runtime, TopicId topic, IEnumerable input, AgentType? entryAgent);
+ protected abstract ValueTask StartAsync(IAgentRuntime runtime, TopicId topic, IEnumerable input, ActorType? entryAgent);
///
/// Orchestration specific registration, including members and returns an optional entry agent.
@@ -159,7 +159,7 @@ public abstract partial class AgentOrchestration
/// A registration context.
/// The logger to use during registration
/// The entry AgentType for the orchestration, if any.
- protected abstract ValueTask RegisterOrchestrationAsync(IAgentRuntime runtime, OrchestrationContext context, RegistrationContext registrar, ILogger logger);
+ protected abstract ValueTask RegisterOrchestrationAsync(IAgentRuntime runtime, OrchestrationContext context, RegistrationContext registrar, ILogger logger);
///
/// Formats and returns a unique AgentType based on the provided topic and suffix.
@@ -167,7 +167,7 @@ public abstract partial class AgentOrchestration
/// The topic identifier used in formatting the agent type.
/// A suffix to differentiate the agent type.
/// A formatted AgentType object.
- protected AgentType FormatAgentType(TopicId topic, string suffix) => new($"{topic.Type}_{suffix}");
+ protected ActorType FormatAgentType(TopicId topic, string suffix) => new($"{topic.Type}_{suffix}");
///
/// Registers the orchestration's root and boot agents, setting up completion and target routing.
@@ -177,7 +177,7 @@ public abstract partial class AgentOrchestration
/// A TaskCompletionSource for the orchestration.
/// The actor type used for handoff. Only defined for nested orchestrations.
/// The AgentType representing the orchestration entry point.
- private async ValueTask RegisterAsync(IAgentRuntime runtime, OrchestrationContext context, TaskCompletionSource completion, AgentType? handoff)
+ private async ValueTask RegisterAsync(IAgentRuntime runtime, OrchestrationContext context, TaskCompletionSource completion, ActorType? handoff)
{
// Create a logger for the orchestration registration.
ILogger logger = context.LoggerFactory.CreateLogger(this.GetType());
@@ -185,10 +185,10 @@ public abstract partial class AgentOrchestration
// Register orchestration
RegistrationContext registrar = new(this.FormatAgentType(context.Topic, "Root"), runtime, context, completion, this.ResultTransform);
- AgentType? entryAgent = await this.RegisterOrchestrationAsync(runtime, context, registrar, logger).ConfigureAwait(false);
+ ActorType? entryAgent = await this.RegisterOrchestrationAsync(runtime, context, registrar, logger).ConfigureAwait(false);
// Register actor for orchestration entry-point
- AgentType orchestrationEntry =
+ ActorType orchestrationEntry =
await runtime.RegisterOrchestrationAgentAsync(
this.FormatAgentType(context.Topic, "Boot"),
(agentId, runtime) =>
@@ -201,11 +201,7 @@ public abstract partial class AgentOrchestration
completion,
StartAsync,
context.LoggerFactory.CreateLogger());
-#if !NETCOREAPP
- return new ValueTask(actor);
-#else
- return ValueTask.FromResult(actor);
-#endif
+ return new ValueTask(actor);
}).ConfigureAwait(false);
logger.LogOrchestrationRegistrationDone(context.Orchestration, context.Topic);
@@ -219,7 +215,7 @@ public abstract partial class AgentOrchestration
/// A context used during registration ().
///
public sealed class RegistrationContext(
- AgentType agentType,
+ ActorType agentType,
IAgentRuntime runtime,
OrchestrationContext context,
TaskCompletionSource completion,
@@ -228,10 +224,10 @@ public abstract partial class AgentOrchestration
///
/// Register the final result type.
///
- public async ValueTask RegisterResultTypeAsync(OrchestrationResultTransform resultTransform)
+ public async ValueTask RegisterResultTypeAsync(OrchestrationResultTransform resultTransform)
{
// Register actor for final result
- AgentType registeredType =
+ ActorType registeredType =
await runtime.RegisterOrchestrationAgentAsync(
agentType,
(agentId, runtime) =>
@@ -244,11 +240,7 @@ public abstract partial class AgentOrchestration
outputTransform,
completion,
context.LoggerFactory.CreateLogger>());
-#if !NETCOREAPP
- return new ValueTask(actor);
-#else
- return ValueTask.FromResult(actor);
-#endif
+ return new ValueTask(actor);
}).ConfigureAwait(false);
return registeredType;
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Concurrent/ConcurrentActor.cs b/dotnet/src/Microsoft.Agents.Orchestration/Concurrent/ConcurrentActor.cs
index 5a783c6db4..5a1a10e7b1 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/Concurrent/ConcurrentActor.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/Concurrent/ConcurrentActor.cs
@@ -1,5 +1,6 @@
// Copyright (c) Microsoft. All rights reserved.
+using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.AI.Agents;
@@ -11,9 +12,9 @@ namespace Microsoft.Agents.Orchestration.Concurrent;
///
/// An used with the .
///
-internal sealed class ConcurrentActor : AgentActor, IHandle
+internal sealed class ConcurrentActor : AgentActor
{
- private readonly AgentType _handoffActor;
+ private readonly ActorType _handoffActor;
///
/// Initializes a new instance of the class.
@@ -24,21 +25,22 @@ internal sealed class ConcurrentActor : AgentActor, IHandleAn .
/// Identifies the actor collecting results.
/// The logger to use for the actor
- public ConcurrentActor(AgentId id, IAgentRuntime runtime, OrchestrationContext context, Agent agent, AgentType resultActor, ILogger? logger = null)
+ public ConcurrentActor(ActorId id, IAgentRuntime runtime, OrchestrationContext context, Agent agent, ActorType resultActor, ILogger? logger = null)
: base(id, runtime, context, agent, logger)
{
this._handoffActor = resultActor;
+
+ this.RegisterMessageHandler(this.HandleAsync);
}
- ///
- public async ValueTask HandleAsync(ConcurrentMessages.Request item, MessageContext messageContext)
+ private async ValueTask HandleAsync(ConcurrentMessages.Request item, MessageContext messageContext, CancellationToken cancellationToken)
{
this.Logger.LogConcurrentAgentInvoke(this.Id);
- ChatMessage response = await this.InvokeAsync(item.Messages, messageContext.CancellationToken).ConfigureAwait(false);
+ ChatMessage response = await this.InvokeAsync(item.Messages, cancellationToken).ConfigureAwait(false);
this.Logger.LogConcurrentAgentResult(this.Id, response.Text);
- await this.PublishMessageAsync(response.AsResultMessage(), this._handoffActor, messageContext.CancellationToken).ConfigureAwait(false);
+ await this.PublishMessageAsync(response.AsResultMessage(), this._handoffActor, cancellationToken).ConfigureAwait(false);
}
}
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Concurrent/ConcurrentOrchestration.cs b/dotnet/src/Microsoft.Agents.Orchestration/Concurrent/ConcurrentOrchestration.cs
index 7f94189e14..6e9e19d0a1 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/Concurrent/ConcurrentOrchestration.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/Concurrent/ConcurrentOrchestration.cs
@@ -30,18 +30,18 @@ public class ConcurrentOrchestration
}
///
- protected override ValueTask StartAsync(IAgentRuntime runtime, TopicId topic, IEnumerable input, AgentType? entryAgent)
+ protected override ValueTask StartAsync(IAgentRuntime runtime, TopicId topic, IEnumerable input, ActorType? entryAgent)
{
return runtime.PublishMessageAsync(input.AsInputMessage(), topic);
}
///
- protected override async ValueTask RegisterOrchestrationAsync(IAgentRuntime runtime, OrchestrationContext context, RegistrationContext registrar, ILogger logger)
+ protected override async ValueTask RegisterOrchestrationAsync(IAgentRuntime runtime, OrchestrationContext context, RegistrationContext registrar, ILogger logger)
{
- AgentType outputType = await registrar.RegisterResultTypeAsync(response => [.. response.Select(r => r.Message)]).ConfigureAwait(false);
+ ActorType outputType = await registrar.RegisterResultTypeAsync(response => [.. response.Select(r => r.Message)]).ConfigureAwait(false);
// Register result actor
- AgentType resultType = this.FormatAgentType(context.Topic, "Results");
+ ActorType resultType = this.FormatAgentType(context.Topic, "Results");
await runtime.RegisterOrchestrationAgentAsync(
resultType,
async (agentId, runtime) =>
@@ -57,17 +57,13 @@ public class ConcurrentOrchestration
{
++agentCount;
- AgentType agentType =
- await runtime.RegisterAgentFactoryAsync(
+ ActorType agentType =
+ await runtime.RegisterActorFactoryAsync(
this.FormatAgentType(context.Topic, $"Agent_{agentCount}"),
(agentId, runtime) =>
{
ConcurrentActor actor = new(agentId, runtime, context, agent, resultType, context.LoggerFactory.CreateLogger());
-#if !NETCOREAPP
- return new ValueTask(actor);
-#else
- return ValueTask.FromResult(actor);
-#endif
+ return new ValueTask(actor);
}).ConfigureAwait(false);
logger.LogRegisterActor(this.OrchestrationLabel, agentType, "MEMBER", agentCount);
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Concurrent/ConcurrentResultActor.cs b/dotnet/src/Microsoft.Agents.Orchestration/Concurrent/ConcurrentResultActor.cs
index c6b4dbd14d..db5bbcb573 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/Concurrent/ConcurrentResultActor.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/Concurrent/ConcurrentResultActor.cs
@@ -11,12 +11,10 @@ namespace Microsoft.Agents.Orchestration.Concurrent;
///
/// Actor for capturing each message.
///
-internal sealed class ConcurrentResultActor :
- OrchestrationActor,
- IHandle
+internal sealed class ConcurrentResultActor : OrchestrationActor
{
private readonly ConcurrentQueue _results;
- private readonly AgentType _orchestrationType;
+ private readonly ActorType _orchestrationType;
private readonly int _expectedCount;
private int _resultCount;
@@ -30,10 +28,10 @@ internal sealed class ConcurrentResultActor :
/// The expected number of messages to be received.
/// The logger to use for the actor
public ConcurrentResultActor(
- AgentId id,
+ ActorId id,
IAgentRuntime runtime,
OrchestrationContext context,
- AgentType orchestrationType,
+ ActorType orchestrationType,
int expectedCount,
ILogger logger)
: base(id, runtime, context, "Captures the results of the ConcurrentOrchestration", logger)
@@ -41,10 +39,11 @@ internal sealed class ConcurrentResultActor :
this._orchestrationType = orchestrationType;
this._expectedCount = expectedCount;
this._results = [];
+
+ this.RegisterMessageHandler(this.HandleAsync);
}
- ///
- public async ValueTask HandleAsync(ConcurrentMessages.Result item, MessageContext messageContext)
+ private async ValueTask HandleAsync(ConcurrentMessages.Result item, MessageContext messageContext, CancellationToken cancellationToken)
{
this.Logger.LogConcurrentResultCapture(this.Id, this._resultCount + 1, this._expectedCount);
@@ -52,7 +51,7 @@ internal sealed class ConcurrentResultActor :
if (Interlocked.Increment(ref this._resultCount) == this._expectedCount)
{
- await this.PublishMessageAsync(this._results.ToArray(), this._orchestrationType, messageContext.CancellationToken).ConfigureAwait(false);
+ await this.PublishMessageAsync(this._results.ToArray(), this._orchestrationType, cancellationToken).ConfigureAwait(false);
}
}
}
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Extensions/RuntimeExtensions.cs b/dotnet/src/Microsoft.Agents.Orchestration/Extensions/RuntimeExtensions.cs
index eadf788292..94400a6746 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/Extensions/RuntimeExtensions.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/Extensions/RuntimeExtensions.cs
@@ -15,9 +15,9 @@ public static class RuntimeExtensions
///
/// Sends a message to the specified agent.
///
- public static async ValueTask PublishMessageAsync(this IAgentRuntime runtime, object message, AgentType agentType, CancellationToken cancellationToken = default)
+ public static async ValueTask PublishMessageAsync(this IAgentRuntime runtime, object message, ActorType agentType, CancellationToken cancellationToken = default)
{
- await runtime.PublishMessageAsync(message, new TopicId(agentType), sender: null, messageId: null, cancellationToken).ConfigureAwait(false);
+ await runtime.PublishMessageAsync(message, new TopicId(agentType.Name), sender: null, messageId: null, cancellationToken).ConfigureAwait(false);
}
///
@@ -27,12 +27,12 @@ public static class RuntimeExtensions
/// The type of agent to register.
/// The factory function for creating the agent.
/// The registered agent type.
- public static async ValueTask RegisterOrchestrationAgentAsync(this IAgentRuntime runtime, AgentType agentType, Func> factoryFunc)
+ public static async ValueTask RegisterOrchestrationAgentAsync(this IAgentRuntime runtime, ActorType agentType, Func> factoryFunc)
{
- AgentType registeredType = await runtime.RegisterAgentFactoryAsync(agentType, factoryFunc).ConfigureAwait(false);
+ ActorType registeredType = await runtime.RegisterActorFactoryAsync(agentType, factoryFunc).ConfigureAwait(false);
// Subscribe agent to its own unique topic
- await runtime.SubscribeAsync(registeredType).ConfigureAwait(false);
+ await runtime.SubscribeAsync(new(registeredType.Name)).ConfigureAwait(false);
return registeredType;
}
@@ -42,9 +42,9 @@ public static class RuntimeExtensions
///
/// The runtime for managing the subscription.
/// The agent type to subscribe.
- public static async Task SubscribeAsync(this IAgentRuntime runtime, string agentType)
+ public static async Task SubscribeAsync(this IAgentRuntime runtime, ActorType agentType)
{
- await runtime.AddSubscriptionAsync(new TypeSubscription(agentType, agentType)).ConfigureAwait(false);
+ await runtime.AddSubscriptionAsync(new TypeSubscription(agentType.Name, agentType)).ConfigureAwait(false);
}
///
@@ -53,7 +53,7 @@ public static class RuntimeExtensions
/// The runtime for managing the subscription.
/// The agent type to subscribe.
/// A variable list of topics for subscription.
- public static async Task SubscribeAsync(this IAgentRuntime runtime, string agentType, params TopicId[] topics)
+ public static async Task SubscribeAsync(this IAgentRuntime runtime, ActorType agentType, params TopicId[] topics)
{
for (int index = 0; index < topics.Length; ++index)
{
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatAgentActor.cs b/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatAgentActor.cs
index a668430182..c71d8dc398 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatAgentActor.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatAgentActor.cs
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Collections.Generic;
+using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.AI.Agents;
@@ -12,11 +13,7 @@ namespace Microsoft.Agents.Orchestration.GroupChat;
///
/// An used with the .
///
-internal sealed class GroupChatAgentActor :
- AgentActor,
- IHandle,
- IHandle,
- IHandle
+internal sealed class GroupChatAgentActor : AgentActor
{
private readonly List _cache;
@@ -28,46 +25,37 @@ internal sealed class GroupChatAgentActor :
/// The orchestration context.
/// An .
/// The logger to use for the actor
- public GroupChatAgentActor(AgentId id, IAgentRuntime runtime, OrchestrationContext context, Agent agent, ILogger? logger = null)
+ public GroupChatAgentActor(ActorId id, IAgentRuntime runtime, OrchestrationContext context, Agent agent, ILogger? logger = null)
: base(id, runtime, context, agent, logger)
{
this._cache = [];
+
+ this.RegisterMessageHandler(this.HandleAsync);
+ this.RegisterMessageHandler(this.HandleAsync);
+ this.RegisterMessageHandler(this.HandleAsync);
}
- ///
- public ValueTask HandleAsync(GroupChatMessages.Group item, MessageContext messageContext)
+ private ValueTask HandleAsync(GroupChatMessages.Group item, MessageContext messageContext, CancellationToken cancellationToken)
{
this._cache.AddRange(item.Messages);
-
-#if !NETCOREAPP
- return new ValueTask();
-#else
- return ValueTask.CompletedTask;
-#endif
+ return default;
}
- ///
- public ValueTask HandleAsync(GroupChatMessages.Reset item, MessageContext messageContext)
+ private ValueTask HandleAsync(GroupChatMessages.Reset item, MessageContext messageContext, CancellationToken cancellationToken)
{
this.ResetThread();
-
-#if !NETCOREAPP
- return new ValueTask();
-#else
- return ValueTask.CompletedTask;
-#endif
+ return default;
}
- ///
- public async ValueTask HandleAsync(GroupChatMessages.Speak item, MessageContext messageContext)
+ private async ValueTask HandleAsync(GroupChatMessages.Speak item, MessageContext messageContext, CancellationToken cancellationToken)
{
this.Logger.LogChatAgentInvoke(this.Id);
- ChatMessage response = await this.InvokeAsync(this._cache, messageContext.CancellationToken).ConfigureAwait(false);
+ ChatMessage response = await this.InvokeAsync(this._cache, cancellationToken).ConfigureAwait(false);
this.Logger.LogChatAgentResult(this.Id, response.Text);
this._cache.Clear();
- await this.PublishMessageAsync(response.AsGroupMessage(), this.Context.Topic).ConfigureAwait(false);
+ await this.PublishMessageAsync(response.AsGroupMessage(), 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 d82e961da0..de6999d928 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatManager.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatManager.cs
@@ -96,11 +96,6 @@ public abstract class GroupChatManager
}
GroupChatManagerResult result = new(resultValue) { Reason = reason };
-
-#if !NETCOREAPP
return new ValueTask>(result);
-#else
- return ValueTask.FromResult(result);
-#endif
}
}
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatManagerActor.cs b/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatManagerActor.cs
index f7e51017d9..69eb38972b 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatManagerActor.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatManagerActor.cs
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Collections.Generic;
+using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.AI.Agents.Runtime;
@@ -11,17 +12,14 @@ namespace Microsoft.Agents.Orchestration.GroupChat;
///
/// An used to manage a .
///
-internal sealed class GroupChatManagerActor :
- OrchestrationActor,
- IHandle,
- IHandle
+internal sealed class GroupChatManagerActor : OrchestrationActor
{
///
/// A common description for the manager.
///
public const string DefaultDescription = "Orchestrates a team of agents to accomplish a defined task.";
- private readonly AgentType _orchestrationType;
+ private readonly ActorType _orchestrationType;
private readonly GroupChatManager _manager;
private readonly List _chat;
private readonly GroupChatTeam _team;
@@ -36,65 +34,67 @@ internal sealed class GroupChatManagerActor :
/// The team of agents being orchestrated
/// Identifies the orchestration agent.
/// The logger to use for the actor
- public GroupChatManagerActor(AgentId id, IAgentRuntime runtime, OrchestrationContext context, GroupChatManager manager, GroupChatTeam team, AgentType orchestrationType, ILogger? logger = null)
+ public GroupChatManagerActor(ActorId id, IAgentRuntime runtime, OrchestrationContext context, GroupChatManager manager, GroupChatTeam team, ActorType orchestrationType, ILogger? logger = null)
: base(id, runtime, context, DefaultDescription, logger)
{
this._chat = [];
this._manager = manager;
this._orchestrationType = orchestrationType;
this._team = team;
+
+ this.RegisterMessageHandler(this.HandleAsync);
+ this.RegisterMessageHandler(this.HandleAsync);
}
- ///
- public async ValueTask HandleAsync(GroupChatMessages.InputTask item, MessageContext messageContext)
+ private async ValueTask HandleAsync(GroupChatMessages.InputTask item, MessageContext messageContext, CancellationToken cancellationToken)
{
this.Logger.LogChatManagerInit(this.Id);
this._chat.AddRange(item.Messages);
- await this.PublishMessageAsync(item.Messages.AsGroupMessage(), this.Context.Topic).ConfigureAwait(false);
+ await this.PublishMessageAsync(item.Messages.AsGroupMessage(), this.Context.Topic, cancellationToken: cancellationToken).ConfigureAwait(false);
- await this.ManageAsync(messageContext).ConfigureAwait(false);
+ await this.ManageAsync(messageContext, cancellationToken).ConfigureAwait(false);
}
///
- public async ValueTask HandleAsync(GroupChatMessages.Group item, MessageContext messageContext)
+ private async ValueTask HandleAsync(GroupChatMessages.Group item, MessageContext messageContext, CancellationToken cancellationToken)
{
this.Logger.LogChatManagerInvoke(this.Id);
this._chat.AddRange(item.Messages);
- await this.ManageAsync(messageContext).ConfigureAwait(false);
+ await this.ManageAsync(messageContext, cancellationToken).ConfigureAwait(false);
}
- private async ValueTask ManageAsync(MessageContext messageContext)
+ private async ValueTask ManageAsync(MessageContext messageContext, CancellationToken cancellationToken)
{
if (this._manager.InteractiveCallback != null)
{
- GroupChatManagerResult inputResult = await this._manager.ShouldRequestUserInput(this._chat, messageContext.CancellationToken).ConfigureAwait(false);
+ GroupChatManagerResult inputResult = await this._manager.ShouldRequestUserInput(this._chat, cancellationToken).ConfigureAwait(false);
this.Logger.LogChatManagerInput(this.Id, inputResult.Value, inputResult.Reason);
if (inputResult.Value)
{
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).ConfigureAwait(false);
+ await this.PublishMessageAsync(input.AsGroupMessage(), this.Context.Topic, cancellationToken: cancellationToken).ConfigureAwait(false);
}
}
- GroupChatManagerResult terminateResult = await this._manager.ShouldTerminate(this._chat, messageContext.CancellationToken).ConfigureAwait(false);
+ GroupChatManagerResult terminateResult = await this._manager.ShouldTerminate(this._chat, cancellationToken).ConfigureAwait(false);
this.Logger.LogChatManagerTerminate(this.Id, terminateResult.Value, terminateResult.Reason);
if (terminateResult.Value)
{
- GroupChatManagerResult filterResult = await this._manager.FilterResults(this._chat, messageContext.CancellationToken).ConfigureAwait(false);
+ 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, messageContext.CancellationToken).ConfigureAwait(false);
+ await this.PublishMessageAsync(filterResult.Value.AsResultMessage(), this._orchestrationType, cancellationToken).ConfigureAwait(false);
return;
}
- GroupChatManagerResult selectionResult = await this._manager.SelectNextAgent(this._chat, this._team, messageContext.CancellationToken).ConfigureAwait(false);
- AgentType selectionType = this._team[selectionResult.Value].Type;
+ GroupChatManagerResult selectionResult = await this._manager.SelectNextAgent(this._chat, this._team, cancellationToken).ConfigureAwait(false);
+ ActorType selectionType = new(this._team[selectionResult.Value].Type);
this.Logger.LogChatManagerSelect(this.Id, selectionType);
- await this.PublishMessageAsync(new GroupChatMessages.Speak(), selectionType, messageContext.CancellationToken).ConfigureAwait(false);
+ await this.PublishMessageAsync(new GroupChatMessages.Speak(), selectionType, cancellationToken: cancellationToken).ConfigureAwait(false);
}
}
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatOrchestration.cs b/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatOrchestration.cs
index a5440767a0..e921eb7362 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatOrchestration.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatOrchestration.cs
@@ -36,7 +36,7 @@ public class GroupChatOrchestration :
}
///
- protected override ValueTask StartAsync(IAgentRuntime runtime, TopicId topic, IEnumerable input, AgentType? entryAgent)
+ protected override ValueTask StartAsync(IAgentRuntime runtime, TopicId topic, IEnumerable input, ActorType? entryAgent)
{
if (!entryAgent.HasValue)
{
@@ -46,37 +46,33 @@ public class GroupChatOrchestration :
}
///
- protected override async ValueTask RegisterOrchestrationAsync(IAgentRuntime runtime, OrchestrationContext context, RegistrationContext registrar, ILogger logger)
+ protected override async ValueTask RegisterOrchestrationAsync(IAgentRuntime runtime, OrchestrationContext context, RegistrationContext registrar, ILogger logger)
{
- AgentType outputType = await registrar.RegisterResultTypeAsync(response => [response.Message]).ConfigureAwait(false);
+ ActorType outputType = await registrar.RegisterResultTypeAsync(response => [response.Message]).ConfigureAwait(false);
int agentCount = 0;
GroupChatTeam team = [];
foreach (Agent agent in this.Members)
{
++agentCount;
- AgentType agentType = await RegisterAgentAsync(agent, agentCount).ConfigureAwait(false);
- string name = agent.Name ?? agent.Id ?? agentType;
+ ActorType agentType = await RegisterAgentAsync(agent, agentCount).ConfigureAwait(false);
+ string name = agent.Name ?? agent.Id ?? agentType.Name;
string? description = agent.Description;
- team[name] = (agentType, description ?? DefaultAgentDescription);
+ team[name] = (agentType.Name, description ?? DefaultAgentDescription);
logger.LogRegisterActor(this.OrchestrationLabel, agentType, "MEMBER", agentCount);
await runtime.SubscribeAsync(agentType, context.Topic).ConfigureAwait(false);
}
- AgentType managerType =
+ ActorType managerType =
await runtime.RegisterOrchestrationAgentAsync(
this.FormatAgentType(context.Topic, "Manager"),
(agentId, runtime) =>
{
GroupChatManagerActor actor = new(agentId, runtime, context, this._manager, team, outputType, context.LoggerFactory.CreateLogger());
-#if !NETCOREAPP
- return new ValueTask(actor);
-#else
- return ValueTask.FromResult(actor);
-#endif
+ return new ValueTask(actor);
}).ConfigureAwait(false);
logger.LogRegisterActor(this.OrchestrationLabel, managerType, "MANAGER");
@@ -84,17 +80,13 @@ public class GroupChatOrchestration :
return managerType;
- ValueTask RegisterAgentAsync(Agent agent, int agentCount) =>
+ ValueTask RegisterAgentAsync(Agent agent, int agentCount) =>
runtime.RegisterOrchestrationAgentAsync(
this.FormatAgentType(context.Topic, $"Agent_{agentCount}"),
(agentId, runtime) =>
{
GroupChatAgentActor actor = new(agentId, runtime, context, agent, context.LoggerFactory.CreateLogger());
-#if !NETCOREAPP
- return new ValueTask(actor);
-#else
- return ValueTask.FromResult(actor);
-#endif
+ return new ValueTask(actor);
});
}
}
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/RoundRobinGroupChatManager.cs b/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/RoundRobinGroupChatManager.cs
index e0f11475ec..cb65c6c7e9 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/RoundRobinGroupChatManager.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/RoundRobinGroupChatManager.cs
@@ -22,11 +22,7 @@ public class RoundRobinGroupChatManager : GroupChatManager
public override ValueTask> FilterResults(IReadOnlyCollection history, CancellationToken cancellationToken = default)
{
GroupChatManagerResult result = new(history.LastOrDefault()?.Text ?? string.Empty) { Reason = "Default result filter provides the final chat message." };
-#if !NETCOREAPP
return new ValueTask>(result);
-#else
- return ValueTask.FromResult(result);
-#endif
}
///
@@ -35,21 +31,13 @@ public class RoundRobinGroupChatManager : GroupChatManager
string nextAgent = team.Skip(this._currentAgentIndex).First().Key;
this._currentAgentIndex = (this._currentAgentIndex + 1) % team.Count;
GroupChatManagerResult result = new(nextAgent) { Reason = $"Selected agent at index: {this._currentAgentIndex}" };
-#if !NETCOREAPP
return new ValueTask>(result);
-#else
- return ValueTask.FromResult(result);
-#endif
}
///
public override ValueTask> ShouldRequestUserInput(IReadOnlyCollection history, CancellationToken cancellationToken = default)
{
GroupChatManagerResult result = new(false) { Reason = "The default round-robin group chat manager does not request user input." };
-#if !NETCOREAPP
return new ValueTask>(result);
-#else
- return ValueTask.FromResult(result);
-#endif
}
}
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Handoff/HandoffActor.cs b/dotnet/src/Microsoft.Agents.Orchestration/Handoff/HandoffActor.cs
index 7894477385..cbd57e6f29 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/Handoff/HandoffActor.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/Handoff/HandoffActor.cs
@@ -14,15 +14,11 @@ namespace Microsoft.Agents.Orchestration.Handoff;
///
/// An actor used with the .
///
-internal sealed class HandoffActor :
- AgentActor,
- IHandle,
- IHandle,
- IHandle
+internal sealed class HandoffActor : AgentActor
{
private readonly ChatClientAgent _chatAgent;
private readonly HandoffLookup _handoffs;
- private readonly AgentType _resultHandoff;
+ private readonly ActorType _resultHandoff;
private readonly List _cache;
private readonly ChatOptions _options;
@@ -39,7 +35,7 @@ internal sealed class HandoffActor :
/// The handoffs available to this agent
/// The handoff agent for capturing the result.
/// The logger to use for the actor
- public HandoffActor(AgentId id, IAgentRuntime runtime, OrchestrationContext context, ChatClientAgent agent, HandoffLookup handoffs, AgentType resultHandoff, ILogger? logger = null)
+ public HandoffActor(ActorId id, IAgentRuntime runtime, OrchestrationContext context, ChatClientAgent agent, HandoffLookup handoffs, ActorType resultHandoff, ILogger? logger = null)
: base(id, runtime, context, agent, logger)
{
if (handoffs.ContainsKey(agent.Name ?? agent.Id))
@@ -57,6 +53,10 @@ internal sealed class HandoffActor :
Tools = [.. this.CreateHandoffFunctions()],
ToolMode = ChatToolMode.Auto
};
+
+ this.RegisterMessageHandler(this.HandleAsync);
+ this.RegisterMessageHandler(this.HandleAsync);
+ this.RegisterMessageHandler(this.HandleAsync);
}
///
@@ -85,33 +85,20 @@ internal sealed class HandoffActor :
///
public OrchestrationInteractiveCallback? InteractiveCallback { get; init; }
- ///
- public ValueTask HandleAsync(HandoffMessages.InputTask item, MessageContext messageContext)
+ private ValueTask HandleAsync(HandoffMessages.InputTask item, MessageContext messageContext, CancellationToken cancellationToken)
{
this._taskSummary = null;
this._cache.AddRange(item.Messages);
-
-#if !NETCOREAPP
- return new ValueTask();
-#else
- return ValueTask.CompletedTask;
-#endif
+ return default;
}
- ///
- public ValueTask HandleAsync(HandoffMessages.Response item, MessageContext messageContext)
+ private ValueTask HandleAsync(HandoffMessages.Response item, MessageContext messageContext, CancellationToken cancellationToken)
{
this._cache.Add(item.Message);
-
-#if !NETCOREAPP
- return new ValueTask();
-#else
- return ValueTask.CompletedTask;
-#endif
+ return default;
}
- ///
- public async ValueTask HandleAsync(HandoffMessages.Request item, MessageContext messageContext)
+ private async ValueTask HandleAsync(HandoffMessages.Request item, MessageContext messageContext, CancellationToken cancellationToken)
{
try
{
@@ -122,7 +109,7 @@ internal sealed class HandoffActor :
ChatMessage response;
try
{
- response = await this.InvokeAsync(this._cache, messageContext.CancellationToken).ConfigureAwait(false);
+ response = await this.InvokeAsync(this._cache, cancellationToken).ConfigureAwait(false);
}
catch (Exception exception)
{
@@ -139,13 +126,13 @@ internal sealed class HandoffActor :
// 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, messageContext.CancellationToken).ConfigureAwait(false);
+ await this.PublishMessageAsync(new HandoffMessages.Response { Message = response }, this.Context.Topic, messageId: null, cancellationToken).ConfigureAwait(false);
}
if (this._handoffAgent != null)
{
- AgentType handoffType = this._handoffs[this._handoffAgent].AgentType;
- await this.PublishMessageAsync(new HandoffMessages.Request(), handoffType, messageContext.CancellationToken).ConfigureAwait(false);
+ ActorType handoffType = this._handoffs[this._handoffAgent].AgentType;
+ await this.PublishMessageAsync(new HandoffMessages.Request(), handoffType, cancellationToken).ConfigureAwait(false);
this._handoffAgent = null;
break;
@@ -154,12 +141,12 @@ internal sealed class HandoffActor :
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, messageContext.CancellationToken).ConfigureAwait(false);
+ await this.PublishMessageAsync(new HandoffMessages.Response { Message = input }, this.Context.Topic, messageId: null, cancellationToken).ConfigureAwait(false);
this._cache.Add(input);
continue;
}
- await this.EndAsync(response.Text ?? "No handoff or human response function requested. Ending task.", messageContext.CancellationToken).ConfigureAwait(false);
+ await this.EndAsync(response.Text ?? "No handoff or human response function requested. Ending task.", cancellationToken).ConfigureAwait(false);
}
}
catch (Exception exception)
@@ -176,7 +163,7 @@ internal sealed class HandoffActor :
name: "end_task",
description: "Complete the task with a summary when no further requests are given.");
- foreach (KeyValuePair handoff in this._handoffs)
+ foreach (KeyValuePair handoff in this._handoffs)
{
AIFunction handoffFunction =
AIFunctionFactory.Create(
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Handoff/HandoffOrchestration.cs b/dotnet/src/Microsoft.Agents.Orchestration/Handoff/HandoffOrchestration.cs
index a98a40e972..b90063aa81 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/Handoff/HandoffOrchestration.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/Handoff/HandoffOrchestration.cs
@@ -50,7 +50,7 @@ public class HandoffOrchestration : AgentOrchestration
- protected override async ValueTask StartAsync(IAgentRuntime runtime, TopicId topic, IEnumerable input, AgentType? entryAgent)
+ protected override async ValueTask StartAsync(IAgentRuntime runtime, TopicId topic, IEnumerable input, ActorType? entryAgent)
{
if (!entryAgent.HasValue)
{
@@ -61,14 +61,14 @@ public class HandoffOrchestration : AgentOrchestration
- protected override async ValueTask RegisterOrchestrationAsync(IAgentRuntime runtime, OrchestrationContext context, RegistrationContext registrar, ILogger logger)
+ protected override async ValueTask RegisterOrchestrationAsync(IAgentRuntime runtime, OrchestrationContext context, RegistrationContext registrar, ILogger logger)
{
- AgentType outputType = await registrar.RegisterResultTypeAsync(response => [response.Message]).ConfigureAwait(false);
+ ActorType outputType = await registrar.RegisterResultTypeAsync(response => [response.Message]).ConfigureAwait(false);
// Each agent handsoff its result to the next agent.
- Dictionary agentMap = [];
+ Dictionary agentMap = [];
Dictionary handoffMap = [];
- AgentType agentType = outputType;
+ ActorType agentType = outputType;
for (int index = this.Members.Count - 1; index >= 0; --index)
{
Agent agent = this.Members[index];
@@ -84,11 +84,7 @@ public class HandoffOrchestration : AgentOrchestration(actor);
-#else
- return ValueTask.FromResult(actor);
-#endif
+ return new ValueTask(actor);
}).ConfigureAwait(false);
agentMap[agent.Name ?? agent.Id] = agentType;
@@ -112,5 +108,5 @@ public class HandoffOrchestration : AgentOrchestration this.FormatAgentType(topic, $"Agent_{index + 1}");
+ private ActorType GetAgentType(TopicId topic, int index) => this.FormatAgentType(topic, $"Agent_{index + 1}");
}
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Handoff/Handoffs.cs b/dotnet/src/Microsoft.Agents.Orchestration/Handoff/Handoffs.cs
index 9a562ee7c9..d779abbe27 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/Handoff/Handoffs.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/Handoff/Handoffs.cs
@@ -142,6 +142,6 @@ public static class OrchestrationHandoffsExtensions
///
/// Handoff relationships post-processed into a name-based lookup table that includes the agent type and handoff description.
-/// Maps agent names/IDs to a tuple of and handoff description.
+/// Maps agent names/IDs to a tuple of and handoff description.
///
-internal sealed class HandoffLookup : Dictionary;
+internal sealed class HandoffLookup : Dictionary;
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Logging/AgentOrchestrationLogMessages.cs b/dotnet/src/Microsoft.Agents.Orchestration/Logging/AgentOrchestrationLogMessages.cs
index 88efced26b..9008ff8e35 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/Logging/AgentOrchestrationLogMessages.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/Logging/AgentOrchestrationLogMessages.cs
@@ -37,7 +37,7 @@ internal static partial class AgentOrchestrationLogMessages
public static partial void LogRegisterActor(
this ILogger logger,
string orchestration,
- AgentType agentType,
+ ActorType agentType,
string label);
///
@@ -49,7 +49,7 @@ internal static partial class AgentOrchestrationLogMessages
public static partial void LogRegisterActor(
this ILogger logger,
string orchestration,
- AgentType agentType,
+ ActorType agentType,
string label,
int count);
@@ -96,7 +96,7 @@ internal static partial class AgentOrchestrationLogMessages
public static partial void LogOrchestrationStart(
this ILogger logger,
string orchestration,
- AgentId agentId);
+ ActorId agentId);
///
/// Logs that orchestration request actor is active
@@ -107,7 +107,7 @@ internal static partial class AgentOrchestrationLogMessages
public static partial void LogOrchestrationRequestInvoke(
this ILogger logger,
string orchestration,
- AgentId agentId);
+ ActorId agentId);
///
/// Logs that orchestration request actor experienced an unexpected failure.
@@ -118,7 +118,7 @@ internal static partial class AgentOrchestrationLogMessages
public static partial void LogOrchestrationRequestFailure(
this ILogger logger,
string orchestration,
- AgentId agentId,
+ ActorId agentId,
Exception exception);
///
@@ -130,7 +130,7 @@ internal static partial class AgentOrchestrationLogMessages
public static partial void LogOrchestrationResultInvoke(
this ILogger logger,
string orchestration,
- AgentId agentId);
+ ActorId agentId);
///
/// Logs that orchestration result actor experienced an unexpected failure.
@@ -141,6 +141,6 @@ internal static partial class AgentOrchestrationLogMessages
public static partial void LogOrchestrationResultFailure(
this ILogger logger,
string orchestration,
- AgentId agentId,
+ ActorId agentId,
Exception exception);
}
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Logging/ConcurrentOrchestrationLogMessages.cs b/dotnet/src/Microsoft.Agents.Orchestration/Logging/ConcurrentOrchestrationLogMessages.cs
index b266310669..eb76da967b 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/Logging/ConcurrentOrchestrationLogMessages.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/Logging/ConcurrentOrchestrationLogMessages.cs
@@ -22,14 +22,14 @@ internal static partial class ConcurrentOrchestrationLogMessages
Message = "REQUEST Concurrent agent [{AgentId}]")]
public static partial void LogConcurrentAgentInvoke(
this ILogger logger,
- AgentId agentId);
+ ActorId agentId);
[LoggerMessage(
Level = LogLevel.Trace,
Message = "RESULT Concurrent agent [{AgentId}]: {Message}")]
public static partial void LogConcurrentAgentResult(
this ILogger logger,
- AgentId agentId,
+ ActorId agentId,
string? message);
///
@@ -40,7 +40,7 @@ internal static partial class ConcurrentOrchestrationLogMessages
Message = "COLLECT Concurrent result [{AgentId}]: #{ResultCount} / {ExpectedCount}")]
public static partial void LogConcurrentResultCapture(
this ILogger logger,
- AgentId agentId,
+ ActorId agentId,
int resultCount,
int expectedCount);
}
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Logging/GroupChatOrchestrationLogMessages.cs b/dotnet/src/Microsoft.Agents.Orchestration/Logging/GroupChatOrchestrationLogMessages.cs
index 76a60c270a..6f06c39dee 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/Logging/GroupChatOrchestrationLogMessages.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/Logging/GroupChatOrchestrationLogMessages.cs
@@ -22,14 +22,14 @@ internal static partial class GroupChatOrchestrationLogMessages
Message = "CHAT AGENT invoked [{AgentId}]")]
public static partial void LogChatAgentInvoke(
this ILogger logger,
- AgentId agentId);
+ ActorId agentId);
[LoggerMessage(
Level = LogLevel.Trace,
Message = "CHAT AGENT result [{AgentId}]: {Message}")]
public static partial void LogChatAgentResult(
this ILogger logger,
- AgentId agentId,
+ ActorId agentId,
string? message);
[LoggerMessage(
@@ -37,21 +37,21 @@ internal static partial class GroupChatOrchestrationLogMessages
Message = "CHAT MANAGER initialized [{AgentId}]")]
public static partial void LogChatManagerInit(
this ILogger logger,
- AgentId agentId);
+ ActorId agentId);
[LoggerMessage(
Level = LogLevel.Debug,
Message = "CHAT MANAGER invoked [{AgentId}]")]
public static partial void LogChatManagerInvoke(
this ILogger logger,
- AgentId agentId);
+ ActorId agentId);
[LoggerMessage(
Level = LogLevel.Debug,
Message = "CHAT MANAGER terminate? [{AgentId}]: {Result} ({Reason})")]
public static partial void LogChatManagerTerminate(
this ILogger logger,
- AgentId agentId,
+ ActorId agentId,
bool result,
string reason);
@@ -60,15 +60,15 @@ internal static partial class GroupChatOrchestrationLogMessages
Message = "CHAT MANAGER select: {NextAgent} [{AgentId}]")]
public static partial void LogChatManagerSelect(
this ILogger logger,
- AgentId agentId,
- AgentType nextAgent);
+ ActorId agentId,
+ ActorType nextAgent);
[LoggerMessage(
Level = LogLevel.Debug,
Message = "CHAT MANAGER result [{AgentId}]: '{Result}' ({Reason})")]
public static partial void LogChatManagerResult(
this ILogger logger,
- AgentId agentId,
+ ActorId agentId,
string result,
string reason);
@@ -77,7 +77,7 @@ internal static partial class GroupChatOrchestrationLogMessages
Message = "CHAT MANAGER user-input? [{AgentId}]: {Result} ({Reason})")]
public static partial void LogChatManagerInput(
this ILogger logger,
- AgentId agentId,
+ ActorId agentId,
bool result,
string reason);
@@ -86,6 +86,6 @@ internal static partial class GroupChatOrchestrationLogMessages
Message = "CHAT AGENT user-input [{AgentId}]: {Message}")]
public static partial void LogChatManagerUserInput(
this ILogger logger,
- AgentId agentId,
+ ActorId agentId,
string? message);
}
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Logging/HandoffOrchestrationLogMessages.cs b/dotnet/src/Microsoft.Agents.Orchestration/Logging/HandoffOrchestrationLogMessages.cs
index 132167e6a1..2eb0a4e0ce 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/Logging/HandoffOrchestrationLogMessages.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/Logging/HandoffOrchestrationLogMessages.cs
@@ -22,14 +22,14 @@ internal static partial class HandoffOrchestrationLogMessages
Message = "REQUEST Handoff agent [{AgentId}]")]
public static partial void LogHandoffAgentInvoke(
this ILogger logger,
- AgentId agentId);
+ ActorId agentId);
[LoggerMessage(
Level = LogLevel.Trace,
Message = "RESULT Handoff agent [{AgentId}]: {Message}")]
public static partial void LogHandoffAgentResult(
this ILogger logger,
- AgentId agentId,
+ ActorId agentId,
string? message);
[LoggerMessage(
@@ -37,7 +37,7 @@ internal static partial class HandoffOrchestrationLogMessages
Message = "TOOL Handoff [{AgentId}]: {Name}")]
public static partial void LogHandoffFunctionCall(
this ILogger logger,
- AgentId agentId,
+ ActorId agentId,
string name);
[LoggerMessage(
@@ -45,6 +45,6 @@ internal static partial class HandoffOrchestrationLogMessages
Message = "RESULT Handoff summary [{AgentId}]: {Summary}")]
public static partial void LogHandoffSummary(
this ILogger logger,
- AgentId agentId,
+ ActorId agentId,
string? summary);
}
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Logging/SequentialOrchestrationLogMessages.cs b/dotnet/src/Microsoft.Agents.Orchestration/Logging/SequentialOrchestrationLogMessages.cs
index 39425ff792..93d28eef8a 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/Logging/SequentialOrchestrationLogMessages.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/Logging/SequentialOrchestrationLogMessages.cs
@@ -22,13 +22,13 @@ internal static partial class SequentialOrchestrationLogMessages
Message = "REQUEST Sequential agent [{AgentId}]")]
public static partial void LogSequentialAgentInvoke(
this ILogger logger,
- AgentId agentId);
+ ActorId agentId);
[LoggerMessage(
Level = LogLevel.Trace,
Message = "RESULT Sequential agent [{AgentId}]: {Message}")]
public static partial void LogSequentialAgentResult(
this ILogger logger,
- AgentId agentId,
+ ActorId agentId,
string? message);
}
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/OrchestrationActor.cs b/dotnet/src/Microsoft.Agents.Orchestration/OrchestrationActor.cs
index 5b1366c1a7..d3f3f53629 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/OrchestrationActor.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/OrchestrationActor.cs
@@ -10,12 +10,12 @@ namespace Microsoft.Agents.Orchestration;
///
/// Base abstractions for any actor that participates in an orchestration.
///
-public abstract class OrchestrationActor : BaseAgent
+public abstract class OrchestrationActor : RuntimeActor
{
///
/// Initializes a new instance of the class.
///
- protected OrchestrationActor(AgentId id, IAgentRuntime runtime, OrchestrationContext context, string description, ILogger? logger = null)
+ protected OrchestrationActor(ActorId id, IAgentRuntime runtime, OrchestrationContext context, string description, ILogger? logger = null)
: base(id, runtime, description, logger)
{
this.Context = context;
@@ -35,9 +35,9 @@ public abstract class OrchestrationActor : BaseAgent
/// The agent identifier, if it exists.
protected async ValueTask PublishMessageAsync(
object message,
- AgentType agentType,
+ ActorType agentType,
CancellationToken cancellationToken = default)
{
- await base.PublishMessageAsync(message, new TopicId(agentType), messageId: null, cancellationToken).ConfigureAwait(false);
+ await base.PublishMessageAsync(message, new TopicId(agentType.Name), messageId: null, cancellationToken).ConfigureAwait(false);
}
}
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/OrchestrationResult.cs b/dotnet/src/Microsoft.Agents.Orchestration/OrchestrationResult.cs
index 2ecf0e2c75..c5de6b2e8d 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/OrchestrationResult.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/OrchestrationResult.cs
@@ -34,8 +34,11 @@ public sealed class OrchestrationResult : IDisposable
///
public void Dispose()
{
- this.Dispose(disposing: true);
- GC.SuppressFinalize(this);
+ if (!this._isDisposed)
+ {
+ this._cancelSource.Dispose();
+ this._isDisposed = true;
+ }
}
///
@@ -60,13 +63,13 @@ public sealed class OrchestrationResult : IDisposable
/// Thrown if the orchestration does not complete within the specified timeout period.
public async ValueTask GetValueAsync(TimeSpan? timeout = null, CancellationToken cancellationToken = default)
{
-#if !NETCOREAPP
+#if NET
+ ObjectDisposedException.ThrowIf(this._isDisposed, this);
+#else
if (this._isDisposed)
{
throw new ObjectDisposedException(this.GetType().Name);
}
-#else
- ObjectDisposedException.ThrowIf(this._isDisposed, this);
#endif
this._logger.LogOrchestrationResultAwait(this.Orchestration, this.Topic);
@@ -96,30 +99,17 @@ public sealed class OrchestrationResult : IDisposable
///
public void Cancel()
{
-#if !NETCOREAPP
+#if NET
+ ObjectDisposedException.ThrowIf(this._isDisposed, this);
+#else
if (this._isDisposed)
{
throw new ObjectDisposedException(this.GetType().Name);
}
-#else
- ObjectDisposedException.ThrowIf(this._isDisposed, this);
#endif
this._logger.LogOrchestrationResultCancelled(this.Orchestration, this.Topic);
this._cancelSource.Cancel();
this._completion.SetCanceled();
}
-
- private void Dispose(bool disposing)
- {
- if (!this._isDisposed)
- {
- if (disposing)
- {
- this._cancelSource.Dispose();
- }
-
- this._isDisposed = true;
- }
- }
}
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Sequential/SequentialActor.cs b/dotnet/src/Microsoft.Agents.Orchestration/Sequential/SequentialActor.cs
index 59876e6cd0..6be2d40fdc 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/Sequential/SequentialActor.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/Sequential/SequentialActor.cs
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Collections.Generic;
+using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.AI.Agents;
@@ -12,12 +13,9 @@ namespace Microsoft.Agents.Orchestration.Sequential;
///
/// An actor used with the .
///
-internal sealed class SequentialActor :
- AgentActor,
- IHandle,
- IHandle
+internal sealed class SequentialActor : AgentActor
{
- private readonly AgentType _nextAgent;
+ private readonly ActorType _nextAgent;
///
/// Initializes a new instance of the class.
@@ -28,35 +26,32 @@ internal sealed class SequentialActor :
/// An .
/// The identifier of the next agent for which to handoff the result
/// The logger to use for the actor
- public SequentialActor(AgentId id, IAgentRuntime runtime, OrchestrationContext context, Agent agent, AgentType nextAgent, ILogger? logger = null)
+ public SequentialActor(ActorId id, IAgentRuntime runtime, OrchestrationContext context, Agent agent, ActorType nextAgent, ILogger? logger = null)
: base(id, runtime, context, agent, logger)
{
logger?.LogInformation("ACTOR {ActorId} {NextAgent}", this.Id, nextAgent);
this._nextAgent = nextAgent;
+
+ this.RegisterMessageHandler(this.HandleAsync);
+ this.RegisterMessageHandler(this.HandleAsync);
}
- ///
- public async ValueTask HandleAsync(SequentialMessages.Request item, MessageContext messageContext)
- {
- await this.InvokeAgentAsync(item.Messages, messageContext).ConfigureAwait(false);
- }
+ public ValueTask HandleAsync(SequentialMessages.Request item, MessageContext messageContext, CancellationToken cancellationToken) =>
+ this.InvokeAgentAsync(item.Messages, messageContext, cancellationToken);
- ///
- public async ValueTask HandleAsync(SequentialMessages.Response item, MessageContext messageContext)
- {
- await this.InvokeAgentAsync([item.Message], messageContext).ConfigureAwait(false);
- }
+ public ValueTask HandleAsync(SequentialMessages.Response item, MessageContext messageContext, CancellationToken cancellationToken) =>
+ this.InvokeAgentAsync([item.Message], messageContext, cancellationToken);
- private async ValueTask InvokeAgentAsync(IList input, MessageContext messageContext)
+ private async ValueTask InvokeAgentAsync(IList input, MessageContext messageContext, CancellationToken cancellationToken)
{
this.Logger.LogInformation("INVOKE {ActorId} {NextAgent}", this.Id, this._nextAgent);
this.Logger.LogSequentialAgentInvoke(this.Id);
- ChatMessage response = await this.InvokeAsync(input, messageContext.CancellationToken).ConfigureAwait(false);
+ ChatMessage response = await this.InvokeAsync(input, cancellationToken).ConfigureAwait(false);
this.Logger.LogSequentialAgentResult(this.Id, response.Text);
- await this.PublishMessageAsync(response.AsResponseMessage(), this._nextAgent).ConfigureAwait(false);
+ await this.PublishMessageAsync(response.AsResponseMessage(), this._nextAgent, cancellationToken: cancellationToken).ConfigureAwait(false);
}
}
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Sequential/SequentialOrchestration.cs b/dotnet/src/Microsoft.Agents.Orchestration/Sequential/SequentialOrchestration.cs
index ba53494ffa..c8dba0116e 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/Sequential/SequentialOrchestration.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/Sequential/SequentialOrchestration.cs
@@ -27,7 +27,7 @@ public class SequentialOrchestration : AgentOrchestration
- protected override async ValueTask StartAsync(IAgentRuntime runtime, TopicId topic, IEnumerable input, AgentType? entryAgent)
+ protected override async ValueTask StartAsync(IAgentRuntime runtime, TopicId topic, IEnumerable input, ActorType? entryAgent)
{
if (!entryAgent.HasValue)
{
@@ -37,12 +37,12 @@ public class SequentialOrchestration : AgentOrchestration
- protected override async ValueTask RegisterOrchestrationAsync(IAgentRuntime runtime, OrchestrationContext context, RegistrationContext registrar, ILogger logger)
+ protected override async ValueTask RegisterOrchestrationAsync(IAgentRuntime runtime, OrchestrationContext context, RegistrationContext registrar, ILogger logger)
{
- AgentType outputType = await registrar.RegisterResultTypeAsync(response => [response.Message]).ConfigureAwait(false);
+ ActorType outputType = await registrar.RegisterResultTypeAsync(response => [response.Message]).ConfigureAwait(false);
// Each agent handsoff its result to the next agent.
- AgentType nextAgent = outputType;
+ ActorType nextAgent = outputType;
for (int index = this.Members.Count - 1; index >= 0; --index)
{
Agent agent = this.Members[index];
@@ -53,20 +53,15 @@ public class SequentialOrchestration : AgentOrchestration RegisterAgentAsync(Agent agent, int index, AgentType nextAgent) =>
+ ValueTask RegisterAgentAsync(Agent agent, int index, ActorType nextAgent) =>
runtime.RegisterOrchestrationAgentAsync(
this.GetAgentType(context.Topic, index),
(agentId, runtime) =>
{
SequentialActor actor = new(agentId, runtime, context, agent, nextAgent, context.LoggerFactory.CreateLogger());
-
-#if !NETCOREAPP
- return new ValueTask(actor);
-#else
- return ValueTask.FromResult(actor);
-#endif
+ return new ValueTask(actor);
});
}
- private AgentType GetAgentType(TopicId topic, int index) => this.FormatAgentType(topic, $"Agent_{index + 1}");
+ private ActorType GetAgentType(TopicId topic, int index) => this.FormatAgentType(topic, $"Agent_{index + 1}");
}
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Transforms/DefaultTransforms.cs b/dotnet/src/Microsoft.Agents.Orchestration/Transforms/DefaultTransforms.cs
index e5b080188f..b9e082f4bf 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/Transforms/DefaultTransforms.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/Transforms/DefaultTransforms.cs
@@ -13,11 +13,7 @@ internal static class DefaultTransforms
{
public static ValueTask> FromInput(TInput input, CancellationToken cancellationToken = default)
{
-#if !NETCOREAPP
return new ValueTask>(TransformInput());
-#else
- return ValueTask.FromResult(TransformInput());
-#endif
IEnumerable TransformInput() =>
input switch
diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/ActorId.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/ActorId.cs
new file mode 100644
index 0000000000..8a4c433fc8
--- /dev/null
+++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/ActorId.cs
@@ -0,0 +1,116 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System;
+using System.Diagnostics.CodeAnalysis;
+
+namespace Microsoft.Extensions.AI.Agents.Runtime;
+
+///
+/// Provides a unique identifier for an actor instance within an agent runtime,
+/// serving as the "address" of the actor instance for receiving messages.
+///
+public readonly struct ActorId : IEquatable
+{
+ ///
+ /// Initializes a new instance of the struct from an .
+ ///
+ /// The actor type.
+ /// Actor instance identifier.
+ public ActorId(string type, string key) : this(new ActorType(type), key)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the struct from an .
+ ///
+ /// The actor type.
+ /// Actor instance identifier.
+ public ActorId(ActorType type, string key)
+ {
+ if (!IsValidKey(key))
+ {
+ throw new ArgumentException($"Invalid {nameof(ActorId)} key.", nameof(key));
+ }
+
+ this.Type = type;
+ this.Key = key;
+ }
+
+ ///
+ /// Gets an identifier that associates an actor with a specific factory function.
+ ///
+ ///
+ /// Strings may only be composed of alphanumeric letters (a-z) and (0-9), or underscores (_).
+ ///
+ public ActorType Type { get; }
+
+ ///
+ /// Gets an actor instance identifier.
+ ///
+ ///
+ /// Strings may only be composed of alphanumeric letters (a-z) and (0-9), or underscores (_).
+ ///
+ public string Key { get; }
+
+ ///
+ /// Convert a string of the format "type/key" into an .
+ ///
+ /// The actor ID string.
+ /// An instance of .
+ public static ActorId Parse(string actorId)
+ {
+ if (!KeyValueParser.TryParse(actorId, out string? type, out string? key))
+ {
+ throw new FormatException($"Invalid actor ID: '{actorId}'. Expected format is 'type/key'.");
+ }
+
+ return new ActorId(type, key);
+ }
+
+ ///
+ public override readonly string ToString() => $"{this.Type}/{this.Key}";
+
+ ///
+ public override readonly bool Equals([NotNullWhen(true)] object? obj) =>
+ obj is ActorId other && this.Equals(other);
+
+ ///
+ public readonly bool Equals(ActorId other) =>
+ this.Type == other.Type && this.Key == other.Key;
+
+ ///
+ public override readonly int GetHashCode() =>
+ HashCode.Combine(this.Type, this.Key);
+
+ ///
+ public static bool operator ==(ActorId left, ActorId right) =>
+ left.Equals(right);
+
+ ///
+ public static bool operator !=(ActorId left, ActorId right) =>
+ !left.Equals(right);
+
+ /// Determines whether the specified key is valid.
+ /// It must be non-null, not be only whitespace, and only contain printable ASCII characters.
+ internal static bool IsValidKey(string key)
+ {
+ if (string.IsNullOrWhiteSpace(key))
+ {
+ return false;
+ }
+
+#if NET
+ return !key.AsSpan().ContainsAnyExceptInRange((char)32, (char)126);
+#else
+ foreach (char c in key)
+ {
+ if ((int)c is < 32 or > 126)
+ {
+ return false;
+ }
+ }
+
+ return true;
+#endif
+ }
+}
diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/ActorMetadata.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/ActorMetadata.cs
new file mode 100644
index 0000000000..6e28c69f93
--- /dev/null
+++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/ActorMetadata.cs
@@ -0,0 +1,72 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System;
+
+namespace Microsoft.Extensions.AI.Agents.Runtime;
+
+///
+/// Represents metadata associated with an actor, including its type, unique key, and description.
+///
+public readonly struct ActorMetadata : IEquatable
+{
+ ///
+ /// Initializes a new instance of the class with the specified type, key, and description.
+ ///
+ /// The type of the actor.
+ /// The unique key associated with the actor.
+ /// A brief description of the actor.
+ public ActorMetadata(ActorType type, string key, string description)
+ {
+ if (!ActorId.IsValidKey(key))
+ {
+ 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;
+ }
+
+ ///
+ /// Gets an identifier that associates an actor with a specific factory function.
+ ///
+ public ActorType Type { get; }
+
+ ///
+ /// A unique key identifying the actor instance.
+ /// Strings may only be composed of alphanumeric letters (a-z, 0-9), or underscores (_).
+ ///
+ public string Key { get; }
+
+ ///
+ /// A brief description of the actor's purpose or functionality.
+ ///
+ public string Description { get; }
+
+ ///
+ public override readonly bool Equals(object? obj) =>
+ obj is ActorMetadata actorMetadata && this.Equals(actorMetadata);
+
+ ///
+ public readonly bool Equals(ActorMetadata other) =>
+ this.Type == other.Type &&
+ this.Key == other.Key &&
+ this.Description == other.Description;
+
+ ///
+ public override readonly int GetHashCode() =>
+ HashCode.Combine(this.Type, this.Key, this.Description);
+
+ ///
+ public static bool operator ==(ActorMetadata left, ActorMetadata right) =>
+ left.Equals(right);
+
+ ///
+ public static bool operator !=(ActorMetadata left, ActorMetadata right) =>
+ !(left == right);
+}
diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/ActorType.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/ActorType.cs
new file mode 100644
index 0000000000..6f101b46f4
--- /dev/null
+++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/ActorType.cs
@@ -0,0 +1,68 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System;
+using System.Text.RegularExpressions;
+
+namespace Microsoft.Extensions.AI.Agents.Runtime;
+
+///
+/// Represents the type of an actor.
+///
+public readonly partial struct ActorType : IEquatable
+{
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The actor type.
+ public ActorType(string type)
+ {
+ if (!IsValid(type))
+ {
+ throw new ArgumentException($"Invalid type: '{type}'. Must be alphanumeric (a-z, 0-9, _) and cannot start with a number or contain spaces.");
+ }
+
+ this.Name = type;
+ }
+
+ ///
+ /// The string representation of this actor type.
+ ///
+ public string Name { get; }
+
+ ///
+ /// Returns the string representation of the .
+ ///
+ /// A string in the format "type/source".
+ public override readonly string ToString() =>
+ this.Name;
+
+ ///
+ public override bool Equals(object? obj) =>
+ obj is ActorType other && this.Equals(other);
+
+ ///
+ public bool Equals(ActorType other) =>
+ this.Name.Equals(other.Name, StringComparison.Ordinal);
+
+ ///
+ public override int GetHashCode() =>
+ this.Name.GetHashCode();
+
+ ///
+ public static bool operator ==(ActorType left, ActorType right) =>
+ left.Equals(right);
+
+ ///
+ public static bool operator !=(ActorType left, ActorType right) =>
+ !(left == right);
+
+ internal static bool IsValid(string type) =>
+ type is not null && TypeRegex().IsMatch(type);
+
+#if NET
+ [GeneratedRegex("^[a-zA-Z_][a-zA-Z_0-9]*$")]
+ private static partial Regex TypeRegex();
+#else
+ private static Regex TypeRegex() => new("^[a-zA-Z_][a-zA-Z_0-9]*$", RegexOptions.Compiled);
+#endif
+}
diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/AgentId.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/AgentId.cs
deleted file mode 100644
index 7b6b462568..0000000000
--- a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/AgentId.cs
+++ /dev/null
@@ -1,135 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using System;
-using System.Diagnostics;
-using System.Diagnostics.CodeAnalysis;
-using System.Text.RegularExpressions;
-
-namespace Microsoft.Extensions.AI.Agents.Runtime;
-
-///
-/// Agent ID uniquely identifies an agent instance within an agent runtime, including a distributed runtime.
-/// It serves as the "address" of the agent instance for receiving messages.
-/// \
-///
-/// See the Python equivalent:
-/// AgentId in AutoGen (Python).
-///
-[DebuggerDisplay($"AgentId(type=\"{{{nameof(Type)}}}\", key=\"{{{nameof(Key)}}}\")")]
-public struct AgentId : IEquatable
-{
- ///
- /// The default source value used when no source is explicitly provided.
- ///
- public const string DefaultKey = "default";
-
- private static readonly Regex KeyRegex = new(@"^[\x20-\x7E]+$", RegexOptions.Compiled); // ASCII 32-126
-
- ///
- /// An identifier that associates an agent with a specific factory function.
- /// Strings may only be composed of alphanumeric letters (a-z) and (0-9), or underscores (_).
- ///
- public string Type { get; }
-
- ///
- /// Agent instance identifier.
- /// Strings may only be composed of alphanumeric letters (a-z) and (0-9), or underscores (_).
- ///
- public string Key { get; }
-
- internal static Regex KeyRegex1 => KeyRegex2;
-
- internal static Regex KeyRegex2 => KeyRegex;
-
- ///
- /// Initializes a new instance of the struct.
- ///
- /// The agent type.
- /// Agent instance identifier.
- public AgentId(string type, string key)
- {
- AgentType.Validate(type);
-
- if (string.IsNullOrWhiteSpace(key) || !KeyRegex.IsMatch(key))
- {
- throw new ArgumentException($"Invalid AgentId key: '{key}'. Must only contain ASCII characters 32-126.");
- }
-
- this.Type = type;
- this.Key = key;
- }
-
- ///
- /// Initializes a new instance of the struct from a tuple.
- ///
- /// A tuple containing the agent type and key.
- public AgentId((string Type, string Key) kvPair)
- : this(kvPair.Type, kvPair.Key)
- {
- }
-
- ///
- /// Initializes a new instance of the struct from an .
- ///
- /// The agent type.
- /// Agent instance identifier.
- public AgentId(AgentType type, string key)
- : this(type.Name, key)
- {
- }
-
- ///
- /// Convert a string of the format "type/key" into an .
- ///
- /// The agent ID string.
- /// An instance of .
- public static AgentId FromStr(string maybeAgentId) => new(maybeAgentId.ToKeyValuePair(nameof(Type), nameof(Key)));
-
- ///
- /// Returns the string representation of the .
- ///
- /// A string in the format "type/key".
- public override readonly string ToString() => $"{this.Type}/{this.Key}";
-
- ///
- /// Determines whether the specified object is equal to the current .
- ///
- /// The object to compare with the current instance.
- /// true if the specified object is equal to the current ; otherwise, false.
- public override readonly bool Equals([NotNullWhen(true)] object? obj)
- {
- return (obj is AgentId other && this.Equals(other));
- }
-
- ///
- public readonly bool Equals(AgentId other)
- {
- return this.Type == other.Type && this.Key == other.Key;
- }
-
- ///
- /// Returns a hash code for this .
- ///
- /// A hash code for the current instance.
- public override readonly int GetHashCode()
- {
- return HashCode.Combine(this.Type, this.Key);
- }
-
- ///
- /// Explicitly converts a string to an .
- ///
- /// The string representation of an agent ID.
- /// An instance of .
- public static explicit operator AgentId(string id) => FromStr(id);
-
- ///
- /// Equality operator for .
- ///
- public static bool operator ==(AgentId left, AgentId right) => left.Equals(right);
-
- ///
- /// Inequality operator for .
- ///
- public static bool operator !=(AgentId left, AgentId right) => !left.Equals(right);
-}
diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/AgentMetadata.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/AgentMetadata.cs
deleted file mode 100644
index f90b95a048..0000000000
--- a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/AgentMetadata.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using System;
-
-namespace Microsoft.Extensions.AI.Agents.Runtime;
-
-///
-/// Represents metadata associated with an agent, including its type, unique key, and description.
-///
-public readonly struct AgentMetadata(string type, string key, string description) : IEquatable
-{
- ///
- /// An identifier that associates an agent with a specific factory function.
- /// Strings may only be composed of alphanumeric letters (a-z, 0-9), or underscores (_).
- ///
- public string Type { get; } = type;
-
- ///
- /// A unique key identifying the agent instance.
- /// Strings may only be composed of alphanumeric letters (a-z, 0-9), or underscores (_).
- ///
- public string Key { get; } = key;
-
- ///
- /// A brief description of the agent's purpose or functionality.
- ///
- public string Description { get; } = description;
-
- ///
- public override readonly bool Equals(object? obj)
- {
- return obj is AgentMetadata agentMetadata && this.Equals(agentMetadata);
- }
-
- ///
- public readonly bool Equals(AgentMetadata other)
- {
- return this.Type.Equals(other.Type, StringComparison.Ordinal) && this.Key.Equals(other.Key, StringComparison.Ordinal);
- }
-
- ///
- public override readonly int GetHashCode()
- {
- return HashCode.Combine(this.Type, this.Key);
- }
-
- ///
- public static bool operator ==(AgentMetadata left, AgentMetadata right)
- {
- return left.Equals(right);
- }
-
- ///
- public static bool operator !=(AgentMetadata left, AgentMetadata right)
- {
- return !(left == right);
- }
-}
diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/AgentProxy.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/AgentProxy.cs
deleted file mode 100644
index 9ece29fda3..0000000000
--- a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/AgentProxy.cs
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using System.Text.Json;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace Microsoft.Extensions.AI.Agents.Runtime;
-
-///
-/// A proxy that allows you to use an in place of its associated .
-///
-public class AgentProxy
-{
- ///
- /// The runtime instance used to interact with agents.
- ///
- private readonly IAgentRuntime _runtime;
- private AgentMetadata? _metadata;
-
- ///
- /// Initializes a new instance of the class.
- ///
- public AgentProxy(AgentId agentId, IAgentRuntime runtime)
- {
- this.Id = agentId;
- this._runtime = runtime;
- }
-
- ///
- /// The target agent for this proxy.
- ///
- public AgentId Id { get; }
-
- ///
- /// Gets the metadata of the agent.
- ///
- ///
- /// An instance of containing details about the agent.
- ///
- public AgentMetadata Metadata => this._metadata ??= this.QueryMetadataAndUnwrap();
-
- ///
- /// Sends a message to the agent and processes the response.
- ///
- /// The message to send to the agent.
- /// The agent that is sending the message.
- ///
- /// The message ID. If null, a new message ID will be generated.
- /// This message ID must be unique and is recommended to be a UUID.
- ///
- ///
- /// A token used to cancel an in-progress operation. Defaults to null.
- ///
- /// A task representing the asynchronous operation, returning the response from the agent.
- public ValueTask