diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents/ChatCompletion/ChatClientAgentExtensions.cs b/dotnet/src/Microsoft.Extensions.AI.Agents/ChatCompletion/ChatClientAgentExtensions.cs
deleted file mode 100644
index 8dd36477a1..0000000000
--- a/dotnet/src/Microsoft.Extensions.AI.Agents/ChatCompletion/ChatClientAgentExtensions.cs
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.Shared.Diagnostics;
-
-namespace Microsoft.Extensions.AI.Agents;
-
-///
-/// Extensions for agent types.
-///
-public static class ChatClientAgentExtensions
-{
- ///
- /// Run the agent with the provided messages and an optional thread.
- ///
- /// Target agent to run.
- /// 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.
- /// Optional parameters for agent invocation.
- /// Optional chat options.
- /// The to monitor for cancellation requests. The default is .
- /// A containing the list of items.
- public static Task RunAsync(
- this ChatClientAgent agent,
- IReadOnlyCollection messages,
- AgentThread? thread = null,
- AgentRunOptions? agentRunOptions = null,
- ChatOptions? chatOptions = null,
- CancellationToken cancellationToken = default)
- {
- Throw.IfNull(agent);
- Throw.IfNull(messages);
-
- return agent.RunAsync(messages, thread, new ChatClientAgentRunOptions(chatOptions), cancellationToken);
- }
-
- ///
- /// Run the agent with the provided prompt.
- ///
- /// Target agent to run.
- /// The prompt 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.
- /// Optional parameters for agent invocation.
- /// Optional chat options.
- /// The to monitor for cancellation requests. The default is .
- /// A containing the list of items.
- public static Task RunAsync(
- this ChatClientAgent agent,
- string prompt,
- AgentThread? thread = null,
- AgentRunOptions? agentRunOptions = null,
- ChatOptions? chatOptions = null,
- CancellationToken cancellationToken = default)
- {
- Throw.IfNull(agent);
- Throw.IfNullOrWhitespace(prompt);
-
- return agent.RunAsync([new ChatMessage(ChatRole.User, prompt)], thread, agentRunOptions, chatOptions, cancellationToken);
- }
-
- ///
- /// Run the agent with the provided message and arguments.
- ///
- /// Target agent to run.
- /// 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.
- /// Optional parameters for agent invocation.
- /// Optional chat options.
- /// The to monitor for cancellation requests. The default is .
- public static IAsyncEnumerable RunStreamingAsync(
- this ChatClientAgent agent,
- IReadOnlyCollection messages,
- AgentThread? thread = null,
- AgentRunOptions? agentRunOptions = null,
- ChatOptions? chatOptions = null,
- CancellationToken cancellationToken = default)
- {
- Throw.IfNull(agent);
- Throw.IfNull(messages);
-
- return agent.RunStreamingAsync(messages, thread, new ChatClientAgentRunOptions(chatOptions), cancellationToken);
- }
-
- ///
- /// Run the agent with the provided prompt in streaming mode.
- ///
- /// Target agent to run.
- /// The prompt 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.
- /// Optional parameters for agent invocation.
- /// Optional chat options.
- /// The to monitor for cancellation requests. The default is .
- /// An async enumerable of items for streaming the response.
- public static IAsyncEnumerable RunStreamingAsync(
- this ChatClientAgent agent,
- string prompt,
- AgentThread? thread = null,
- AgentRunOptions? agentRunOptions = null,
- ChatOptions? chatOptions = null,
- CancellationToken cancellationToken = default)
- {
- Throw.IfNull(agent);
- Throw.IfNullOrWhitespace(prompt);
-
- return agent.RunStreamingAsync([new ChatMessage(ChatRole.User, prompt)], thread, agentRunOptions, chatOptions, cancellationToken);
- }
-}
diff --git a/dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/ChatCompletion/ChatClientAgentExtensionsTests.cs b/dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/ChatCompletion/ChatClientAgentExtensionsTests.cs
deleted file mode 100644
index 6302bdeaa6..0000000000
--- a/dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/ChatCompletion/ChatClientAgentExtensionsTests.cs
+++ /dev/null
@@ -1,857 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using Moq;
-
-#pragma warning disable RCS1196 // Call extension method as instance method
-
-namespace Microsoft.Extensions.AI.Agents.UnitTests.ChatCompletion;
-
-public class ChatClientAgentExtensionsTests
-{
- #region RunAsync with IReadOnlyCollection Tests
-
- ///
- /// Verify that RunAsync extension method with messages works with valid parameters.
- ///
- [Fact]
- public async Task RunAsyncWithMessagesWorksWithValidParametersAsync()
- {
- // Arrange
- Mock mockService = new();
- mockService.Setup(
- s => s.GetResponseAsync(
- It.IsAny>(),
- It.IsAny(),
- It.IsAny())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]));
-
- ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
- var messages = new List { new(ChatRole.User, "test message") };
-
- // Act & Assert - Should not throw
- var result = await ChatClientAgentExtensions.RunAsync(agent, messages);
- Assert.NotNull(result);
- }
-
- ///
- /// Verify that RunAsync extension method with messages throws ArgumentNullException when agent is null.
- ///
- [Fact]
- public async Task RunAsyncWithMessagesThrowsArgumentNullExceptionWhenAgentIsNullAsync()
- {
- // Arrange
- ChatClientAgent agent = null!;
- var messages = new List { new(ChatRole.User, "test") };
-
- // Act & Assert
- var exception = await Assert.ThrowsAsync(() =>
- ChatClientAgentExtensions.RunAsync(agent, messages));
- Assert.Equal("agent", exception.ParamName);
- }
-
- ///
- /// Verify that RunAsync extension method with messages throws ArgumentNullException when messages is null.
- ///
- [Fact]
- public async Task RunAsyncWithMessagesThrowsArgumentNullExceptionWhenMessagesIsNullAsync()
- {
- // Arrange
- var chatClient = new Mock().Object;
- ChatClientAgent agent = new(chatClient, options: new() { Instructions = "test instructions" });
-
- // Act & Assert
- var exception = await Assert.ThrowsAsync(() =>
- ChatClientAgentExtensions.RunAsync(agent, (IReadOnlyCollection)null!));
- Assert.Equal("messages", exception.ParamName);
- }
-
- ///
- /// Verify that RunAsync extension method with messages works with ChatOptions parameter.
- ///
- [Fact]
- public async Task RunAsyncWithMessagesWorksWithChatOptionsAsync()
- {
- // Arrange
- var chatOptions = new ChatOptions { MaxOutputTokens = 100 };
- Mock mockService = new();
- mockService.Setup(
- s => s.GetResponseAsync(
- It.IsAny>(),
- It.IsAny(),
- It.IsAny())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]));
-
- ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
- var messages = new List { new(ChatRole.User, "test") };
-
- // Act - Call extension method (should not throw)
- var result = await ChatClientAgentExtensions.RunAsync(agent, messages, chatOptions: chatOptions);
-
- // Assert - Extension method completed successfully
- Assert.NotNull(result);
- Assert.Single(result.Messages);
- }
-
- ///
- /// Verify that RunAsync extension method with messages passes Instructions correctly.
- ///
- [Fact]
- public async Task RunAsyncWithMessagesPassesInstructionsCorrectlyAsync()
- {
- // Arrange
- Mock mockService = new();
- List capturedMessages = [];
- List capturedChatOptions = [];
- mockService.Setup(
- s => s.GetResponseAsync(
- It.IsAny>(),
- It.IsAny(),
- It.IsAny()))
- .Callback, ChatOptions, CancellationToken>((msgs, opts, ct) =>
- {
- capturedChatOptions.Add(opts);
- capturedMessages.AddRange(msgs);
- })
- .ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]));
-
- ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "base instructions" });
- var messages = new List { new(ChatRole.User, "test") };
- var runOptions = new AgentRunOptions();
-
- // Act
- await ChatClientAgentExtensions.RunAsync(agent, messages, agentRunOptions: runOptions);
-
- // Assert
- Assert.Contains(capturedMessages, m => m.Text == "test" && m.Role == ChatRole.User);
- Assert.Single(capturedChatOptions);
- Assert.Equal("base instructions", capturedChatOptions[0].Instructions);
- }
-
- ///
- /// Verify that RunAsync extension method with messages works with thread parameter.
- ///
- [Fact]
- public async Task RunAsyncWithMessagesWorksWithThreadParameterAsync()
- {
- // Arrange
- Mock mockService = new();
- mockService.Setup(
- s => s.GetResponseAsync(
- It.IsAny>(),
- It.IsAny(),
- It.IsAny())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]));
-
- ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
- var messages = new List { new(ChatRole.User, "test") };
- var thread = agent.GetNewThread();
-
- // Act
- var result = await ChatClientAgentExtensions.RunAsync(agent, messages, thread: thread);
-
- // Assert
- Assert.NotNull(result);
- Assert.Single(result.Messages);
- mockService.Verify(
- x => x.GetResponseAsync(
- It.IsAny>(),
- It.IsAny(),
- It.IsAny()),
- Times.Once);
- }
-
- ///
- /// Verify that RunAsync extension method with messages respects cancellation token.
- ///
- [Fact]
- public async Task RunAsyncWithMessagesRespectsCancellationTokenAsync()
- {
- // Arrange
- using var cts = new CancellationTokenSource();
- cts.Cancel();
-
- Mock mockService = new();
- mockService.Setup(
- s => s.GetResponseAsync(
- It.IsAny>(),
- It.IsAny(),
- It.IsAny())).ThrowsAsync(new OperationCanceledException());
-
- ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
- var messages = new List { new(ChatRole.User, "test") };
-
- // Act & Assert
- await Assert.ThrowsAsync(() => ChatClientAgentExtensions.RunAsync(agent, messages, cancellationToken: cts.Token));
- }
-
- #endregion
-
- #region RunAsync with string prompt Tests
-
- ///
- /// Verify that RunAsync extension method with prompt calls the underlying agent method correctly.
- ///
- [Fact]
- public async Task RunAsyncWithPromptCallsUnderlyingAgentMethodAsync()
- {
- // Arrange
- Mock mockService = new();
- mockService.Setup(
- s => s.GetResponseAsync(
- It.IsAny>(),
- It.IsAny(),
- It.IsAny())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]));
-
- ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
- const string TestPrompt = "test prompt";
-
- // Act
- var result = await ChatClientAgentExtensions.RunAsync(agent, TestPrompt);
-
- // Assert
- Assert.NotNull(result);
- Assert.Single(result.Messages);
- Assert.Equal("response", result.Messages[0].Text);
- mockService.Verify(
- x => x.GetResponseAsync(
- It.IsAny>(),
- It.IsAny(),
- It.IsAny()),
- Times.Once);
- }
-
- ///
- /// Verify that RunAsync extension method with prompt throws ArgumentNullException when agent is null.
- ///
- [Fact]
- public async Task RunAsyncWithPromptThrowsArgumentNullExceptionWhenAgentIsNullAsync()
- {
- // Arrange
- ChatClientAgent agent = null!;
- const string TestPrompt = "test prompt";
-
- // Act & Assert
- var exception = await Assert.ThrowsAsync(() =>
- ChatClientAgentExtensions.RunAsync(agent, TestPrompt));
- Assert.Equal("agent", exception.ParamName);
- }
-
- ///
- /// Verify that RunAsync extension method with prompt throws ArgumentNullException when prompt is null.
- ///
- [Fact]
- public async Task RunAsyncWithPromptThrowsArgumentNullExceptionWhenPromptIsNullAsync()
- {
- // Arrange
- var chatClient = new Mock().Object;
- ChatClientAgent agent = new(chatClient, options: new() { Instructions = "test instructions" });
-
- // Act & Assert
- var exception = await Assert.ThrowsAsync(() =>
- ChatClientAgentExtensions.RunAsync(agent, (string)null!));
- Assert.Equal("prompt", exception.ParamName);
- }
-
- ///
- /// Verify that RunAsync extension method with prompt throws ArgumentException when prompt is whitespace.
- ///
- [Fact]
- public async Task RunAsyncWithPromptThrowsArgumentExceptionWhenPromptIsWhitespaceAsync()
- {
- // Arrange
- var chatClient = new Mock().Object;
- ChatClientAgent agent = new(chatClient, options: new() { Instructions = "test instructions" });
-
- // Act & Assert
- var exception = await Assert.ThrowsAsync(() =>
- ChatClientAgentExtensions.RunAsync(agent, " "));
- Assert.Equal("prompt", exception.ParamName);
- }
-
- ///
- /// Verify that RunAsync extension method with prompt converts prompt to ChatMessage correctly.
- ///
- [Fact]
- public async Task RunAsyncWithPromptConvertsPromptToChatMessageCorrectlyAsync()
- {
- // Arrange
- Mock mockService = new();
- List capturedMessages = [];
- mockService.Setup(
- s => s.GetResponseAsync(
- It.IsAny>(),
- It.IsAny(),
- It.IsAny()))
- .Callback, ChatOptions, CancellationToken>((msgs, opts, ct) =>
- capturedMessages.AddRange(msgs))
- .ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]));
-
- ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
- const string TestPrompt = "test prompt";
-
- // Act
- await ChatClientAgentExtensions.RunAsync(agent, TestPrompt);
-
- // Assert
- Assert.Contains(capturedMessages, m => m.Text == "test prompt" && m.Role == ChatRole.User);
- }
-
- ///
- /// Verify that RunAsync extension method with prompt passes ChatOptions correctly.
- ///
- [Fact]
- public async Task RunAsyncWithPromptPassesChatOptionsCorrectlyAsync()
- {
- // Arrange
- var chatOptions = new ChatOptions { MaxOutputTokens = 200 };
- Mock mockService = new();
- mockService.Setup(
- s => s.GetResponseAsync(
- It.IsAny>(),
- It.Is(opts => opts.MaxOutputTokens == 200),
- It.IsAny())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]));
-
- ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
- const string TestPrompt = "test prompt";
-
- // Act
- await ChatClientAgentExtensions.RunAsync(agent, TestPrompt, chatOptions: chatOptions);
-
- // Assert
- mockService.Verify(
- x => x.GetResponseAsync(
- It.IsAny>(),
- It.Is(opts => opts.MaxOutputTokens == 200),
- It.IsAny()),
- Times.Once);
- }
-
- ///
- /// Verify that RunAsync extension method with prompt passes AgentRunOptions correctly.
- ///
- [Fact]
- public async Task RunAsyncWithPromptPassesAgentRunOptionsCorrectlyAsync()
- {
- // Arrange
- Mock mockService = new();
- List capturedMessages = [];
- mockService.Setup(
- s => s.GetResponseAsync(
- It.IsAny>(),
- It.IsAny(),
- It.IsAny()))
- .Callback, ChatOptions, CancellationToken>((msgs, opts, ct) =>
- capturedMessages.AddRange(msgs))
- .ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]));
-
- ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "base instructions" });
- const string TestPrompt = "test prompt";
- var runOptions = new AgentRunOptions();
-
- // Act
- await ChatClientAgentExtensions.RunAsync(agent, TestPrompt, agentRunOptions: runOptions);
-
- // Assert
- Assert.Contains(capturedMessages, m => m.Text == "test prompt" && m.Role == ChatRole.User);
- }
-
- ///
- /// Verify that RunAsync extension method with prompt works with thread parameter.
- ///
- [Fact]
- public async Task RunAsyncWithPromptWorksWithThreadParameterAsync()
- {
- // Arrange
- Mock mockService = new();
- mockService.Setup(
- s => s.GetResponseAsync(
- It.IsAny>(),
- It.IsAny(),
- It.IsAny())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]));
-
- ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
- const string TestPrompt = "test prompt";
- var thread = agent.GetNewThread();
-
- // Act
- var result = await ChatClientAgentExtensions.RunAsync(agent, TestPrompt, thread: thread);
-
- // Assert
- Assert.NotNull(result);
- Assert.Single(result.Messages);
- mockService.Verify(
- x => x.GetResponseAsync(
- It.IsAny>(),
- It.IsAny(),
- It.IsAny()),
- Times.Once);
- }
-
- ///
- /// Verify that RunAsync extension method with prompt respects cancellation token.
- ///
- [Fact]
- public async Task RunAsyncWithPromptRespectsCancellationTokenAsync()
- {
- // Arrange
- using var cts = new CancellationTokenSource();
- cts.Cancel();
-
- Mock mockService = new();
- mockService.Setup(
- s => s.GetResponseAsync(
- It.IsAny>(),
- It.IsAny(),
- It.IsAny())).ThrowsAsync(new OperationCanceledException());
-
- ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
- const string TestPrompt = "test prompt";
-
- // Act & Assert
- await Assert.ThrowsAsync(() => agent.RunAsync(TestPrompt, cancellationToken: cts.Token));
- }
-
- #endregion
-
- #region RunStreamingAsync with IReadOnlyCollection Tests
-
- ///
- /// Verify that RunStreamingAsync extension method with messages calls the underlying agent method correctly.
- ///
- [Fact]
- public async Task RunStreamingAsyncWithMessagesCallsUnderlyingAgentMethodAsync()
- {
- // Arrange
- ChatResponseUpdate[] returnUpdates =
- [
- new ChatResponseUpdate(role: ChatRole.Assistant, content: "Hello"),
- new ChatResponseUpdate(role: ChatRole.Assistant, content: " World"),
- ];
-
- Mock mockService = new();
- mockService.Setup(
- s => s.GetStreamingResponseAsync(
- It.IsAny>(),
- It.IsAny(),
- It.IsAny())).Returns(returnUpdates.ToAsyncEnumerable());
-
- ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
- var messages = new List { new(ChatRole.User, "test message") };
-
- // Act
- var updates = new List();
- await foreach (var update in ChatClientAgentExtensions.RunStreamingAsync(agent, messages))
- {
- updates.Add(update);
- }
-
- // Assert
- Assert.Equal(2, updates.Count);
- Assert.Equal("Hello", updates[0].Text);
- Assert.Equal(" World", updates[1].Text);
- mockService.Verify(
- x => x.GetStreamingResponseAsync(
- It.IsAny>(),
- It.IsAny(),
- It.IsAny()),
- Times.Once);
- }
-
- ///
- /// Verify that RunStreamingAsync extension method with messages throws ArgumentNullException when agent is null.
- ///
- [Fact]
- public async Task RunStreamingAsyncWithMessagesThrowsArgumentNullExceptionWhenAgentIsNullAsync()
- {
- // Arrange
- ChatClientAgent agent = null!;
- var messages = new List { new(ChatRole.User, "test") };
-
- // Act & Assert
- var exception = await Assert.ThrowsAsync(async () =>
- {
- await foreach (var update in ChatClientAgentExtensions.RunStreamingAsync(agent, messages))
- {
- // Should not reach here
- }
- });
- Assert.Equal("agent", exception.ParamName);
- }
-
- ///
- /// Verify that RunStreamingAsync extension method with messages throws ArgumentNullException when messages is null.
- ///
- [Fact]
- public async Task RunStreamingAsyncWithMessagesThrowsArgumentNullExceptionWhenMessagesIsNullAsync()
- {
- // Arrange
- var chatClient = new Mock().Object;
- ChatClientAgent agent = new(chatClient, options: new() { Instructions = "test instructions" });
-
- // Act & Assert
- var exception = await Assert.ThrowsAsync(async () =>
- {
- await foreach (var update in ChatClientAgentExtensions.RunStreamingAsync(agent, (IReadOnlyCollection)null!))
- {
- // Should not reach here
- }
- });
- Assert.Equal("messages", exception.ParamName);
- }
-
- ///
- /// Verify that RunStreamingAsync extension method with messages passes ChatOptions correctly.
- ///
- [Fact]
- public async Task RunStreamingAsyncWithMessagesPassesChatOptionsCorrectlyAsync()
- {
- // Arrange
- var chatOptions = new ChatOptions { MaxOutputTokens = 100 };
- ChatResponseUpdate[] returnUpdates = [new ChatResponseUpdate(role: ChatRole.Assistant, content: "response")];
-
- Mock mockService = new();
- mockService.Setup(
- s => s.GetStreamingResponseAsync(
- It.IsAny>(),
- It.Is(opts => opts.MaxOutputTokens == 100),
- It.IsAny())).Returns(returnUpdates.ToAsyncEnumerable());
-
- ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
- var messages = new List { new(ChatRole.User, "test") };
-
- // Act
- var updates = new List();
- await foreach (var update in ChatClientAgentExtensions.RunStreamingAsync(agent, messages, chatOptions: chatOptions))
- {
- updates.Add(update);
- }
-
- // Assert
- Assert.Single(updates);
- mockService.Verify(
- x => x.GetStreamingResponseAsync(
- It.IsAny>(),
- It.Is(opts => opts.MaxOutputTokens == 100),
- It.IsAny()),
- Times.Once);
- }
-
- ///
- /// Verify that RunStreamingAsync extension method with messages works with thread parameter.
- ///
- [Fact]
- public async Task RunStreamingAsyncWithMessagesWorksWithThreadParameterAsync()
- {
- // Arrange
- ChatResponseUpdate[] returnUpdates = [new ChatResponseUpdate(role: ChatRole.Assistant, content: "response")];
-
- Mock mockService = new();
- mockService.Setup(
- s => s.GetStreamingResponseAsync(
- It.IsAny>(),
- It.IsAny(),
- It.IsAny())).Returns(returnUpdates.ToAsyncEnumerable());
-
- ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
- var messages = new List { new(ChatRole.User, "test") };
- var thread = agent.GetNewThread();
-
- // Act
- var updates = new List();
- await foreach (var update in ChatClientAgentExtensions.RunStreamingAsync(agent, messages, thread: thread))
- {
- updates.Add(update);
- }
-
- // Assert
- Assert.Single(updates);
- mockService.Verify(
- x => x.GetStreamingResponseAsync(
- It.IsAny>(),
- It.IsAny(),
- It.IsAny()),
- Times.Once);
- }
-
- ///
- /// Verify that RunStreamingAsync extension method with messages respects cancellation token.
- ///
- [Fact]
- public async Task RunStreamingAsyncWithMessagesRespectsCancellationTokenAsync()
- {
- // Arrange
- using var cts = new CancellationTokenSource();
- cts.Cancel();
-
- Mock mockService = new();
- mockService.Setup(
- s => s.GetStreamingResponseAsync(
- It.IsAny>(),
- It.IsAny(),
- It.IsAny())).Throws(new OperationCanceledException());
-
- ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
- var messages = new List { new(ChatRole.User, "test") };
-
- // Act & Assert
- await Assert.ThrowsAsync(async () =>
- {
- await foreach (var update in ChatClientAgentExtensions.RunStreamingAsync(agent, messages, cancellationToken: cts.Token))
- {
- // Should not reach here
- }
- });
- }
-
- #endregion
-
- #region RunStreamingAsync with string prompt Tests
-
- ///
- /// Verify that RunStreamingAsync extension method with prompt calls the underlying agent method correctly.
- ///
- [Fact]
- public async Task RunStreamingAsyncWithPromptCallsUnderlyingAgentMethodAsync()
- {
- // Arrange
- ChatResponseUpdate[] returnUpdates =
- [
- new ChatResponseUpdate(role: ChatRole.Assistant, content: "Hello"),
- new ChatResponseUpdate(role: ChatRole.Assistant, content: " World"),
- ];
-
- Mock mockService = new();
- mockService.Setup(
- s => s.GetStreamingResponseAsync(
- It.IsAny>(),
- It.IsAny(),
- It.IsAny())).Returns(returnUpdates.ToAsyncEnumerable());
-
- ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
- const string TestPrompt = "test prompt";
-
- // Act
- var updates = new List();
- await foreach (var update in ChatClientAgentExtensions.RunStreamingAsync(agent, TestPrompt))
- {
- updates.Add(update);
- }
-
- // Assert
- Assert.Equal(2, updates.Count);
- Assert.Equal("Hello", updates[0].Text);
- Assert.Equal(" World", updates[1].Text);
- mockService.Verify(
- x => x.GetStreamingResponseAsync(
- It.IsAny>(),
- It.IsAny(),
- It.IsAny()),
- Times.Once);
- }
-
- ///
- /// Verify that RunStreamingAsync extension method with prompt throws ArgumentNullException when agent is null.
- ///
- [Fact]
- public async Task RunStreamingAsyncWithPromptThrowsArgumentNullExceptionWhenAgentIsNullAsync()
- {
- // Arrange
- ChatClientAgent agent = null!;
- const string TestPrompt = "test prompt";
-
- // Act & Assert
- var exception = await Assert.ThrowsAsync(async () =>
- {
- await foreach (var update in ChatClientAgentExtensions.RunStreamingAsync(agent, TestPrompt))
- {
- // Should not reach here
- }
- });
- Assert.Equal("agent", exception.ParamName);
- }
-
- ///
- /// Verify that RunStreamingAsync extension method with prompt throws ArgumentNullException when prompt is null.
- ///
- [Fact]
- public async Task RunStreamingAsyncWithPromptThrowsArgumentNullExceptionWhenPromptIsNullAsync()
- {
- // Arrange
- var chatClient = new Mock().Object;
- ChatClientAgent agent = new(chatClient, options: new() { Instructions = "test instructions" });
-
- // Act & Assert
- var exception = await Assert.ThrowsAsync(async () =>
- {
- await foreach (var update in ChatClientAgentExtensions.RunStreamingAsync(agent, (string)null!))
- {
- // Should not reach here
- }
- });
- Assert.Equal("prompt", exception.ParamName);
- }
-
- ///
- /// Verify that RunStreamingAsync extension method with prompt throws ArgumentException when prompt is whitespace.
- ///
- [Fact]
- public async Task RunStreamingAsyncWithPromptThrowsArgumentExceptionWhenPromptIsWhitespaceAsync()
- {
- // Arrange
- var chatClient = new Mock().Object;
- ChatClientAgent agent = new(chatClient, options: new() { Instructions = "test instructions" });
-
- // Act & Assert
- var exception = await Assert.ThrowsAsync(async () =>
- {
- await foreach (var update in ChatClientAgentExtensions.RunStreamingAsync(agent, " "))
- {
- // Should not reach here
- }
- });
- Assert.Equal("prompt", exception.ParamName);
- }
-
- ///
- /// Verify that RunStreamingAsync extension method with prompt converts prompt to ChatMessage correctly.
- ///
- [Fact]
- public async Task RunStreamingAsyncWithPromptConvertsPromptToChatMessageCorrectlyAsync()
- {
- // Arrange
- ChatResponseUpdate[] returnUpdates = [new ChatResponseUpdate(role: ChatRole.Assistant, content: "response")];
-
- Mock mockService = new();
- List capturedMessages = [];
- mockService.Setup(
- s => s.GetStreamingResponseAsync(
- It.IsAny>(),
- It.IsAny(),
- It.IsAny()))
- .Callback, ChatOptions, CancellationToken>((msgs, opts, ct) =>
- capturedMessages.AddRange(msgs))
- .Returns(returnUpdates.ToAsyncEnumerable());
-
- ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
- const string TestPrompt = "test prompt";
-
- // Act
- var updates = new List();
- await foreach (var update in agent.RunStreamingAsync(TestPrompt))
- {
- updates.Add(update);
- }
-
- // Assert
- Assert.Single(updates);
- Assert.Contains(capturedMessages, m => m.Text == "test prompt" && m.Role == ChatRole.User);
- }
-
- ///
- /// Verify that RunStreamingAsync extension method with prompt passes ChatOptions correctly.
- ///
- [Fact]
- public async Task RunStreamingAsyncWithPromptPassesChatOptionsCorrectlyAsync()
- {
- // Arrange
- var chatOptions = new ChatOptions { MaxOutputTokens = 200 };
- ChatResponseUpdate[] returnUpdates = [new ChatResponseUpdate(role: ChatRole.Assistant, content: "response")];
-
- Mock mockService = new();
- mockService.Setup(
- s => s.GetStreamingResponseAsync(
- It.IsAny>(),
- It.Is(opts => opts.MaxOutputTokens == 200),
- It.IsAny())).Returns(returnUpdates.ToAsyncEnumerable());
-
- ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
- const string TestPrompt = "test prompt";
-
- // Act
- var updates = new List();
- await foreach (var update in agent.RunStreamingAsync(TestPrompt, chatOptions: chatOptions))
- {
- updates.Add(update);
- }
-
- // Assert
- Assert.Single(updates);
- mockService.Verify(
- x => x.GetStreamingResponseAsync(
- It.IsAny>(),
- It.Is(opts => opts.MaxOutputTokens == 200),
- It.IsAny()),
- Times.Once);
- }
-
- ///
- /// Verify that RunStreamingAsync extension method with prompt works with thread parameter.
- ///
- [Fact]
- public async Task RunStreamingAsyncWithPromptWorksWithThreadParameterAsync()
- {
- // Arrange
- ChatResponseUpdate[] returnUpdates = [new ChatResponseUpdate(role: ChatRole.Assistant, content: "response")];
-
- Mock mockService = new();
- mockService.Setup(
- s => s.GetStreamingResponseAsync(
- It.IsAny>(),
- It.IsAny(),
- It.IsAny())).Returns(returnUpdates.ToAsyncEnumerable());
-
- ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
- const string TestPrompt = "test prompt";
- var thread = agent.GetNewThread();
-
- // Act
- var updates = new List();
- await foreach (var update in agent.RunStreamingAsync(TestPrompt, thread: thread))
- {
- updates.Add(update);
- }
-
- // Assert
- Assert.Single(updates);
- mockService.Verify(
- x => x.GetStreamingResponseAsync(
- It.IsAny>(),
- It.IsAny(),
- It.IsAny()),
- Times.Once);
- }
-
- ///
- /// Verify that RunStreamingAsync extension method with prompt respects cancellation token.
- ///
- [Fact]
- public async Task RunStreamingAsyncWithPromptRespectsCancellationTokenAsync()
- {
- // Arrange
- using var cts = new CancellationTokenSource();
- cts.Cancel();
-
- Mock mockService = new();
- mockService.Setup(
- s => s.GetStreamingResponseAsync(
- It.IsAny>(),
- It.IsAny(),
- It.IsAny())).Throws(new OperationCanceledException());
-
- ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
- const string TestPrompt = "test prompt";
-
- // Act & Assert
- await Assert.ThrowsAsync(async () =>
- {
- await foreach (var update in agent.RunStreamingAsync(TestPrompt, cancellationToken: cts.Token))
- {
- // Should not reach here
- }
- });
- }
-
- #endregion
-}