From 6b22b6bbc7744329bf212ad16764bd077fb834aa Mon Sep 17 00:00:00 2001
From: westey <164392973+westey-m@users.noreply.github.com>
Date: Thu, 28 Aug 2025 11:59:23 +0100
Subject: [PATCH] .NET: Fixes for agent abstractions to improve MEAI static
analysis compliance (#498)
* Fixes for agent abstractions to improve MEAI static analysis compliance
* Fix build error
---
.../AIAgent.cs | 50 ++++++++++++++-----
.../AgentRunOptions.cs | 2 +-
.../AgentRunResponse.cs | 14 ++++--
.../AgentRunResponseUpdate.cs | 2 +-
.../AgentRunResponseUpdateExtensions.cs | 4 +-
.../AgentThread.cs | 47 ++++++++---------
.../IChatMessageStore.cs | 1 +
.../InMemoryChatMessageStore.cs | 4 +-
.../ChatCompletion/ChatClientAgent.cs | 8 +--
.../{AgentTests.cs => AIAgentTests.cs} | 45 ++++++++++-------
.../AgentRunOptionsTests.cs | 1 +
...ests.cs => AgentRunResponseUpdateTests.cs} | 0
.../AgentThreadTests.cs | 14 ++++--
13 files changed, 120 insertions(+), 72 deletions(-)
rename dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/{AgentTests.cs => AIAgentTests.cs} (88%)
rename dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/{AgentRunResponseUpdatesTests.cs => AgentRunResponseUpdateTests.cs} (100%)
diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AIAgent.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AIAgent.cs
index 328d15352f..05bdf49039 100644
--- a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AIAgent.cs
+++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AIAgent.cs
@@ -100,7 +100,10 @@ public abstract class AIAgent
///
/// Run the agent with no message assuming that all required instructions are already provided to the agent or on the thread.
///
- /// The conversation thread to continue with this invocation. If not provided, creates a new thread. The thread will be mutated with the provided messages and agent response.
+ ///
+ /// The conversation thread to continue with this invocation. If not provided, creates a new thread.
+ /// The thread will be mutated with the provided messages and agent response.
+ ///
/// Optional parameters for agent invocation.
/// The to monitor for cancellation requests. The default is .
/// A containing the list of items.
@@ -116,7 +119,10 @@ public abstract class AIAgent
/// Run the agent with the provided message and arguments.
///
/// The message to pass to the agent.
- /// The conversation thread to continue with this invocation. If not provided, creates a new thread. The thread will be mutated with the provided messages and agent response.
+ ///
+ /// The conversation thread to continue with this invocation. If not provided, creates a new thread.
+ /// The thread will be mutated with the provided messages and agent response.
+ ///
/// Optional parameters for agent invocation.
/// The to monitor for cancellation requests. The default is .
/// A containing the list of items.
@@ -129,7 +135,7 @@ public abstract class AIAgent
AgentRunOptions? options = null,
CancellationToken cancellationToken = default)
{
- Throw.IfNullOrWhitespace(message);
+ _ = Throw.IfNullOrWhitespace(message);
return this.RunAsync(new ChatMessage(ChatRole.User, message), thread, options, cancellationToken);
}
@@ -138,7 +144,10 @@ public abstract class AIAgent
/// Run the agent with the provided message and arguments.
///
/// The message to pass to the agent.
- /// The conversation thread to continue with this invocation. If not provided, creates a new thread. The thread will be mutated with the provided messages and agent response.
+ ///
+ /// The conversation thread to continue with this invocation. If not provided, creates a new thread.
+ /// The thread will be mutated with the provided messages and agent response.
+ ///
/// Optional parameters for agent invocation.
/// The to monitor for cancellation requests. The default is .
/// A containing the list of items.
@@ -148,7 +157,7 @@ public abstract class AIAgent
AgentRunOptions? options = null,
CancellationToken cancellationToken = default)
{
- Throw.IfNull(message);
+ _ = Throw.IfNull(message);
return this.RunAsync([message], thread, options, cancellationToken);
}
@@ -157,7 +166,10 @@ public abstract class AIAgent
/// Run the agent with the provided message and arguments.
///
/// The messages to pass to the agent.
- /// The conversation thread to continue with this invocation. If not provided, creates a new thread. The thread will be mutated with the provided messages and agent response.
+ ///
+ /// The conversation thread to continue with this invocation. If not provided, creates a new thread.
+ /// The thread will be mutated with the provided messages and agent response.
+ ///
/// Optional parameters for agent invocation.
/// The to monitor for cancellation requests. The default is .
/// A containing the list of items.
@@ -170,7 +182,10 @@ public abstract class AIAgent
///
/// Run the agent with no message assuming that all required instructions are already provided to the agent or on the thread.
///
- /// The conversation thread to continue with this invocation. If not provided, creates a new thread. The thread will be mutated with the provided messages and agent response.
+ ///
+ /// The conversation thread to continue with this invocation. If not provided, creates a new thread.
+ /// The thread will be mutated with the provided messages and agent response.
+ ///
/// Optional parameters for agent invocation.
/// The to monitor for cancellation requests. The default is .
/// An async list of response items that each contain a .
@@ -186,7 +201,10 @@ public abstract class AIAgent
/// Run the agent with the provided message and arguments.
///
/// The message to pass to the agent.
- /// The conversation thread to continue with this invocation. If not provided, creates a new thread. The thread will be mutated with the provided messages and agent response.
+ ///
+ /// The conversation thread to continue with this invocation. If not provided, creates a new thread.
+ /// The thread will be mutated with the provided messages and agent response.
+ ///
/// Optional parameters for agent invocation.
/// The to monitor for cancellation requests. The default is .
/// An async list of response items that each contain a .
@@ -199,7 +217,7 @@ public abstract class AIAgent
AgentRunOptions? options = null,
CancellationToken cancellationToken = default)
{
- Throw.IfNullOrWhitespace(message);
+ _ = Throw.IfNullOrWhitespace(message);
return this.RunStreamingAsync(new ChatMessage(ChatRole.User, message), thread, options, cancellationToken);
}
@@ -208,7 +226,10 @@ public abstract class AIAgent
/// Run the agent with the provided message and arguments.
///
/// The message to pass to the agent.
- /// The conversation thread to continue with this invocation. If not provided, creates a new thread. The thread will be mutated with the provided messages and agent response.
+ ///
+ /// The conversation thread to continue with this invocation. If not provided, creates a new thread.
+ /// The thread will be mutated with the provided messages and agent response.
+ ///
/// Optional parameters for agent invocation.
/// The to monitor for cancellation requests. The default is .
/// An async list of response items that each contain a .
@@ -218,7 +239,7 @@ public abstract class AIAgent
AgentRunOptions? options = null,
CancellationToken cancellationToken = default)
{
- Throw.IfNull(message);
+ _ = Throw.IfNull(message);
return this.RunStreamingAsync([message], thread, options, cancellationToken);
}
@@ -227,7 +248,10 @@ public abstract class AIAgent
/// Run the agent with the provided message and arguments.
///
/// The messages to pass to the agent.
- /// The conversation thread to continue with this invocation. If not provided, creates a new thread. The thread will be mutated with the provided messages and agent response.
+ ///
+ /// The conversation thread to continue with this invocation. If not provided, creates a new thread.
+ /// The thread will be mutated with the provided messages and agent response.
+ ///
/// Optional parameters for agent invocation.
/// The to monitor for cancellation requests. The default is .
/// An async list of response items that each contain a .
@@ -261,7 +285,7 @@ public abstract class AIAgent
/// The messages to pass to the thread.
/// The to monitor for cancellation requests. The default is .
/// An async task that completes once the notification is complete.
- protected async Task NotifyThreadOfNewMessagesAsync(AgentThread thread, IReadOnlyCollection messages, CancellationToken cancellationToken)
+ protected static async Task NotifyThreadOfNewMessagesAsync(AgentThread thread, IReadOnlyCollection messages, CancellationToken cancellationToken)
{
_ = Throw.IfNull(thread);
_ = Throw.IfNull(messages);
diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AgentRunOptions.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AgentRunOptions.cs
index 83a98ffd4e..2b0784255c 100644
--- a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AgentRunOptions.cs
+++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AgentRunOptions.cs
@@ -22,6 +22,6 @@ public class AgentRunOptions
/// The options to clone.
public AgentRunOptions(AgentRunOptions options)
{
- Throw.IfNull(options);
+ _ = Throw.IfNull(options);
}
}
diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AgentRunResponse.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AgentRunResponse.cs
index 0f28bfbae9..f2591a4081 100644
--- a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AgentRunResponse.cs
+++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AgentRunResponse.cs
@@ -1,14 +1,14 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
-#if NET8_0_OR_GREATER
+#if NET9_0_OR_GREATER
using System.Buffers;
#endif
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
-#if NET8_0_OR_GREATER
+#if NET9_0_OR_GREATER
using System.Text;
#endif
using System.Text.Json;
@@ -16,6 +16,9 @@ using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;
using Microsoft.Shared.Diagnostics;
+#pragma warning disable S109 // Magic numbers should not be used
+#pragma warning disable S1121 // Assignments should not be made from within sub-expressions
+
namespace Microsoft.Extensions.AI.Agents;
/// Represents the response to an Agent run request.
@@ -84,7 +87,7 @@ public class AgentRunResponse
[JsonIgnore]
public string Text => this._messages?.ConcatText() ?? string.Empty;
- /// Gets or sets the user input requests associated with the response.
+ /// Gets the user input requests associated with the response.
///
/// This property concatenates all instances in the response.
///
@@ -170,7 +173,6 @@ public class AgentRunResponse
return updates;
}
- // TODO: Add overloads without serializer options.
///
/// Deserializes the response text into the given type using the specified serializer options.
///
@@ -180,6 +182,8 @@ public class AgentRunResponse
/// The result is not parsable into the requested type.
public T Deserialize(JsonSerializerOptions serializerOptions)
{
+ _ = Throw.IfNull(serializerOptions);
+
var structuredOutput = this.GetResultCore(serializerOptions, out var failureReason);
return failureReason switch
{
@@ -198,6 +202,8 @@ public class AgentRunResponse
/// if parsing was successful; otherwise, .
public bool TryDeserialize(JsonSerializerOptions serializerOptions, [NotNullWhen(true)] out T? structuredOutput)
{
+ _ = Throw.IfNull(serializerOptions);
+
try
{
structuredOutput = this.GetResultCore(serializerOptions, out var failureReason);
diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AgentRunResponseUpdate.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AgentRunResponseUpdate.cs
index a40fac7c3c..4f158c9e81 100644
--- a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AgentRunResponseUpdate.cs
+++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AgentRunResponseUpdate.cs
@@ -93,7 +93,7 @@ public class AgentRunResponseUpdate
[JsonIgnore]
public string Text => this._contents is not null ? this._contents.ConcatText() : string.Empty;
- /// Gets or sets the user input requests associated with the response.
+ /// Gets the user input requests associated with the response.
///
/// This property concatenates all instances in the response.
///
diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AgentRunResponseUpdateExtensions.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AgentRunResponseUpdateExtensions.cs
index 4078ce5939..37ff85ffdd 100644
--- a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AgentRunResponseUpdateExtensions.cs
+++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AgentRunResponseUpdateExtensions.cs
@@ -2,12 +2,14 @@
using System;
using System.Collections.Generic;
-using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Shared.Diagnostics;
+#pragma warning disable S109 // Magic numbers should not be used
+#pragma warning disable S1121 // Assignments should not be made from within sub-expressions
+
namespace Microsoft.Extensions.AI.Agents;
///
diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AgentThread.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AgentThread.cs
index 1a8d33a53b..a705ba6530 100644
--- a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AgentThread.cs
+++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AgentThread.cs
@@ -51,7 +51,7 @@ public class AgentThread
///
public string? ConversationId
{
- get { return this._conversationId; }
+ get => this._conversationId;
set
{
if (string.IsNullOrWhiteSpace(this._conversationId) && string.IsNullOrWhiteSpace(value))
@@ -90,7 +90,7 @@ public class AgentThread
///
public IChatMessageStore? MessageStore
{
- get { return this._messageStore; }
+ get => this._messageStore;
set
{
if (this._messageStore is null && value is null)
@@ -126,6 +126,27 @@ public class AgentThread
}
}
+ ///
+ /// Serializes the current object's state to a using the specified serialization options.
+ ///
+ /// The JSON serialization options to use.
+ /// The to monitor for cancellation requests. The default is .
+ /// A representation of the object's state.
+ public virtual async Task SerializeAsync(JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
+ {
+ var storeState = this._messageStore is null ?
+ null :
+ await this._messageStore.SerializeStateAsync(jsonSerializerOptions, cancellationToken).ConfigureAwait(false);
+
+ var state = new ThreadState
+ {
+ ConversationId = this.ConversationId,
+ StoreState = storeState
+ };
+
+ return JsonSerializer.SerializeToElement(state, AgentAbstractionsJsonUtilities.DefaultOptions.GetTypeInfo(typeof(ThreadState)));
+ }
+
///
/// This method is called when new messages have been contributed to the chat by any participant.
///
@@ -168,6 +189,7 @@ public class AgentThread
/// A representing the state of the thread.
/// Optional settings for customizing the JSON deserialization process.
/// The to monitor for cancellation requests. The default is .
+ /// A that completes when the state has been deserialized.
protected internal virtual async Task DeserializeAsync(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
{
var state = JsonSerializer.Deserialize(
@@ -197,27 +219,6 @@ public class AgentThread
await this._messageStore.DeserializeStateAsync(state!.StoreState.Value, jsonSerializerOptions, cancellationToken).ConfigureAwait(false);
}
- ///
- /// Serializes the current object's state to a using the specified serialization options.
- ///
- /// The JSON serialization options to use.
- /// The to monitor for cancellation requests. The default is .
- /// A representation of the object's state.
- public virtual async Task SerializeAsync(JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
- {
- var storeState = this._messageStore is null ?
- (JsonElement?)null :
- await this._messageStore.SerializeStateAsync(jsonSerializerOptions, cancellationToken).ConfigureAwait(false);
-
- var state = new ThreadState
- {
- ConversationId = this.ConversationId,
- StoreState = storeState
- };
-
- return JsonSerializer.SerializeToElement(state, AgentAbstractionsJsonUtilities.DefaultOptions.GetTypeInfo(typeof(ThreadState)));
- }
-
internal class ThreadState
{
public string? ConversationId { get; set; }
diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/IChatMessageStore.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/IChatMessageStore.cs
index f54c091264..d1713e9336 100644
--- a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/IChatMessageStore.cs
+++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/IChatMessageStore.cs
@@ -50,6 +50,7 @@ public interface IChatMessageStore
/// A representing the state of the store.
/// Optional settings for customizing the JSON deserialization process.
/// The to monitor for cancellation requests. The default is .
+ /// A that completes when the state has been deserialized.
///
/// This method, together with can be used to save and load messages from a persistent store
/// if this store only has messages in memory.
diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/InMemoryChatMessageStore.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/InMemoryChatMessageStore.cs
index 57dce94c71..b535f10081 100644
--- a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/InMemoryChatMessageStore.cs
+++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/InMemoryChatMessageStore.cs
@@ -48,7 +48,7 @@ internal class InMemoryChatMessageStore : IList, IChatMessageStore
{
if (serializedStoreState is null)
{
- return new ValueTask();
+ return default;
}
var state = JsonSerializer.Deserialize(
@@ -60,7 +60,7 @@ internal class InMemoryChatMessageStore : IList, IChatMessageStore
this._messages.AddRange(messages);
}
- return new ValueTask();
+ return default;
}
///
diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents/ChatCompletion/ChatClientAgent.cs b/dotnet/src/Microsoft.Extensions.AI.Agents/ChatCompletion/ChatClientAgent.cs
index f53b6248a8..e33b3a7f9f 100644
--- a/dotnet/src/Microsoft.Extensions.AI.Agents/ChatCompletion/ChatClientAgent.cs
+++ b/dotnet/src/Microsoft.Extensions.AI.Agents/ChatCompletion/ChatClientAgent.cs
@@ -122,7 +122,7 @@ public sealed class ChatClientAgent : AIAgent
this.UpdateThreadWithTypeAndConversationId(safeThread, chatResponse.ConversationId);
// Only notify the thread of new messages if the chatResponse was successful to avoid inconsistent messages state in the thread.
- await this.NotifyThreadOfNewMessagesAsync(safeThread, messages, cancellationToken).ConfigureAwait(false);
+ await NotifyThreadOfNewMessagesAsync(safeThread, messages, cancellationToken).ConfigureAwait(false);
// Ensure that the author name is set for each message in the response.
foreach (ChatMessage chatResponseMessage in chatResponse.Messages)
@@ -133,7 +133,7 @@ public sealed class ChatClientAgent : AIAgent
// Convert the chat response messages to a valid IReadOnlyCollection for notification signatures below.
var chatResponseMessages = chatResponse.Messages as IReadOnlyCollection ?? [.. chatResponse.Messages];
- await this.NotifyThreadOfNewMessagesAsync(safeThread, chatResponseMessages, cancellationToken).ConfigureAwait(false);
+ await NotifyThreadOfNewMessagesAsync(safeThread, chatResponseMessages, cancellationToken).ConfigureAwait(false);
return new(chatResponse) { AgentId = this.Id };
}
@@ -186,9 +186,9 @@ public sealed class ChatClientAgent : AIAgent
this.UpdateThreadWithTypeAndConversationId(safeThread, chatResponse.ConversationId);
// To avoid inconsistent state we only notify the thread of the input messages if no error occurs after the initial request.
- await this.NotifyThreadOfNewMessagesAsync(safeThread, inputMessages, cancellationToken).ConfigureAwait(false);
+ await NotifyThreadOfNewMessagesAsync(safeThread, inputMessages, cancellationToken).ConfigureAwait(false);
- await this.NotifyThreadOfNewMessagesAsync(safeThread, chatResponseMessages, cancellationToken).ConfigureAwait(false);
+ await NotifyThreadOfNewMessagesAsync(safeThread, chatResponseMessages, cancellationToken).ConfigureAwait(false);
}
///
diff --git a/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentTests.cs b/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AIAgentTests.cs
similarity index 88%
rename from dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentTests.cs
rename to dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AIAgentTests.cs
index ec8175f26b..5456c1b548 100644
--- a/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentTests.cs
+++ b/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AIAgentTests.cs
@@ -13,24 +13,24 @@ namespace Microsoft.Extensions.AI.Agents.Abstractions.UnitTests;
///
/// Unit tests for the class.
///
-public class AgentTests
+public class AIAgentTests
{
private readonly Mock _agentMock;
private readonly Mock _agentThreadMock;
- private readonly AgentRunResponse _invokeResponse = new();
+ private readonly AgentRunResponse _invokeResponse;
private readonly List _invokeStreamingResponses = [];
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
- public AgentTests()
+ public AIAgentTests()
{
this._agentThreadMock = new Mock(MockBehavior.Strict);
this._invokeResponse = new AgentRunResponse(new ChatMessage(ChatRole.Assistant, "Hi"));
this._invokeStreamingResponses.Add(new AgentRunResponseUpdate(ChatRole.Assistant, "Hi"));
- this._agentMock = new Mock() { CallBase = true };
+ this._agentMock = new Mock { CallBase = true };
this._agentMock
.Setup(x => x.RunAsync(
It.IsAny>(),
@@ -56,7 +56,7 @@ public class AgentTests
{
// Arrange
var options = new AgentRunOptions();
- var cancellationToken = new CancellationToken();
+ var cancellationToken = default(CancellationToken);
// Act
var response = await this._agentMock.Object.RunAsync(this._agentThreadMock.Object, options, cancellationToken);
@@ -82,7 +82,7 @@ public class AgentTests
// Arrange
var message = "Hello, Agent!";
var options = new AgentRunOptions();
- var cancellationToken = new CancellationToken();
+ var cancellationToken = default(CancellationToken);
// Act
var response = await this._agentMock.Object.RunAsync(message, this._agentThreadMock.Object, options, cancellationToken);
@@ -108,7 +108,7 @@ public class AgentTests
// Arrange
var message = new ChatMessage(ChatRole.User, "Hello, Agent!");
var options = new AgentRunOptions();
- var cancellationToken = new CancellationToken();
+ var cancellationToken = default(CancellationToken);
// Act
var response = await this._agentMock.Object.RunAsync(message, this._agentThreadMock.Object, options, cancellationToken);
@@ -133,7 +133,7 @@ public class AgentTests
{
// Arrange
var options = new AgentRunOptions();
- var cancellationToken = new CancellationToken();
+ var cancellationToken = default(CancellationToken);
// Act
await foreach (var response in this._agentMock.Object.RunStreamingAsync(this._agentThreadMock.Object, options, cancellationToken))
@@ -162,7 +162,7 @@ public class AgentTests
// Arrange
var message = "Hello, Agent!";
var options = new AgentRunOptions();
- var cancellationToken = new CancellationToken();
+ var cancellationToken = default(CancellationToken);
// Act
await foreach (var response in this._agentMock.Object.RunStreamingAsync(message, this._agentThreadMock.Object, options, cancellationToken))
@@ -191,7 +191,7 @@ public class AgentTests
// Arrange
var message = new ChatMessage(ChatRole.User, "Hello, Agent!");
var options = new AgentRunOptions();
- var cancellationToken = new CancellationToken();
+ var cancellationToken = default(CancellationToken);
// Act
await foreach (var response in this._agentMock.Object.RunStreamingAsync(message, this._agentThreadMock.Object, options, cancellationToken))
@@ -223,16 +223,15 @@ public class AgentTests
[Fact]
public async Task NotifyThreadOfNewMessagesNotifiesThreadAsync()
{
- var cancellationToken = new CancellationToken();
+ var cancellationToken = default(CancellationToken);
var messages = new[] { new ChatMessage(ChatRole.User, "msg1"), new ChatMessage(ChatRole.User, "msg2") };
- var threadMock = new Mock() { CallBase = true };
+ var threadMock = new Mock { CallBase = true };
threadMock.SetupAllProperties();
threadMock.Object.ConversationId = "test-thread-id";
- var agent = new MockAgent();
- await agent.NotifyThreadOfNewMessagesAsync(threadMock.Object, messages, cancellationToken);
+ await MockAgent.NotifyThreadOfNewMessagesAsync(threadMock.Object, messages, cancellationToken);
threadMock.Protected().Verify("OnNewMessagesAsync", Times.Once(), messages, cancellationToken);
}
@@ -360,17 +359,25 @@ public class AgentTests
private sealed class MockAgent : AIAgent
{
- public new Task NotifyThreadOfNewMessagesAsync(AgentThread thread, IReadOnlyCollection messages, CancellationToken cancellationToken)
+ public static new Task NotifyThreadOfNewMessagesAsync(AgentThread thread, IReadOnlyCollection messages, CancellationToken cancellationToken)
{
- return base.NotifyThreadOfNewMessagesAsync(thread, messages, cancellationToken);
+ return AIAgent.NotifyThreadOfNewMessagesAsync(thread, messages, cancellationToken);
}
- public override Task RunAsync(IReadOnlyCollection messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default)
+ public override Task RunAsync(
+ IReadOnlyCollection messages,
+ AgentThread? thread = null,
+ AgentRunOptions? options = null,
+ CancellationToken cancellationToken = default)
{
throw new System.NotImplementedException();
}
- public override IAsyncEnumerable RunStreamingAsync(IReadOnlyCollection messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default)
+ public override IAsyncEnumerable RunStreamingAsync(
+ IReadOnlyCollection messages,
+ AgentThread? thread = null,
+ AgentRunOptions? options = null,
+ CancellationToken cancellationToken = default)
{
throw new System.NotImplementedException();
}
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 473501a554..7ea4b83ff0 100644
--- a/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentRunOptionsTests.cs
+++ b/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentRunOptionsTests.cs
@@ -17,6 +17,7 @@ public class AgentRunOptionsTests
// Act
var clone = new AgentRunOptions(options);
+ Assert.NotNull(clone);
}
[Fact]
diff --git a/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentRunResponseUpdatesTests.cs b/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentRunResponseUpdateTests.cs
similarity index 100%
rename from dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentRunResponseUpdatesTests.cs
rename to dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentRunResponseUpdateTests.cs
diff --git a/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentThreadTests.cs b/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentThreadTests.cs
index 3a598c36a6..09c00a6c8d 100644
--- a/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentThreadTests.cs
+++ b/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AgentThreadTests.cs
@@ -59,8 +59,10 @@ public class AgentThreadTests
public void SetConversationIdThrowsWhenMessageStoreIsSet()
{
// Arrange
- var thread = new AgentThread();
- thread.MessageStore = new InMemoryChatMessageStore();
+ var thread = new AgentThread
+ {
+ MessageStore = new InMemoryChatMessageStore()
+ };
// Act & Assert
var exception = Assert.Throws(() => thread.ConversationId = "new-thread-id");
@@ -72,8 +74,10 @@ public class AgentThreadTests
public void SetChatMessageStoreThrowsWhenConversationIdIsSet()
{
// Arrange
- var thread = new AgentThread();
- thread.ConversationId = "existing-thread-id";
+ var thread = new AgentThread
+ {
+ ConversationId = "existing-thread-id"
+ };
var store = new InMemoryChatMessageStore();
// Act & Assert
@@ -149,6 +153,8 @@ public class AgentThreadTests
// Act
await thread.OnNewMessagesAsync(messages, CancellationToken.None);
+ Assert.Equal("thread-123", thread.ConversationId);
+ Assert.Null(thread.MessageStore);
}
[Fact]