diff --git a/src/AnthropicClient/Json/JsonSerializationOptions.cs b/src/AnthropicClient/Json/JsonSerializationOptions.cs index 6e9dece..a5a9ce8 100644 --- a/src/AnthropicClient/Json/JsonSerializationOptions.cs +++ b/src/AnthropicClient/Json/JsonSerializationOptions.cs @@ -17,6 +17,7 @@ static class JsonSerializationOptions new EventDataConverter(), new ContentDeltaConverter(), new JsonStringEnumConverter(), + new MessageRequestConverter(), }, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, }; diff --git a/src/AnthropicClient/Json/MessageRequestConverter.cs b/src/AnthropicClient/Json/MessageRequestConverter.cs new file mode 100644 index 0000000..4e5e538 --- /dev/null +++ b/src/AnthropicClient/Json/MessageRequestConverter.cs @@ -0,0 +1,19 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +using AnthropicClient.Models; + +namespace AnthropicClient.Json; + +class MessageRequestConverter : JsonConverter +{ + public override MessageRequest Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return JsonSerializer.Deserialize(ref reader, options)!; + } + + public override void Write(Utf8JsonWriter writer, MessageRequest value, JsonSerializerOptions options) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/src/AnthropicClient/Models/BaseMessageRequest.cs b/src/AnthropicClient/Models/BaseMessageRequest.cs index a46dce6..7765310 100644 --- a/src/AnthropicClient/Models/BaseMessageRequest.cs +++ b/src/AnthropicClient/Models/BaseMessageRequest.cs @@ -19,6 +19,11 @@ public abstract class BaseMessageRequest /// public string? System { get; init; } = null; + /// + /// Gets the messages to send to the model. + /// + public List? SystemMessages { get; init; } = null; + /// /// Gets the messages to send to the model. /// @@ -74,41 +79,21 @@ public abstract class BaseMessageRequest [JsonConstructor] internal BaseMessageRequest() { } - - /// - /// Initializes a new instance of the class. - /// - /// The model ID to use for the request. - /// The messages to send to the model. - /// The maximum number of tokens to generate. - /// The system ID to use for the request. - /// The metadata to include with the request. - /// The temperature to use for the request. - /// The top-K value to use for the request. - /// The top-P value to use for the request. - /// The tool choice mode to use for the request. - /// The tools to use for the request. - /// A value indicating whether the message should be streamed. - /// The prompt stop sequences. - /// Thrown when the model ID is invalid. - /// Thrown when the model or messages is null. - /// Thrown when the messages contain no messages. - /// Thrown when the max tokens is less than one. - /// Thrown when the temperature is less than zero or greater than one. - /// A new instance of the class. - protected BaseMessageRequest( + + private BaseMessageRequest( string model, List messages, - int maxTokens = 1024, - string? system = null, - Dictionary? metadata = null, - decimal temperature = 0.0m, - int? topK = null, - decimal? topP = null, - ToolChoice? toolChoice = null, - List? tools = null, - bool stream = false, - List? stopSequences = null + int maxTokens, + string? system, + List? systemMessages, + Dictionary? metadata, + decimal temperature, + int? topK, + decimal? topP, + ToolChoice? toolChoice, + List? tools, + bool stream, + List? stopSequences ) { ArgumentValidator.ThrowIfNull(model, nameof(model)); @@ -138,6 +123,7 @@ public abstract class BaseMessageRequest Messages = messages; MaxTokens = maxTokens; System = system; + SystemMessages = systemMessages; Metadata = metadata; Temperature = temperature; TopK = topK; @@ -147,4 +133,108 @@ public abstract class BaseMessageRequest Stream = stream; StopSequences = stopSequences ?? []; } + + /// + /// Initializes a new instance of the class. + /// + /// The model ID to use for the request. + /// The messages to send to the model. + /// The maximum number of tokens to generate. + /// The system prompt to use for the request. + /// The metadata to include with the request. + /// The temperature to use for the request. + /// The top-K value to use for the request. + /// The top-P value to use for the request. + /// The tool choice mode to use for the request. + /// The tools to use for the request. + /// A value indicating whether the message should be streamed. + /// The prompt stop sequences. + /// Thrown when the model ID is invalid. + /// Thrown when the model or messages is null. + /// Thrown when the messages contain no messages. + /// Thrown when the max tokens is less than one. + /// Thrown when the temperature is less than zero or greater than one. + /// A new instance of the class. + protected BaseMessageRequest( + string model, + List messages, + int maxTokens = 1024, + string? system = null, + Dictionary? metadata = null, + decimal temperature = 0.0m, + int? topK = null, + decimal? topP = null, + ToolChoice? toolChoice = null, + List? tools = null, + bool stream = false, + List? stopSequences = null + ) : this( + model, + messages, + maxTokens, + system, + null, + metadata, + temperature, + topK, + topP, + toolChoice, + tools, + stream, + stopSequences + ) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The model ID to use for the request. + /// The messages to send to the model. + /// The maximum number of tokens to generate. + /// The system messages to send to the model to be used as the system prompt. + /// The metadata to include with the request. + /// The temperature to use for the request. + /// The top-K value to use for the request. + /// The top-P value to use for the request. + /// The tool choice mode to use for the request. + /// The tools to use for the request. + /// A value indicating whether the message should be streamed. + /// The prompt stop sequences. + /// Thrown when the model ID is invalid. + /// Thrown when the model or messages is null. + /// Thrown when the messages contain no messages. + /// Thrown when the max tokens is less than one. + /// Thrown when the temperature is less than zero or greater than one. + /// A new instance of the class. + protected BaseMessageRequest( + string model, + List messages, + int maxTokens = 1024, + List? systemMessages = null, + Dictionary? metadata = null, + decimal temperature = 0.0m, + int? topK = null, + decimal? topP = null, + ToolChoice? toolChoice = null, + List? tools = null, + bool stream = false, + List? stopSequences = null + ) : this( + model, + messages, + maxTokens, + null, + systemMessages, + metadata, + temperature, + topK, + topP, + toolChoice, + tools, + stream, + stopSequences + ) + { + } } \ No newline at end of file diff --git a/src/AnthropicClient/Models/CacheControl.cs b/src/AnthropicClient/Models/CacheControl.cs new file mode 100644 index 0000000..d29dafb --- /dev/null +++ b/src/AnthropicClient/Models/CacheControl.cs @@ -0,0 +1,27 @@ +using AnthropicClient.Utils; + +namespace AnthropicClient.Models; + +/// +/// Represents the cache control to be used for content. +/// +public class CacheControl +{ + /// + /// Gets the type of the cache control. + /// + public string Type { get; init; } = string.Empty; + + /// + /// Initializes a new instance of the class. + /// + /// The type of the cache control. + /// A new instance of the class. + /// Thrown when the type is null or whitespace. + public CacheControl(string type) + { + ArgumentValidator.ThrowIfNullOrWhitespace(type, nameof(type)); + + Type = type; + } +} \ No newline at end of file diff --git a/src/AnthropicClient/Models/CacheControlType.cs b/src/AnthropicClient/Models/CacheControlType.cs new file mode 100644 index 0000000..aa24f57 --- /dev/null +++ b/src/AnthropicClient/Models/CacheControlType.cs @@ -0,0 +1,12 @@ +namespace AnthropicClient.Models; + +/// +/// Provides constants for cache control types. +/// +public static class CacheControlType +{ + /// + /// The cache control type for an ephemeral cache. + /// + public const string Ephemeral = "ephemeral"; +} \ No newline at end of file diff --git a/src/AnthropicClient/Models/Content.cs b/src/AnthropicClient/Models/Content.cs index 2970c3f..946b8c5 100644 --- a/src/AnthropicClient/Models/Content.cs +++ b/src/AnthropicClient/Models/Content.cs @@ -11,6 +11,12 @@ public abstract class Content /// Gets the type of the content. /// public string Type { get; init; } = string.Empty; + + /// + /// Gets the cache control to be used for the content. + /// + [JsonPropertyName("cache_control")] + public CacheControl? CacheControl { get; init; } [JsonConstructor] internal Content() @@ -26,4 +32,16 @@ public abstract class Content { Type = type; } + + /// + /// Initializes a new instance of the class. + /// + /// The type of the content. + /// The cache control to be used for the content. + /// A new instance of the class. + protected Content(string type, CacheControl cacheControl) + { + Type = type; + CacheControl = cacheControl; + } } \ No newline at end of file diff --git a/src/AnthropicClient/Models/ImageContent.cs b/src/AnthropicClient/Models/ImageContent.cs index 6fd5232..20c071c 100644 --- a/src/AnthropicClient/Models/ImageContent.cs +++ b/src/AnthropicClient/Models/ImageContent.cs @@ -33,4 +33,20 @@ public class ImageContent : Content Source = new(mediaType, data); } + + /// + /// Initializes a new instance of the class. + /// + /// The media type of the image. + /// The data of the image. + /// The cache control to be used for the content. + /// A new instance of the class. + /// Thrown when the media type, data, or cache control is null. + public ImageContent(string mediaType, string data, CacheControl cacheControl) : base(ContentType.Image, cacheControl) + { + ArgumentValidator.ThrowIfNull(mediaType, nameof(mediaType)); + ArgumentValidator.ThrowIfNull(data, nameof(data)); + + Source = new(mediaType, data); + } } \ No newline at end of file diff --git a/src/AnthropicClient/Models/TextContent.cs b/src/AnthropicClient/Models/TextContent.cs index b68a1a3..01b14ee 100644 --- a/src/AnthropicClient/Models/TextContent.cs +++ b/src/AnthropicClient/Models/TextContent.cs @@ -31,4 +31,18 @@ public class TextContent : Content Text = text; } + + /// + /// Initializes a new instance of the class. + /// + /// The text of the content. + /// The cache control to be used for the content. + /// A new instance of the class. + /// Thrown when the text or cache control is null. + public TextContent(string text, CacheControl cacheControl) : base(ContentType.Text, cacheControl) + { + ArgumentValidator.ThrowIfNull(text, nameof(text)); + + Text = text; + } } \ No newline at end of file diff --git a/src/AnthropicClient/Models/Tool.cs b/src/AnthropicClient/Models/Tool.cs index 0b1ba3f..e861568 100644 --- a/src/AnthropicClient/Models/Tool.cs +++ b/src/AnthropicClient/Models/Tool.cs @@ -55,6 +55,11 @@ public class Tool [JsonIgnore] public AnthropicFunction Function { get; } + /// + /// Gets or sets the cache control to be used for the tool. + /// + public CacheControl? CacheControl { get; set; } + /// /// Gets the display name of the tool. /// @@ -73,7 +78,7 @@ public class Tool DisplayName = string.Empty; } - internal Tool(string name, string description, AnthropicFunction function) + internal Tool(string name, string description, AnthropicFunction function, CacheControl? cacheControl = null) { ArgumentValidator.ThrowIfNullOrWhitespace(name, nameof(name)); ArgumentValidator.ThrowIfNullOrWhitespace(description, nameof(description)); @@ -89,6 +94,7 @@ public class Tool Description = description; Function = function; InputSchema = JsonSchemaGenerator.GenerateInputSchema(function); + CacheControl = cacheControl; } /// @@ -99,7 +105,7 @@ public class Tool /// Thrown when the function of the tool is null. /// The created tool as instance of . /// The implementation of must have a parameterless constructor. - public static Tool CreateFromClass() where T : ITool, new() + public static Tool CreateFromClass(CacheControl? cacheControl = null) where T : ITool, new() { var tool = new T(); @@ -107,7 +113,7 @@ public class Tool ArgumentValidator.ThrowIfNullOrWhitespace(tool.Description, nameof(tool.Description)); ArgumentValidator.ThrowIfNull(tool.Function, nameof(tool.Function)); - return new Tool(tool.Name, tool.Description, new AnthropicFunction(tool.Function, tool)); + return new Tool(tool.Name, tool.Description, new AnthropicFunction(tool.Function, tool), cacheControl); } /// @@ -117,12 +123,19 @@ public class Tool /// The description of the tool. /// The type that contains the method. /// The name of the method. + /// The cache control to be used for the tool. /// Thrown when is null or empty. /// Thrown when is null. /// Thrown when the method is not found in the type. /// The created tool as instance of . /// The name of the tool will be sanitized to conform to the Anthropic tool naming rules. - public static Tool CreateFromStaticMethod(string name, string description, Type type, string methodName) + public static Tool CreateFromStaticMethod( + string name, + string description, + Type type, + string methodName, + CacheControl? cacheControl = null + ) { ArgumentValidator.ThrowIfNullOrWhitespace(methodName, nameof(methodName)); ArgumentValidator.ThrowIfNull(type, nameof(type)); @@ -134,7 +147,7 @@ public class Tool throw new ArgumentException($"Method '{methodName}' not found in type '{type.FullName}'.", nameof(methodName)); } - return new Tool(name, description, new AnthropicFunction(method)); + return new Tool(name, description, new AnthropicFunction(method), cacheControl); } /// @@ -144,12 +157,19 @@ public class Tool /// The description of the tool. /// The instance that contains the method. /// The name of the method. + /// The cache control to be used for the tool. /// Thrown when is null or empty. /// Thrown when is null. /// Thrown when is not found in the type of . /// The created tool as instance of . /// The name of the tool will be sanitized to conform to the Anthropic tool naming rules. - public static Tool CreateFromInstanceMethod(string name, string description, object instance, string methodName) + public static Tool CreateFromInstanceMethod( + string name, + string description, + object instance, + string methodName, + CacheControl? cacheControl = null + ) { ArgumentValidator.ThrowIfNullOrWhitespace(methodName, nameof(methodName)); ArgumentValidator.ThrowIfNull(instance, nameof(instance)); @@ -161,7 +181,7 @@ public class Tool throw new ArgumentException($"Method '{methodName}' not found in type '{instance.GetType().FullName}'.", nameof(methodName)); } - return new Tool(name, description, new AnthropicFunction(method, instance)); + return new Tool(name, description, new AnthropicFunction(method, instance), null); } /// @@ -171,14 +191,20 @@ public class Tool /// The name of the tool. /// The description of the tool. /// The function. + /// The cache control to be used for the tool. /// Thrown when is null. /// The created tool as instance of . /// The name of the tool will be sanitized to conform to the Anthropic tool naming rules. - public static Tool CreateFromFunction(string name, string description, Func func) + public static Tool CreateFromFunction( + string name, + string description, + Func func, + CacheControl? cacheControl = null + ) { ArgumentValidator.ThrowIfNull(func, nameof(func)); - return new Tool(name, description, new AnthropicFunction(func.Method, func.Target)); + return new Tool(name, description, new AnthropicFunction(func.Method, func.Target), cacheControl); } /// @@ -189,14 +215,20 @@ public class Tool /// The name of the tool. /// The description of the tool. /// The function. + /// The cache control to be used for the tool. /// Thrown when is null. /// The created tool as instance of . /// The name of the tool will be sanitized to conform to the Anthropic tool naming rules. - public static Tool CreateFromFunction(string name, string description, Func func) + public static Tool CreateFromFunction( + string name, + string description, + Func func, + CacheControl? cacheControl = null + ) { ArgumentValidator.ThrowIfNull(func, nameof(func)); - return new Tool(name, description, new AnthropicFunction(func.Method, func.Target)); + return new Tool(name, description, new AnthropicFunction(func.Method, func.Target), cacheControl); } /// @@ -208,14 +240,20 @@ public class Tool /// The name of the tool. /// The description of the tool. /// The function. + /// The cache control to be used for the tool. /// Thrown when is null. /// The created tool as instance of . /// The name of the tool will be sanitized to conform to the Anthropic tool naming rules. - public static Tool CreateFromFunction(string name, string description, Func func) + public static Tool CreateFromFunction( + string name, + string description, + Func func, + CacheControl? cacheControl = null + ) { ArgumentValidator.ThrowIfNull(func, nameof(func)); - return new Tool(name, description, new AnthropicFunction(func.Method, func.Target)); + return new Tool(name, description, new AnthropicFunction(func.Method, func.Target), cacheControl); } diff --git a/src/AnthropicClient/Models/Usage.cs b/src/AnthropicClient/Models/Usage.cs index 34310ed..09bde25 100644 --- a/src/AnthropicClient/Models/Usage.cs +++ b/src/AnthropicClient/Models/Usage.cs @@ -18,4 +18,16 @@ public class Usage /// [JsonPropertyName("output_tokens")] public int OutputTokens { get; init; } + + /// + /// Gets the number of tokens written to the cache when creating a new entry + /// + [JsonPropertyName("cache_creation_input_tokens")] + public int CacheCreationInputTokens { get; init; } + + /// + /// Gets the number of tokens retrieved from the cache for the request. + /// + [JsonPropertyName("cache_read_input_tokens")] + public int CacheReadInputTokens { get; init; } } \ No newline at end of file