Add ChatClientAgent constructor overload (#188)

* Add ChatClientAgent constructor overload

* Add unit tests for new constructor

* Up code coverage with extra tests

* Address PR comments.

* Address PR comment

* Add additional test to increase code coverage.
This commit is contained in:
westey
2025-07-16 13:36:17 +00:00
committed by GitHub
parent e6d611abe4
commit 95a3264c8b
17 changed files with 276 additions and 107 deletions
@@ -73,7 +73,7 @@ public class AzureAIAgentsPersistentFixture : IChatClientAgentFixture
return new ChatClientAgent(
this._persistentAgentsClient.AsIChatClient(persistentAgent.Id),
new()
options: new()
{
Id = persistentAgent.Id,
ChatOptions = new() { Tools = aiTools }
@@ -27,7 +27,7 @@ public class ChatClientAgentExtensionsTests
It.IsAny<ChatOptions>(),
It.IsAny<CancellationToken>())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]));
ChatClientAgent agent = new(mockService.Object, new() { Instructions = "test instructions" });
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
var messages = new List<ChatMessage> { new(ChatRole.User, "test message") };
// Act & Assert - Should not throw
@@ -59,7 +59,7 @@ public class ChatClientAgentExtensionsTests
{
// Arrange
var chatClient = new Mock<IChatClient>().Object;
ChatClientAgent agent = new(chatClient, new() { Instructions = "test instructions" });
ChatClientAgent agent = new(chatClient, options: new() { Instructions = "test instructions" });
// Act & Assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(() =>
@@ -82,7 +82,7 @@ public class ChatClientAgentExtensionsTests
It.IsAny<ChatOptions>(),
It.IsAny<CancellationToken>())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]));
ChatClientAgent agent = new(mockService.Object, new() { Instructions = "test instructions" });
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
var messages = new List<ChatMessage> { new(ChatRole.User, "test") };
// Act - Call extension method (should not throw)
@@ -115,7 +115,7 @@ public class ChatClientAgentExtensionsTests
})
.ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]));
ChatClientAgent agent = new(mockService.Object, new() { Instructions = "base instructions" });
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "base instructions" });
var messages = new List<ChatMessage> { new(ChatRole.User, "test") };
var runOptions = new AgentRunOptions();
@@ -142,7 +142,7 @@ public class ChatClientAgentExtensionsTests
It.IsAny<ChatOptions>(),
It.IsAny<CancellationToken>())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]));
ChatClientAgent agent = new(mockService.Object, new() { Instructions = "test instructions" });
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
var messages = new List<ChatMessage> { new(ChatRole.User, "test") };
var thread = agent.GetNewThread();
@@ -177,7 +177,7 @@ public class ChatClientAgentExtensionsTests
It.IsAny<ChatOptions>(),
It.IsAny<CancellationToken>())).ThrowsAsync(new OperationCanceledException());
ChatClientAgent agent = new(mockService.Object, new() { Instructions = "test instructions" });
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
var messages = new List<ChatMessage> { new(ChatRole.User, "test") };
// Act & Assert
@@ -202,7 +202,7 @@ public class ChatClientAgentExtensionsTests
It.IsAny<ChatOptions>(),
It.IsAny<CancellationToken>())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]));
ChatClientAgent agent = new(mockService.Object, new() { Instructions = "test instructions" });
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
const string TestPrompt = "test prompt";
// Act
@@ -244,7 +244,7 @@ public class ChatClientAgentExtensionsTests
{
// Arrange
var chatClient = new Mock<IChatClient>().Object;
ChatClientAgent agent = new(chatClient, new() { Instructions = "test instructions" });
ChatClientAgent agent = new(chatClient, options: new() { Instructions = "test instructions" });
// Act & Assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(() =>
@@ -260,7 +260,7 @@ public class ChatClientAgentExtensionsTests
{
// Arrange
var chatClient = new Mock<IChatClient>().Object;
ChatClientAgent agent = new(chatClient, new() { Instructions = "test instructions" });
ChatClientAgent agent = new(chatClient, options: new() { Instructions = "test instructions" });
// Act & Assert
var exception = await Assert.ThrowsAsync<ArgumentException>(() =>
@@ -286,7 +286,7 @@ public class ChatClientAgentExtensionsTests
capturedMessages.AddRange(msgs))
.ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]));
ChatClientAgent agent = new(mockService.Object, new() { Instructions = "test instructions" });
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
const string TestPrompt = "test prompt";
// Act
@@ -312,7 +312,7 @@ public class ChatClientAgentExtensionsTests
It.Is<ChatOptions>(opts => opts.MaxOutputTokens == 200),
It.IsAny<CancellationToken>())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]));
ChatClientAgent agent = new(mockService.Object, new() { Instructions = "test instructions" });
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
const string TestPrompt = "test prompt";
// Act
@@ -345,7 +345,7 @@ public class ChatClientAgentExtensionsTests
capturedMessages.AddRange(msgs))
.ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]));
ChatClientAgent agent = new(mockService.Object, new() { Instructions = "base instructions" });
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "base instructions" });
const string TestPrompt = "test prompt";
var runOptions = new AgentRunOptions();
@@ -371,7 +371,7 @@ public class ChatClientAgentExtensionsTests
It.IsAny<ChatOptions>(),
It.IsAny<CancellationToken>())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]));
ChatClientAgent agent = new(mockService.Object, new() { Instructions = "test instructions" });
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
const string TestPrompt = "test prompt";
var thread = agent.GetNewThread();
@@ -406,7 +406,7 @@ public class ChatClientAgentExtensionsTests
It.IsAny<ChatOptions>(),
It.IsAny<CancellationToken>())).ThrowsAsync(new OperationCanceledException());
ChatClientAgent agent = new(mockService.Object, new() { Instructions = "test instructions" });
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
const string TestPrompt = "test prompt";
// Act & Assert
@@ -437,7 +437,7 @@ public class ChatClientAgentExtensionsTests
It.IsAny<ChatOptions>(),
It.IsAny<CancellationToken>())).Returns(returnUpdates.ToAsyncEnumerable());
ChatClientAgent agent = new(mockService.Object, new() { Instructions = "test instructions" });
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
var messages = new List<ChatMessage> { new(ChatRole.User, "test message") };
// Act
@@ -488,7 +488,7 @@ public class ChatClientAgentExtensionsTests
{
// Arrange
var chatClient = new Mock<IChatClient>().Object;
ChatClientAgent agent = new(chatClient, new() { Instructions = "test instructions" });
ChatClientAgent agent = new(chatClient, options: new() { Instructions = "test instructions" });
// Act & Assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(async () =>
@@ -518,7 +518,7 @@ public class ChatClientAgentExtensionsTests
It.Is<ChatOptions>(opts => opts.MaxOutputTokens == 100),
It.IsAny<CancellationToken>())).Returns(returnUpdates.ToAsyncEnumerable());
ChatClientAgent agent = new(mockService.Object, new() { Instructions = "test instructions" });
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
var messages = new List<ChatMessage> { new(ChatRole.User, "test") };
// Act
@@ -554,7 +554,7 @@ public class ChatClientAgentExtensionsTests
It.IsAny<ChatOptions>(),
It.IsAny<CancellationToken>())).Returns(returnUpdates.ToAsyncEnumerable());
ChatClientAgent agent = new(mockService.Object, new() { Instructions = "test instructions" });
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
var messages = new List<ChatMessage> { new(ChatRole.User, "test") };
var thread = agent.GetNewThread();
@@ -592,7 +592,7 @@ public class ChatClientAgentExtensionsTests
It.IsAny<ChatOptions>(),
It.IsAny<CancellationToken>())).Throws(new OperationCanceledException());
ChatClientAgent agent = new(mockService.Object, new() { Instructions = "test instructions" });
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
var messages = new List<ChatMessage> { new(ChatRole.User, "test") };
// Act & Assert
@@ -629,7 +629,7 @@ public class ChatClientAgentExtensionsTests
It.IsAny<ChatOptions>(),
It.IsAny<CancellationToken>())).Returns(returnUpdates.ToAsyncEnumerable());
ChatClientAgent agent = new(mockService.Object, new() { Instructions = "test instructions" });
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
const string TestPrompt = "test prompt";
// Act
@@ -680,7 +680,7 @@ public class ChatClientAgentExtensionsTests
{
// Arrange
var chatClient = new Mock<IChatClient>().Object;
ChatClientAgent agent = new(chatClient, new() { Instructions = "test instructions" });
ChatClientAgent agent = new(chatClient, options: new() { Instructions = "test instructions" });
// Act & Assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(async () =>
@@ -701,7 +701,7 @@ public class ChatClientAgentExtensionsTests
{
// Arrange
var chatClient = new Mock<IChatClient>().Object;
ChatClientAgent agent = new(chatClient, new() { Instructions = "test instructions" });
ChatClientAgent agent = new(chatClient, options: new() { Instructions = "test instructions" });
// Act & Assert
var exception = await Assert.ThrowsAsync<ArgumentException>(async () =>
@@ -734,7 +734,7 @@ public class ChatClientAgentExtensionsTests
capturedMessages.AddRange(msgs))
.Returns(returnUpdates.ToAsyncEnumerable());
ChatClientAgent agent = new(mockService.Object, new() { Instructions = "test instructions" });
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
const string TestPrompt = "test prompt";
// Act
@@ -767,7 +767,7 @@ public class ChatClientAgentExtensionsTests
It.Is<ChatOptions>(opts => opts.MaxOutputTokens == 200),
It.IsAny<CancellationToken>())).Returns(returnUpdates.ToAsyncEnumerable());
ChatClientAgent agent = new(mockService.Object, new() { Instructions = "test instructions" });
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
const string TestPrompt = "test prompt";
// Act
@@ -803,7 +803,7 @@ public class ChatClientAgentExtensionsTests
It.IsAny<ChatOptions>(),
It.IsAny<CancellationToken>())).Returns(returnUpdates.ToAsyncEnumerable());
ChatClientAgent agent = new(mockService.Object, new() { Instructions = "test instructions" });
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
const string TestPrompt = "test prompt";
var thread = agent.GetNewThread();
@@ -841,7 +841,7 @@ public class ChatClientAgentExtensionsTests
It.IsAny<ChatOptions>(),
It.IsAny<CancellationToken>())).Throws(new OperationCanceledException());
ChatClientAgent agent = new(mockService.Object, new() { Instructions = "test instructions" });
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
const string TestPrompt = "test prompt";
// Act & Assert
@@ -21,7 +21,7 @@ public class ChatClientAgentTests
var chatClient = new Mock<IChatClient>().Object;
ChatClientAgent agent =
new(chatClient,
new()
options: new()
{
Id = "test-agent-id",
Name = "test name",
@@ -54,7 +54,7 @@ public class ChatClientAgentTests
It.IsAny<CancellationToken>())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "I'm here!")]));
ChatClientAgent agent =
new(mockService.Object, new()
new(mockService.Object, options: new()
{
Instructions = "test instructions"
});
@@ -90,7 +90,7 @@ public class ChatClientAgentTests
{
// Arrange
var chatClient = new Mock<IChatClient>().Object;
ChatClientAgent agent = new(chatClient, new() { Instructions = "test instructions" });
ChatClientAgent agent = new(chatClient, options: new() { Instructions = "test instructions" });
// Act & Assert
await Assert.ThrowsAsync<ArgumentNullException>(() => agent.RunAsync((IReadOnlyCollection<ChatMessage>)null!));
@@ -111,7 +111,7 @@ public class ChatClientAgentTests
It.Is<ChatOptions>(opts => opts.MaxOutputTokens == 100),
It.IsAny<CancellationToken>())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]));
ChatClientAgent agent = new(mockService.Object, new() { Instructions = "test instructions" });
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
// Act
await agent.RunAsync([new(ChatRole.User, "test")], chatOptions: chatOptions);
@@ -139,7 +139,7 @@ public class ChatClientAgentTests
null,
It.IsAny<CancellationToken>())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]));
ChatClientAgent agent = new(mockService.Object, new() { Instructions = "test instructions" });
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
var runOptions = new AgentRunOptions();
// Act
@@ -172,7 +172,7 @@ public class ChatClientAgentTests
capturedMessages.AddRange(msgs))
.ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]));
ChatClientAgent agent = new(mockService.Object, new() { Instructions = "base instructions" });
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "base instructions" });
var runOptions = new AgentRunOptions();
// Act
@@ -202,7 +202,7 @@ public class ChatClientAgentTests
It.IsAny<ChatOptions>(),
It.IsAny<CancellationToken>())).ReturnsAsync(new ChatResponse(responseMessages));
ChatClientAgent agent = new(mockService.Object, new() { Instructions = "test instructions", Name = "TestAgent" });
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions", Name = "TestAgent" });
// Act
var result = await agent.RunAsync([new(ChatRole.User, "test")]);
@@ -229,7 +229,7 @@ public class ChatClientAgentTests
capturedMessages.AddRange(msgs))
.ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]));
ChatClientAgent agent = new(mockService.Object, new() { Instructions = "test instructions" });
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
// Create a thread using the agent's GetNewThread method
var thread = agent.GetNewThread();
@@ -261,7 +261,7 @@ public class ChatClientAgentTests
capturedMessages.AddRange(msgs))
.ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]));
ChatClientAgent agent = new(mockService.Object, new() { Instructions = null });
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = null });
// Act
await agent.RunAsync([new(ChatRole.User, "test message")]);
@@ -291,7 +291,7 @@ public class ChatClientAgentTests
capturedMessages.AddRange(msgs))
.ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]));
ChatClientAgent agent = new(mockService.Object, new() { Instructions = "test instructions" });
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
// Act
await agent.RunAsync([]);
@@ -319,7 +319,7 @@ public class ChatClientAgentTests
It.Is<ChatOptions>(opts => opts.ConversationId == "ConvId"),
It.IsAny<CancellationToken>())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]) { ConversationId = "ConvId" });
ChatClientAgent agent = new(mockService.Object, new() { Instructions = "test instructions" });
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
ChatClientAgentThread thread = new("ConvId");
@@ -338,7 +338,7 @@ public class ChatClientAgentTests
var chatOptions = new ChatOptions { ConversationId = "ConvId" };
Mock<IChatClient> mockService = new();
ChatClientAgent agent = new(mockService.Object, new() { Instructions = "test instructions" });
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
ChatClientAgentThread thread = new("ThreadId");
@@ -361,7 +361,7 @@ public class ChatClientAgentTests
It.Is<ChatOptions>(opts => opts.MaxOutputTokens == 100 && opts.ConversationId == "ConvId"),
It.IsAny<CancellationToken>())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]) { ConversationId = "ConvId" });
ChatClientAgent agent = new(mockService.Object, new() { Instructions = "test instructions" });
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
ChatClientAgentThread thread = new("ConvId");
@@ -386,7 +386,7 @@ public class ChatClientAgentTests
It.IsAny<ChatOptions>(),
It.IsAny<CancellationToken>())).ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]));
ChatClientAgent agent = new(mockService.Object, new() { Instructions = "test instructions" });
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
ChatClientAgentThread thread = new("ConvId");
@@ -419,7 +419,7 @@ public class ChatClientAgentTests
{
// Arrange
var chatClient = new Mock<IChatClient>().Object;
ChatClientAgent agent = new(chatClient, null);
ChatClientAgent agent = new(chatClient);
// Act & Assert
Assert.NotNull(agent.Id);
@@ -469,7 +469,7 @@ public class ChatClientAgentTests
{
// Arrange
var chatClient = new Mock<IChatClient>().Object;
ChatClientAgent agent = new(chatClient, null);
ChatClientAgent agent = new(chatClient);
// Act & Assert
Assert.Null(agent.Name);
@@ -513,7 +513,7 @@ public class ChatClientAgentTests
{
// Arrange
var chatClient = new Mock<IChatClient>().Object;
ChatClientAgent agent = new(chatClient, null);
ChatClientAgent agent = new(chatClient);
// Act & Assert
Assert.Null(agent.Description);
@@ -557,7 +557,7 @@ public class ChatClientAgentTests
{
// Arrange
var chatClient = new Mock<IChatClient>().Object;
ChatClientAgent agent = new(chatClient, null);
ChatClientAgent agent = new(chatClient);
// Act & Assert
Assert.Null(agent.Instructions);
@@ -580,6 +580,68 @@ public class ChatClientAgentTests
#endregion
#region Options params Constructor Tests
/// <summary>
/// Checks that all params are set correctly when using the constructor with optional parameters.
/// </summary>
[Fact]
public void ConstructorUsesOptionalParams()
{
// Arrange
var chatClient = new Mock<IChatClient>().Object;
ChatClientAgent agent = new(chatClient, instructions: "TestInstructions", name: "TestName", description: "TestDescription", tools: [AIFunctionFactory.Create(() => { })]);
// Act & Assert
Assert.Equal("TestInstructions", agent.Instructions);
Assert.Equal("TestName", agent.Name);
Assert.Equal("TestDescription", agent.Description);
Assert.NotNull(agent.ChatOptions);
Assert.NotNull(agent.ChatOptions.Tools);
Assert.Single(agent.ChatOptions.Tools!);
}
/// <summary>
/// Verify that ChatOptions property returns null when no params are provided that require a ChatOptions instance.
/// </summary>
[Fact]
public void ChatOptionsReturnsNullWhenConstructorToolsNotProvided()
{
// Arrange
var chatClient = new Mock<IChatClient>().Object;
ChatClientAgent agent = new(chatClient, instructions: "TestInstructions", name: "TestName", description: "TestDescription");
// Act & Assert
Assert.Equal("TestInstructions", agent.Instructions);
Assert.Equal("TestName", agent.Name);
Assert.Equal("TestDescription", agent.Description);
Assert.Null(agent.ChatOptions);
}
#endregion
#region Options Constructor Tests
/// <summary>
/// Checks that the various properties on <see cref="ChatClientAgent"/> are null or defaulted when not provided to the constructor.
/// </summary>
[Fact]
public void OptionsPropertiesNullOrDefaultWhenNotProvidedToConstructor()
{
// Arrange
var chatClient = new Mock<IChatClient>().Object;
ChatClientAgent agent = new(chatClient, options: null!);
// Act & Assert
Assert.NotNull(agent.Id);
Assert.Null(agent.Instructions);
Assert.Null(agent.Name);
Assert.Null(agent.Description);
Assert.Null(agent.ChatOptions);
}
#endregion
#region ChatOptions Property Tests
/// <summary>
@@ -590,7 +652,7 @@ public class ChatClientAgentTests
{
// Arrange
var chatClient = new Mock<IChatClient>().Object;
ChatClientAgent agent = new(chatClient, null);
ChatClientAgent agent = new(chatClient);
// Act & Assert
Assert.Null(agent.ChatOptions);
@@ -656,7 +718,7 @@ public class ChatClientAgentTests
capturedChatOptions = opts)
.ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]));
ChatClientAgent agent = new(mockService.Object, new()
ChatClientAgent agent = new(mockService.Object, options: new()
{
Instructions = "test instructions",
ChatOptions = agentChatOptions
@@ -691,7 +753,7 @@ public class ChatClientAgentTests
capturedChatOptions = opts)
.ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]));
ChatClientAgent agent = new(mockService.Object, new() { Instructions = "test instructions" });
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
var messages = new List<ChatMessage> { new(ChatRole.User, "test") };
// Act
@@ -746,7 +808,7 @@ public class ChatClientAgentTests
capturedChatOptions = opts)
.ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]));
ChatClientAgent agent = new(mockService.Object, new()
ChatClientAgent agent = new(mockService.Object, options: new()
{
Instructions = "test instructions",
ChatOptions = agentChatOptions
@@ -785,7 +847,7 @@ public class ChatClientAgentTests
capturedChatOptions = opts)
.ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]));
ChatClientAgent agent = new(mockService.Object, new() { Instructions = "test instructions" });
ChatClientAgent agent = new(mockService.Object, options: new() { Instructions = "test instructions" });
var messages = new List<ChatMessage> { new(ChatRole.User, "test") };
// Act
@@ -825,7 +887,7 @@ public class ChatClientAgentTests
capturedChatOptions = opts)
.ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]));
ChatClientAgent agent = new(mockService.Object, new()
ChatClientAgent agent = new(mockService.Object, options: new()
{
Instructions = "test instructions",
ChatOptions = agentChatOptions
@@ -874,7 +936,7 @@ public class ChatClientAgentTests
capturedChatOptions = opts)
.ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]));
ChatClientAgent agent = new(mockService.Object, new()
ChatClientAgent agent = new(mockService.Object, options: new()
{
Instructions = "test instructions",
ChatOptions = agentChatOptions
@@ -891,6 +953,52 @@ public class ChatClientAgentTests
Assert.Contains(agentTool, capturedChatOptions.Tools); // Should contain the agent's tool
}
/// <summary>
/// Verify that ChatOptions merging uses RawRepresentationFactory from request first, with fallback to agent.
/// </summary>
[Theory]
[InlineData("MockAgentSetting", "MockRequestSetting", "MockRequestSetting")]
[InlineData("MockAgentSetting", null, "MockAgentSetting")]
[InlineData(null, "MockRequestSetting", "MockRequestSetting")]
public async Task ChatOptionsMergingUsesRawRepresentationFactoryWithFallbackAsync(string? agentSetting, string? requestSetting, string expectedSetting)
{
// Arrange
var agentChatOptions = new ChatOptions
{
RawRepresentationFactory = (_) => agentSetting
};
var requestChatOptions = new ChatOptions
{
RawRepresentationFactory = (_) => requestSetting
};
Mock<IChatClient> mockService = new();
ChatOptions? capturedChatOptions = null;
mockService.Setup(
s => s.GetResponseAsync(
It.IsAny<IEnumerable<ChatMessage>>(),
It.IsAny<ChatOptions>(),
It.IsAny<CancellationToken>()))
.Callback<IEnumerable<ChatMessage>, ChatOptions, CancellationToken>((msgs, opts, ct) =>
capturedChatOptions = opts)
.ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]));
ChatClientAgent agent = new(mockService.Object, options: new()
{
Instructions = "test instructions",
ChatOptions = agentChatOptions
});
var messages = new List<ChatMessage> { new(ChatRole.User, "test") };
// Act
await agent.RunAsync(messages, chatOptions: requestChatOptions);
// Assert
Assert.NotNull(capturedChatOptions);
Assert.NotNull(capturedChatOptions.RawRepresentationFactory);
Assert.Equal(expectedSetting, capturedChatOptions.RawRepresentationFactory(null!));
}
/// <summary>
/// Verify that ChatOptions merging handles all scalar properties correctly.
/// </summary>
@@ -950,7 +1058,7 @@ public class ChatClientAgentTests
capturedChatOptions = opts)
.ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")]));
ChatClientAgent agent = new(mockService.Object, new()
ChatClientAgent agent = new(mockService.Object, options: new()
{
Instructions = "test instructions",
ChatOptions = agentChatOptions
@@ -1007,7 +1115,7 @@ public class ChatClientAgentTests
It.IsAny<CancellationToken>())).Returns(returnUpdates.ToAsyncEnumerable());
ChatClientAgent agent =
new(mockService.Object, new()
new(mockService.Object, options: new()
{
Instructions = "test instructions"
});
@@ -69,7 +69,7 @@ public class ChatClientAgentThreadTests
.ReturnsAsync(new ChatResponse([assistantMessage]));
// Create ChatClientAgent with the mocked client
var agent = new ChatClientAgent(mockChatClient.Object, new()
var agent = new ChatClientAgent(mockChatClient.Object, options: new()
{
Instructions = "You are a helpful assistant"
});
@@ -190,7 +190,7 @@ public class ChatClientAgentThreadTests
It.IsAny<CancellationToken>()))
.ReturnsAsync(new ChatResponse([new ChatMessage(ChatRole.Assistant, "response")]));
var agent = new ChatClientAgent(mockChatClient.Object, new());
var agent = new ChatClientAgent(mockChatClient.Object, options: new());
// Act
var thread = agent.GetNewThread();
@@ -210,7 +210,7 @@ public class ChatClientAgentThreadTests
{
// Arrange
var mockChatClient = new Mock<IChatClient>();
var agent = new ChatClientAgent(mockChatClient.Object, new());
var agent = new ChatClientAgent(mockChatClient.Object, options: new());
// Act
var thread1 = agent.GetNewThread();
@@ -242,7 +242,7 @@ public class ChatClientAgentThreadTests
It.IsAny<CancellationToken>()))
.ReturnsAsync(new ChatResponse([assistantMessage]) { ConversationId = responseConversationId });
var agent = new ChatClientAgent(mockChatClient.Object, new() { Instructions = "Test instructions" });
var agent = new ChatClientAgent(mockChatClient.Object, options: new() { Instructions = "Test instructions" });
// Act
var thread = agent.GetNewThread();
@@ -298,7 +298,7 @@ public class ChatClientAgentThreadTests
.ReturnsAsync(new ChatResponse([messages[1]]))
.ReturnsAsync(new ChatResponse([messages[3]]));
var agent = new ChatClientAgent(mockChatClient.Object, new());
var agent = new ChatClientAgent(mockChatClient.Object, options: new());
var thread = agent.GetNewThread();
// Act - Add messages through multiple agent runs
@@ -349,7 +349,7 @@ public class ChatClientAgentThreadTests
.Returns(returnUpdates.ToAsyncEnumerable());
// Create ChatClientAgent with the mocked client
var agent = new ChatClientAgent(mockChatClient.Object, new()
var agent = new ChatClientAgent(mockChatClient.Object, options: new()
{
Instructions = "You are a helpful assistant"
});
@@ -415,7 +415,7 @@ public class ChatClientAgentThreadTests
.Returns(firstReturnUpdates.ToAsyncEnumerable())
.Returns(secondReturnUpdates.ToAsyncEnumerable());
var agent = new ChatClientAgent(mockChatClient.Object, new());
var agent = new ChatClientAgent(mockChatClient.Object, options: new());
var thread = agent.GetNewThread();
// Act - Make two streaming calls
@@ -486,7 +486,7 @@ public class ChatClientAgentThreadTests
It.IsAny<CancellationToken>()))
.Returns(streamingUpdates.ToAsyncEnumerable());
var agent = new ChatClientAgent(mockChatClient.Object, new());
var agent = new ChatClientAgent(mockChatClient.Object, options: new());
var thread = agent.GetNewThread();
// Act - First, make a regular call to populate the thread
@@ -538,7 +538,7 @@ public class ChatClientAgentThreadTests
It.IsAny<CancellationToken>()))
.Returns(returnUpdates.ToAsyncEnumerable());
var agent = new ChatClientAgent(mockChatClient.Object, new());
var agent = new ChatClientAgent(mockChatClient.Object, options: new());
var thread = agent.GetNewThread();
// Act - Run the agent with streaming that returns no updates
@@ -593,7 +593,7 @@ public class ChatClientAgentThreadTests
It.IsAny<CancellationToken>()))
.Returns(returnUpdates.ToAsyncEnumerable());
var agent = new ChatClientAgent(mockChatClient.Object, new());
var agent = new ChatClientAgent(mockChatClient.Object, options: new());
var thread = agent.GetNewThread();
// Act - Run the agent with streaming that returns multiple updates
@@ -638,7 +638,7 @@ public class ChatClientAgentThreadTests
It.IsAny<CancellationToken>()))
.Throws(new InvalidOperationException("Streaming failed"));
var agent = new ChatClientAgent(mockChatClient.Object, new());
var agent = new ChatClientAgent(mockChatClient.Object, options: new());
var thread = agent.GetNewThread();
// Act & Assert - Verify that streaming throws an exception
@@ -688,7 +688,7 @@ public class ChatClientAgentThreadTests
It.IsAny<CancellationToken>()))
.Returns(GetUpdatesWithExceptionAsync());
var agent = new ChatClientAgent(mockChatClient.Object, new());
var agent = new ChatClientAgent(mockChatClient.Object, options: new());
var thread = agent.GetNewThread();
// Act & Assert - Verify that streaming throws an exception after some updates
@@ -4,6 +4,7 @@
<TargetFrameworks>$(ProjectsTargetFrameworks)</TargetFrameworks>
<TargetFrameworks Condition="'$(Configuration)' == 'Debug'">$(ProjectsDebugTargetFrameworks)</TargetFrameworks>
<InjectSharedIntegrationTestCode>True</InjectSharedIntegrationTestCode>
<NoWarn>$(NoWarn);OPENAI001;</NoWarn>
</PropertyGroup>
<ItemGroup>
@@ -13,8 +13,6 @@ using Shared.IntegrationTests;
namespace OpenAIAssistant.IntegrationTests;
#pragma warning disable OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
public class OpenAIAssistantFixture : IChatClientAgentFixture
{
private static readonly OpenAIConfiguration s_config = TestConfiguration.LoadSection<OpenAIConfiguration>();
@@ -67,7 +65,7 @@ public class OpenAIAssistantFixture : IChatClientAgentFixture
return new ChatClientAgent(
this._assistantClient.AsIChatClient(assistant.Value.Id),
new()
options: new()
{
Id = assistant.Value.Id,
ChatOptions = new() { Tools = aiTools }
@@ -50,7 +50,7 @@ public class OpenAIChatCompletionFixture : IChatClientAgentFixture
.GetChatClient(this._useReasoningModel ? s_config.ChatReasoningModelId : s_config.ChatModelId)
.AsIChatClient();
return Task.FromResult(new ChatClientAgent(chatClient, new()
return Task.FromResult(new ChatClientAgent(chatClient, options: new()
{
Name = name,
Instructions = instructions,
@@ -4,6 +4,7 @@
<TargetFrameworks>$(ProjectsTargetFrameworks)</TargetFrameworks>
<TargetFrameworks Condition="'$(Configuration)' == 'Debug'">$(ProjectsDebugTargetFrameworks)</TargetFrameworks>
<InjectSharedIntegrationTestCode>True</InjectSharedIntegrationTestCode>
<NoWarn>$(NoWarn);OPENAI001;</NoWarn>
</PropertyGroup>
<ItemGroup>
@@ -12,8 +12,6 @@ using OpenAI;
using OpenAI.Responses;
using Shared.IntegrationTests;
#pragma warning disable OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
namespace OpenAIResponse.IntegrationTests;
public class OpenAIResponseFixture(bool store) : IChatClientAgentFixture
@@ -80,7 +78,7 @@ public class OpenAIResponseFixture(bool store) : IChatClientAgentFixture
{
return Task.FromResult(new ChatClientAgent(
this._openAIResponseClient.AsIChatClient(),
new()
options: new()
{
Name = name,
Instructions = instructions,