diff --git a/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentCustomOptions.cs b/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentCustomOptions.cs
new file mode 100644
index 0000000000..b0cbd3d793
--- /dev/null
+++ b/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentCustomOptions.cs
@@ -0,0 +1,253 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System.Collections.Generic;
+using System.Text.Json;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.AI;
+
+namespace Microsoft.Agents.AI;
+
+///
+/// Provides extension methods for to enable discoverability of .
+///
+public partial class ChatClientAgent
+{
+ ///
+ /// 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 use for this invocation. If , a new thread will be created.
+ /// The thread will be updated with any response messages generated during invocation.
+ ///
+ /// Configuration parameters for controlling the agent's invocation behavior.
+ /// The to monitor for cancellation requests. The default is .
+ /// A task that represents the asynchronous operation. The task result contains an with the agent's output.
+ public Task RunAsync(
+ AgentThread? thread,
+ ChatClientAgentRunOptions? options,
+ CancellationToken cancellationToken = default) =>
+ this.RunAsync(thread, (AgentRunOptions?)options, cancellationToken);
+
+ ///
+ /// Runs the agent with a text message from the user.
+ ///
+ /// The user message to send to the agent.
+ ///
+ /// The conversation thread to use for this invocation. If , a new thread will be created.
+ /// The thread will be updated with the input message and any response messages generated during invocation.
+ ///
+ /// Configuration parameters for controlling the agent's invocation behavior.
+ /// The to monitor for cancellation requests. The default is .
+ /// A task that represents the asynchronous operation. The task result contains an with the agent's output.
+ public Task RunAsync(
+ string message,
+ AgentThread? thread,
+ ChatClientAgentRunOptions? options,
+ CancellationToken cancellationToken = default) =>
+ this.RunAsync(message, thread, (AgentRunOptions?)options, cancellationToken);
+
+ ///
+ /// Runs the agent with a single chat message.
+ ///
+ /// The chat message to send to the agent.
+ ///
+ /// The conversation thread to use for this invocation. If , a new thread will be created.
+ /// The thread will be updated with the input message and any response messages generated during invocation.
+ ///
+ /// Configuration parameters for controlling the agent's invocation behavior.
+ /// The to monitor for cancellation requests. The default is .
+ /// A task that represents the asynchronous operation. The task result contains an with the agent's output.
+ public Task RunAsync(
+ ChatMessage message,
+ AgentThread? thread,
+ ChatClientAgentRunOptions? options,
+ CancellationToken cancellationToken = default) =>
+ this.RunAsync(message, thread, (AgentRunOptions?)options, cancellationToken);
+
+ ///
+ /// Runs the agent with a collection of chat messages.
+ ///
+ /// The collection of messages to send to the agent for processing.
+ ///
+ /// The conversation thread to use for this invocation. If , a new thread will be created.
+ /// The thread will be updated with the input messages and any response messages generated during invocation.
+ ///
+ /// Configuration parameters for controlling the agent's invocation behavior.
+ /// The to monitor for cancellation requests. The default is .
+ /// A task that represents the asynchronous operation. The task result contains an with the agent's output.
+ public Task RunAsync(
+ IEnumerable messages,
+ AgentThread? thread,
+ ChatClientAgentRunOptions? options,
+ CancellationToken cancellationToken = default) =>
+ this.RunAsync(messages, thread, (AgentRunOptions?)options, cancellationToken);
+
+ ///
+ /// Runs the agent in streaming mode without providing new input messages, relying on existing context and instructions.
+ ///
+ ///
+ /// The conversation thread to use for this invocation. If , a new thread will be created.
+ /// The thread will be updated with any response messages generated during invocation.
+ ///
+ /// Configuration parameters for controlling the agent's invocation behavior.
+ /// The to monitor for cancellation requests. The default is .
+ /// An asynchronous enumerable of instances representing the streaming response.
+ public IAsyncEnumerable RunStreamingAsync(
+ AgentThread? thread,
+ ChatClientAgentRunOptions? options,
+ CancellationToken cancellationToken = default) =>
+ this.RunStreamingAsync(thread, (AgentRunOptions?)options, cancellationToken);
+
+ ///
+ /// Runs the agent in streaming mode with a text message from the user.
+ ///
+ /// The user message to send to the agent.
+ ///
+ /// The conversation thread to use for this invocation. If , a new thread will be created.
+ /// The thread will be updated with the input message and any response messages generated during invocation.
+ ///
+ /// Configuration parameters for controlling the agent's invocation behavior.
+ /// The to monitor for cancellation requests. The default is .
+ /// An asynchronous enumerable of instances representing the streaming response.
+ public IAsyncEnumerable RunStreamingAsync(
+ string message,
+ AgentThread? thread,
+ ChatClientAgentRunOptions? options,
+ CancellationToken cancellationToken = default) =>
+ this.RunStreamingAsync(message, thread, (AgentRunOptions?)options, cancellationToken);
+
+ ///
+ /// Runs the agent in streaming mode with a single chat message.
+ ///
+ /// The chat message to send to the agent.
+ ///
+ /// The conversation thread to use for this invocation. If , a new thread will be created.
+ /// The thread will be updated with the input message and any response messages generated during invocation.
+ ///
+ /// Configuration parameters for controlling the agent's invocation behavior.
+ /// The to monitor for cancellation requests. The default is .
+ /// An asynchronous enumerable of instances representing the streaming response.
+ public IAsyncEnumerable RunStreamingAsync(
+ ChatMessage message,
+ AgentThread? thread,
+ ChatClientAgentRunOptions? options,
+ CancellationToken cancellationToken = default) =>
+ this.RunStreamingAsync(message, thread, (AgentRunOptions?)options, cancellationToken);
+
+ ///
+ /// Runs the agent in streaming mode with a collection of chat messages.
+ ///
+ /// The collection of messages to send to the agent for processing.
+ ///
+ /// The conversation thread to use for this invocation. If , a new thread will be created.
+ /// The thread will be updated with the input messages and any response updates generated during invocation.
+ ///
+ /// Configuration parameters for controlling the agent's invocation behavior.
+ /// The to monitor for cancellation requests. The default is .
+ /// An asynchronous enumerable of instances representing the streaming response.
+ public IAsyncEnumerable RunStreamingAsync(
+ IEnumerable messages,
+ AgentThread? thread,
+ ChatClientAgentRunOptions? options,
+ CancellationToken cancellationToken = default) =>
+ this.RunStreamingAsync(messages, thread, (AgentRunOptions?)options, cancellationToken);
+
+ ///
+ /// Run the agent with no message assuming that all required instructions are already provided to the agent or on the thread, and requesting a response of the specified type .
+ ///
+ ///
+ /// The conversation thread to use for this invocation. If , a new thread will be created.
+ /// The thread will be updated with any response messages generated during invocation.
+ ///
+ /// The JSON serialization options to use.
+ /// Configuration parameters for controlling the agent's invocation behavior.
+ ///
+ /// to set a JSON schema on the ; otherwise, . The default is .
+ /// Using a JSON schema improves reliability if the underlying model supports native structured output with a schema, but might cause an error if the model does not support it.
+ ///
+ /// The to monitor for cancellation requests. The default is .
+ /// A task that represents the asynchronous operation. The task result contains an with the agent's output.
+ public Task> RunAsync(
+ AgentThread? thread,
+ JsonSerializerOptions? serializerOptions,
+ ChatClientAgentRunOptions? options,
+ bool? useJsonSchemaResponseFormat = null,
+ CancellationToken cancellationToken = default) =>
+ this.RunAsync(thread, serializerOptions, (AgentRunOptions?)options, useJsonSchemaResponseFormat, cancellationToken);
+
+ ///
+ /// Runs the agent with a text message from the user, requesting a response of the specified type .
+ ///
+ /// The user message to send to the agent.
+ ///
+ /// The conversation thread to use for this invocation. If , a new thread will be created.
+ /// The thread will be updated with the input message and any response messages generated during invocation.
+ ///
+ /// The JSON serialization options to use.
+ /// Configuration parameters for controlling the agent's invocation behavior.
+ ///
+ /// to set a JSON schema on the ; otherwise, . The default is .
+ /// Using a JSON schema improves reliability if the underlying model supports native structured output with a schema, but might cause an error if the model does not support it.
+ ///
+ /// The to monitor for cancellation requests. The default is .
+ /// A task that represents the asynchronous operation. The task result contains an with the agent's output.
+ public Task> RunAsync(
+ string message,
+ AgentThread? thread,
+ JsonSerializerOptions? serializerOptions,
+ ChatClientAgentRunOptions? options,
+ bool? useJsonSchemaResponseFormat = null,
+ CancellationToken cancellationToken = default) =>
+ this.RunAsync(message, thread, serializerOptions, (AgentRunOptions?)options, useJsonSchemaResponseFormat, cancellationToken);
+
+ ///
+ /// Runs the agent with a single chat message, requesting a response of the specified type .
+ ///
+ /// The chat message to send to the agent.
+ ///
+ /// The conversation thread to use for this invocation. If , a new thread will be created.
+ /// The thread will be updated with the input message and any response messages generated during invocation.
+ ///
+ /// The JSON serialization options to use.
+ /// Configuration parameters for controlling the agent's invocation behavior.
+ ///
+ /// to set a JSON schema on the ; otherwise, . The default is .
+ /// Using a JSON schema improves reliability if the underlying model supports native structured output with a schema, but might cause an error if the model does not support it.
+ ///
+ /// The to monitor for cancellation requests. The default is .
+ /// A task that represents the asynchronous operation. The task result contains an with the agent's output.
+ public Task> RunAsync(
+ ChatMessage message,
+ AgentThread? thread,
+ JsonSerializerOptions? serializerOptions,
+ ChatClientAgentRunOptions? options,
+ bool? useJsonSchemaResponseFormat = null,
+ CancellationToken cancellationToken = default) =>
+ this.RunAsync(message, thread, serializerOptions, (AgentRunOptions?)options, useJsonSchemaResponseFormat, cancellationToken);
+
+ ///
+ /// Runs the agent with a collection of chat messages, requesting a response of the specified type .
+ ///
+ /// The collection of messages to send to the agent for processing.
+ ///
+ /// The conversation thread to use for this invocation. If , a new thread will be created.
+ /// The thread will be updated with the input messages and any response messages generated during invocation.
+ ///
+ /// The JSON serialization options to use.
+ /// Configuration parameters for controlling the agent's invocation behavior.
+ ///
+ /// to set a JSON schema on the ; otherwise, . The default is .
+ /// Using a JSON schema improves reliability if the underlying model supports native structured output with a schema, but might cause an error if the model does not support it.
+ ///
+ /// The to monitor for cancellation requests. The default is .
+ /// A task that represents the asynchronous operation. The task result contains an with the agent's output.
+ public Task> RunAsync(
+ IEnumerable messages,
+ AgentThread? thread,
+ JsonSerializerOptions? serializerOptions,
+ ChatClientAgentRunOptions? options,
+ bool? useJsonSchemaResponseFormat = null,
+ CancellationToken cancellationToken = default) =>
+ this.RunAsync(messages, thread, serializerOptions, (AgentRunOptions?)options, useJsonSchemaResponseFormat, cancellationToken);
+}
diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgent_RunWithCustomOptionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgent_RunWithCustomOptionsTests.cs
new file mode 100644
index 0000000000..85cb4cf0b4
--- /dev/null
+++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgent_RunWithCustomOptionsTests.cs
@@ -0,0 +1,456 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text.Json.Serialization;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.AI;
+using Moq;
+
+namespace Microsoft.Agents.AI.UnitTests;
+
+///
+/// Tests for run methods with .
+///
+public sealed partial class ChatClientAgent_RunWithCustomOptionsTests
+{
+ #region RunAsync Tests
+
+ [Fact]
+ public async Task RunAsync_WithThreadAndOptions_CallsBaseMethodAsync()
+ {
+ // Arrange
+ Mock mockChatClient = new();
+ mockChatClient.Setup(
+ s => s.GetResponseAsync(
+ It.IsAny>(),
+ It.IsAny(),
+ It.IsAny())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "Response")]));
+
+ ChatClientAgent agent = new(mockChatClient.Object);
+ AgentThread thread = agent.GetNewThread();
+ ChatClientAgentRunOptions options = new();
+
+ // Act
+ AgentRunResponse result = await agent.RunAsync(thread, options);
+
+ // Assert
+ Assert.NotNull(result);
+ Assert.Single(result.Messages);
+ mockChatClient.Verify(
+ x => x.GetResponseAsync(
+ It.IsAny>(),
+ It.IsAny(),
+ It.IsAny()),
+ Times.Once);
+ }
+
+ [Fact]
+ public async Task RunAsync_WithStringMessageAndOptions_CallsBaseMethodAsync()
+ {
+ // Arrange
+ Mock mockChatClient = new();
+ mockChatClient.Setup(
+ s => s.GetResponseAsync(
+ It.IsAny>(),
+ It.IsAny(),
+ It.IsAny())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "Response")]));
+
+ ChatClientAgent agent = new(mockChatClient.Object);
+ AgentThread thread = agent.GetNewThread();
+ ChatClientAgentRunOptions options = new();
+
+ // Act
+ AgentRunResponse result = await agent.RunAsync("Test message", thread, options);
+
+ // Assert
+ Assert.NotNull(result);
+ Assert.Single(result.Messages);
+ mockChatClient.Verify(
+ x => x.GetResponseAsync(
+ It.Is>(msgs => msgs.Any(m => m.Text == "Test message")),
+ It.IsAny(),
+ It.IsAny()),
+ Times.Once);
+ }
+
+ [Fact]
+ public async Task RunAsync_WithChatMessageAndOptions_CallsBaseMethodAsync()
+ {
+ // Arrange
+ Mock mockChatClient = new();
+ mockChatClient.Setup(
+ s => s.GetResponseAsync(
+ It.IsAny>(),
+ It.IsAny(),
+ It.IsAny())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "Response")]));
+
+ ChatClientAgent agent = new(mockChatClient.Object);
+ AgentThread thread = agent.GetNewThread();
+ ChatMessage message = new(ChatRole.User, "Test message");
+ ChatClientAgentRunOptions options = new();
+
+ // Act
+ AgentRunResponse result = await agent.RunAsync(message, thread, options);
+
+ // Assert
+ Assert.NotNull(result);
+ Assert.Single(result.Messages);
+ mockChatClient.Verify(
+ x => x.GetResponseAsync(
+ It.Is>(msgs => msgs.Contains(message)),
+ It.IsAny(),
+ It.IsAny()),
+ Times.Once);
+ }
+
+ [Fact]
+ public async Task RunAsync_WithMessagesCollectionAndOptions_CallsBaseMethodAsync()
+ {
+ // Arrange
+ Mock mockChatClient = new();
+ mockChatClient.Setup(
+ s => s.GetResponseAsync(
+ It.IsAny>(),
+ It.IsAny(),
+ It.IsAny())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "Response")]));
+
+ ChatClientAgent agent = new(mockChatClient.Object);
+ AgentThread thread = agent.GetNewThread();
+ IEnumerable messages = [new(ChatRole.User, "Message 1"), new(ChatRole.User, "Message 2")];
+ ChatClientAgentRunOptions options = new();
+
+ // Act
+ AgentRunResponse result = await agent.RunAsync(messages, thread, options);
+
+ // Assert
+ Assert.NotNull(result);
+ Assert.Single(result.Messages);
+ mockChatClient.Verify(
+ x => x.GetResponseAsync(
+ It.IsAny>(),
+ It.IsAny(),
+ It.IsAny()),
+ Times.Once);
+ }
+
+ [Fact]
+ public async Task RunAsync_WithChatOptionsInRunOptions_UsesChatOptionsAsync()
+ {
+ // Arrange
+ Mock mockChatClient = new();
+ mockChatClient.Setup(
+ s => s.GetResponseAsync(
+ It.IsAny>(),
+ It.IsAny(),
+ It.IsAny())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "Response")]));
+
+ ChatClientAgent agent = new(mockChatClient.Object);
+ ChatClientAgentRunOptions options = new(new ChatOptions { Temperature = 0.5f });
+
+ // Act
+ AgentRunResponse result = await agent.RunAsync("Test", null, options);
+
+ // Assert
+ Assert.NotNull(result);
+ mockChatClient.Verify(
+ x => x.GetResponseAsync(
+ It.IsAny>(),
+ It.Is(opts => opts.Temperature == 0.5f),
+ It.IsAny()),
+ Times.Once);
+ }
+
+ #endregion
+
+ #region RunStreamingAsync Tests
+
+ [Fact]
+ public async Task RunStreamingAsync_WithThreadAndOptions_CallsBaseMethodAsync()
+ {
+ // Arrange
+ Mock mockChatClient = new();
+ mockChatClient.Setup(
+ s => s.GetStreamingResponseAsync(
+ It.IsAny>(),
+ It.IsAny(),
+ It.IsAny())).Returns(GetAsyncUpdatesAsync());
+
+ ChatClientAgent agent = new(mockChatClient.Object);
+ AgentThread thread = agent.GetNewThread();
+ ChatClientAgentRunOptions options = new();
+
+ // Act
+ var updates = new List();
+ await foreach (var update in agent.RunStreamingAsync(thread, options))
+ {
+ updates.Add(update);
+ }
+
+ // Assert
+ Assert.NotEmpty(updates);
+ mockChatClient.Verify(
+ x => x.GetStreamingResponseAsync(
+ It.IsAny>(),
+ It.IsAny(),
+ It.IsAny()),
+ Times.Once);
+ }
+
+ [Fact]
+ public async Task RunStreamingAsync_WithStringMessageAndOptions_CallsBaseMethodAsync()
+ {
+ // Arrange
+ Mock mockChatClient = new();
+ mockChatClient.Setup(
+ s => s.GetStreamingResponseAsync(
+ It.IsAny>(),
+ It.IsAny(),
+ It.IsAny())).Returns(GetAsyncUpdatesAsync());
+
+ ChatClientAgent agent = new(mockChatClient.Object);
+ AgentThread thread = agent.GetNewThread();
+ ChatClientAgentRunOptions options = new();
+
+ // Act
+ var updates = new List();
+ await foreach (var update in agent.RunStreamingAsync("Test message", thread, options))
+ {
+ updates.Add(update);
+ }
+
+ // Assert
+ Assert.NotEmpty(updates);
+ mockChatClient.Verify(
+ x => x.GetStreamingResponseAsync(
+ It.Is>(msgs => msgs.Any(m => m.Text == "Test message")),
+ It.IsAny(),
+ It.IsAny()),
+ Times.Once);
+ }
+
+ [Fact]
+ public async Task RunStreamingAsync_WithChatMessageAndOptions_CallsBaseMethodAsync()
+ {
+ // Arrange
+ Mock mockChatClient = new();
+ mockChatClient.Setup(
+ s => s.GetStreamingResponseAsync(
+ It.IsAny>(),
+ It.IsAny(),
+ It.IsAny())).Returns(GetAsyncUpdatesAsync());
+
+ ChatClientAgent agent = new(mockChatClient.Object);
+ AgentThread thread = agent.GetNewThread();
+ ChatMessage message = new(ChatRole.User, "Test message");
+ ChatClientAgentRunOptions options = new();
+
+ // Act
+ var updates = new List();
+ await foreach (var update in agent.RunStreamingAsync(message, thread, options))
+ {
+ updates.Add(update);
+ }
+
+ // Assert
+ Assert.NotEmpty(updates);
+ mockChatClient.Verify(
+ x => x.GetStreamingResponseAsync(
+ It.Is>(msgs => msgs.Contains(message)),
+ It.IsAny(),
+ It.IsAny()),
+ Times.Once);
+ }
+
+ [Fact]
+ public async Task RunStreamingAsync_WithMessagesCollectionAndOptions_CallsBaseMethodAsync()
+ {
+ // Arrange
+ Mock mockChatClient = new();
+ mockChatClient.Setup(
+ s => s.GetStreamingResponseAsync(
+ It.IsAny>(),
+ It.IsAny(),
+ It.IsAny())).Returns(GetAsyncUpdatesAsync());
+
+ ChatClientAgent agent = new(mockChatClient.Object);
+ AgentThread thread = agent.GetNewThread();
+ IEnumerable messages = [new ChatMessage(ChatRole.User, "Message 1"), new ChatMessage(ChatRole.User, "Message 2")];
+ ChatClientAgentRunOptions options = new();
+
+ // Act
+ var updates = new List();
+ await foreach (var update in agent.RunStreamingAsync(messages, thread, options))
+ {
+ updates.Add(update);
+ }
+
+ // Assert
+ Assert.NotEmpty(updates);
+ mockChatClient.Verify(
+ x => x.GetStreamingResponseAsync(
+ It.IsAny>(),
+ It.IsAny(),
+ It.IsAny()),
+ Times.Once);
+ }
+
+ #endregion
+
+ #region Helper Methods
+
+ private static async IAsyncEnumerable GetAsyncUpdatesAsync()
+ {
+ yield return new ChatResponseUpdate { Contents = new[] { new TextContent("Hello") } };
+ yield return new ChatResponseUpdate { Contents = new[] { new TextContent(" World") } };
+ await Task.CompletedTask;
+ }
+
+ #endregion
+
+ #region RunAsync{T} Tests
+
+ [Fact]
+ public async Task RunAsyncOfT_WithThreadAndOptions_CallsBaseMethodAsync()
+ {
+ // Arrange
+ Mock mockChatClient = new();
+ mockChatClient.Setup(
+ s => s.GetResponseAsync(
+ It.IsAny>(),
+ It.IsAny(),
+ It.IsAny())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, """{"id":2, "fullName":"Tigger", "species":"Tiger"}""")]));
+
+ ChatClientAgent agent = new(mockChatClient.Object);
+ AgentThread thread = agent.GetNewThread();
+ ChatClientAgentRunOptions options = new();
+
+ // Act
+ AgentRunResponse agentRunResponse = await agent.RunAsync(thread, JsonContext_WithCustomRunOptions.Default.Options, options);
+
+ // Assert
+ Assert.NotNull(agentRunResponse);
+ Assert.Single(agentRunResponse.Messages);
+ Assert.Equal("Tigger", agentRunResponse.Result.FullName);
+ mockChatClient.Verify(
+ x => x.GetResponseAsync(
+ It.IsAny>(),
+ It.IsAny(),
+ It.IsAny()),
+ Times.Once);
+ }
+
+ [Fact]
+ public async Task RunAsyncOfT_WithStringMessageAndOptions_CallsBaseMethodAsync()
+ {
+ // Arrange
+ Mock mockChatClient = new();
+ mockChatClient.Setup(
+ s => s.GetResponseAsync(
+ It.IsAny>(),
+ It.IsAny(),
+ It.IsAny())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, """{"id":2, "fullName":"Tigger", "species":"Tiger"}""")]));
+
+ ChatClientAgent agent = new(mockChatClient.Object);
+ AgentThread thread = agent.GetNewThread();
+ ChatClientAgentRunOptions options = new();
+
+ // Act
+ AgentRunResponse agentRunResponse = await agent.RunAsync("Test message", thread, JsonContext_WithCustomRunOptions.Default.Options, options);
+
+ // Assert
+ Assert.NotNull(agentRunResponse);
+ Assert.Single(agentRunResponse.Messages);
+ Assert.Equal("Tigger", agentRunResponse.Result.FullName);
+ mockChatClient.Verify(
+ x => x.GetResponseAsync(
+ It.Is>(msgs => msgs.Any(m => m.Text == "Test message")),
+ It.IsAny(),
+ It.IsAny()),
+ Times.Once);
+ }
+
+ [Fact]
+ public async Task RunAsyncOfT_WithChatMessageAndOptions_CallsBaseMethodAsync()
+ {
+ // Arrange
+ Mock mockChatClient = new();
+ mockChatClient.Setup(
+ s => s.GetResponseAsync(
+ It.IsAny>(),
+ It.IsAny(),
+ It.IsAny())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, """{"id":2, "fullName":"Tigger", "species":"Tiger"}""")]));
+
+ ChatClientAgent agent = new(mockChatClient.Object);
+ AgentThread thread = agent.GetNewThread();
+ ChatMessage message = new(ChatRole.User, "Test message");
+ ChatClientAgentRunOptions options = new();
+
+ // Act
+ AgentRunResponse agentRunResponse = await agent.RunAsync(message, thread, JsonContext_WithCustomRunOptions.Default.Options, options);
+
+ // Assert
+ Assert.NotNull(agentRunResponse);
+ Assert.Single(agentRunResponse.Messages);
+ Assert.Equal("Tigger", agentRunResponse.Result.FullName);
+ mockChatClient.Verify(
+ x => x.GetResponseAsync(
+ It.Is>(msgs => msgs.Contains(message)),
+ It.IsAny(),
+ It.IsAny()),
+ Times.Once);
+ }
+
+ [Fact]
+ public async Task RunAsyncOfT_WithMessagesCollectionAndOptions_CallsBaseMethodAsync()
+ {
+ // Arrange
+ Mock mockChatClient = new();
+ mockChatClient.Setup(
+ s => s.GetResponseAsync(
+ It.IsAny>(),
+ It.IsAny(),
+ It.IsAny())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, """{"id":2, "fullName":"Tigger", "species":"Tiger"}""")]));
+
+ ChatClientAgent agent = new(mockChatClient.Object);
+ AgentThread thread = agent.GetNewThread();
+ IEnumerable messages = [new(ChatRole.User, "Message 1"), new(ChatRole.User, "Message 2")];
+ ChatClientAgentRunOptions options = new();
+
+ // Act
+ AgentRunResponse agentRunResponse = await agent.RunAsync(messages, thread, JsonContext_WithCustomRunOptions.Default.Options, options);
+
+ // Assert
+ Assert.NotNull(agentRunResponse);
+ Assert.Single(agentRunResponse.Messages);
+ Assert.Equal("Tigger", agentRunResponse.Result.FullName);
+ mockChatClient.Verify(
+ x => x.GetResponseAsync(
+ It.IsAny>(),
+ It.IsAny(),
+ It.IsAny()),
+ Times.Once);
+ }
+
+ #endregion
+
+ private sealed class Animal
+ {
+ public int Id { get; set; }
+ public string? FullName { get; set; }
+ public Species Species { get; set; }
+ }
+
+ private enum Species
+ {
+ Bear,
+ Tiger,
+ Walrus,
+ }
+
+ [JsonSourceGenerationOptions(UseStringEnumConverter = true, PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)]
+ [JsonSerializable(typeof(Animal))]
+ private sealed partial class JsonContext_WithCustomRunOptions : JsonSerializerContext;
+}