feat: create chat message

This commit is contained in:
Stevan Freeborn
2024-06-27 23:26:53 -05:00
parent 68b359163d
commit 4c2cee643a
77 changed files with 3860 additions and 0 deletions
@@ -0,0 +1,34 @@
using System.Text.Json.Serialization;
namespace AnthropicClient.Models;
/// <summary>
/// Represents an error response from the Anthropic API.
/// </summary>
public class AnthropicError
{
/// <summary>
/// The type of the error.
/// </summary>
public string Type { get; init; } = "error";
/// <summary>
/// The error object.
/// </summary>
public Error? Error { get; init; } = null;
[JsonConstructor]
internal AnthropicError()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="AnthropicError"/> class.
/// </summary>
/// <param name="error">The error as an <see cref="Error"/> object.</param>
/// <returns>A new instance of the <see cref="AnthropicError"/> class.</returns>
public AnthropicError(Error error)
{
Error = error;
}
}
@@ -0,0 +1,29 @@
namespace AnthropicClient.Models;
/// <summary>
/// Provides constants for the Anthropic models.
/// </summary>
public static class AnthropicModels
{
/// <summary>
/// The Claude-3 Opus model.
/// </summary>
public const string Claude3Opus = "claude-3-opus-20240229";
/// <summary>
/// The Claude-3 Sonnet model.
/// </summary>
public const string Claude3Sonnet = "claude-3-sonnet-20240229";
/// <summary>
/// The Claude-3.5 Sonnet model.
/// </summary>
public const string Claude35Sonnet = "claude-3-5-sonnet-20240620";
/// <summary>
/// The Claude-3 Haiku model.
/// </summary>
public const string Claude3Haiku = "claude-3-haiku-20240307";
internal static bool IsValidModel(string modelId) => modelId is Claude3Opus or Claude3Sonnet or Claude35Sonnet or Claude3Haiku;
}
@@ -0,0 +1,66 @@
using AnthropicClient.Models;
/// <summary>
/// Represents the result of an Anthropic API operation.
/// </summary>
/// <typeparam name="T">The type of the result value.</typeparam>
public class AnthropicResult<T>
{
/// <summary>
/// The value of the result.
/// </summary>
public T Value { get; }
/// <summary>
/// The error of the result.
/// </summary>
public AnthropicError Error { get; }
/// <summary>
/// Indicates whether the operation was successful.
/// </summary>
public bool IsSuccess { get; }
/// <summary>
/// The request ID of the operation.
/// </summary>
public string RequestId { get; }
/// <summary>
/// Initializes a new instance of the <see cref="AnthropicResult{T}"/> class.
/// </summary>
/// <param name="value">The value of the result.</param>
/// <param name="error">The error of the result.</param>
/// <param name="isSuccess">Indicates whether the operation was successful.</param>
/// <param name="requestId">The request ID of the operation.</param>
/// <returns>A new instance of the <see cref="AnthropicResult{T}"/> class.</returns>
protected AnthropicResult(T value, AnthropicError error, bool isSuccess, string requestId)
{
Value = value;
Error = error;
IsSuccess = isSuccess;
RequestId = requestId;
}
/// <summary>
/// Creates a successful result.
/// </summary>
/// <param name="value">The value of the result.</param>
/// <param name="requestId">The request ID of the operation.</param>
/// <returns>A new instance of the <see cref="AnthropicResult{T}"/> class.</returns>
public static AnthropicResult<T> Success(T value, string requestId)
{
return new AnthropicResult<T>(value, null!, true, requestId);
}
/// <summary>
/// Creates a failed result.
/// </summary>
/// <param name="error">The error of the result.</param>
/// <param name="requestId">The request ID of the operation.</param>
/// <returns>A new instance of the <see cref="AnthropicResult{T}"/> class.</returns>
public static AnthropicResult<T> Failure(AnthropicError error, string requestId)
{
return new AnthropicResult<T>(default!, error, false, requestId);
}
}
@@ -0,0 +1,13 @@
namespace AnthropicClient.Models;
/// <summary>
/// Represents the any tool choice mode.
/// </summary>
public class AnyToolChoice : ToolChoice
{
/// <summary>
/// Initializes a new instance of the <see cref="AnyToolChoice"/> class.
/// </summary>
/// <returns>A new instance of the <see cref="AnyToolChoice"/> class.</returns>
public AnyToolChoice() : base(ToolChoiceType.Any) { }
}
+24
View File
@@ -0,0 +1,24 @@
using System.Text.Json.Serialization;
namespace AnthropicClient.Models;
/// <summary>
/// Represents an api_error response from the Anthropic API.
/// </summary>
public class ApiError : Error
{
[JsonConstructor]
internal ApiError() : base(ErrorType.ApiError)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ApiError"/> class.
/// </summary>
/// <param name="message">The error message.</param>
/// <returns>A new instance of the <see cref="ApiError"/> class.</returns>
public ApiError(string message) : base(ErrorType.ApiError)
{
Message = message;
}
}
@@ -0,0 +1,24 @@
using System.Text.Json.Serialization;
namespace AnthropicClient.Models;
/// <summary>
/// Represents an authentication_error response from the Anthropic API.
/// </summary>
public class AuthenticationError : Error
{
[JsonConstructor]
internal AuthenticationError() : base(ErrorType.AuthenticationError)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="AuthenticationError"/> class.
/// </summary>
/// <param name="message">The error message.</param>
/// <returns>A new instance of the <see cref="AuthenticationError"/> class.</returns>
public AuthenticationError(string message) : base(ErrorType.AuthenticationError)
{
Message = message;
}
}
@@ -0,0 +1,13 @@
namespace AnthropicClient.Models;
/// <summary>
/// Represents the auto tool choice mode.
/// </summary>
public class AutoToolChoice : ToolChoice
{
/// <summary>
/// Initializes a new instance of the <see cref="AutoToolChoice"/> class.
/// </summary>
/// <returns>A new instance of the <see cref="AutoToolChoice"/> class.</returns>
public AutoToolChoice() : base(ToolChoiceType.Auto) { }
}
+48
View File
@@ -0,0 +1,48 @@
using System.Text.Json.Serialization;
using AnthropicClient.Utils;
namespace AnthropicClient.Models;
/// <summary>
/// Represents a chat message.
/// </summary>
public class ChatMessage
{
/// <summary>
/// Gets the role of the message.
/// </summary>
public string Role { get; init; } = string.Empty;
/// <summary>
/// Gets the contents of the message.
/// </summary>
public List<Content> Content { get; init; } = [];
[JsonConstructor]
internal ChatMessage()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ChatMessage"/> class.
/// </summary>
/// <param name="role">The role of the message.</param>
/// <param name="content">The contents of the message.</param>
/// <exception cref="ArgumentException">Thrown when the role is invalid.</exception>
/// <exception cref="ArgumentNullException">Thrown when the role or content is null.</exception>
/// <returns>A new instance of the <see cref="ChatMessage"/> class.</returns>
public ChatMessage(string role, List<Content> content)
{
ArgumentValidator.ThrowIfNull(role, nameof(role));
ArgumentValidator.ThrowIfNull(content, nameof(content));
if (MessageRole.IsValidRole(role) is false)
{
throw new ArgumentException($"Invalid role: {role}");
}
Role = role;
Content = content;
}
}
@@ -0,0 +1,139 @@
using System.Text.Json.Serialization;
using AnthropicClient.Utils;
namespace AnthropicClient.Models;
/// <summary>
/// Represents a chat message request.
/// </summary>
public class ChatMessageRequest : MessageRequest
{
/// <summary>
/// Gets the model ID to use for the request.
/// </summary>
public string Model { get; init; } = string.Empty;
/// <summary>
/// Gets the system ID to use for the request.
/// </summary>
public string? System { get; init; } = null;
/// <summary>
/// Gets the messages to send to the model.
/// </summary>
public List<ChatMessage> Messages { get; init; } = [];
/// <summary>
/// Gets the maximum number of tokens to generate.
/// </summary>
[JsonPropertyName("max_tokens")]
public int MaxTokens { get; init; } = 1024;
/// <summary>
/// Gets the metadata to include with the request.
/// </summary>
public Dictionary<string, object>? Metadata { get; init; } = null;
/// <summary>
/// Gets the prompt stop sequences.
/// </summary>
[JsonPropertyName("stop_sequences")]
public List<string> StopSequences { get; init; } = [];
/// <summary>
/// Gets the temperature to use for the request.
/// </summary>
public decimal Temperature { get; init; } = 0.0m;
/// <summary>
/// Gets the top-K value to use for the request.
/// </summary>
public int? TopK { get; init; } = null;
/// <summary>
/// Gets the top-P value to use for the request.
/// </summary>
public decimal? TopP { get; init; } = null;
/// <summary>
/// Gets the tool choice mode to use for the request.
/// </summary>
[JsonPropertyName("tool_choice")]
public ToolChoice? ToolChoice { get; init; } = null;
/// <summary>
/// Gets the tools to use for the request.
/// </summary>
public List<Tool>? Tools { get; init; } = null;
[JsonConstructor]
internal ChatMessageRequest() : base(false) { }
/// <summary>
/// Initializes a new instance of the <see cref="ChatMessageRequest"/> class.
/// </summary>
/// <param name="model">The model ID to use for the request.</param>
/// <param name="messages">The messages to send to the model.</param>
/// <param name="maxTokens">The maximum number of tokens to generate.</param>
/// <param name="system">The system ID to use for the request.</param>
/// <param name="metadata">The metadata to include with the request.</param>
/// <param name="temperature">The temperature to use for the request.</param>
/// <param name="topK">The top-K value to use for the request.</param>
/// <param name="topP">The top-P value to use for the request.</param>
/// <param name="toolChoice">The tool choice mode to use for the request.</param>
/// <param name="tools">The tools to use for the request.</param>
/// <exception cref="ArgumentException">Thrown when the model ID is invalid.</exception>
/// <exception cref="ArgumentNullException">Thrown when the model or messages is null.</exception>
/// <exception cref="ArgumentException">Thrown when the messages contain no messages.</exception>
/// <exception cref="ArgumentException">Thrown when the max tokens is less than one.</exception>
/// <exception cref="ArgumentException">Thrown when the temperature is less than zero or greater than one.</exception>
/// <returns>A new instance of the <see cref="ChatMessageRequest"/> class.</returns>
public ChatMessageRequest(
string model,
List<ChatMessage> messages,
int maxTokens = 1024,
string? system = null,
Dictionary<string, object>? metadata = null,
decimal temperature = 0.0m,
int? topK = null,
decimal? topP = null,
ToolChoice? toolChoice = null,
List<Tool>? tools = null
) : base(false)
{
ArgumentValidator.ThrowIfNull(model, nameof(model));
ArgumentValidator.ThrowIfNull(messages, nameof(messages));
if (AnthropicModels.IsValidModel(model) is false)
{
throw new ArgumentException($"Invalid model ID: {model}");
}
if (messages.Count < 1)
{
throw new ArgumentException("Messages must contain at least one message");
}
if (maxTokens < 1)
{
throw new ArgumentException($"Invalid max tokens: {maxTokens}");
}
if (temperature < 0.0m || temperature > 1.0m)
{
throw new ArgumentException($"Invalid temperature: {temperature}");
}
Model = model;
Messages = messages;
MaxTokens = maxTokens;
System = system;
Metadata = metadata;
Temperature = temperature;
TopK = topK;
TopP = topP;
ToolChoice = toolChoice;
Tools = tools;
}
}
@@ -0,0 +1,51 @@
using System.Text.Json.Serialization;
namespace AnthropicClient.Models;
/// <summary>
/// Represents a chat response.
/// </summary>
public class ChatResponse
{
/// <summary>
/// Gets the ID of the chat response.
/// </summary>
public string Id { get; set; } = string.Empty;
/// <summary>
/// Gets the model used for the chat response.
/// </summary>
public string Model { get; set; } = string.Empty;
/// <summary>
/// Gets the role of the chat response.
/// </summary>
public string Role { get; set; } = string.Empty;
/// <summary>
/// Gets the stop reason of the chat response.
/// </summary>
[JsonPropertyName("stop_reason")]
public string StopReason { get; set; } = string.Empty;
/// <summary>
/// Gets the stop sequence of the chat response.
/// </summary>
[JsonPropertyName("stop_sequence")]
public string StopSequence { get; set; } = string.Empty;
/// <summary>
/// Gets the type of the chat response.
/// </summary>
public string Type { get; set; } = string.Empty;
/// <summary>
/// Gets the usage of the chat response.
/// </summary>
public ChatUsage Usage { get; set; } = new();
/// <summary>
/// Gets the contents of the chat response.
/// </summary>
public List<Content> Content { get; set; } = [];
}
+21
View File
@@ -0,0 +1,21 @@
using System.Text.Json.Serialization;
namespace AnthropicClient.Models;
/// <summary>
/// Represents the usage of a chat response.
/// </summary>
public class ChatUsage
{
/// <summary>
/// Gets the number of input tokens used.
/// </summary>
[JsonPropertyName("input_tokens")]
public int InputTokens { get; set; }
/// <summary>
/// Gets the number of output tokens used.
/// </summary>
[JsonPropertyName("output_tokens")]
public int OutputTokens { get; set; }
}
+83
View File
@@ -0,0 +1,83 @@
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using AnthropicClient.Json;
using AnthropicClient.Utils;
namespace AnthropicClient.Models;
/// <summary>
/// Represents a client for interacting with the Anthropic API.
/// </summary>
public interface IClient
{
/// <summary>
/// Creates a chat message asynchronously.
/// </summary>
/// <param name="request">The chat message request to create.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains the chat response as an <see cref="AnthropicResult{T}"/>.</returns>
Task<AnthropicResult<ChatResponse>> CreateChatMessageAsync(ChatMessageRequest request);
}
/// <inheritdoc cref="IClient"/>
public class Client : IClient
{
private const string BaseUrl = "https://api.anthropic.com/v1/";
private const string ApiKeyHeader = "x-api-key";
private const string MessagesEndpoint = "messages";
private const string JsonContentType = "application/json";
private const string RequestIdHeader = "request-id";
private readonly Dictionary<string, string> _defaultHeaders = new()
{
{ "anthropic-version", "2023-06-01" },
};
private readonly HttpClient _httpClient;
/// <summary>
/// Initializes a new instance of the <see cref="Client"/> class.
/// </summary>
/// <param name="apiKey">The API key to use for the client.</param>
/// <param name="httpClient">The HTTP client to use for the client.</param>
/// <exception cref="ArgumentNullException">Thrown when the API key or HTTP client is null.</exception>
/// <returns>A new instance of the <see cref="Client"/> class.</returns>
public Client(string apiKey, HttpClient httpClient)
{
ArgumentValidator.ThrowIfNull(apiKey, nameof(apiKey));
ArgumentValidator.ThrowIfNull(httpClient, nameof(httpClient));
_httpClient = httpClient;
_httpClient.BaseAddress = new Uri(BaseUrl);
_httpClient.DefaultRequestHeaders.Add(ApiKeyHeader, apiKey);
_httpClient.DefaultRequestHeaders
.Accept
.Add(new MediaTypeWithQualityHeaderValue(JsonContentType));
foreach (var pair in _defaultHeaders)
{
_httpClient.DefaultRequestHeaders.Add(pair.Key, pair.Value);
}
}
/// <inheritdoc />
public async Task<AnthropicResult<ChatResponse>> CreateChatMessageAsync(ChatMessageRequest request)
{
var requestContent = new StringContent(Serialize(request), Encoding.UTF8, JsonContentType);
var response = await _httpClient.PostAsync(MessagesEndpoint, requestContent);
var requestId = GetRequestId(response);
var responseContent = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode is false)
{
var error = Deserialize<AnthropicError>(responseContent) ?? new AnthropicError();
return AnthropicResult<ChatResponse>.Failure(error, requestId);
}
var chatResponse = Deserialize<ChatResponse>(responseContent) ?? new ChatResponse();
return AnthropicResult<ChatResponse>.Success(chatResponse, requestId);
}
private string Serialize<T>(T obj) => JsonSerializer.Serialize(obj, JsonSerializationOptions.DefaultOptions);
private T? Deserialize<T>(string json) => JsonSerializer.Deserialize<T>(json, JsonSerializationOptions.DefaultOptions);
private string GetRequestId(HttpResponseMessage response) => response.Headers.GetValues(RequestIdHeader).FirstOrDefault() ?? string.Empty;
}
+29
View File
@@ -0,0 +1,29 @@
using System.Text.Json.Serialization;
namespace AnthropicClient.Models;
/// <summary>
/// Represents part of the content of a chat message.
/// </summary>
public abstract class Content
{
/// <summary>
/// Gets the type of the content.
/// </summary>
public string Type { get; init; } = string.Empty;
[JsonConstructor]
internal Content()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Content"/> class.
/// </summary>
/// <param name="type">The type of the content.</param>
/// <returns>A new instance of the <see cref="Content"/> class.</returns>
protected Content(string type)
{
Type = type;
}
}
+27
View File
@@ -0,0 +1,27 @@
namespace AnthropicClient.Models;
/// <summary>
/// Represents the content type.
/// </summary>
public static class ContentType
{
/// <summary>
/// Represents the text content type.
/// </summary>
public const string Text = "text";
/// <summary>
/// Represents the image content type.
/// </summary>
public const string Image = "image";
/// <summary>
/// Represents the tool use content type.
/// </summary>
public const string ToolUse = "tool_use";
/// <summary>
/// Represents the tool result content type.
/// </summary>
public const string ToolResult = "tool_result";
}
+27
View File
@@ -0,0 +1,27 @@
namespace AnthropicClient.Models;
/// <summary>
/// Represents an error.
/// </summary>
public abstract class Error
{
/// <summary>
/// Gets the type of the error.
/// </summary>
public string Type { get; init; } = string.Empty;
/// <summary>
/// Gets the message of the error.
/// </summary>
public string Message { get; init; } = string.Empty;
/// <summary>
/// Initializes a new instance of the <see cref="Error"/> class.
/// </summary>
/// <param name="type">The type of the error.</param>
/// <returns>A new instance of the <see cref="Error"/> class.</returns>
protected Error(string type)
{
Type = type;
}
}
+42
View File
@@ -0,0 +1,42 @@
namespace AnthropicClient.Models;
/// <summary>
/// Represents the error type.
/// </summary>
public class ErrorType
{
/// <summary>
/// Represents the invalid_request error type.
/// </summary>
public const string InvalidRequestError = "invalid_request_error";
/// <summary>
/// Represents the authentication_error type.
/// </summary>
public const string AuthenticationError = "authentication_error";
/// <summary>
/// Represents the permission_error type.
/// </summary>
public const string PermissionError = "permission_error";
/// <summary>
/// Represents the not_found_error type.
/// </summary>
public const string NotFoundError = "not_found_error";
/// <summary>
/// Represents the rate_limit_error type.
/// </summary>
public const string RateLimitError = "rate_limit_error";
/// <summary>
/// Represents the api_error type.
/// </summary>
public const string ApiError = "api_error";
/// <summary>
/// Represents the overloaded_error type.
/// </summary>
public const string OverloadedError = "overloaded_error";
}
@@ -0,0 +1,36 @@
using System.Text.Json.Serialization;
using AnthropicClient.Utils;
namespace AnthropicClient.Models;
/// <summary>
/// Represents image content that is part of a chat message.
/// </summary>
public class ImageContent : Content
{
/// <summary>
/// Gets the source of the image.
/// </summary>
public ImageSource Source { get; init; } = new();
[JsonConstructor]
internal ImageContent()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ImageContent"/> class.
/// </summary>
/// <param name="mediaType">The media type of the image.</param>
/// <param name="data">The data of the image.</param>
/// <exception cref="ArgumentNullException">Thrown when the media type or data is null.</exception>
/// <returns>A new instance of the <see cref="ImageContent"/> class.</returns>
public ImageContent(string mediaType, string data) : base(ContentType.Image)
{
ArgumentValidator.ThrowIfNull(mediaType, nameof(mediaType));
ArgumentValidator.ThrowIfNull(data, nameof(data));
Source = new(mediaType, data);
}
}
+50
View File
@@ -0,0 +1,50 @@
using System.Text.Json.Serialization;
using AnthropicClient.Utils;
namespace AnthropicClient.Models;
/// <summary>
/// Represents an image source.
/// </summary>
public class ImageSource
{
/// <summary>
/// Gets the media type of the image.
/// </summary>
[JsonPropertyName("media_type")]
public string MediaType { get; init; } = string.Empty;
/// <summary>
/// Gets the data of the image.
/// </summary>
public string Data { get; init; } = string.Empty;
[JsonConstructor]
internal ImageSource()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ImageSource"/> class.
/// </summary>
/// <param name="mediaType">The media type of the image.</param>
/// <param name="data">The data of the image.</param>
/// <exception cref="ArgumentException">Thrown when the media type is invalid.</exception>
/// <exception cref="ArgumentNullException">Thrown when the media type or data is null.</exception>
/// <returns>A new instance of the <see cref="ImageSource"/> class.</returns>
public ImageSource(string mediaType, string data)
{
ArgumentValidator.ThrowIfNull(mediaType, nameof(mediaType));
if (ImageType.IsValidImageType(mediaType) is false)
{
throw new ArgumentException($"Invalid media type: {mediaType}");
}
ArgumentValidator.ThrowIfNull(data, nameof(data));
MediaType = mediaType;
Data = data;
}
}
+29
View File
@@ -0,0 +1,29 @@
namespace AnthropicClient.Models;
/// <summary>
/// Represents the image type.
/// </summary>
public static class ImageType
{
/// <summary>
/// Represents the JPEG image type.
/// </summary>
public const string Jpg = "image/jpeg";
/// <summary>
/// Represents the PNG image type.
/// </summary>
public const string Png = "image/png";
/// <summary>
/// Represents the GIF image type.
/// </summary>
public const string Gif = "image/gif";
/// <summary>
/// Represents the WebP image type.
/// </summary>
public const string Webp = "image/webp";
internal static bool IsValidImageType(string imageType) => imageType is Jpg or Png or Gif or Webp;
}
@@ -0,0 +1,42 @@
using System.Text.Json.Serialization;
using AnthropicClient.Utils;
namespace AnthropicClient.Models;
/// <summary>
/// Represents an input property.
/// </summary>
public class InputProperty
{
/// <summary>
/// Gets the type of the input property.
/// </summary>
public string Type { get; init; } = string.Empty;
/// <summary>
/// Gets the description of the input property.
/// </summary>
public string Description { get; init; } = string.Empty;
[JsonConstructor]
internal InputProperty()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="InputProperty"/> class.
/// </summary>
/// <param name="type">The type of the input property.</param>
/// <param name="description">The description of the input property.</param>
/// <exception cref="ArgumentNullException">Thrown when the type or description is null.</exception>
/// <returns>A new instance of the <see cref="InputProperty"/> class.</returns>
public InputProperty(string type, string description)
{
ArgumentValidator.ThrowIfNull(type, nameof(type));
ArgumentValidator.ThrowIfNull(description, nameof(description));
Type = type;
Description = description;
}
}
+53
View File
@@ -0,0 +1,53 @@
using System.Text.Json.Serialization;
using AnthropicClient.Utils;
namespace AnthropicClient.Models;
/// <summary>
/// Represents an input schema.
/// </summary>
public class InputSchema
{
/// <summary>
/// Gets the type of the input schema.
/// </summary>
public string Type { get; init; } = "object";
/// <summary>
/// Gets the properties of the input schema.
/// </summary>
public Dictionary<string, InputProperty> Properties { get; init; } = [];
/// <summary>
/// Gets the required properties of the input schema.
/// </summary>
public List<string> Required { get; init; } = [];
[JsonConstructor]
internal InputSchema()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="InputSchema"/> class.
/// </summary>
/// <param name="properties">The properties of the input schema.</param>
/// <param name="required">The required properties of the input schema.</param>
/// <exception cref="ArgumentNullException">Thrown when the properties or required is null.</exception>
/// <exception cref="ArgumentException">Thrown when the required properties are not present in the properties dictionary.</exception>
/// <returns>A new instance of the <see cref="InputSchema"/> class.</returns>
public InputSchema(Dictionary<string, InputProperty> properties, List<string> required)
{
ArgumentValidator.ThrowIfNull(properties, nameof(properties));
ArgumentValidator.ThrowIfNull(required, nameof(required));
if (required.Any(r => properties.ContainsKey(r) is false))
{
throw new ArgumentException("Required properties must be present in the properties dictionary.");
}
Properties = properties;
Required = required;
}
}
@@ -0,0 +1,24 @@
using System.Text.Json.Serialization;
namespace AnthropicClient.Models;
/// <summary>
/// Represents an invalid_request error.
/// </summary>
public class InvalidRequestError : Error
{
[JsonConstructor]
internal InvalidRequestError() : base(ErrorType.InvalidRequestError)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="InvalidRequestError"/> class.
/// </summary>
/// <param name="message">The message of the error.</param>
/// <returns>A new instance of the <see cref="InvalidRequestError"/> class.</returns>
public InvalidRequestError(string message) : base(ErrorType.InvalidRequestError)
{
Message = message;
}
}
@@ -0,0 +1,7 @@
using System.ComponentModel;
namespace System.Runtime.CompilerServices
{
[EditorBrowsable(EditorBrowsableState.Never)]
class IsExternalInit { }
}
@@ -0,0 +1,22 @@
namespace AnthropicClient.Models;
/// <summary>
/// Represents a message request.
/// </summary>
public abstract class MessageRequest
{
/// <summary>
/// Gets a value indicating whether the message should be streamed.
/// </summary>
public bool Stream { get; init; }
/// <summary>
/// Initializes a new instance of the <see cref="MessageRequest"/> class.
/// </summary>
/// <param name="stream">A value indicating whether the message should be streamed.</param>
/// <returns>A new instance of the <see cref="MessageRequest"/> class.</returns>
public MessageRequest(bool stream = false)
{
Stream = stream;
}
}
+19
View File
@@ -0,0 +1,19 @@
namespace AnthropicClient.Models;
/// <summary>
/// Represents the message role.
/// </summary>
public static class MessageRole
{
/// <summary>
/// Represents the user role.
/// </summary>
public const string User = "user";
/// <summary>
/// Represents the assistant role.
/// </summary>
public const string Assistant = "assistant";
internal static bool IsValidRole(string role) => role is User or Assistant;
}
@@ -0,0 +1,24 @@
using System.Text.Json.Serialization;
namespace AnthropicClient.Models;
/// <summary>
/// Represents a not_found error.
/// </summary>
public class NotFoundError : Error
{
[JsonConstructor]
internal NotFoundError() : base(ErrorType.NotFoundError)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="NotFoundError"/> class.
/// </summary>
/// <param name="message">The message of the error.</param>
/// <returns>A new instance of the <see cref="NotFoundError"/> class.</returns>
public NotFoundError(string message) : base(ErrorType.NotFoundError)
{
Message = message;
}
}
@@ -0,0 +1,24 @@
using System.Text.Json.Serialization;
namespace AnthropicClient.Models;
/// <summary>
/// Represents an overloaded error.
/// </summary>
public class OverloadedError : Error
{
[JsonConstructor]
internal OverloadedError() : base(ErrorType.OverloadedError)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="OverloadedError"/> class.
/// </summary>
/// <param name="message">The message of the error.</param>
/// <returns>A new instance of the <see cref="OverloadedError"/> class.</returns>
public OverloadedError(string message) : base(ErrorType.OverloadedError)
{
Message = message;
}
}
@@ -0,0 +1,24 @@
using System.Text.Json.Serialization;
namespace AnthropicClient.Models;
/// <summary>
/// Represents a permission error.
/// </summary>
public class PermissionError : Error
{
[JsonConstructor]
internal PermissionError() : base(ErrorType.PermissionError)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="PermissionError"/> class.
/// </summary>
/// <param name="message">The message of the error.</param>
/// <returns>A new instance of the <see cref="PermissionError"/> class.</returns>
public PermissionError(string message) : base(ErrorType.PermissionError)
{
Message = message;
}
}
@@ -0,0 +1,24 @@
using System.Text.Json.Serialization;
namespace AnthropicClient.Models;
/// <summary>
/// Represents a rate_limit error.
/// </summary>
public class RateLimitError : Error
{
[JsonConstructor]
internal RateLimitError() : base(ErrorType.RateLimitError)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="RateLimitError"/> class.
/// </summary>
/// <param name="message">The message of the error.</param>
/// <returns>A new instance of the <see cref="RateLimitError"/> class.</returns>
public RateLimitError(string message) : base(ErrorType.RateLimitError)
{
Message = message;
}
}
@@ -0,0 +1,34 @@
using System.Text.Json.Serialization;
using AnthropicClient.Utils;
namespace AnthropicClient.Models;
/// <summary>
/// Represents the specific tool choice mode.
/// </summary>
public class SpecificToolChoice : ToolChoice
{
/// <summary>
/// Gets the name of the tool.
/// </summary>
public string Name { get; init; } = string.Empty;
[JsonConstructor]
internal SpecificToolChoice() : base(ToolChoiceType.Tool)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="SpecificToolChoice"/> class.
/// </summary>
/// <param name="name">The name of the tool.</param>
/// <exception cref="ArgumentNullException">Thrown when the name is null.</exception>
/// <returns>A new instance of the <see cref="SpecificToolChoice"/> class.</returns>
public SpecificToolChoice(string name) : base(ToolChoiceType.Tool)
{
ArgumentValidator.ThrowIfNull(name, nameof(name));
Name = name;
}
}
@@ -0,0 +1,27 @@
namespace AnthropicClient.Models;
/// <summary>
/// Represents the stop reason type.
/// </summary>
public static class StopReasonType
{
/// <summary>
/// Represents the end_turn stop reason.
/// </summary>
public const string EndTurn = "end_turn";
/// <summary>
/// Represents the max_tokens stop reason.
/// </summary>
public const string MaxTokens = "max_tokens";
/// <summary>
/// Represents the stop_sequence stop reason.
/// </summary>
public const string StopSequence = "stop_sequence";
/// <summary>
/// Represents the tool_use stop reason.
/// </summary>
public const string ToolUse = "tool_use";
}
+34
View File
@@ -0,0 +1,34 @@
using System.Text.Json.Serialization;
using AnthropicClient.Utils;
namespace AnthropicClient.Models;
/// <summary>
/// Represents text content that is part of a chat message.
/// </summary>
public class TextContent : Content
{
/// <summary>
/// Gets the text of the content.
/// </summary>
public string Text { get; init; } = string.Empty;
[JsonConstructor]
internal TextContent() : base(ContentType.Text)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="TextContent"/> class.
/// </summary>
/// <param name="text">The text of the content.</param>
/// <exception cref="ArgumentNullException">Thrown when the text is null.</exception>
/// <returns>A new instance of the <see cref="TextContent"/> class.</returns>
public TextContent(string text) : base(ContentType.Text)
{
ArgumentValidator.ThrowIfNull(text, nameof(text));
Text = text;
}
}
+51
View File
@@ -0,0 +1,51 @@
using System.Text.Json.Serialization;
using AnthropicClient.Utils;
namespace AnthropicClient.Models;
/// <summary>
/// Represents a tool that can be used in the chat.
/// </summary>
public class Tool
{
/// <summary>
/// Gets the name of the tool.
/// </summary>
public string Name { get; init; } = string.Empty;
/// <summary>
/// Gets the description of the tool.
/// </summary>
public string Description { get; init; } = string.Empty;
/// <summary>
/// Gets the input schema of the tool.
/// </summary>
[JsonPropertyName("input_schema")]
public InputSchema InputSchema { get; init; } = new();
[JsonConstructor]
internal Tool()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Tool"/> class.
/// </summary>
/// <param name="name">The name of the tool.</param>
/// <param name="description">The description of the tool.</param>
/// <param name="inputSchema">The input schema of the tool.</param>
/// <exception cref="ArgumentNullException">Thrown when the name, description, or input schema is null.</exception>
/// <returns>A new instance of the <see cref="Tool"/> class.</returns>
public Tool(string name, string description, InputSchema inputSchema)
{
ArgumentValidator.ThrowIfNull(name, nameof(name));
ArgumentValidator.ThrowIfNull(description, nameof(description));
ArgumentValidator.ThrowIfNull(inputSchema, nameof(inputSchema));
Name = name;
Description = description;
InputSchema = inputSchema;
}
}
+22
View File
@@ -0,0 +1,22 @@
namespace AnthropicClient.Models;
/// <summary>
/// Represents a tool choice mode.
/// </summary>
public abstract class ToolChoice
{
/// <summary>
/// Gets the type of the tool choice.
/// </summary>
public string Type { get; init; } = string.Empty;
/// <summary>
/// Initializes a new instance of the <see cref="ToolChoice"/> class.
/// </summary>
/// <param name="type">The type of the tool choice.</param>
/// <returns>A new instance of the <see cref="ToolChoice"/> class.</returns>
protected ToolChoice(string type)
{
Type = type;
}
}
@@ -0,0 +1,24 @@
namespace AnthropicClient.Models;
/// <summary>
/// Represents the tool choice type.
/// </summary>
public class ToolChoiceType
{
/// <summary>
/// Represents the auto tool choice type.
/// </summary>
public const string Auto = "auto";
/// <summary>
/// Represents the any tool choice type.
/// </summary>
public const string Any = "any";
/// <summary>
/// Represents the specific tool choice type.
/// </summary>
public const string Tool = "tool";
internal static bool IsValidType(string type) => type is Auto or Any or Tool;
}
@@ -0,0 +1,41 @@
using System.Text.Json.Serialization;
using AnthropicClient.Utils;
namespace AnthropicClient.Models;
/// <summary>
/// Represents tool result content that is part of a chat message.
/// </summary>
public class ToolResultContent : Content
{
/// <summary>
/// Gets the tool use ID of the content.
/// </summary>
[JsonPropertyName("tool_use_id")]
public string ToolUseId { get; set; } = string.Empty;
/// <summary>
/// Gets the content of the tool result.
/// </summary>
public string Content { get; set; } = string.Empty;
[JsonConstructor]
internal ToolResultContent() : base(ContentType.ToolResult) { }
/// <summary>
/// Initializes a new instance of the <see cref="ToolResultContent"/> class.
/// </summary>
/// <param name="toolUseId">The tool use ID of the content.</param>
/// <param name="content">The content of the tool result.</param>
/// <exception cref="ArgumentNullException">Thrown when the tool use ID or content is null.</exception>
/// <returns>A new instance of the <see cref="ToolResultContent"/> class.</returns>
public ToolResultContent(string toolUseId, string content) : base(ContentType.ToolResult)
{
ArgumentValidator.ThrowIfNull(toolUseId, nameof(toolUseId));
ArgumentValidator.ThrowIfNull(content, nameof(content));
ToolUseId = toolUseId;
Content = content;
}
}
@@ -0,0 +1,30 @@
namespace AnthropicClient.Models;
/// <summary>
/// Represents tool use content that is part of a message.
/// </summary>
public class ToolUseContent : Content
{
/// <summary>
/// Gets the ID of the tool use.
/// </summary>
public string Id { get; set; } = string.Empty;
/// <summary>
/// Gets the name of the tool.
/// </summary>
public string Name { get; set; } = string.Empty;
/// <summary>
/// Gets the input of the tool.
/// </summary>
public Dictionary<string, object?> Input { get; set; } = [];
/// <summary>
/// Initializes a new instance of the <see cref="ToolUseContent"/> class.
/// </summary>
/// <returns>A new instance of the <see cref="ToolUseContent"/> class.</returns>
public ToolUseContent() : base(ContentType.ToolUse)
{
}
}