diff --git a/src/AnthropicClient/AnthropicApiClient.cs b/src/AnthropicClient/AnthropicApiClient.cs index ad5d0c8..2c57cdd 100644 --- a/src/AnthropicClient/AnthropicApiClient.cs +++ b/src/AnthropicClient/AnthropicApiClient.cs @@ -191,6 +191,8 @@ public class AnthropicApiClient : IAnthropicApiClient { InputTokens = existingUsage.InputTokens + msgDeltaData.Usage.InputTokens, OutputTokens = existingUsage.OutputTokens + msgDeltaData.Usage.OutputTokens, + CacheCreationInputTokens = existingUsage.CacheCreationInputTokens + msgDeltaData.Usage.CacheCreationInputTokens, + CacheReadInputTokens = existingUsage.CacheReadInputTokens + msgDeltaData.Usage.CacheReadInputTokens, }; msgResponse = new MessageResponse() diff --git a/src/AnthropicClient/Json/JsonSerializationOptions.cs b/src/AnthropicClient/Json/JsonSerializationOptions.cs index a5a9ce8..6e9dece 100644 --- a/src/AnthropicClient/Json/JsonSerializationOptions.cs +++ b/src/AnthropicClient/Json/JsonSerializationOptions.cs @@ -17,7 +17,6 @@ static class JsonSerializationOptions new EventDataConverter(), new ContentDeltaConverter(), new JsonStringEnumConverter(), - new MessageRequestConverter(), }, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, }; diff --git a/src/AnthropicClient/Models/BaseMessageRequest.cs b/src/AnthropicClient/Models/BaseMessageRequest.cs index 7765310..fb5413b 100644 --- a/src/AnthropicClient/Models/BaseMessageRequest.cs +++ b/src/AnthropicClient/Models/BaseMessageRequest.cs @@ -15,15 +15,40 @@ public abstract class BaseMessageRequest public string Model { get; init; } = string.Empty; /// - /// Gets the system prompt to use for the request. + /// Gets the system message that will be used as the system prompt if no system messages are provided. /// + [JsonIgnore] public string? System { get; init; } = null; /// - /// Gets the messages to send to the model. + /// Gets the system messages to send to the model to be used as the system prompt. /// + [JsonIgnore] public List? SystemMessages { get; init; } = null; + /// + /// Gets the system prompt that will be used for the request. + /// If will return the system messages if they are provided, otherwise it will return the system message. + /// If neither are provided, it will return null. + /// + [JsonPropertyName("system")] + public List? SystemPrompt => GetSystemPrompt(); + + private List? GetSystemPrompt() + { + if (SystemMessages is not null) + { + return SystemMessages; + } + + if (System is not null) + { + return [new TextContent(System)]; + } + + return null; + } + /// /// Gets the messages to send to the model. /// diff --git a/tests/AnthropicClient.Tests/Unit/Models/AnthropicEventTests.cs b/tests/AnthropicClient.Tests/Unit/Models/AnthropicEventTests.cs index 931616b..28da978 100644 --- a/tests/AnthropicClient.Tests/Unit/Models/AnthropicEventTests.cs +++ b/tests/AnthropicClient.Tests/Unit/Models/AnthropicEventTests.cs @@ -13,7 +13,9 @@ public class AnthropicEventTests : SerializationTest ""stop_sequence"": """", ""usage"": { ""input_tokens"": 472, - ""output_tokens"": 2 + ""output_tokens"": 2, + ""cache_creation_input_tokens"": 0, + ""cache_read_input_tokens"": 0 }, ""content"": [], ""stop_reason"": """" @@ -70,7 +72,9 @@ public class AnthropicEventTests : SerializationTest }, ""usage"": { ""output_tokens"": 89, - ""input_tokens"": 0 + ""input_tokens"": 0, + ""cache_creation_input_tokens"": 0, + ""cache_read_input_tokens"": 0 }, ""type"": ""message_delta"" } diff --git a/tests/AnthropicClient.Tests/Unit/Models/MessageDeltaEventDataTests.cs b/tests/AnthropicClient.Tests/Unit/Models/MessageDeltaEventDataTests.cs index 0d22412..b164915 100644 --- a/tests/AnthropicClient.Tests/Unit/Models/MessageDeltaEventDataTests.cs +++ b/tests/AnthropicClient.Tests/Unit/Models/MessageDeltaEventDataTests.cs @@ -10,7 +10,9 @@ public class MessageDeltaEventDataTests : SerializationTest }, ""usage"": { ""input_tokens"": 1, - ""output_tokens"": 1 + ""output_tokens"": 1, + ""cache_creation_input_tokens"": 1, + ""cache_read_input_tokens"": 1 } }"; @@ -18,7 +20,13 @@ public class MessageDeltaEventDataTests : SerializationTest public void Constructor_WhenCalled_ItShouldInitializeProperties() { var expectedDelta = new MessageDelta("max_tokens", "max_tokens"); - var expectedUsage = new Usage { InputTokens = 1, OutputTokens = 1 }; + var expectedUsage = new Usage + { + InputTokens = 1, + OutputTokens = 1, + CacheCreationInputTokens = 1, + CacheReadInputTokens = 1, + }; var messageDeltaEventData = new MessageDeltaEventData(expectedDelta, expectedUsage); @@ -30,7 +38,13 @@ public class MessageDeltaEventDataTests : SerializationTest public void JsonSerialization_WhenSerialized_ItShouldHaveExpectedShape() { var expectedDelta = new MessageDelta("max_tokens", "max_tokens"); - var expectedUsage = new Usage { InputTokens = 1, OutputTokens = 1 }; + var expectedUsage = new Usage + { + InputTokens = 1, + OutputTokens = 1, + CacheCreationInputTokens = 1, + CacheReadInputTokens = 1, + }; var messageDeltaEventData = new MessageDeltaEventData(expectedDelta, expectedUsage); @@ -43,7 +57,13 @@ public class MessageDeltaEventDataTests : SerializationTest public void JsonDeserialization_WhenDeserialized_ItShouldHaveExpectedValues() { var expectedDelta = new MessageDelta("max_tokens", "max_tokens"); - var expectedUsage = new Usage { InputTokens = 1, OutputTokens = 1 }; + var expectedUsage = new Usage + { + InputTokens = 1, + OutputTokens = 1, + CacheCreationInputTokens = 1, + CacheReadInputTokens = 1, + }; var messageDeltaEventData = Deserialize(_testJson); diff --git a/tests/AnthropicClient.Tests/Unit/Models/MessageRequestTests.cs b/tests/AnthropicClient.Tests/Unit/Models/MessageRequestTests.cs index a1ae3b0..bcd3532 100644 --- a/tests/AnthropicClient.Tests/Unit/Models/MessageRequestTests.cs +++ b/tests/AnthropicClient.Tests/Unit/Models/MessageRequestTests.cs @@ -4,7 +4,10 @@ public class MessageRequestTests : SerializationTest { private readonly string _testJson = @"{ ""model"": ""claude-3-sonnet-20240229"", - ""system"": ""test-system"", + ""system"": [{ + ""type"": ""text"", + ""text"": ""test-system"" + }], ""messages"": [ { ""role"": ""user"", ""content"": [{ ""text"": ""Hello!"", ""type"": ""text"" }] } ], @@ -21,7 +24,10 @@ public class MessageRequestTests : SerializationTest private readonly string _testJsonWithAnyToolChoice = @"{ ""model"": ""claude-3-sonnet-20240229"", - ""system"": ""test-system"", + ""system"": [{ + ""type"": ""text"", + ""text"": ""test-system"" + }], ""messages"":[ { ""role"": ""user"", ""content"": [{ ""text"": ""Hello!"", ""type"":""text"" }] } ], @@ -38,7 +44,10 @@ public class MessageRequestTests : SerializationTest private readonly string _testJsonWithSpecificToolChoice = @"{ ""model"": ""claude-3-sonnet-20240229"", - ""system"": ""test-system"", + ""system"": [{ + ""type"": ""text"", + ""text"": ""test-system"" + }], ""messages"": [ { ""role"": ""user"", ""content"": [{ ""text"": ""Hello!"", ""type"": ""text"" }] } ], @@ -55,7 +64,10 @@ public class MessageRequestTests : SerializationTest private readonly string _testJsonWithImageContent = @"{ ""model"": ""claude-3-sonnet-20240229"", - ""system"": ""test-system"", + ""system"": [{ + ""type"": ""text"", + ""text"": ""test-system"" + }], ""messages"":[ { ""role"": ""user"", @@ -80,7 +92,10 @@ public class MessageRequestTests : SerializationTest private readonly string _testJsonWithUnknownContent = @"{ ""model"": ""claude-3-sonnet-20240229"", - ""system"": ""test-system"", + ""system"": [{ + ""type"": ""text"", + ""text"": ""test-system"" + }], ""messages"": [{ ""role"": ""user"", ""content"": [{ ""type"": ""unknown"", ""text"": ""text"" }] }], ""max_tokens"": 512, ""metadata"": { ""test"": ""test"" }, @@ -95,7 +110,10 @@ public class MessageRequestTests : SerializationTest private readonly string _testJsonWithToolUseContent = @"{ ""model"": ""claude-3-sonnet-20240229"", - ""system"": ""test-system"", + ""system"": [{ + ""type"": ""text"", + ""text"": ""test-system"" + }], ""messages"": [ { ""role"": ""assistant"", @@ -124,7 +142,10 @@ public class MessageRequestTests : SerializationTest private readonly string _testJsonWithToolResultContent = @"{ ""model"": ""claude-3-sonnet-20240229"", - ""system"": ""test-system"", + ""system"": [{ + ""type"": ""text"", + ""text"": ""test-system"" + }], ""messages"": [ { ""role"": ""assistant"", @@ -307,7 +328,7 @@ public class MessageRequestTests : SerializationTest var messageRequest = Deserialize(_testJson); messageRequest!.Model.Should().Be(AnthropicModels.Claude3Sonnet); - messageRequest.System.Should().Be("test-system"); + messageRequest.System.Should().BeNull(); messageRequest.Messages.Should().HaveCount(1); messageRequest.MaxTokens.Should().Be(512); messageRequest.Metadata.Should().HaveCount(1); @@ -330,7 +351,7 @@ public class MessageRequestTests : SerializationTest var messageRequest = Deserialize(_testJsonWithAnyToolChoice); messageRequest!.Model.Should().Be(AnthropicModels.Claude3Sonnet); - messageRequest.System.Should().Be("test-system"); + messageRequest.System.Should().BeNull(); messageRequest.Messages.Should().HaveCount(1); messageRequest.MaxTokens.Should().Be(512); messageRequest.Metadata.Should().HaveCount(1); @@ -352,7 +373,7 @@ public class MessageRequestTests : SerializationTest var messageRequest = Deserialize(_testJsonWithSpecificToolChoice); messageRequest!.Model.Should().Be(AnthropicModels.Claude3Sonnet); - messageRequest.System.Should().Be("test-system"); + messageRequest.System.Should().BeNull(); messageRequest.Messages.Should().HaveCount(1); messageRequest.MaxTokens.Should().Be(512); messageRequest.Metadata.Should().HaveCount(1); @@ -387,7 +408,7 @@ public class MessageRequestTests : SerializationTest var messageRequest = Deserialize(_testJsonWithImageContent); messageRequest!.Model.Should().Be(AnthropicModels.Claude3Sonnet); - messageRequest.System.Should().Be("test-system"); + messageRequest.System.Should().BeNull(); messageRequest.Messages.Should().HaveCount(1); messageRequest.MaxTokens.Should().Be(512); messageRequest.Metadata.Should().HaveCount(1); @@ -416,7 +437,7 @@ public class MessageRequestTests : SerializationTest var messageRequest = Deserialize(_testJsonWithToolUseContent); messageRequest!.Model.Should().Be(AnthropicModels.Claude3Sonnet); - messageRequest.System.Should().Be("test-system"); + messageRequest.System.Should().BeNull(); messageRequest.Messages.Should().HaveCount(1); messageRequest.MaxTokens.Should().Be(512); messageRequest.Metadata.Should().HaveCount(1); @@ -447,7 +468,7 @@ public class MessageRequestTests : SerializationTest var messageRequest = Deserialize(_testJsonWithToolResultContent); messageRequest!.Model.Should().Be(AnthropicModels.Claude3Sonnet); - messageRequest.System.Should().Be("test-system"); + messageRequest.System.Should().BeNull(); messageRequest.Messages.Should().HaveCount(1); messageRequest.MaxTokens.Should().Be(512); messageRequest.Metadata.Should().HaveCount(1); diff --git a/tests/AnthropicClient.Tests/Unit/Models/MessageResponseTests.cs b/tests/AnthropicClient.Tests/Unit/Models/MessageResponseTests.cs index e2bf053..43a3e02 100644 --- a/tests/AnthropicClient.Tests/Unit/Models/MessageResponseTests.cs +++ b/tests/AnthropicClient.Tests/Unit/Models/MessageResponseTests.cs @@ -53,7 +53,12 @@ public class MessageResponseTests : SerializationTest ""stop_reason"": ""stop reason"", ""stop_sequence"": ""stop sequence"", ""type"": ""type"", - ""usage"": { ""input_tokens"": 1, ""output_tokens"": 2 }, + ""usage"": { + ""input_tokens"": 1, + ""output_tokens"": 2, + ""cache_creation_input_tokens"": 0, + ""cache_read_input_tokens"": 0 + }, ""content"": [ { ""text"": ""text content"", ""type"": ""text"" } ] @@ -93,7 +98,12 @@ public class MessageResponseTests : SerializationTest ""stop_reason"": ""stop reason"", ""stop_sequence"": ""stop sequence"", ""type"": ""type"", - ""usage"": { ""input_tokens"": 1, ""output_tokens"": 2 }, + ""usage"": { + ""input_tokens"": 1, + ""output_tokens"": 2, + ""cache_creation_input_tokens"": 0, + ""cache_read_input_tokens"": 0 + }, ""content"": [ { ""text"": ""text content"", ""type"": ""text"" } ] diff --git a/tests/AnthropicClient.Tests/Unit/Models/MessageStartEventDataTests.cs b/tests/AnthropicClient.Tests/Unit/Models/MessageStartEventDataTests.cs index 3a550d8..bc6a5fd 100644 --- a/tests/AnthropicClient.Tests/Unit/Models/MessageStartEventDataTests.cs +++ b/tests/AnthropicClient.Tests/Unit/Models/MessageStartEventDataTests.cs @@ -14,7 +14,9 @@ public class MessageStartEventDataTests : SerializationTest ""stop_sequence"": """", ""usage"": { ""input_tokens"": 25, - ""output_tokens"": 1 + ""output_tokens"": 1, + ""cache_creation_input_tokens"": 0, + ""cache_read_input_tokens"": 0 } } }"; diff --git a/tests/AnthropicClient.Tests/Unit/Models/StreamMessageRequestTests.cs b/tests/AnthropicClient.Tests/Unit/Models/StreamMessageRequestTests.cs index 16c1274..67ebd1e 100644 --- a/tests/AnthropicClient.Tests/Unit/Models/StreamMessageRequestTests.cs +++ b/tests/AnthropicClient.Tests/Unit/Models/StreamMessageRequestTests.cs @@ -4,7 +4,10 @@ public class StreamMessageRequestTests : SerializationTest { private readonly string _testJson = @"{ ""model"": ""claude-3-sonnet-20240229"", - ""system"": ""test-system"", + ""system"": [{ + ""type"": ""text"", + ""text"": ""test-system"" + }], ""messages"": [ { ""role"": ""user"", ""content"": [{ ""text"": ""Hello!"", ""type"": ""text"" }] } ], @@ -178,7 +181,7 @@ public class StreamMessageRequestTests : SerializationTest var messageRequest = Deserialize(_testJson); messageRequest!.Model.Should().Be(AnthropicModels.Claude3Sonnet); - messageRequest.System.Should().Be("test-system"); + messageRequest.System.Should().BeNull(); messageRequest.Messages.Should().HaveCount(1); messageRequest.MaxTokens.Should().Be(512); messageRequest.Metadata.Should().HaveCount(1); diff --git a/tests/AnthropicClient.Tests/Unit/Models/UsageTests.cs b/tests/AnthropicClient.Tests/Unit/Models/UsageTests.cs index 345527a..a0ca68f 100644 --- a/tests/AnthropicClient.Tests/Unit/Models/UsageTests.cs +++ b/tests/AnthropicClient.Tests/Unit/Models/UsageTests.cs @@ -7,26 +7,39 @@ public class UsageTests : SerializationTest { var expectedInputTokens = 1; var expectedOutputTokens = 2; + var expectedCacheCreationInputTokens = 3; + var expectedCacheReadInputTokens = 4; var usage = new Usage { InputTokens = expectedInputTokens, - OutputTokens = expectedOutputTokens + OutputTokens = expectedOutputTokens, + CacheCreationInputTokens = expectedCacheCreationInputTokens, + CacheReadInputTokens = expectedCacheReadInputTokens }; usage.InputTokens.Should().Be(expectedInputTokens); usage.OutputTokens.Should().Be(expectedOutputTokens); + usage.CacheCreationInputTokens.Should().Be(expectedCacheCreationInputTokens); + usage.CacheReadInputTokens.Should().Be(expectedCacheReadInputTokens); } [Fact] public void JsonSerialization_WhenCalled_ItShouldSerializeCorrectly() { - var expectedJson = @"{ ""input_tokens"": 1, ""output_tokens"": 2 }"; + var expectedJson = @"{ + ""input_tokens"": 1, + ""output_tokens"": 2, + ""cache_creation_input_tokens"": 3, + ""cache_read_input_tokens"": 4 + }"; var usage = new Usage { InputTokens = 1, - OutputTokens = 2 + OutputTokens = 2, + CacheCreationInputTokens = 3, + CacheReadInputTokens = 4 }; var actual = Serialize(usage); @@ -37,11 +50,18 @@ public class UsageTests : SerializationTest [Fact] public void JsonDeserialization_WhenCalled_ItShouldDeserializeCorrectly() { - var json = @"{ ""input_tokens"": 1, ""output_tokens"": 2 }"; + var json = @"{ + ""input_tokens"": 1, + ""output_tokens"": 2, + ""cache_creation_input_tokens"": 3, + ""cache_read_input_tokens"": 4 + }"; var usage = Deserialize(json); usage!.InputTokens.Should().Be(1); usage.OutputTokens.Should().Be(2); + usage.CacheCreationInputTokens.Should().Be(3); + usage.CacheReadInputTokens.Should().Be(4); } } \ No newline at end of file