diff --git a/dotnet/Directory.Build.props b/dotnet/Directory.Build.props
index 24ddabdb2b..695cbd32bd 100644
--- a/dotnet/Directory.Build.props
+++ b/dotnet/Directory.Build.props
@@ -9,7 +9,8 @@
12enabledisable
- $(NoWarn);IDE0290;IDE0079
+ $(NoWarn);IDE0290;IDE0079;NU5128
+ truenet9.0;net8.0;netstandard2.0;net472net9.0;net472true
diff --git a/dotnet/samples/GettingStarted/External/MEAI.OpenAI/NewOpenAIAssistantChatClient.cs b/dotnet/samples/GettingStarted/External/MEAI.OpenAI/NewOpenAIAssistantChatClient.cs
index 9a747ea620..8c58871438 100644
--- a/dotnet/samples/GettingStarted/External/MEAI.OpenAI/NewOpenAIAssistantChatClient.cs
+++ b/dotnet/samples/GettingStarted/External/MEAI.OpenAI/NewOpenAIAssistantChatClient.cs
@@ -609,7 +609,7 @@ internal static class OpenAIClientExtensions2
// Roundtrip the schema through the ToolJson model type to remove extra properties
// and force missing ones into existence, then return the serialized UTF8 bytes as BinaryData.
- var tool = JsonSerializer.Deserialize(jsonSchema, OpenAIJsonContext.Default.ToolJson)!;
+ var tool = jsonSchema.Deserialize(OpenAIJsonContext.Default.ToolJson)!;
var functionParameters = BinaryData.FromBytes(JsonSerializer.SerializeToUtf8Bytes(tool, OpenAIJsonContext.Default.ToolJson));
return functionParameters;
diff --git a/dotnet/samples/GettingStarted/Orchestration/GroupChatOrchestration_With_AIManager.cs b/dotnet/samples/GettingStarted/Orchestration/GroupChatOrchestration_With_AIManager.cs
index 42f4844a92..185bccdc52 100644
--- a/dotnet/samples/GettingStarted/Orchestration/GroupChatOrchestration_With_AIManager.cs
+++ b/dotnet/samples/GettingStarted/Orchestration/GroupChatOrchestration_With_AIManager.cs
@@ -167,19 +167,19 @@ public class GroupChatOrchestration_With_AIManager(ITestOutputHelper output) : O
}
///
- public override ValueTask> FilterResults(IReadOnlyCollection history, CancellationToken cancellationToken = default) =>
+ protected override ValueTask> FilterResults(IReadOnlyCollection history, CancellationToken cancellationToken = default) =>
this.GetResponseAsync(history, Prompts.Filter(topic), cancellationToken);
///
- public override ValueTask> SelectNextAgent(IReadOnlyCollection history, GroupChatTeam team, CancellationToken cancellationToken = default) =>
+ protected override ValueTask> SelectNextAgent(IReadOnlyCollection history, GroupChatTeam team, CancellationToken cancellationToken = default) =>
this.GetResponseAsync(history, Prompts.Selection(topic, team.FormatList()), cancellationToken);
///
- public override ValueTask> ShouldRequestUserInput(IReadOnlyCollection history, CancellationToken cancellationToken = default) =>
+ protected override ValueTask> ShouldRequestUserInput(IReadOnlyCollection history, CancellationToken cancellationToken = default) =>
new(new GroupChatManagerResult(false) { Reason = "The AI group chat manager does not request user input." });
///
- public override async ValueTask> ShouldTerminate(IReadOnlyCollection history, CancellationToken cancellationToken = default)
+ protected override async ValueTask> ShouldTerminate(IReadOnlyCollection history, CancellationToken cancellationToken = default)
{
GroupChatManagerResult result = await base.ShouldTerminate(history, cancellationToken);
if (!result.Value)
diff --git a/dotnet/samples/GettingStarted/Orchestration/GroupChatOrchestration_With_HumanInTheLoop.cs b/dotnet/samples/GettingStarted/Orchestration/GroupChatOrchestration_With_HumanInTheLoop.cs
index 9e6e6286f5..7418cc54d8 100644
--- a/dotnet/samples/GettingStarted/Orchestration/GroupChatOrchestration_With_HumanInTheLoop.cs
+++ b/dotnet/samples/GettingStarted/Orchestration/GroupChatOrchestration_With_HumanInTheLoop.cs
@@ -84,7 +84,7 @@ public class GroupChatOrchestration_With_HumanInTheLoop(ITestOutputHelper output
///
private sealed class CustomRoundRobinGroupChatManager : RoundRobinGroupChatManager
{
- public override ValueTask> ShouldRequestUserInput(IReadOnlyCollection history, CancellationToken cancellationToken = default)
+ protected override ValueTask> ShouldRequestUserInput(IReadOnlyCollection history, CancellationToken cancellationToken = default)
{
string? lastAgent = history.LastOrDefault()?.AuthorName;
diff --git a/dotnet/src/LegacySupport/IsExternalInit/IsExternalInit.cs b/dotnet/src/LegacySupport/IsExternalInit/IsExternalInit.cs
index e86a0cd0e7..94efaa8ebc 100644
--- a/dotnet/src/LegacySupport/IsExternalInit/IsExternalInit.cs
+++ b/dotnet/src/LegacySupport/IsExternalInit/IsExternalInit.cs
@@ -4,6 +4,4 @@
namespace System.Runtime.CompilerServices;
-internal static class IsExternalInit
-{
-}
+internal static class IsExternalInit;
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/AgentOrchestration.RequestActor.cs b/dotnet/src/Microsoft.Agents.Orchestration/AgentOrchestration.RequestActor.cs
index 7a03982f97..0cc1d13a74 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.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.AI;
@@ -17,7 +18,7 @@ public abstract partial class AgentOrchestration
///
private sealed class RequestActor : OrchestrationActor
{
- private readonly Func>> _transform;
+ private readonly Func>> _transform;
private readonly Func, ValueTask> _action;
private readonly TaskCompletionSource _completionSource;
@@ -35,7 +36,7 @@ public abstract partial class AgentOrchestration
ActorId id,
IAgentRuntime runtime,
OrchestrationContext context,
- Func>> transform,
+ Func>> transform,
TaskCompletionSource completionSource,
Func, ValueTask> action,
ILogger? logger = null)
@@ -60,8 +61,8 @@ public abstract partial class AgentOrchestration
this.Logger.LogOrchestrationRequestInvoke(this.Context.Orchestration, this.Id);
try
{
- IEnumerable input = await this._transform.Invoke(item, cancellationToken).ConfigureAwait(false);
- Task task = this._action.Invoke(input).AsTask();
+ IEnumerable input = await this._transform.Invoke(item, messageContext.SerializerOptions, cancellationToken).ConfigureAwait(false);
+ var task = this._action.Invoke(input);
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 a593c47a58..44f4d672cf 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.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.AI;
@@ -19,7 +20,7 @@ public abstract partial class AgentOrchestration
{
private readonly TaskCompletionSource _completionSource;
private readonly Func> _transformResult;
- private readonly Func, CancellationToken, ValueTask> _transform;
+ private readonly Func, JsonSerializerOptions?, CancellationToken, ValueTask> _transform;
///
/// Initializes a new instance of the class.
@@ -36,7 +37,7 @@ public abstract partial class AgentOrchestration
IAgentRuntime runtime,
OrchestrationContext context,
Func> transformResult,
- Func, CancellationToken, ValueTask> transformOutput,
+ Func, JsonSerializerOptions?, CancellationToken, ValueTask> transformOutput,
TaskCompletionSource completionSource,
ILogger>? logger = null)
: base(id, runtime, context, $"{id.Type}_Actor", logger)
@@ -66,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, cancellationToken).ConfigureAwait(false);
+ TOutput output = await this._transform.Invoke(result, messageContext.SerializerOptions, 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 c282207dd5..07d4c30594 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/AgentOrchestration.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/AgentOrchestration.cs
@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
+using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.AI;
@@ -16,11 +17,6 @@ using Microsoft.Shared.Diagnostics;
namespace Microsoft.Agents.Orchestration;
-///
-/// Called when human interaction is requested.
-///
-public delegate ValueTask OrchestrationInteractiveCallback();
-
///
/// Base class for multi-agent agent orchestration patterns.
///
@@ -67,22 +63,22 @@ public abstract partial class AgentOrchestration
///
/// Transforms the orchestration input into a source input suitable for processing.
///
- public Func>> InputTransform { get; init; } = DefaultTransforms.FromInput;
+ public Func>>? InputTransform { get; set; }
///
/// Transforms the processed result into the final output form.
///
- public Func, CancellationToken, ValueTask> ResultTransform { get; init; } = DefaultTransforms.ToOutput;
+ public Func, JsonSerializerOptions?, CancellationToken, ValueTask>? ResultTransform { get; set; }
///
/// Optional callback that is invoked for every agent response.
///
- public Func, ValueTask>? ResponseCallback { get; init; }
+ public Func, ValueTask>? ResponseCallback { get; set; }
///
/// Optional callback that is invoked for every agent update.
///
- public Func? StreamingResponseCallback { get; init; }
+ public Func? StreamingResponseCallback { get; set; }
///
/// Gets the list of member targets involved in the orchestration.
@@ -183,7 +179,7 @@ public abstract partial class AgentOrchestration
logger.LogOrchestrationRegistrationStart(context.Orchestration, context.Topic);
// Register orchestration
- RegistrationContext registrar = new(this.FormatAgentType(context.Topic, "Root"), runtime, context, completion, this.ResultTransform);
+ RegistrationContext registrar = new(this.FormatAgentType(context.Topic, "Root"), runtime, context, completion, this.ResultTransform ?? DefaultTransforms.ToOutput);
ActorType? entryAgent = await this.RegisterOrchestrationAsync(runtime, context, registrar, logger).ConfigureAwait(false);
// Register actor for orchestration entry-point
@@ -196,7 +192,7 @@ public abstract partial class AgentOrchestration
new(agentId,
runtime,
context,
- this.InputTransform,
+ this.InputTransform ?? DefaultTransforms.FromInput,
completion,
input => this.StartAsync(runtime, context.Topic, input, entryAgent),
context.LoggerFactory.CreateLogger());
@@ -216,7 +212,7 @@ public abstract partial class AgentOrchestration
IAgentRuntime runtime,
OrchestrationContext context,
TaskCompletionSource completion,
- Func, CancellationToken, ValueTask> outputTransform)
+ Func, JsonSerializerOptions?, CancellationToken, ValueTask> outputTransform)
{
///
/// Register the final result type.
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Concurrent/ConcurrentOrchestration.String.cs b/dotnet/src/Microsoft.Agents.Orchestration/Concurrent/ConcurrentOrchestration.String.cs
index 114b3bd26b..81d49f6f3d 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/Concurrent/ConcurrentOrchestration.String.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/Concurrent/ConcurrentOrchestration.String.cs
@@ -20,7 +20,7 @@ public sealed class ConcurrentOrchestration : ConcurrentOrchestration
+ (response, _, cancellationToken) =>
{
string[] result = [.. response.Select(r => r.Text)];
return new ValueTask(result);
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/DefaultTransforms.cs b/dotnet/src/Microsoft.Agents.Orchestration/DefaultTransforms.cs
index 9b24dcb31b..054004bb6f 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/DefaultTransforms.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/DefaultTransforms.cs
@@ -6,68 +6,58 @@ using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.AI;
+using Microsoft.Extensions.AI.Agents;
+using Microsoft.Shared.Diagnostics;
namespace Microsoft.Agents.Orchestration;
internal static class DefaultTransforms
{
- public static ValueTask> FromInput(TInput input, CancellationToken cancellationToken = default) =>
- new(input switch
+ public static ValueTask> FromInput(TInput input, JsonSerializerOptions? serializerOptions = null, CancellationToken cancellationToken = default)
+ {
+ serializerOptions ??= AgentAbstractionsJsonUtilities.DefaultOptions;
+ return new(input switch
{
IEnumerable messages => messages,
ChatMessage message => [message],
string text => [new ChatMessage(ChatRole.User, text)],
- _ => [new ChatMessage(ChatRole.User, JsonSerializer.Serialize(input))]
+ _ => [new ChatMessage(ChatRole.User, JsonSerializer.Serialize(input, serializerOptions.GetTypeInfo(typeof(TInput))))]
});
+ }
- public static ValueTask ToOutput(IList result, CancellationToken cancellationToken = default)
+ public static ValueTask ToOutput(IList result, JsonSerializerOptions? serializerOptions = null, CancellationToken cancellationToken = default)
{
+ Throw.IfNull(result);
+
+ serializerOptions ??= AgentAbstractionsJsonUtilities.DefaultOptions;
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 (result is TOutput)
{
- if (isSingleResult)
- {
- try
- {
- return JsonSerializer.Deserialize(result[0].Text);
- }
- catch (JsonException)
- {
- }
- }
-
- return default;
+ return new((TOutput)(object)result);
}
- TOutput? GetDefaultOutput()
+ if (isSingleResult)
{
- if (typeof(TOutput).IsInstanceOfType(result))
+ if (typeof(ChatMessage).IsAssignableFrom(typeof(TOutput)))
{
- return (TOutput)(object)result;
+ return new((TOutput)(object)result[0]);
}
- if (isSingleResult)
+ if (typeof(string) == typeof(TOutput))
{
- 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 new((TOutput)(object)(result[0].Text ?? string.Empty));
}
- return default;
+ try
+ {
+ return new((TOutput)JsonSerializer.Deserialize(result[0].Text, serializerOptions.GetTypeInfo(typeof(TOutput)))!);
+ }
+ catch (JsonException)
+ {
+ }
}
+
+ throw new InvalidOperationException($"Unable to transform output to {typeof(TOutput)}.");
}
}
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatManager.cs b/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatManager.cs
index 8063475e8c..01436ad5fe 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatManager.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatManager.cs
@@ -1,5 +1,6 @@
// Copyright (c) Microsoft. All rights reserved.
+using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
@@ -50,7 +51,7 @@ public abstract class GroupChatManager
///
/// Gets or sets the callback to be invoked for interactive input.
///
- public OrchestrationInteractiveCallback? InteractiveCallback { get; init; }
+ public Func>? InteractiveCallback { get; init; }
///
/// Filters the results of the group chat based on the provided chat history.
@@ -58,7 +59,7 @@ public abstract class GroupChatManager
/// The chat history to filter.
/// A cancellation token that can be used to cancel the operation.
/// A containing the filtered result as a string.
- public abstract ValueTask> FilterResults(IReadOnlyCollection history, CancellationToken cancellationToken = default);
+ protected internal abstract ValueTask> FilterResults(IReadOnlyCollection history, CancellationToken cancellationToken = default);
///
/// Selects the next agent to participate in the group chat based on the provided chat history and team.
@@ -67,7 +68,7 @@ public abstract class GroupChatManager
/// The group of agents participating in the chat.
/// A cancellation token that can be used to cancel the operation.
/// A containing the identifier of the next agent as a string.
- public abstract ValueTask> SelectNextAgent(IReadOnlyCollection history, GroupChatTeam team, CancellationToken cancellationToken = default);
+ protected internal abstract ValueTask> SelectNextAgent(IReadOnlyCollection history, GroupChatTeam team, CancellationToken cancellationToken = default);
///
/// Determines whether user input should be requested based on the provided chat history.
@@ -75,7 +76,7 @@ public abstract class GroupChatManager
/// The chat history to consider.
/// A cancellation token that can be used to cancel the operation.
/// A indicating whether user input should be requested.
- public abstract ValueTask> ShouldRequestUserInput(IReadOnlyCollection history, CancellationToken cancellationToken = default);
+ protected internal abstract ValueTask> ShouldRequestUserInput(IReadOnlyCollection history, CancellationToken cancellationToken = default);
///
/// Determines whether the group chat should be terminated based on the provided chat history and invocation count.
@@ -83,7 +84,7 @@ public abstract class GroupChatManager
/// The chat history to consider.
/// A cancellation token that can be used to cancel the operation.
/// A indicating whether the chat should be terminated.
- public virtual ValueTask> ShouldTerminate(IReadOnlyCollection history, CancellationToken cancellationToken = default)
+ protected internal virtual ValueTask> ShouldTerminate(IReadOnlyCollection history, CancellationToken cancellationToken = default)
{
Interlocked.Increment(ref this._invocationCount);
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatOrchestration.cs b/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatOrchestration.cs
index ee90e41c2e..0a10e8242b 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatOrchestration.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatOrchestration.cs
@@ -1,6 +1,5 @@
// Copyright (c) Microsoft. All rights reserved.
-using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Extensions.AI;
@@ -39,8 +38,9 @@ public class GroupChatOrchestration :
{
if (!entryAgent.HasValue)
{
- throw new ArgumentException("Entry agent is not defined.", nameof(entryAgent));
+ Throw.ArgumentException(nameof(entryAgent), "Entry agent is not defined.");
}
+
return runtime.PublishMessageAsync(new GroupChatMessages.InputTask(input), entryAgent.Value);
}
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/RoundRobinGroupChatManager.cs b/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/RoundRobinGroupChatManager.cs
index 96170bf341..1e76ed4dd8 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/RoundRobinGroupChatManager.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/RoundRobinGroupChatManager.cs
@@ -19,14 +19,14 @@ public class RoundRobinGroupChatManager : GroupChatManager
private int _currentAgentIndex;
///
- public override ValueTask> FilterResults(IReadOnlyCollection history, CancellationToken cancellationToken = default)
+ protected internal 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." };
return new ValueTask>(result);
}
///
- public override ValueTask> SelectNextAgent(IReadOnlyCollection history, GroupChatTeam team, CancellationToken cancellationToken = default)
+ protected internal override ValueTask> SelectNextAgent(IReadOnlyCollection history, GroupChatTeam team, CancellationToken cancellationToken = default)
{
string nextAgent = team.Skip(this._currentAgentIndex).First().Key;
this._currentAgentIndex = (this._currentAgentIndex + 1) % team.Count;
@@ -35,7 +35,7 @@ public class RoundRobinGroupChatManager : GroupChatManager
}
///
- public override ValueTask> ShouldRequestUserInput(IReadOnlyCollection history, CancellationToken cancellationToken = default)
+ protected internal 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." };
return new ValueTask>(result);
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Handoff/HandoffActor.cs b/dotnet/src/Microsoft.Agents.Orchestration/Handoff/HandoffActor.cs
index fef0536db3..f6c8c1a151 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/Handoff/HandoffActor.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/Handoff/HandoffActor.cs
@@ -9,6 +9,7 @@ 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;
@@ -39,21 +40,19 @@ internal sealed partial class HandoffActor : AgentActor
public HandoffActor(ActorId id, IAgentRuntime runtime, OrchestrationContext context, ChatClientAgent agent, HandoffLookup handoffs, ActorType resultHandoff, ILogger? logger = null)
: base(id, runtime, context, agent, logger)
{
+ Throw.IfNull(handoffs);
+ Throw.IfNull(resultHandoff);
+
if (handoffs.ContainsKey(agent.Name ?? agent.Id))
{
- throw new ArgumentException($"The agent {agent.Name ?? agent.Id} cannot have a handoff to itself.", nameof(handoffs));
+ Throw.ArgumentException(nameof(handoffs), $"The agent {agent.Name ?? agent.Id} cannot have a handoff to itself.");
}
this._cache = [];
this._chatAgent = agent;
this._handoffs = handoffs;
this._resultHandoff = resultHandoff;
- this._options =
- new ChatOptions
- {
- Tools = [.. this.CreateHandoffFunctions()],
- ToolMode = ChatToolMode.Auto
- };
+ this._options = new() { Tools = this.CreateHandoffFunctions() };
this.RegisterMessageHandler(this.Handle);
this.RegisterMessageHandler(this.HandleAsync);
@@ -73,7 +72,7 @@ internal sealed partial class HandoffActor : AgentActor
///
/// Gets or sets the callback to be invoked for interactive input.
///
- public OrchestrationInteractiveCallback? InteractiveCallback { get; init; }
+ public Func>? InteractiveCallback { get; init; }
private void Handle(HandoffMessages.InputTask item, MessageContext messageContext)
{
@@ -144,23 +143,24 @@ internal sealed partial class HandoffActor : AgentActor
}
}
- private IEnumerable CreateHandoffFunctions()
+ private List CreateHandoffFunctions()
{
- yield return AIFunctionFactory.Create(
+ List functions = [];
+
+ functions.Add(AIFunctionFactory.Create(
this.EndAsync,
name: "end_task",
- description: "Complete the task with a summary when no further requests are given.");
+ description: "Complete the task with a summary when no further requests are given."));
foreach (KeyValuePair handoff in this._handoffs)
{
- AIFunction handoffFunction =
- AIFunctionFactory.Create(
+ functions.Add(AIFunctionFactory.Create(
() => this.Handoff(handoff.Key),
name: $"transfer_to_{InvalidNameCharsRegex().Replace(handoff.Key, "_")}",
- description: handoff.Value.Description);
-
- yield return handoffFunction;
+ description: handoff.Value.Description));
}
+
+ return functions;
}
private void Handoff(string agentName)
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Handoff/HandoffOrchestration.cs b/dotnet/src/Microsoft.Agents.Orchestration/Handoff/HandoffOrchestration.cs
index 0bd7fb05b2..2d515b9332 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/Handoff/HandoffOrchestration.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/Handoff/HandoffOrchestration.cs
@@ -8,6 +8,7 @@ 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;
@@ -41,7 +42,7 @@ public class HandoffOrchestration : AgentOrchestration 0)
{
- throw new ArgumentException($"The following agents are not defined in the orchestration: {string.Join(", ", badNames)}", nameof(handoffs));
+ Throw.ArgumentException(nameof(handoffs), $"The following agents are not defined in the orchestration: {string.Join(", ", badNames)}");
}
this._handoffs = handoffs;
@@ -50,15 +51,13 @@ public class HandoffOrchestration : AgentOrchestration
/// Gets or sets the callback to be invoked for interactive input.
///
- public OrchestrationInteractiveCallback? InteractiveCallback { get; init; }
+ public Func>? InteractiveCallback { get; init; }
///
protected override async ValueTask StartAsync(IAgentRuntime runtime, TopicId topic, IEnumerable input, ActorType? entryAgent)
{
- if (!entryAgent.HasValue)
- {
- throw new ArgumentException("Entry agent is not defined.", nameof(entryAgent));
- }
+ Throw.IfNull(entryAgent);
+
await runtime.PublishMessageAsync(new HandoffMessages.InputTask([.. input]), topic).ConfigureAwait(false);
await runtime.PublishMessageAsync(new HandoffMessages.Request(), entryAgent.Value).ConfigureAwait(false);
}
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Handoff/Handoffs.cs b/dotnet/src/Microsoft.Agents.Orchestration/Handoff/Handoffs.cs
index 5fe8ec70f7..d1aac7efd2 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/Handoff/Handoffs.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/Handoff/Handoffs.cs
@@ -1,6 +1,5 @@
// Copyright (c) Microsoft. All rights reserved.
-using System;
using System.Collections.Generic;
using Microsoft.Extensions.AI.Agents;
using Microsoft.Extensions.AI.Agents.Runtime;
@@ -82,7 +81,7 @@ public sealed class OrchestrationHandoffs : Dictionary
{
if (string.IsNullOrWhiteSpace(target.Description) && string.IsNullOrWhiteSpace(target.Name))
{
- 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.");
+ Throw.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);
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Microsoft.Agents.Orchestration.csproj b/dotnet/src/Microsoft.Agents.Orchestration/Microsoft.Agents.Orchestration.csproj
index 78878572cc..de550fc682 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/Microsoft.Agents.Orchestration.csproj
+++ b/dotnet/src/Microsoft.Agents.Orchestration/Microsoft.Agents.Orchestration.csproj
@@ -5,7 +5,6 @@
$(ProjectsDebugTargetFrameworks)Microsoft.Agents.Orchestrationalpha
- false
diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Transforms/StructuredOutputTransform.cs b/dotnet/src/Microsoft.Agents.Orchestration/Transforms/StructuredOutputTransform.cs
index 52c7fb08a0..3a1af8bd0c 100644
--- a/dotnet/src/Microsoft.Agents.Orchestration/Transforms/StructuredOutputTransform.cs
+++ b/dotnet/src/Microsoft.Agents.Orchestration/Transforms/StructuredOutputTransform.cs
@@ -2,9 +2,11 @@
using System;
using System.Collections.Generic;
+using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.AI;
+using Microsoft.Extensions.AI.Agents;
using Microsoft.Shared.Diagnostics;
namespace Microsoft.Agents.Orchestration;
@@ -42,17 +44,23 @@ public sealed class StructuredOutputTransform
/// Transforms the provided into a strongly-typed structured output by invoking the chat completion service and deserializing the response.
///
/// The chat messages to process.
+ /// The JSON serializer options to use when performing any JSON serialization.
/// A cancellation token to observe while waiting for the task to complete.
/// The structured output of type .
/// Thrown if the response cannot be deserialized into .
- public async ValueTask TransformAsync(IList messages, CancellationToken cancellationToken = default)
+ public async ValueTask TransformAsync(IList messages, JsonSerializerOptions? serializerOptions = null, CancellationToken cancellationToken = default)
{
- IEnumerable input =
+ Throw.IfNull(messages);
+
+ ChatResponse response = await this._client.GetResponseAsync(
[
new ChatMessage(ChatRole.System, this.Instructions),
.. messages,
- ];
- ChatResponse response = await this._client.GetResponseAsync(input, this._options, useJsonSchemaResponseFormat: true, cancellationToken).ConfigureAwait(false);
+ ],
+ serializerOptions ?? AgentAbstractionsJsonUtilities.DefaultOptions,
+ this._options,
+ cancellationToken: cancellationToken).ConfigureAwait(false);
+
return response.Result;
}
}
diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AIAgent.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AIAgent.cs
index a8fc41ed1a..334e5b5b30 100644
--- a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AIAgent.cs
+++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AIAgent.cs
@@ -207,7 +207,9 @@ public abstract class AIAgent
Func constructThread)
where TThreadType : AgentThread
{
- thread ??= constructThread is not null ? constructThread() : throw new ArgumentNullException(nameof(constructThread));
+ Throw.IfNull(constructThread);
+
+ thread ??= constructThread();
if (thread is not TThreadType concreteThreadType)
{
diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AgentAbstractionsJsonUtilities.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AgentAbstractionsJsonUtilities.cs
new file mode 100644
index 0000000000..524b0b96a5
--- /dev/null
+++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AgentAbstractionsJsonUtilities.cs
@@ -0,0 +1,62 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System.Diagnostics.CodeAnalysis;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace Microsoft.Extensions.AI.Agents;
+
+/// Provides a collection of utility methods for working with JSON data in the context of agents.
+public static partial class AgentAbstractionsJsonUtilities
+{
+ ///
+ /// Gets the singleton used as the default in JSON serialization operations.
+ ///
+ ///
+ ///
+ /// For Native AOT or applications disabling , this instance
+ /// includes source generated contracts for all common exchange types contained in this library.
+ ///
+ ///
+ /// It additionally turns on the following settings:
+ ///
+ /// Enables defaults.
+ /// Enables as the default ignore condition for properties.
+ /// Enables as the default number handling for number types.
+ ///
+ ///
+ ///
+ public static JsonSerializerOptions DefaultOptions { get; } = CreateDefaultOptions();
+
+ ///
+ /// Creates default options to use for agents-related serialization.
+ ///
+ /// The configured options.
+ [UnconditionalSuppressMessage("ReflectionAnalysis", "IL3050:RequiresDynamicCode", Justification = "Converter is guarded by IsReflectionEnabledByDefault check.")]
+ [UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access", Justification = "Converter is guarded by IsReflectionEnabledByDefault check.")]
+ private static JsonSerializerOptions CreateDefaultOptions()
+ {
+ // Copy the configuration from the source generated context.
+ JsonSerializerOptions options = new(JsonContext.Default.Options);
+
+ // Chain with all supported types from MEAI.
+ options.TypeInfoResolverChain.Add(AIJsonUtilities.DefaultOptions.TypeInfoResolver!);
+
+ options.MakeReadOnly();
+ return options;
+ }
+
+ // Keep in sync with CreateDefaultOptions above.
+ [JsonSourceGenerationOptions(JsonSerializerDefaults.Web,
+ DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
+ NumberHandling = JsonNumberHandling.AllowReadingFromString)]
+
+ // Agent abstraction types
+ [JsonSerializable(typeof(AgentRunOptions))]
+ [JsonSerializable(typeof(AgentRunResponse))]
+ [JsonSerializable(typeof(AgentRunResponseUpdate))]
+ [JsonSerializable(typeof(AgentThread))]
+
+ [ExcludeFromCodeCoverage]
+ internal sealed partial class JsonContext : JsonSerializerContext;
+}
diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AgentRunResponse.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AgentRunResponse.cs
index 2dab486bf8..14ff73e884 100644
--- a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AgentRunResponse.cs
+++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AgentRunResponse.cs
@@ -1,10 +1,14 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
+#if NET9_0_OR_GREATER
using System.Buffers;
+#endif
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
+#if NET9_0_OR_GREATER
using System.Text;
+#endif
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;
@@ -22,13 +26,6 @@ namespace Microsoft.Extensions.AI.Agents;
///
public class AgentRunResponse
{
- private static readonly JsonReaderOptions s_allowMultipleValuesJsonReaderOptions = new()
- {
-#if NET9_0_OR_GREATER
- AllowMultipleValues = true
-#endif
- };
-
/// The response messages.
private IList? _messages;
@@ -184,15 +181,16 @@ public class AgentRunResponse
}
#pragma warning disable CA1031 // Do not catch general exception types
catch
+#pragma warning restore CA1031
{
structuredOutput = default;
return false;
}
-#pragma warning restore CA1031 // Do not catch general exception types
}
private static T? DeserializeFirstTopLevelObject(string json, JsonTypeInfo typeInfo)
{
+#if NET9_0_OR_GREATER
// We need to deserialize only the first top-level object as a workaround for a common LLM backend
// issue. GPT 3.5 Turbo commonly returns multiple top-level objects after doing a function call.
// See https://community.openai.com/t/2-json-objects-returned-when-using-function-calling-and-json-mode/574348
@@ -201,14 +199,16 @@ public class AgentRunResponse
try
{
var utf8SpanLength = Encoding.UTF8.GetBytes(json, 0, json.Length, buffer, 0);
- var utf8Span = new ReadOnlySpan(buffer, 0, utf8SpanLength);
- var reader = new Utf8JsonReader(utf8Span, s_allowMultipleValuesJsonReaderOptions);
+ var reader = new Utf8JsonReader(new ReadOnlySpan(buffer, 0, utf8SpanLength), new() { AllowMultipleValues = true });
return JsonSerializer.Deserialize(ref reader, typeInfo);
}
finally
{
ArrayPool.Shared.Return(buffer);
}
+#else
+ return JsonSerializer.Deserialize(json, typeInfo);
+#endif
}
private T? GetResultCore(JsonSerializerOptions serializerOptions, out FailureReason? failureReason)
diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/Microsoft.Extensions.AI.Agents.Abstractions.csproj b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/Microsoft.Extensions.AI.Agents.Abstractions.csproj
index 4bc963ea9c..335022ffd9 100644
--- a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/Microsoft.Extensions.AI.Agents.Abstractions.csproj
+++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/Microsoft.Extensions.AI.Agents.Abstractions.csproj
@@ -9,6 +9,7 @@
true
+ true
diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.CopilotStudio/CopilotStudioAgent.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.CopilotStudio/CopilotStudioAgent.cs
index 8f4e18a866..ede152af99 100644
--- a/dotnet/src/Microsoft.Extensions.AI.Agents.CopilotStudio/CopilotStudioAgent.cs
+++ b/dotnet/src/Microsoft.Extensions.AI.Agents.CopilotStudio/CopilotStudioAgent.cs
@@ -53,12 +53,9 @@ public class CopilotStudioAgent : AIAgent
Throw.IfNull(messages);
// Ensure that we have a valid thread to work with.
+ // If the thread ID is null, we need to start a new conversation and set the thread ID accordingly.
CopilotStudioAgentThread copilotStudioAgentThread = base.ValidateOrCreateThreadType(thread, () => new CopilotStudioAgentThread());
- if (copilotStudioAgentThread.Id is null)
- {
- // If the thread ID is null, we need to start a new conversation and set the thread ID accordingly.
- copilotStudioAgentThread.Id = await this.StartNewConversationAsync(cancellationToken).ConfigureAwait(false);
- }
+ copilotStudioAgentThread.Id ??= await this.StartNewConversationAsync(cancellationToken).ConfigureAwait(false);
// Invoke the Copilot Studio agent with the provided messages.
string question = string.Join("\n", messages.Select(m => m.Text));
@@ -89,12 +86,9 @@ public class CopilotStudioAgent : AIAgent
Throw.IfNull(messages);
// Ensure that we have a valid thread to work with.
+ // If the thread ID is null, we need to start a new conversation and set the thread ID accordingly.
CopilotStudioAgentThread copilotStudioAgentThread = base.ValidateOrCreateThreadType(thread, () => new CopilotStudioAgentThread());
- if (copilotStudioAgentThread.Id is null)
- {
- // If the thread ID is null, we need to start a new conversation and set the thread ID accordingly.
- copilotStudioAgentThread.Id = await this.StartNewConversationAsync(cancellationToken).ConfigureAwait(false);
- }
+ copilotStudioAgentThread.Id ??= await this.StartNewConversationAsync(cancellationToken).ConfigureAwait(false);
// Invoke the Copilot Studio agent with the provided messages.
string question = string.Join("\n", messages.Select(m => m.Text));
diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.CopilotStudio/CopilotStudioAgentThread.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.CopilotStudio/CopilotStudioAgentThread.cs
index b7493a1f7b..cff6d2c399 100644
--- a/dotnet/src/Microsoft.Extensions.AI.Agents.CopilotStudio/CopilotStudioAgentThread.cs
+++ b/dotnet/src/Microsoft.Extensions.AI.Agents.CopilotStudio/CopilotStudioAgentThread.cs
@@ -5,6 +5,4 @@ namespace Microsoft.Extensions.AI.Agents.CopilotStudio;
///
/// Represents a thread for interacting with a Copilot Studio agent.
///
-public class CopilotStudioAgentThread : AgentThread
-{
-}
+public class CopilotStudioAgentThread : AgentThread;
diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/IdProxyActor.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/IdProxyActor.cs
index 250927bdae..6cafeacb9d 100644
--- a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/IdProxyActor.cs
+++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/IdProxyActor.cs
@@ -3,6 +3,7 @@
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
+using Microsoft.Shared.Diagnostics;
namespace Microsoft.Extensions.AI.Agents.Runtime;
@@ -21,6 +22,8 @@ public sealed class IdProxyActor : IRuntimeActor
///
public IdProxyActor(IAgentRuntime runtime, ActorId actorId)
{
+ Throw.IfNull(runtime);
+
this.Id = actorId;
this._runtime = runtime;
}
diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/InProcessRuntime.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/InProcessRuntime.cs
index a947860759..6eb8b1de44 100644
--- a/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/InProcessRuntime.cs
+++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Runtime.Abstractions/InProcessRuntime.cs
@@ -73,6 +73,8 @@ public sealed partial class InProcessRuntime : IAgentRuntime, IAsyncDisposable
///
public ValueTask PublishMessageAsync(object message, TopicId topic, ActorId? sender = null, string? messageId = null, CancellationToken cancellationToken = default)
{
+ Throw.IfNull(message);
+
MessageToProcess m = new(this, message, messageId, sender, topic, cancellationToken);
this.IncrementRemainingWork();
@@ -84,6 +86,8 @@ public sealed partial class InProcessRuntime : IAgentRuntime, IAsyncDisposable
///
public ValueTask
/// The topic to check.
/// true if the topic's type matches exactly, false otherwise.
- public bool Matches(TopicId topic)
- {
- return topic.Type == this.TopicType;
- }
+ public bool Matches(TopicId topic) => topic.Type == this.TopicType;
///
/// Maps a to an . Should only be called if returns true.
@@ -79,14 +79,9 @@ public sealed class TypeSubscription : ISubscriptionDefinition
///
/// The object to compare with the current instance.
/// true if the specified object is equal to this instance; otherwise, false.
- public override bool Equals([NotNullWhen(true)] object? obj)
- {
- return
- obj is TypeSubscription other &&
- (this.Id == other.Id ||
- (this.ActorType == other.ActorType &&
- this.TopicType == other.TopicType));
- }
+ public override bool Equals([NotNullWhen(true)] object? obj) =>
+ obj is TypeSubscription other &&
+ (this.Id == other.Id || (this.ActorType == other.ActorType && this.TopicType == other.TopicType));
///
/// Determines whether the specified subscription is equal to the current subscription.
@@ -99,8 +94,5 @@ public sealed class TypeSubscription : ISubscriptionDefinition
/// Returns a hash code for this instance.
///
/// A hash code for this instance, suitable for use in hashing algorithms and data structures.
- public override int GetHashCode()
- {
- return HashCode.Combine(this.Id, this.ActorType, this.TopicType);
- }
+ public override int GetHashCode() => HashCode.Combine(this.Id, this.ActorType, this.TopicType);
}
diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents/ChatCompletion/ChatClientAgent.cs b/dotnet/src/Microsoft.Extensions.AI.Agents/ChatCompletion/ChatClientAgent.cs
index b35f0e6ab5..9d0b64011f 100644
--- a/dotnet/src/Microsoft.Extensions.AI.Agents/ChatCompletion/ChatClientAgent.cs
+++ b/dotnet/src/Microsoft.Extensions.AI.Agents/ChatCompletion/ChatClientAgent.cs
@@ -360,12 +360,9 @@ public sealed class ChatClientAgent : AIAgent
private void UpdateThreadWithTypeAndConversationId(ChatClientAgentThread chatClientThread, string? responseConversationId)
{
// Set the thread's storage location, the first time that we use it.
- if (chatClientThread.StorageLocation is null)
- {
- chatClientThread.StorageLocation = string.IsNullOrWhiteSpace(responseConversationId)
- ? ChatClientAgentThreadType.InMemoryMessages
- : ChatClientAgentThreadType.ConversationId;
- }
+ chatClientThread.StorageLocation ??= string.IsNullOrWhiteSpace(responseConversationId)
+ ? ChatClientAgentThreadType.InMemoryMessages
+ : ChatClientAgentThreadType.ConversationId;
// If we got a conversation id back from the chat client, it means that the service supports server side thread storage
// so we should capture the id and update the thread with the new id.
diff --git a/dotnet/tests/Microsoft.Agents.Orchestration.UnitTests/DefaultTransformsTests.cs b/dotnet/tests/Microsoft.Agents.Orchestration.UnitTests/DefaultTransformsTests.cs
index 7a760b023d..7abeaf22a6 100644
--- a/dotnet/tests/Microsoft.Agents.Orchestration.UnitTests/DefaultTransformsTests.cs
+++ b/dotnet/tests/Microsoft.Agents.Orchestration.UnitTests/DefaultTransformsTests.cs
@@ -6,6 +6,7 @@ using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.Extensions.AI;
+using Microsoft.Extensions.AI.Agents;
namespace Microsoft.Agents.Orchestration.UnitTest;
@@ -72,7 +73,7 @@ public class DefaultTransformsTests
ChatMessage message = result.First();
Assert.Equal(ChatRole.User, message.Role);
- string expectedJson = JsonSerializer.Serialize(input);
+ string expectedJson = JsonSerializer.Serialize(input, AgentAbstractionsJsonUtilities.DefaultOptions);
Assert.Equal(expectedJson, message.Text);
}
diff --git a/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentRunOptionsTests.cs b/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentRunOptionsTests.cs
index c602af78f0..473501a554 100644
--- a/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentRunOptionsTests.cs
+++ b/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentRunOptionsTests.cs
@@ -13,9 +13,7 @@ public class AgentRunOptionsTests
public void CloningConstructorCopiesProperties()
{
// Arrange
- var options = new AgentRunOptions
- {
- };
+ var options = new AgentRunOptions();
// Act
var clone = new AgentRunOptions(options);
diff --git a/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentRunResponseUpdateExtensionsTests.cs b/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentRunResponseUpdateExtensionsTests.cs
index 7f91394417..041bc201b2 100644
--- a/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentRunResponseUpdateExtensionsTests.cs
+++ b/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentRunResponseUpdateExtensionsTests.cs
@@ -145,7 +145,7 @@ public class AgentRunResponseUpdateExtensionsTests
public async Task ToAgentRunResponseCoalescesTextContentAndTextReasoningContentSeparatelyAsync(bool useAsync)
{
AgentRunResponseUpdate[] updates =
- {
+ [
new(null, "A"),
new(null, "B"),
new(null, "C"),
@@ -162,7 +162,7 @@ public class AgentRunResponseUpdateExtensionsTests
new(null, "N"),
new() { Contents = [new TextReasoningContent("O")] },
new() { Contents = [new TextReasoningContent("P")] },
- };
+ ];
AgentRunResponse response = useAsync ? await YieldAsync(updates).ToAgentRunResponseAsync() : updates.ToAgentRunResponse();
ChatMessage message = Assert.Single(response.Messages);
@@ -181,11 +181,11 @@ public class AgentRunResponseUpdateExtensionsTests
public async Task ToAgentRunResponseUsesContentExtractedFromContentsAsync()
{
AgentRunResponseUpdate[] updates =
- {
+ [
new(null, "Hello, "),
new(null, "world!"),
new() { Contents = [new UsageContent(new() { TotalTokenCount = 42 })] },
- };
+ ];
AgentRunResponse response = await YieldAsync(updates).ToAgentRunResponseAsync();
diff --git a/dotnet/tests/Microsoft.Extensions.AI.Agents.Runtime.Abstractions.UnitTests/TestAgents.cs b/dotnet/tests/Microsoft.Extensions.AI.Agents.Runtime.Abstractions.UnitTests/TestAgents.cs
index 3b35407d67..fabb13704c 100644
--- a/dotnet/tests/Microsoft.Extensions.AI.Agents.Runtime.Abstractions.UnitTests/TestAgents.cs
+++ b/dotnet/tests/Microsoft.Extensions.AI.Agents.Runtime.Abstractions.UnitTests/TestAgents.cs
@@ -42,7 +42,7 @@ public sealed class MockAgent : TestAgent
public override ValueTask LoadStateAsync(JsonElement state, CancellationToken cancellationToken = default)
{
- this.ReceivedMessages = JsonSerializer.Deserialize>(state) ?? throw new InvalidOperationException("Failed to deserialize state");
+ this.ReceivedMessages = state.Deserialize>() ?? throw new InvalidOperationException("Failed to deserialize state");
return default;
}
}
diff --git a/dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/ChatCompletion/ChatClientAgentRunOptionsTests.cs b/dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/ChatCompletion/ChatClientAgentRunOptionsTests.cs
index 6639e05341..12cbe53779 100644
--- a/dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/ChatCompletion/ChatClientAgentRunOptionsTests.cs
+++ b/dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/ChatCompletion/ChatClientAgentRunOptionsTests.cs
@@ -40,9 +40,7 @@ public class ChatClientAgentRunOptionsTests
public void ConstructorCopiesPropertiesFromSourceAgentRunOptions()
{
// Arrange
- var sourceRunOptions = new AgentRunOptions
- {
- };
+ var sourceRunOptions = new AgentRunOptions();
var chatOptions = new ChatOptions { MaxOutputTokens = 200 };
// Act
@@ -59,9 +57,7 @@ public class ChatClientAgentRunOptionsTests
public void ConstructorWorksWithSourceButNullChatOptions()
{
// Arrange
- var sourceRunOptions = new AgentRunOptions
- {
- };
+ var sourceRunOptions = new AgentRunOptions();
// Act
var runOptions = new ChatClientAgentRunOptions(sourceRunOptions, null);