mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
.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
This commit is contained in:
committed by
GitHub
Unverified
parent
802c8e6238
commit
6b22b6bbc7
@@ -100,7 +100,10 @@ public abstract class AIAgent
|
||||
/// <summary>
|
||||
/// Run the agent with no message assuming that all required instructions are already provided to the agent or on the thread.
|
||||
/// </summary>
|
||||
/// <param name="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.</param>
|
||||
/// <param name="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.
|
||||
/// </param>
|
||||
/// <param name="options">Optional parameters for agent invocation.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
|
||||
/// <returns>A <see cref="AgentRunResponse"/> containing the list of <see cref="ChatMessage"/> items.</returns>
|
||||
@@ -116,7 +119,10 @@ public abstract class AIAgent
|
||||
/// Run the agent with the provided message and arguments.
|
||||
/// </summary>
|
||||
/// <param name="message">The message to pass to the agent.</param>
|
||||
/// <param name="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.</param>
|
||||
/// <param name="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.
|
||||
/// </param>
|
||||
/// <param name="options">Optional parameters for agent invocation.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
|
||||
/// <returns>A <see cref="AgentRunResponse"/> containing the list of <see cref="ChatMessage"/> items.</returns>
|
||||
@@ -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.
|
||||
/// </summary>
|
||||
/// <param name="message">The message to pass to the agent.</param>
|
||||
/// <param name="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.</param>
|
||||
/// <param name="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.
|
||||
/// </param>
|
||||
/// <param name="options">Optional parameters for agent invocation.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
|
||||
/// <returns>A <see cref="AgentRunResponse"/> containing the list of <see cref="ChatMessage"/> items.</returns>
|
||||
@@ -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.
|
||||
/// </summary>
|
||||
/// <param name="messages">The messages to pass to the agent.</param>
|
||||
/// <param name="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.</param>
|
||||
/// <param name="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.
|
||||
/// </param>
|
||||
/// <param name="options">Optional parameters for agent invocation.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
|
||||
/// <returns>A <see cref="AgentRunResponse"/> containing the list of <see cref="ChatMessage"/> items.</returns>
|
||||
@@ -170,7 +182,10 @@ public abstract class AIAgent
|
||||
/// <summary>
|
||||
/// Run the agent with no message assuming that all required instructions are already provided to the agent or on the thread.
|
||||
/// </summary>
|
||||
/// <param name="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.</param>
|
||||
/// <param name="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.
|
||||
/// </param>
|
||||
/// <param name="options">Optional parameters for agent invocation.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
|
||||
/// <returns>An async list of response items that each contain a <see cref="AgentRunResponseUpdate"/>.</returns>
|
||||
@@ -186,7 +201,10 @@ public abstract class AIAgent
|
||||
/// Run the agent with the provided message and arguments.
|
||||
/// </summary>
|
||||
/// <param name="message">The message to pass to the agent.</param>
|
||||
/// <param name="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.</param>
|
||||
/// <param name="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.
|
||||
/// </param>
|
||||
/// <param name="options">Optional parameters for agent invocation.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
|
||||
/// <returns>An async list of response items that each contain a <see cref="AgentRunResponseUpdate"/>.</returns>
|
||||
@@ -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.
|
||||
/// </summary>
|
||||
/// <param name="message">The message to pass to the agent.</param>
|
||||
/// <param name="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.</param>
|
||||
/// <param name="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.
|
||||
/// </param>
|
||||
/// <param name="options">Optional parameters for agent invocation.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
|
||||
/// <returns>An async list of response items that each contain a <see cref="AgentRunResponseUpdate"/>.</returns>
|
||||
@@ -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.
|
||||
/// </summary>
|
||||
/// <param name="messages">The messages to pass to the agent.</param>
|
||||
/// <param name="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.</param>
|
||||
/// <param name="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.
|
||||
/// </param>
|
||||
/// <param name="options">Optional parameters for agent invocation.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
|
||||
/// <returns>An async list of response items that each contain a <see cref="AgentRunResponseUpdate"/>.</returns>
|
||||
@@ -261,7 +285,7 @@ public abstract class AIAgent
|
||||
/// <param name="messages">The messages to pass to the thread.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
|
||||
/// <returns>An async task that completes once the notification is complete.</returns>
|
||||
protected async Task NotifyThreadOfNewMessagesAsync(AgentThread thread, IReadOnlyCollection<ChatMessage> messages, CancellationToken cancellationToken)
|
||||
protected static async Task NotifyThreadOfNewMessagesAsync(AgentThread thread, IReadOnlyCollection<ChatMessage> messages, CancellationToken cancellationToken)
|
||||
{
|
||||
_ = Throw.IfNull(thread);
|
||||
_ = Throw.IfNull(messages);
|
||||
|
||||
@@ -22,6 +22,6 @@ public class AgentRunOptions
|
||||
/// <param name="options">The options to clone.</param>
|
||||
public AgentRunOptions(AgentRunOptions options)
|
||||
{
|
||||
Throw.IfNull(options);
|
||||
_ = Throw.IfNull(options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
/// <summary>Represents the response to an Agent run request.</summary>
|
||||
@@ -84,7 +87,7 @@ public class AgentRunResponse
|
||||
[JsonIgnore]
|
||||
public string Text => this._messages?.ConcatText() ?? string.Empty;
|
||||
|
||||
/// <summary>Gets or sets the user input requests associated with the response.</summary>
|
||||
/// <summary>Gets the user input requests associated with the response.</summary>
|
||||
/// <remarks>
|
||||
/// This property concatenates all <see cref="UserInputRequestContent"/> instances in the response.
|
||||
/// </remarks>
|
||||
@@ -170,7 +173,6 @@ public class AgentRunResponse
|
||||
return updates;
|
||||
}
|
||||
|
||||
// TODO: Add overloads without serializer options.
|
||||
/// <summary>
|
||||
/// Deserializes the response text into the given type using the specified serializer options.
|
||||
/// </summary>
|
||||
@@ -180,6 +182,8 @@ public class AgentRunResponse
|
||||
/// <exception cref="InvalidOperationException">The result is not parsable into the requested type.</exception>
|
||||
public T Deserialize<T>(JsonSerializerOptions serializerOptions)
|
||||
{
|
||||
_ = Throw.IfNull(serializerOptions);
|
||||
|
||||
var structuredOutput = this.GetResultCore<T>(serializerOptions, out var failureReason);
|
||||
return failureReason switch
|
||||
{
|
||||
@@ -198,6 +202,8 @@ public class AgentRunResponse
|
||||
/// <returns><see langword="true" /> if parsing was successful; otherwise, <see langword="false" />.</returns>
|
||||
public bool TryDeserialize<T>(JsonSerializerOptions serializerOptions, [NotNullWhen(true)] out T? structuredOutput)
|
||||
{
|
||||
_ = Throw.IfNull(serializerOptions);
|
||||
|
||||
try
|
||||
{
|
||||
structuredOutput = this.GetResultCore<T>(serializerOptions, out var failureReason);
|
||||
|
||||
@@ -93,7 +93,7 @@ public class AgentRunResponseUpdate
|
||||
[JsonIgnore]
|
||||
public string Text => this._contents is not null ? this._contents.ConcatText() : string.Empty;
|
||||
|
||||
/// <summary>Gets or sets the user input requests associated with the response.</summary>
|
||||
/// <summary>Gets the user input requests associated with the response.</summary>
|
||||
/// <remarks>
|
||||
/// This property concatenates all <see cref="UserInputRequestContent"/> instances in the response.
|
||||
/// </remarks>
|
||||
|
||||
+3
-1
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -51,7 +51,7 @@ public class AgentThread
|
||||
/// </remarks>
|
||||
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
|
||||
/// </remarks>
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serializes the current object's state to a <see cref="JsonElement"/> using the specified serialization options.
|
||||
/// </summary>
|
||||
/// <param name="jsonSerializerOptions">The JSON serialization options to use.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
|
||||
/// <returns>A <see cref="JsonElement"/> representation of the object's state.</returns>
|
||||
public virtual async Task<JsonElement> 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)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method is called when new messages have been contributed to the chat by any participant.
|
||||
/// </summary>
|
||||
@@ -168,6 +189,7 @@ public class AgentThread
|
||||
/// <param name="serializedThread">A <see cref="JsonElement"/> representing the state of the thread.</param>
|
||||
/// <param name="jsonSerializerOptions">Optional settings for customizing the JSON deserialization process.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
|
||||
/// <returns>A <see cref="ValueTask"/> that completes when the state has been deserialized.</returns>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serializes the current object's state to a <see cref="JsonElement"/> using the specified serialization options.
|
||||
/// </summary>
|
||||
/// <param name="jsonSerializerOptions">The JSON serialization options to use.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
|
||||
/// <returns>A <see cref="JsonElement"/> representation of the object's state.</returns>
|
||||
public virtual async Task<JsonElement> 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; }
|
||||
|
||||
@@ -50,6 +50,7 @@ public interface IChatMessageStore
|
||||
/// <param name="serializedStoreState">A <see cref="JsonElement"/> representing the state of the store.</param>
|
||||
/// <param name="jsonSerializerOptions">Optional settings for customizing the JSON deserialization process.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
|
||||
/// <returns>A <see cref="ValueTask"/> that completes when the state has been deserialized.</returns>
|
||||
/// <remarks>
|
||||
/// This method, together with <see cref="SerializeStateAsync(JsonSerializerOptions?, CancellationToken)"/> can be used to save and load messages from a persistent store
|
||||
/// if this store only has messages in memory.
|
||||
|
||||
@@ -48,7 +48,7 @@ internal class InMemoryChatMessageStore : IList<ChatMessage>, IChatMessageStore
|
||||
{
|
||||
if (serializedStoreState is null)
|
||||
{
|
||||
return new ValueTask();
|
||||
return default;
|
||||
}
|
||||
|
||||
var state = JsonSerializer.Deserialize(
|
||||
@@ -60,7 +60,7 @@ internal class InMemoryChatMessageStore : IList<ChatMessage>, IChatMessageStore
|
||||
this._messages.AddRange(messages);
|
||||
}
|
||||
|
||||
return new ValueTask();
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -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<ChatMessage> ?? [.. 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);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
||||
+26
-19
@@ -13,24 +13,24 @@ namespace Microsoft.Extensions.AI.Agents.Abstractions.UnitTests;
|
||||
/// <summary>
|
||||
/// Unit tests for the <see cref="AIAgent"/> class.
|
||||
/// </summary>
|
||||
public class AgentTests
|
||||
public class AIAgentTests
|
||||
{
|
||||
private readonly Mock<AIAgent> _agentMock;
|
||||
private readonly Mock<AgentThread> _agentThreadMock;
|
||||
private readonly AgentRunResponse _invokeResponse = new();
|
||||
private readonly AgentRunResponse _invokeResponse;
|
||||
private readonly List<AgentRunResponseUpdate> _invokeStreamingResponses = [];
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AgentTests"/> class.
|
||||
/// Initializes a new instance of the <see cref="AIAgentTests"/> class.
|
||||
/// </summary>
|
||||
public AgentTests()
|
||||
public AIAgentTests()
|
||||
{
|
||||
this._agentThreadMock = new Mock<AgentThread>(MockBehavior.Strict);
|
||||
|
||||
this._invokeResponse = new AgentRunResponse(new ChatMessage(ChatRole.Assistant, "Hi"));
|
||||
this._invokeStreamingResponses.Add(new AgentRunResponseUpdate(ChatRole.Assistant, "Hi"));
|
||||
|
||||
this._agentMock = new Mock<AIAgent>() { CallBase = true };
|
||||
this._agentMock = new Mock<AIAgent> { CallBase = true };
|
||||
this._agentMock
|
||||
.Setup(x => x.RunAsync(
|
||||
It.IsAny<IReadOnlyCollection<ChatMessage>>(),
|
||||
@@ -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<TestAgentThread>() { CallBase = true };
|
||||
var threadMock = new Mock<TestAgentThread> { 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<ChatMessage> messages, CancellationToken cancellationToken)
|
||||
public static new Task NotifyThreadOfNewMessagesAsync(AgentThread thread, IReadOnlyCollection<ChatMessage> messages, CancellationToken cancellationToken)
|
||||
{
|
||||
return base.NotifyThreadOfNewMessagesAsync(thread, messages, cancellationToken);
|
||||
return AIAgent.NotifyThreadOfNewMessagesAsync(thread, messages, cancellationToken);
|
||||
}
|
||||
|
||||
public override Task<AgentRunResponse> RunAsync(IReadOnlyCollection<ChatMessage> messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default)
|
||||
public override Task<AgentRunResponse> RunAsync(
|
||||
IReadOnlyCollection<ChatMessage> messages,
|
||||
AgentThread? thread = null,
|
||||
AgentRunOptions? options = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public override IAsyncEnumerable<AgentRunResponseUpdate> RunStreamingAsync(IReadOnlyCollection<ChatMessage> messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default)
|
||||
public override IAsyncEnumerable<AgentRunResponseUpdate> RunStreamingAsync(
|
||||
IReadOnlyCollection<ChatMessage> messages,
|
||||
AgentThread? thread = null,
|
||||
AgentRunOptions? options = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
+1
@@ -17,6 +17,7 @@ public class AgentRunOptionsTests
|
||||
|
||||
// Act
|
||||
var clone = new AgentRunOptions(options);
|
||||
Assert.NotNull(clone);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
+10
-4
@@ -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<InvalidOperationException>(() => 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]
|
||||
|
||||
Reference in New Issue
Block a user