diff --git a/tests/AnthropicClient.Tests/EndToEnd/AnthropicApiClientTests.cs b/tests/AnthropicClient.Tests/EndToEnd/AnthropicApiClientTests.cs index 01cb07a..5b7a3cd 100644 --- a/tests/AnthropicClient.Tests/EndToEnd/AnthropicApiClientTests.cs +++ b/tests/AnthropicClient.Tests/EndToEnd/AnthropicApiClientTests.cs @@ -1,10 +1,10 @@ -using Xunit.Abstractions; -using Xunit.Sdk; - namespace AnthropicClient.Tests.EndToEnd; public class ClientTests(ConfigurationFixture configFixture) : EndToEndTest(configFixture) { + private string GetTestFilePath(string fileName) => + Path.Combine(Directory.GetCurrentDirectory(), "Files", fileName); + [Fact] public async Task CreateMessageAsync_WhenCalled_ItShouldReturnResponse() { @@ -63,7 +63,7 @@ public class ClientTests(ConfigurationFixture configFixture) : EndToEndTest(conf [Fact] public async Task CreateMessageAsync_WhenImageIsSent_ItShouldReturnResponse() { - var imagePath = Path.Combine(Directory.GetCurrentDirectory(), "Files", "elephant.jpg"); + var imagePath = GetTestFilePath("elephant.jpg"); var mediaType = "image/jpeg"; var bytes = await File.ReadAllBytesAsync(imagePath); var base64Data = Convert.ToBase64String(bytes); @@ -96,4 +96,130 @@ public class ClientTests(ConfigurationFixture configFixture) : EndToEndTest(conf text.Should().Contain("elephant"); } + + [Fact] + public async Task CreateMessageAsync_WhenSystemMessagesContainCacheControl_ItShouldUseCache() + { + var httpClient = new HttpClient(); + httpClient.DefaultRequestHeaders.Add("anthropic-beta", "prompt-caching-2024-07-31"); + + var client = CreateClient(httpClient); + + var storyPath = GetTestFilePath("story.txt"); + var storyText = await File.ReadAllTextAsync(storyPath); + + var request = new MessageRequest( + model: AnthropicModels.Claude3Haiku, + systemMessages: [ + new("You are a helpful assistant who can answer questions about the following text:"), + new(storyText, new EphemeralCacheControl()) + ], + messages: [ + new(MessageRole.User, [ + new TextContent("Give me a one sentence summary of this story.") + ]), + ] + ); + + var resultOne = await client.CreateMessageAsync(request); + + resultOne.IsSuccess.Should().BeTrue(); + resultOne.Value.Should().BeOfType(); + resultOne.Value.Content.Should().NotBeNullOrEmpty(); + resultOne.Value.Usage.Should().Match(u => u.CacheCreationInputTokens > 0 || u.CacheReadInputTokens > 0); + + request.Messages.Add(new(MessageRole.Assistant, resultOne.Value.Content)); + request.Messages.Add(new(MessageRole.User, [new TextContent("What is the main theme of this story?")])); + + var resultTwo = await client.CreateMessageAsync(request); + + resultTwo.IsSuccess.Should().BeTrue(); + resultTwo.Value.Should().BeOfType(); + resultTwo.Value.Content.Should().NotBeNullOrEmpty(); + resultTwo.Value.Usage.CacheReadInputTokens.Should().BeGreaterThan(0); + } + + [Fact] + public async Task CreateMessageAsync_WhenMessagesContainCacheControl_ItShouldUseCache() + { + var httpClient = new HttpClient(); + httpClient.DefaultRequestHeaders.Add("anthropic-beta", "prompt-caching-2024-07-31"); + + var client = CreateClient(httpClient); + + var storyPath = GetTestFilePath("story.txt"); + var storyText = await File.ReadAllTextAsync(storyPath); + + var request = new MessageRequest( + model: AnthropicModels.Claude3Haiku, + messages: [ + new(MessageRole.User, [ + new TextContent("Give me a one sentence summary of this story."), + new TextContent(storyText, new EphemeralCacheControl()) + ]), + ] + ); + + var resultOne = await client.CreateMessageAsync(request); + + resultOne.IsSuccess.Should().BeTrue(); + resultOne.Value.Should().BeOfType(); + resultOne.Value.Content.Should().NotBeNullOrEmpty(); + resultOne.Value.Usage.Should().Match(u => u.CacheCreationInputTokens > 0 || u.CacheReadInputTokens > 0); + + request.Messages.Add(new(MessageRole.Assistant, resultOne.Value.Content)); + request.Messages.Add(new(MessageRole.User, [new TextContent("What is the main theme of this story?")])); + + var resultTwo = await client.CreateMessageAsync(request); + + resultTwo.IsSuccess.Should().BeTrue(); + resultTwo.Value.Should().BeOfType(); + resultTwo.Value.Content.Should().NotBeNullOrEmpty(); + resultTwo.Value.Usage.CacheReadInputTokens.Should().BeGreaterThan(0); + } + + [Fact] + public async Task CreateMessageAsync_WhenToolsContainCacheControl_ItShouldUseCache() + { + var httpClient = new HttpClient(); + httpClient.DefaultRequestHeaders.Add("anthropic-beta", "prompt-caching-2024-07-31"); + + var client = CreateClient(httpClient); + + var func = (string ticker) => ticker; + + var tools = Enumerable + .Range(0, 50) + .Select(i => Tool.CreateFromFunction($"tool-{i}", $"Tool {i}", func)) + .ToList(); + + tools.Last().CacheControl = new EphemeralCacheControl(); + + var request = new MessageRequest( + model: AnthropicModels.Claude3Haiku, + messages: [ + new(MessageRole.User, [ + new TextContent("Hi could you tell me your name?"), + ]), + ], + tools: tools + ); + + var resultOne = await client.CreateMessageAsync(request); + + resultOne.IsSuccess.Should().BeTrue(); + resultOne.Value.Should().BeOfType(); + resultOne.Value.Content.Should().NotBeNullOrEmpty(); + resultOne.Value.Usage.Should().Match(u => u.CacheCreationInputTokens > 0 || u.CacheReadInputTokens > 0); + + request.Messages.Add(new(MessageRole.Assistant, resultOne.Value.Content)); + request.Messages.Add(new(MessageRole.User, [new TextContent("Could you tell me the stock price for AAPL?")])); + + var resultTwo = await client.CreateMessageAsync(request); + + resultTwo.IsSuccess.Should().BeTrue(); + resultTwo.Value.Should().BeOfType(); + resultTwo.Value.Content.Should().NotBeNullOrEmpty(); + resultTwo.Value.Usage.CacheReadInputTokens.Should().BeGreaterThan(0); + } } \ No newline at end of file