diff --git a/dotnet/tests/Microsoft.Agents.AI.Anthropic.UnitTests/Extensions/AnthropicBetaServiceExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.Anthropic.UnitTests/Extensions/AnthropicBetaServiceExtensionsTests.cs index 91d2cb988a..8415b09b25 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Anthropic.UnitTests/Extensions/AnthropicBetaServiceExtensionsTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Anthropic.UnitTests/Extensions/AnthropicBetaServiceExtensionsTests.cs @@ -188,6 +188,205 @@ public void CreateAIAgent_WithNullOptions_ThrowsArgumentNullException() Assert.Equal("options", exception.ParamName); } + /// + /// Verify that CreateAIAgent with tools correctly assigns tools to ChatOptions. + /// + [Fact] + public void CreateAIAgent_WithTools_AssignsToolsCorrectly() + { + // Arrange + var chatClient = new TestAnthropicChatClient(); + IList tools = [AIFunctionFactory.Create(() => "test result", "TestFunction", "A test function")]; + + // Act + var agent = chatClient.Beta.AsAIAgent( + model: "test-model", + name: "Test Agent", + tools: tools); + + // Assert + Assert.NotNull(agent); + Assert.Equal("Test Agent", agent.Name); + // When tools are provided, ChatOptions is created but instructions remain null + Assert.Null(agent.Instructions); + + // Verify that tools are registered in the FunctionInvokingChatClient + var functionInvokingClient = agent.GetService(); + Assert.NotNull(functionInvokingClient); + Assert.NotNull(functionInvokingClient.AdditionalTools); + Assert.Contains(functionInvokingClient.AdditionalTools, t => t is AIFunction func && func.Name == "TestFunction"); + } + + /// + /// Verify that CreateAIAgent with explicit defaultMaxTokens uses the provided value. + /// + [Fact] + public async Task CreateAIAgent_WithExplicitMaxTokens_UsesProvidedValueAsync() + { + // Arrange + int capturedMaxTokens = 0; + var handler = new CapturingHttpHandler(request => + { + // Parse the request body to capture max_tokens + var content = request.Content?.ReadAsStringAsync().GetAwaiter().GetResult(); + if (content is not null) + { + var json = System.Text.Json.JsonDocument.Parse(content); + if (json.RootElement.TryGetProperty("max_tokens", out var maxTokens)) + { + capturedMaxTokens = maxTokens.GetInt32(); + } + } + }); + + var client = new AnthropicClient + { + HttpClient = new HttpClient(handler) { BaseAddress = new Uri("http://localhost") }, + APIKey = "test-key" + }; + + // Act + var agent = client.Beta.AsAIAgent( + model: "claude-haiku-4-5", + name: "Test Agent", + defaultMaxTokens: 8192); + + // Invoke the agent to trigger the request + var thread = await agent.GetNewThreadAsync(); + try + { + await agent.RunAsync("Test message", thread); + } + catch + { + // Expected to fail since we're using a test handler + } + + // Assert + Assert.Equal(8192, capturedMaxTokens); + } + + /// + /// HTTP handler that captures requests for verification. + /// + private sealed class CapturingHttpHandler : HttpMessageHandler + { + private readonly Action _captureRequest; + + public CapturingHttpHandler(Action captureRequest) + { + this._captureRequest = captureRequest; + } + + protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + this._captureRequest(request); + return Task.FromResult(new HttpResponseMessage(System.Net.HttpStatusCode.BadRequest) + { + Content = new StringContent("{\"error\": \"test\"}") + }); + } + } + + /// + /// Verify that CreateAIAgent with tools and instructions correctly assigns both. + /// + [Fact] + public void CreateAIAgent_WithToolsAndInstructions_AssignsBothCorrectly() + { + // Arrange + var chatClient = new TestAnthropicChatClient(); + IList tools = [AIFunctionFactory.Create(() => "test result", "TestFunction", "A test function")]; + + // Act + var agent = chatClient.Beta.AsAIAgent( + model: "test-model", + name: "Test Agent", + instructions: "Test instructions", + tools: tools); + + // Assert + Assert.NotNull(agent); + Assert.Equal("Test Agent", agent.Name); + Assert.Equal("Test instructions", agent.Instructions); + + // Verify that tools are registered in the FunctionInvokingChatClient + var functionInvokingClient = agent.GetService(); + Assert.NotNull(functionInvokingClient); + Assert.NotNull(functionInvokingClient.AdditionalTools); + Assert.Contains(functionInvokingClient.AdditionalTools, t => t is AIFunction func && func.Name == "TestFunction"); + } + + /// + /// Verify that CreateAIAgent with empty tools list does not assign tools. + /// + [Fact] + public void CreateAIAgent_WithEmptyTools_DoesNotAssignTools() + { + // Arrange + var chatClient = new TestAnthropicChatClient(); + IList tools = []; + + // Act + var agent = chatClient.Beta.AsAIAgent( + model: "test-model", + name: "Test Agent", + tools: tools); + + // Assert + Assert.NotNull(agent); + Assert.Equal("Test Agent", agent.Name); + // With empty tools and no instructions, agent instructions remain null + Assert.Null(agent.Instructions); + + // Verify that FunctionInvokingChatClient has no additional tools assigned + var functionInvokingClient = agent.GetService(); + Assert.NotNull(functionInvokingClient); + Assert.True(functionInvokingClient.AdditionalTools is null or { Count: 0 }); + } + + /// + /// Verify that CreateAIAgent with null instructions does not set instructions. + /// + [Fact] + public void CreateAIAgent_WithNullInstructions_DoesNotSetInstructions() + { + // Arrange + var chatClient = new TestAnthropicChatClient(); + + // Act + var agent = chatClient.Beta.AsAIAgent( + model: "test-model", + name: "Test Agent", + instructions: null); + + // Assert + Assert.NotNull(agent); + Assert.Equal("Test Agent", agent.Name); + Assert.Null(agent.Instructions); + } + + /// + /// Verify that CreateAIAgent with whitespace instructions does not set instructions. + /// + [Fact] + public void CreateAIAgent_WithWhitespaceInstructions_DoesNotSetInstructions() + { + // Arrange + var chatClient = new TestAnthropicChatClient(); + + // Act + var agent = chatClient.Beta.AsAIAgent( + model: "test-model", + name: "Test Agent", + instructions: " "); + + // Assert + Assert.NotNull(agent); + Assert.Equal("Test Agent", agent.Name); + Assert.Null(agent.Instructions); + } + /// /// Test custom chat client that can be used to verify clientFactory functionality. /// diff --git a/dotnet/tests/Microsoft.Agents.AI.Anthropic.UnitTests/Extensions/AnthropicClientExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.Anthropic.UnitTests/Extensions/AnthropicClientExtensionsTests.cs index 90f20d15c3..cd7112fc5b 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Anthropic.UnitTests/Extensions/AnthropicClientExtensionsTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Anthropic.UnitTests/Extensions/AnthropicClientExtensionsTests.cs @@ -254,4 +254,203 @@ public void CreateAIAgent_WithNullOptions_ThrowsArgumentNullException() Assert.Equal("options", exception.ParamName); } + + /// + /// Verify that CreateAIAgent with tools correctly assigns tools to ChatOptions. + /// + [Fact] + public void CreateAIAgent_WithTools_AssignsToolsCorrectly() + { + // Arrange + var chatClient = new TestAnthropicChatClient(); + IList tools = [AIFunctionFactory.Create(() => "test result", "TestFunction", "A test function")]; + + // Act + var agent = chatClient.AsAIAgent( + model: "test-model", + name: "Test Agent", + tools: tools); + + // Assert + Assert.NotNull(agent); + Assert.Equal("Test Agent", agent.Name); + // When tools are provided, ChatOptions is created but instructions remain null + Assert.Null(agent.Instructions); + + // Verify that tools are registered in the FunctionInvokingChatClient + var functionInvokingClient = agent.GetService(); + Assert.NotNull(functionInvokingClient); + Assert.NotNull(functionInvokingClient.AdditionalTools); + Assert.Contains(functionInvokingClient.AdditionalTools, t => t is AIFunction func && func.Name == "TestFunction"); + } + + /// + /// Verify that CreateAIAgent with explicit defaultMaxTokens uses the provided value. + /// + [Fact] + public async Task CreateAIAgent_WithExplicitMaxTokens_UsesProvidedValueAsync() + { + // Arrange + int capturedMaxTokens = 0; + var handler = new CapturingHttpHandler(request => + { + // Parse the request body to capture max_tokens + var content = request.Content?.ReadAsStringAsync().GetAwaiter().GetResult(); + if (content is not null) + { + var json = System.Text.Json.JsonDocument.Parse(content); + if (json.RootElement.TryGetProperty("max_tokens", out var maxTokens)) + { + capturedMaxTokens = maxTokens.GetInt32(); + } + } + }); + + var client = new AnthropicClient + { + HttpClient = new HttpClient(handler) { BaseAddress = new Uri("http://localhost") }, + APIKey = "test-key" + }; + + // Act + var agent = client.AsAIAgent( + model: "claude-haiku-4-5", + name: "Test Agent", + defaultMaxTokens: 8192); + + // Invoke the agent to trigger the request + var thread = await agent.GetNewThreadAsync(); + try + { + await agent.RunAsync("Test message", thread); + } + catch + { + // Expected to fail since we're using a test handler + } + + // Assert + Assert.Equal(8192, capturedMaxTokens); + } + + /// + /// HTTP handler that captures requests for verification. + /// + private sealed class CapturingHttpHandler : HttpMessageHandler + { + private readonly Action _captureRequest; + + public CapturingHttpHandler(Action captureRequest) + { + this._captureRequest = captureRequest; + } + + protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + this._captureRequest(request); + return Task.FromResult(new HttpResponseMessage(System.Net.HttpStatusCode.BadRequest) + { + Content = new StringContent("{\"error\": \"test\"}") + }); + } + } + + /// + /// Verify that CreateAIAgent with tools and instructions correctly assigns both. + /// + [Fact] + public void CreateAIAgent_WithToolsAndInstructions_AssignsBothCorrectly() + { + // Arrange + var chatClient = new TestAnthropicChatClient(); + IList tools = [AIFunctionFactory.Create(() => "test result", "TestFunction", "A test function")]; + + // Act + var agent = chatClient.AsAIAgent( + model: "test-model", + name: "Test Agent", + instructions: "Test instructions", + tools: tools); + + // Assert + Assert.NotNull(agent); + Assert.Equal("Test Agent", agent.Name); + Assert.Equal("Test instructions", agent.Instructions); + + // Verify that tools are registered in the FunctionInvokingChatClient + var functionInvokingClient = agent.GetService(); + Assert.NotNull(functionInvokingClient); + Assert.NotNull(functionInvokingClient.AdditionalTools); + Assert.Contains(functionInvokingClient.AdditionalTools, t => t is AIFunction func && func.Name == "TestFunction"); + } + + /// + /// Verify that CreateAIAgent with empty tools list does not assign tools. + /// + [Fact] + public void CreateAIAgent_WithEmptyTools_DoesNotAssignTools() + { + // Arrange + var chatClient = new TestAnthropicChatClient(); + IList tools = []; + + // Act + var agent = chatClient.AsAIAgent( + model: "test-model", + name: "Test Agent", + tools: tools); + + // Assert + Assert.NotNull(agent); + Assert.Equal("Test Agent", agent.Name); + // With empty tools and no instructions, agent instructions remain null + Assert.Null(agent.Instructions); + + // Verify that FunctionInvokingChatClient has no additional tools assigned + var functionInvokingClient = agent.GetService(); + Assert.NotNull(functionInvokingClient); + Assert.True(functionInvokingClient.AdditionalTools is null or { Count: 0 }); + } + + /// + /// Verify that CreateAIAgent with null instructions does not set instructions. + /// + [Fact] + public void CreateAIAgent_WithNullInstructions_DoesNotSetInstructions() + { + // Arrange + var chatClient = new TestAnthropicChatClient(); + + // Act + var agent = chatClient.AsAIAgent( + model: "test-model", + name: "Test Agent", + instructions: null); + + // Assert + Assert.NotNull(agent); + Assert.Equal("Test Agent", agent.Name); + Assert.Null(agent.Instructions); + } + + /// + /// Verify that CreateAIAgent with whitespace instructions does not set instructions. + /// + [Fact] + public void CreateAIAgent_WithWhitespaceInstructions_DoesNotSetInstructions() + { + // Arrange + var chatClient = new TestAnthropicChatClient(); + + // Act + var agent = chatClient.AsAIAgent( + model: "test-model", + name: "Test Agent", + instructions: " "); + + // Assert + Assert.NotNull(agent); + Assert.Equal("Test Agent", agent.Name); + Assert.Null(agent.Instructions); + } }