From dd380c9253d2b00a2823ccc49c5a67bd47c88227 Mon Sep 17 00:00:00 2001 From: Stevan Freeborn <65925598+StevanFreeborn@users.noreply.github.com> Date: Wed, 3 Jul 2024 16:57:27 -0500 Subject: [PATCH] docs: work on README --- .vscode/settings.json | 6 +- README.md | 208 +++++++++++++++- src/AnthropicClient/AnthropicApiClient.cs | 78 +++--- src/AnthropicClient/Models/AnthropicError.cs | 2 +- .../Models/BaseMessageRequest.cs | 147 ++++++++++++ src/AnthropicClient/Models/Content.cs | 2 +- src/AnthropicClient/Models/ImageContent.cs | 2 +- .../Models/{ChatMessage.cs => Message.cs} | 12 +- .../Models/MessageCompleteEventData.cs | 8 +- .../Models/MessageDeltaEventData.cs | 8 +- src/AnthropicClient/Models/MessageRequest.cs | 123 ++-------- .../{ChatResponse.cs => MessageResponse.cs} | 24 +- .../Models/MessageStartEventData.cs | 4 +- .../Models/StreamChatMessageRequest.cs | 58 ----- ...sageRequest.cs => StreamMessageRequest.cs} | 16 +- src/AnthropicClient/Models/TextContent.cs | 2 +- src/AnthropicClient/Models/Tool.cs | 2 +- src/AnthropicClient/Models/ToolCall.cs | 1 - .../Models/ToolResultContent.cs | 2 +- .../Models/{ChatUsage.cs => Usage.cs} | 4 +- tests/AnthropicClient.Tests/.runsettings | 11 + .../{Integration => Data}/ErrorTestData.cs | 2 +- .../{Integration => Data}/EventTestData.cs | 10 +- .../JsonSchemaGeneratorTestData.cs | 8 +- .../EndToEnd/AnthropicApiClientTests.cs | 20 +- .../EndToEnd/HttpClientFixture.cs | 11 - .../Examples/ExampleAttribute.cs | 14 ++ .../Examples/Examples.cs | 108 +++++++++ .../ConfigurationFixture.cs | 2 +- .../Integration/AnthropicApiClientTests.cs | 48 ++-- .../Integration/IntegrationTest.cs | 4 +- .../Models/MessageCompleteEventDataTests.cs | 2 +- .../Unit/Models/MessageDeltaEventDataTests.cs | 6 +- ...RequestTests.cs => MessageRequestTests.cs} | 226 +++++++++--------- ...sponseTests.cs => MessageResponseTests.cs} | 46 ++-- .../Unit/Models/MessageStartEventDataTests.cs | 12 +- .../{ChatMessageTests.cs => MessageTests.cs} | 32 +-- ...tTests.cs => StreamMessageRequestTests.cs} | 74 +++--- .../{ChatUsageTests.cs => UsageTests.cs} | 18 +- .../Unit/Utils/JsonSchemaGeneratorTests.cs | 2 - tests/AnthropicClient.Tests/Usings.cs | 5 +- 41 files changed, 847 insertions(+), 523 deletions(-) create mode 100644 src/AnthropicClient/Models/BaseMessageRequest.cs rename src/AnthropicClient/Models/{ChatMessage.cs => Message.cs} (78%) rename src/AnthropicClient/Models/{ChatResponse.cs => MessageResponse.cs} (60%) delete mode 100644 src/AnthropicClient/Models/StreamChatMessageRequest.cs rename src/AnthropicClient/Models/{ChatMessageRequest.cs => StreamMessageRequest.cs} (83%) rename src/AnthropicClient/Models/{ChatUsage.cs => Usage.cs} (86%) create mode 100644 tests/AnthropicClient.Tests/.runsettings rename tests/AnthropicClient.Tests/{Integration => Data}/ErrorTestData.cs (97%) rename tests/AnthropicClient.Tests/{Integration => Data}/EventTestData.cs (98%) rename tests/AnthropicClient.Tests/{Unit/Utils => Data}/JsonSchemaGeneratorTestData.cs (99%) delete mode 100644 tests/AnthropicClient.Tests/EndToEnd/HttpClientFixture.cs create mode 100644 tests/AnthropicClient.Tests/Examples/ExampleAttribute.cs create mode 100644 tests/AnthropicClient.Tests/Examples/Examples.cs rename tests/AnthropicClient.Tests/{EndToEnd => Fixtures}/ConfigurationFixture.cs (92%) rename tests/AnthropicClient.Tests/Unit/Models/{ChatMessageRequestTests.cs => MessageRequestTests.cs} (58%) rename tests/AnthropicClient.Tests/Unit/Models/{ChatResponseTests.cs => MessageResponseTests.cs} (63%) rename tests/AnthropicClient.Tests/Unit/Models/{ChatMessageTests.cs => MessageTests.cs} (62%) rename tests/AnthropicClient.Tests/Unit/Models/{StreamChatMessageRequestTests.cs => StreamMessageRequestTests.cs} (63%) rename tests/AnthropicClient.Tests/Unit/Models/{ChatUsageTests.cs => UsageTests.cs} (63%) diff --git a/.vscode/settings.json b/.vscode/settings.json index 454b252..0afaa93 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,8 @@ { "dotnet.defaultSolution": "AnthropicClient.sln", "cSpell.words": [ - "Browsable" - ] + "Browsable", + "Szalay" + ], + "dotnet.unitTests.runSettingsPath": "./tests/AnthropicClient.Tests/.runsettings" } \ No newline at end of file diff --git a/README.md b/README.md index 27d0430..7a02e77 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,210 @@ # AnthropicClient +This library for the Anthropic API is meant to simplify development in C# for Anthropic users. + +> [!NOTE] +> This is an unofficial SDK for the Anthropic API. It was not built in consultation with Anthropic or any member of their organization. + +This SDK was developed independently using existing libraries and the Anthropic API documentation as the starting point with the intention of making development of integrations done in C# with Anthropic quicker and more convenient. + > [!NOTE] -> This client library is heavily inspired by the [Anthropic.SDK](https://github.com/tghamm/Anthropic.SDK) library. I chose to create a new library because I wanted to handle streaming and tool calling differently as well as have control over the client library as I plan to use it to build a connector for [SemanticKernel](https://github.com/microsoft/semantic-kernel). +> This client library is heavily inspired by the [Anthropic.SDK](https://github.com/tghamm/Anthropic.SDK) library. I chose to create a new library because I wanted to handle streaming and tool calling differently as well as have control over the client library as I plan to use it to build a connector for [SemanticKernel](https://github.com/microsoft/semantic-kernel). However if you are looking for a client library the Anthropic.SDK is a great place to start. -## ⚠️ Under Construction ⚠️ +## 🛠️ Dependencies -This is a client library for the Anthropic API. It is a work in progress and is not yet ready for use. +### [Microsoft.Bcl.AsyncInterfaces](https://www.nuget.org/packages/Microsoft.Bcl.AsyncInterfaces/) + +![Microsoft.Bcl.AsyncInterfaces NuGet Version](https://img.shields.io/nuget/v/Microsoft.Bcl.AsyncInterfaces) + +Used to support async interfaces when streaming messages + +### [System.Text.Json](https://www.nuget.org/packages/System.Text.Json/) + +![NuGet Version](https://img.shields.io/nuget/v/System.Text.Json) + +Used for JSON serialization and deserialization + +## 💾 Installation + +Install the package from [NuGet](https://www.nuget.org) using the following command: + +```bash +dotnet add package AnthropicClient +``` + +## 🔑 API Key + +In order to use the Anthropic API you will need an API key. You can get one by signing up at [Anthropic](https://www.anthropic.com/api). Please keep your API key secure and do not share it with others. Be mindful of where you store your API key and do not commit it to a public repository. + +## 👨🏻‍💻 Start Coding + +### `AnthropicApiClient` + +The most common way to use the SDK is to create an `AnthropicApiClient` instance and call its methods. Its constructor requires two parameters: + +- `apiKey` - your Anthropic API key +- `httpClient` - an `HttpClient` instance. You can configure and customize the `HttpClient` instance as needed. This library however will perform the necessary configuration to work with the Anthropic API. Such as setting the base address and adding the proper headers. + +> [!NOTE] +> This library does not manage the lifecycle of the `HttpClient` instance. You should create and manage the lifecycle of the `HttpClient` instance in your application. + +It is best practice to read the API key from a secure location such as a configuration file or environment variable. For example using the `appsettings.json` file: + +```json +{ + "AnthropicApiKey": "YOUR_API" +} +``` + +Example constructing an `AnthropicApiClient` instance: + +```csharp +using AnthropicClient; +using Microsoft.Extensions.Configuration; + +var configuration = new ConfigurationBuilder() + .AddJsonFile("appsettings.json") + .Build(); + +var apiKey = configuration["AnthropicApiKey"]; + +var client = new AnthropicApiClient(apiKey, new HttpClient()); +``` + +### `IAnthropicApiClient` + +The library does expose an interface `IAnthropicApiClient` that can be used for dependency injection and testing. The interface is implemented by the `AnthropicApiClient` class. + +### Full API Documentation + +This library was developed to make using the Anthropic API easier within a .NET application. If you are looking for the full API documentation you can find it at [Anthropic API Documentation](https://docs.anthropic.com/). + +## Usage + +The primary use case for working with the Anthropic API is to create a message in response to a request that includes one or more other messages. The created message can then be received either as a complete response or a stream of events. This can be used to create a conversation between the caller and the Anthropic's AI models and/or to use Anthropic's AI models to perform a task. + +> [!NOTE] +> The following examples assume that you have already created an instance of the `AnthropicApiClient` class named `client`. + +### Create a message + +The `AnthropicApiClient` exposes a single method named `CreateMessageAsync` that can be used to create a message. The method requires a `MessageRequest` or a `StreamMessageRequest` instance as a parameter. The `MessageRequest` class is used to create a message whose response is not streamed and the `StreamMessageRequest` class is used to create a message whose response is streamed. The `MessageRequest` instance's properties can be set to configure how the message is created. + +#### Non-Streaming + +```csharp +using AnthropicClient.Models; + +var response = await client.CreateMessageAsync(new MessageRequest( + AnthropicModels.Claude3Haiku, + [ + new( + MessageRole.User, + [new TextContent("Please write a haiku about the ocean.")] + ) + ] +)); + +if (response.IsSuccess is false) +{ + Console.WriteLine($"Failed to create message"); + Console.WriteLine($"Error Type: {0}", response.Error.Error.Type); + Console.WriteLine($"Error Message: {0}", response.Error.Error.Message); +} + +foreach (var content in response.Value.Content) +{ + switch (content) + { + case TextContent textContent: + Console.WriteLine(textContent.Text); + break; + } +} +``` + +#### Streaming + +Anthropic uses Server-Sent Events (SSE) to stream messages. The possible events and the format of those events are documented in the [Anthropic API Documentation](https://docs.anthropic.com/en/api/messages-streaming). This library provides a way to consume them deserialized into strongly-typed C# objects that are returned in an `IAsyncEnumerable` collection. + +This allows you to consume the events as they are received and process them in the way that best fits your use case. The following example demonstrates how to consume the streamed events and build up the complete text response from the model. + +```csharp +using AnthropicClient.Models; + +var events = client.CreateMessageAsync(new StreamMessageRequest( + AnthropicModels.Claude3Haiku, + [ + new( + MessageRole.User, + [new TextContent("Please write a haiku about the ocean.")] + ) + ] +)); + +var msgBuilder = new StringBuilder(); + +await foreach (var e in events) +{ + switch (e.Data) + { + case var data when data is ContentDeltaEventData contentData: + switch (contentData.Delta) + { + case var delta when delta is TextDelta textDelta: + msgBuilder.Append(textDelta.Text); + break; + } + break; + } +} + +Console.WriteLine(msgBuilder.ToString()); +``` + +##### Message Complete Event + +This library also provides a custom `message_complete` event that is yielded when all the message's events have been received. This event is not part of Anthropic's SSE events but is provided to allow for easier consumption of the entire message response if desired and make it easier to implement built in tool calling. + +```csharp +using AnthropicClient.Models; + +var events = client.CreateMessageAsync(new StreamMessageRequest( + AnthropicModels.Claude3Haiku, + [ + new( + MessageRole.User, + [new TextContent("Please write a haiku about the ocean.")] + ) + ] +)); + +MessageResponse? response = null; + +await foreach (var e in events) +{ + switch (e.Data) + { + case var data when data is MessageCompleteEventData msgData: + response = msgData.Message; + break; + } +} + +var textContent = response?.Content + .OfType() + .Aggregate(new StringBuilder(), (sb, c) => sb.Append(c.Text)) + .ToString(); + +Console.WriteLine(textContent); +``` + +### Tool Calling + + + +#### Create a tool + +#### Call a tool + +#### Call a tool in streamed message diff --git a/src/AnthropicClient/AnthropicApiClient.cs b/src/AnthropicClient/AnthropicApiClient.cs index 2dd013e..a9c120c 100644 --- a/src/AnthropicClient/AnthropicApiClient.cs +++ b/src/AnthropicClient/AnthropicApiClient.cs @@ -14,18 +14,18 @@ namespace AnthropicClient; public interface IAnthropicApiClient { /// - /// Creates a chat message asynchronously. + /// Creates a message asynchronously. /// - /// The chat message request to create. - /// A task that represents the asynchronous operation. The task result contains the chat response as an . - Task> CreateChatMessageAsync(ChatMessageRequest request); + /// The message request to create. + /// A task that represents the asynchronous operation. The task result contains the response as an . + Task> CreateMessageAsync(MessageRequest request); /// - /// Creates a chat message asynchronously and streams the response. + /// Creates a message asynchronously and streams the response. /// - /// The chat message request to create. - /// An asynchronous enumerable that yields the chat response line by line. - IAsyncEnumerable CreateChatMessageAsync(StreamChatMessageRequest request); + /// The message request to create. + /// An asynchronous enumerable that yields the response event by event. + IAsyncEnumerable CreateMessageAsync(StreamMessageRequest request); } /// @@ -69,7 +69,7 @@ public class AnthropicApiClient : IAnthropicApiClient } /// - public async Task> CreateChatMessageAsync(ChatMessageRequest request) + public async Task> CreateMessageAsync(MessageRequest request) { var response = await SendRequestAsync(request); var anthropicHeaders = new AnthropicHeaders(response.Headers); @@ -78,21 +78,21 @@ public class AnthropicApiClient : IAnthropicApiClient if (response.IsSuccessStatusCode is false) { var error = Deserialize(responseContent) ?? new AnthropicError(); - return AnthropicResult.Failure(error, anthropicHeaders); + return AnthropicResult.Failure(error, anthropicHeaders); } - var chatResponse = Deserialize(responseContent) ?? new ChatResponse(); + var msgResponse = Deserialize(responseContent) ?? new MessageResponse(); if (request.Tools is not null && request.Tools.Count > 0) { - chatResponse.ToolCall = GetToolCall(chatResponse, request.Tools); + msgResponse.ToolCall = GetToolCall(msgResponse, request.Tools); } - return AnthropicResult.Success(chatResponse, anthropicHeaders); + return AnthropicResult.Success(msgResponse, anthropicHeaders); } /// - public async IAsyncEnumerable CreateChatMessageAsync(StreamChatMessageRequest request) + public async IAsyncEnumerable CreateMessageAsync(StreamMessageRequest request) { var response = await SendRequestAsync(request); var anthropicHeaders = new AnthropicHeaders(response.Headers); @@ -100,7 +100,7 @@ public class AnthropicApiClient : IAnthropicApiClient using var responseContent = await response.Content.ReadAsStreamAsync(); using var streamReader = new StreamReader(responseContent); - ChatResponse? chatResponse = null; + MessageResponse? msgResponse = null; Content? content = null; var toolInputJsonStringBuilder = new StringBuilder(); var currentEvent = new AnthropicEvent(); @@ -111,14 +111,14 @@ public class AnthropicApiClient : IAnthropicApiClient // I know...this is not pretty, but here is why... // as events are being yielded I want to also - // build up the complete chat response + // build up the complete response // so I can yield it as a special event to make tool // calling easier to handle - // initialize chat response on message start + // initialize response on message start if (currentEvent.Type is EventType.MessageStart && currentEvent.Data is MessageStartEventData msgStartData) { - chatResponse = msgStartData.Message; + msgResponse = msgStartData.Message; } // initialize content block on content block start @@ -144,14 +144,14 @@ public class AnthropicApiClient : IAnthropicApiClient } // finalize content block on content block stop - // and add it to the chat response + // and add it to the response if (currentEvent.Type is EventType.ContentBlockStop) { - if (content is not null && chatResponse is not null) + if (content is not null && msgResponse is not null) { if (content is TextContent textContent) { - chatResponse.Content.Add(textContent); + msgResponse.Content.Add(textContent); } if (content is ToolUseContent toolUseContent) @@ -164,51 +164,51 @@ public class AnthropicApiClient : IAnthropicApiClient Input = input!, }; - chatResponse.Content.Add(newToolUseContent); + msgResponse.Content.Add(newToolUseContent); } content = null; } } - // update chat response with message delta data + // update response with message delta data if ( currentEvent.Type is EventType.MessageDelta && currentEvent.Data is MessageDeltaEventData msgDeltaData && - chatResponse is not null + msgResponse is not null ) { - var existingUsage = chatResponse.Usage; - var newUsage = new ChatUsage() + var existingUsage = msgResponse.Usage; + var newUsage = new Usage() { InputTokens = existingUsage.InputTokens + msgDeltaData.Usage.InputTokens, OutputTokens = existingUsage.OutputTokens + msgDeltaData.Usage.OutputTokens, }; - chatResponse = new ChatResponse() + msgResponse = new MessageResponse() { - Id = chatResponse.Id, - Model = chatResponse.Model, - Role = chatResponse.Role, + Id = msgResponse.Id, + Model = msgResponse.Model, + Role = msgResponse.Role, StopReason = msgDeltaData.Delta.StopReason, StopSequence = msgDeltaData.Delta.StopSequence, - Type = chatResponse.Type, + Type = msgResponse.Type, Usage = newUsage, - Content = chatResponse.Content, + Content = msgResponse.Content, }; if (request.Tools is not null && request.Tools.Count > 0) { - chatResponse.ToolCall = GetToolCall(chatResponse, request.Tools); + msgResponse.ToolCall = GetToolCall(msgResponse, request.Tools); } } - // yield chat response on message stop - if (currentEvent.Type is EventType.MessageStop && chatResponse is not null) + // yield response on message stop + if (currentEvent.Type is EventType.MessageStop && msgResponse is not null) { - var eventData = new MessageCompleteEventData(chatResponse, anthropicHeaders); + var eventData = new MessageCompleteEventData(msgResponse, anthropicHeaders); yield return new AnthropicEvent(EventType.MessageComplete, eventData); - chatResponse = null; + msgResponse = null; } if (line is null) @@ -245,7 +245,7 @@ public class AnthropicApiClient : IAnthropicApiClient } while (true); } - private ToolCall? GetToolCall(ChatResponse response, List tools) + private ToolCall? GetToolCall(MessageResponse response, List tools) { var toolUse = response.Content.OfType().FirstOrDefault(); @@ -264,7 +264,7 @@ public class AnthropicApiClient : IAnthropicApiClient return new ToolCall(tool, toolUse); } - private async Task SendRequestAsync(MessageRequest request) + private async Task SendRequestAsync(BaseMessageRequest request) { var requestContent = new StringContent(Serialize(request), Encoding.UTF8, JsonContentType); return await _httpClient.PostAsync(MessagesEndpoint, requestContent); diff --git a/src/AnthropicClient/Models/AnthropicError.cs b/src/AnthropicClient/Models/AnthropicError.cs index 1efcf14..aa9192c 100644 --- a/src/AnthropicClient/Models/AnthropicError.cs +++ b/src/AnthropicClient/Models/AnthropicError.cs @@ -15,7 +15,7 @@ public class AnthropicError /// /// The error object. /// - public Error? Error { get; init; } = null; + public Error Error { get; init; } = new ApiError(); [JsonConstructor] internal AnthropicError() diff --git a/src/AnthropicClient/Models/BaseMessageRequest.cs b/src/AnthropicClient/Models/BaseMessageRequest.cs new file mode 100644 index 0000000..41fcfb7 --- /dev/null +++ b/src/AnthropicClient/Models/BaseMessageRequest.cs @@ -0,0 +1,147 @@ +using System.Text.Json.Serialization; + +using AnthropicClient.Utils; + +namespace AnthropicClient.Models; + +/// +/// Represents a message request. +/// +public abstract class BaseMessageRequest +{ + /// + /// Gets the model ID to use for the request. + /// + public string Model { get; init; } = string.Empty; + + /// + /// Gets the system ID to use for the request. + /// + public string? System { get; init; } = null; + + /// + /// Gets the messages to send to the model. + /// + public List Messages { get; init; } = []; + + /// + /// Gets the maximum number of tokens to generate. + /// + [JsonPropertyName("max_tokens")] + public int MaxTokens { get; init; } = 1024; + + /// + /// Gets the metadata to include with the request. + /// + public Dictionary? Metadata { get; init; } = null; + + /// + /// Gets the prompt stop sequences. + /// + [JsonPropertyName("stop_sequences")] + public List StopSequences { get; init; } = []; + + /// + /// Gets the temperature to use for the request. + /// + public decimal Temperature { get; init; } = 0.0m; + + /// + /// Gets the top-K value to use for the request. + /// + public int? TopK { get; init; } = null; + + /// + /// Gets the top-P value to use for the request. + /// + public decimal? TopP { get; init; } = null; + + /// + /// Gets the tool choice mode to use for the request. + /// + [JsonPropertyName("tool_choice")] + public ToolChoice? ToolChoice { get; init; } = null; + + /// + /// Gets the tools to use for the request. + /// + public List? Tools { get; init; } = null; + + /// + /// Gets a value indicating whether the message should be streamed. + /// + public bool Stream { get; init; } + + [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. + /// 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 + ) + { + 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; + Stream = stream; + } +} \ No newline at end of file diff --git a/src/AnthropicClient/Models/Content.cs b/src/AnthropicClient/Models/Content.cs index 7d4a7ee..2970c3f 100644 --- a/src/AnthropicClient/Models/Content.cs +++ b/src/AnthropicClient/Models/Content.cs @@ -3,7 +3,7 @@ using System.Text.Json.Serialization; namespace AnthropicClient.Models; /// -/// Represents part of the content of a chat message. +/// Represents part of the content of a message. /// public abstract class Content { diff --git a/src/AnthropicClient/Models/ImageContent.cs b/src/AnthropicClient/Models/ImageContent.cs index e129880..6fd5232 100644 --- a/src/AnthropicClient/Models/ImageContent.cs +++ b/src/AnthropicClient/Models/ImageContent.cs @@ -5,7 +5,7 @@ using AnthropicClient.Utils; namespace AnthropicClient.Models; /// -/// Represents image content that is part of a chat message. +/// Represents image content that is part of a message. /// public class ImageContent : Content { diff --git a/src/AnthropicClient/Models/ChatMessage.cs b/src/AnthropicClient/Models/Message.cs similarity index 78% rename from src/AnthropicClient/Models/ChatMessage.cs rename to src/AnthropicClient/Models/Message.cs index e3d09f2..fb73c46 100644 --- a/src/AnthropicClient/Models/ChatMessage.cs +++ b/src/AnthropicClient/Models/Message.cs @@ -5,9 +5,9 @@ using AnthropicClient.Utils; namespace AnthropicClient.Models; /// -/// Represents a chat message. +/// Represents a message. /// -public class ChatMessage +public class Message { /// /// Gets the role of the message. @@ -20,19 +20,19 @@ public class ChatMessage public List Content { get; init; } = []; [JsonConstructor] - internal ChatMessage() + internal Message() { } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The role of the message. /// The contents of the message. /// Thrown when the role is invalid. /// Thrown when the role or content is null. - /// A new instance of the class. - public ChatMessage(string role, List content) + /// A new instance of the class. + public Message(string role, List content) { ArgumentValidator.ThrowIfNull(role, nameof(role)); ArgumentValidator.ThrowIfNull(content, nameof(content)); diff --git a/src/AnthropicClient/Models/MessageCompleteEventData.cs b/src/AnthropicClient/Models/MessageCompleteEventData.cs index 1b694aa..88a79cd 100644 --- a/src/AnthropicClient/Models/MessageCompleteEventData.cs +++ b/src/AnthropicClient/Models/MessageCompleteEventData.cs @@ -11,17 +11,17 @@ public class MessageCompleteEventData : EventData public AnthropicHeaders Headers { get; init; } /// - /// Gets the chat response message. + /// Gets the response message. /// - public ChatResponse Message { get; init; } + public MessageResponse Message { get; init; } /// /// Initializes a new instance of the class. /// - /// The chat response message. + /// The response message. /// The anthropic headers. /// A new instance of the class. - public MessageCompleteEventData(ChatResponse message, AnthropicHeaders headers) : base(EventType.MessageComplete) + public MessageCompleteEventData(MessageResponse message, AnthropicHeaders headers) : base(EventType.MessageComplete) { Message = message; Headers = headers; diff --git a/src/AnthropicClient/Models/MessageDeltaEventData.cs b/src/AnthropicClient/Models/MessageDeltaEventData.cs index 0830b82..dedaab9 100644 --- a/src/AnthropicClient/Models/MessageDeltaEventData.cs +++ b/src/AnthropicClient/Models/MessageDeltaEventData.cs @@ -13,9 +13,9 @@ public class MessageDeltaEventData : EventData public MessageDelta Delta { get; init; } = new(); /// - /// Gets the chat usage. + /// Gets the usage. /// - public ChatUsage Usage { get; init; } = new(); + public Usage Usage { get; init; } = new(); [JsonConstructor] internal MessageDeltaEventData() : base(EventType.MessageDelta) @@ -26,9 +26,9 @@ public class MessageDeltaEventData : EventData /// Initializes a new instance of the class. /// /// The message delta. - /// The chat usage. + /// The usage. /// A new instance of the class. - public MessageDeltaEventData(MessageDelta delta, ChatUsage usage) : base(EventType.MessageDelta) + public MessageDeltaEventData(MessageDelta delta, Usage usage) : base(EventType.MessageDelta) { Delta = delta; Usage = usage; diff --git a/src/AnthropicClient/Models/MessageRequest.cs b/src/AnthropicClient/Models/MessageRequest.cs index b477581..ed02cf2 100644 --- a/src/AnthropicClient/Models/MessageRequest.cs +++ b/src/AnthropicClient/Models/MessageRequest.cs @@ -1,79 +1,14 @@ using System.Text.Json.Serialization; -using AnthropicClient.Utils; - namespace AnthropicClient.Models; /// /// Represents a message request. /// -public abstract class MessageRequest +public class MessageRequest : BaseMessageRequest { - /// - /// Gets the model ID to use for the request. - /// - public string Model { get; init; } = string.Empty; - - /// - /// Gets the system ID to use for the request. - /// - public string? System { get; init; } = null; - - /// - /// Gets the messages to send to the model. - /// - public List Messages { get; init; } = []; - - /// - /// Gets the maximum number of tokens to generate. - /// - [JsonPropertyName("max_tokens")] - public int MaxTokens { get; init; } = 1024; - - /// - /// Gets the metadata to include with the request. - /// - public Dictionary? Metadata { get; init; } = null; - - /// - /// Gets the prompt stop sequences. - /// - [JsonPropertyName("stop_sequences")] - public List StopSequences { get; init; } = []; - - /// - /// Gets the temperature to use for the request. - /// - public decimal Temperature { get; init; } = 0.0m; - - /// - /// Gets the top-K value to use for the request. - /// - public int? TopK { get; init; } = null; - - /// - /// Gets the top-P value to use for the request. - /// - public decimal? TopP { get; init; } = null; - - /// - /// Gets the tool choice mode to use for the request. - /// - [JsonPropertyName("tool_choice")] - public ToolChoice? ToolChoice { get; init; } = null; - - /// - /// Gets the tools to use for the request. - /// - public List? Tools { get; init; } = null; - - /// - /// Gets a value indicating whether the message should be streamed. - /// - public bool Stream { get; init; } - [JsonConstructor] - internal MessageRequest() { } + internal MessageRequest() : base() { } /// /// Initializes a new instance of the class. @@ -88,16 +23,15 @@ public abstract class MessageRequest /// 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. /// 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 MessageRequest( + public MessageRequest( string model, - List messages, + List messages, int maxTokens = 1024, string? system = null, Dictionary? metadata = null, @@ -105,43 +39,20 @@ public abstract class MessageRequest int? topK = null, decimal? topP = null, ToolChoice? toolChoice = null, - List? tools = null, - bool stream = false + List? tools = null + ) : base( + model, + messages, + maxTokens, + system, + metadata, + temperature, + topK, + topP, + toolChoice, + tools, + 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; - Stream = stream; } } \ No newline at end of file diff --git a/src/AnthropicClient/Models/ChatResponse.cs b/src/AnthropicClient/Models/MessageResponse.cs similarity index 60% rename from src/AnthropicClient/Models/ChatResponse.cs rename to src/AnthropicClient/Models/MessageResponse.cs index 6f61fad..184d4cc 100644 --- a/src/AnthropicClient/Models/ChatResponse.cs +++ b/src/AnthropicClient/Models/MessageResponse.cs @@ -3,54 +3,54 @@ using System.Text.Json.Serialization; namespace AnthropicClient.Models; /// -/// Represents a chat response. +/// Represents a response. /// -public class ChatResponse +public class MessageResponse { /// - /// Gets the ID of the chat response. + /// Gets the ID of the response. /// public string Id { get; init; } = string.Empty; /// - /// Gets the model used for the chat response. + /// Gets the model used for the response. /// public string Model { get; init; } = string.Empty; /// - /// Gets the role of the chat response. + /// Gets the role of the response. /// public string Role { get; init; } = string.Empty; /// - /// Gets the stop reason of the chat response. + /// Gets the stop reason of the response. /// [JsonPropertyName("stop_reason")] public string? StopReason { get; init; } /// - /// Gets the stop sequence of the chat response. + /// Gets the stop sequence of the response. /// [JsonPropertyName("stop_sequence")] public string? StopSequence { get; init; } /// - /// Gets the type of the chat response. + /// Gets the type of the response. /// public string Type { get; init; } = string.Empty; /// - /// Gets the usage of the chat response. + /// Gets the usage of the response. /// - public ChatUsage Usage { get; init; } = new(); + public Usage Usage { get; init; } = new(); /// - /// Gets the contents of the chat response. + /// Gets the contents of the response. /// public List Content { get; init; } = []; /// - /// Gets the tool call of the chat response. If the chat response does not contain a tool call, this property is null. + /// Gets the tool call of the response. If the response does not contain a tool call, this property is null. /// [JsonIgnore] public ToolCall? ToolCall { get; set; } = null; diff --git a/src/AnthropicClient/Models/MessageStartEventData.cs b/src/AnthropicClient/Models/MessageStartEventData.cs index f095332..a5ee5b0 100644 --- a/src/AnthropicClient/Models/MessageStartEventData.cs +++ b/src/AnthropicClient/Models/MessageStartEventData.cs @@ -10,7 +10,7 @@ public class MessageStartEventData : EventData /// /// Gets the message. /// - public ChatResponse Message { get; init; } = new(); + public MessageResponse Message { get; init; } = new(); [JsonConstructor] internal MessageStartEventData() : base(EventType.MessageStart) @@ -22,7 +22,7 @@ public class MessageStartEventData : EventData /// /// The message. /// A new instance of the class. - public MessageStartEventData(ChatResponse message) : base(EventType.MessageStart) + public MessageStartEventData(MessageResponse message) : base(EventType.MessageStart) { Message = message; } diff --git a/src/AnthropicClient/Models/StreamChatMessageRequest.cs b/src/AnthropicClient/Models/StreamChatMessageRequest.cs deleted file mode 100644 index bcf50fb..0000000 --- a/src/AnthropicClient/Models/StreamChatMessageRequest.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System.Text.Json.Serialization; - -namespace AnthropicClient.Models; - -/// -/// Represents a chat message request. -/// -public class StreamChatMessageRequest : MessageRequest -{ - [JsonConstructor] - internal StreamChatMessageRequest() : base() { } - - /// - /// 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. - /// 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. - public StreamChatMessageRequest( - 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 - ) : base( - model, - messages, - maxTokens, - system, - metadata, - temperature, - topK, - topP, - toolChoice, - tools, - true - ) - { - } -} \ No newline at end of file diff --git a/src/AnthropicClient/Models/ChatMessageRequest.cs b/src/AnthropicClient/Models/StreamMessageRequest.cs similarity index 83% rename from src/AnthropicClient/Models/ChatMessageRequest.cs rename to src/AnthropicClient/Models/StreamMessageRequest.cs index 895565a..64f7437 100644 --- a/src/AnthropicClient/Models/ChatMessageRequest.cs +++ b/src/AnthropicClient/Models/StreamMessageRequest.cs @@ -3,15 +3,15 @@ using System.Text.Json.Serialization; namespace AnthropicClient.Models; /// -/// Represents a chat message request. +/// Represents a message request. /// -public class ChatMessageRequest : MessageRequest +public class StreamMessageRequest : BaseMessageRequest { [JsonConstructor] - internal ChatMessageRequest() : base() { } + internal StreamMessageRequest() : base() { } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The model ID to use for the request. /// The messages to send to the model. @@ -28,10 +28,10 @@ public class ChatMessageRequest : MessageRequest /// 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. - public ChatMessageRequest( + /// A new instance of the class. + public StreamMessageRequest( string model, - List messages, + List messages, int maxTokens = 1024, string? system = null, Dictionary? metadata = null, @@ -51,7 +51,7 @@ public class ChatMessageRequest : MessageRequest topP, toolChoice, tools, - false + true ) { } diff --git a/src/AnthropicClient/Models/TextContent.cs b/src/AnthropicClient/Models/TextContent.cs index 1cf44b3..b68a1a3 100644 --- a/src/AnthropicClient/Models/TextContent.cs +++ b/src/AnthropicClient/Models/TextContent.cs @@ -5,7 +5,7 @@ using AnthropicClient.Utils; namespace AnthropicClient.Models; /// -/// Represents text content that is part of a chat message. +/// Represents text content that is part of a message. /// public class TextContent : Content { diff --git a/src/AnthropicClient/Models/Tool.cs b/src/AnthropicClient/Models/Tool.cs index 5476436..b207e48 100644 --- a/src/AnthropicClient/Models/Tool.cs +++ b/src/AnthropicClient/Models/Tool.cs @@ -29,7 +29,7 @@ public interface ITool } /// -/// Represents a tool that can be used in the chat. +/// Represents a tool that can be used. /// public class Tool { diff --git a/src/AnthropicClient/Models/ToolCall.cs b/src/AnthropicClient/Models/ToolCall.cs index 824a317..5665214 100644 --- a/src/AnthropicClient/Models/ToolCall.cs +++ b/src/AnthropicClient/Models/ToolCall.cs @@ -1,6 +1,5 @@ using System.Reflection; using System.Text.Json; -using System.Threading.Tasks; using AnthropicClient.Json; diff --git a/src/AnthropicClient/Models/ToolResultContent.cs b/src/AnthropicClient/Models/ToolResultContent.cs index b49f918..1ada98c 100644 --- a/src/AnthropicClient/Models/ToolResultContent.cs +++ b/src/AnthropicClient/Models/ToolResultContent.cs @@ -5,7 +5,7 @@ using AnthropicClient.Utils; namespace AnthropicClient.Models; /// -/// Represents tool result content that is part of a chat message. +/// Represents tool result content that is part of a message. /// public class ToolResultContent : Content { diff --git a/src/AnthropicClient/Models/ChatUsage.cs b/src/AnthropicClient/Models/Usage.cs similarity index 86% rename from src/AnthropicClient/Models/ChatUsage.cs rename to src/AnthropicClient/Models/Usage.cs index 1dd7749..34310ed 100644 --- a/src/AnthropicClient/Models/ChatUsage.cs +++ b/src/AnthropicClient/Models/Usage.cs @@ -3,9 +3,9 @@ using System.Text.Json.Serialization; namespace AnthropicClient.Models; /// -/// Represents the usage of a chat response. +/// Represents the usage of a response. /// -public class ChatUsage +public class Usage { /// /// Gets the number of input tokens used. diff --git a/tests/AnthropicClient.Tests/.runsettings b/tests/AnthropicClient.Tests/.runsettings new file mode 100644 index 0000000..e45f4af --- /dev/null +++ b/tests/AnthropicClient.Tests/.runsettings @@ -0,0 +1,11 @@ + + + + + + quiet + + + + + \ No newline at end of file diff --git a/tests/AnthropicClient.Tests/Integration/ErrorTestData.cs b/tests/AnthropicClient.Tests/Data/ErrorTestData.cs similarity index 97% rename from tests/AnthropicClient.Tests/Integration/ErrorTestData.cs rename to tests/AnthropicClient.Tests/Data/ErrorTestData.cs index ed20c58..1c3efc3 100644 --- a/tests/AnthropicClient.Tests/Integration/ErrorTestData.cs +++ b/tests/AnthropicClient.Tests/Data/ErrorTestData.cs @@ -1,4 +1,4 @@ -namespace AnthropicClient.Tests.Integration; +namespace AnthropicClient.Tests.Data; public class ErrorTestData : IEnumerable { diff --git a/tests/AnthropicClient.Tests/Integration/EventTestData.cs b/tests/AnthropicClient.Tests/Data/EventTestData.cs similarity index 98% rename from tests/AnthropicClient.Tests/Integration/EventTestData.cs rename to tests/AnthropicClient.Tests/Data/EventTestData.cs index 1ba2a56..5c68347 100644 --- a/tests/AnthropicClient.Tests/Integration/EventTestData.cs +++ b/tests/AnthropicClient.Tests/Data/EventTestData.cs @@ -1,4 +1,4 @@ -namespace AnthropicClient.Tests.Integration; +namespace AnthropicClient.Tests.Data; public class EventTestData : IEnumerable { @@ -21,7 +21,7 @@ public class EventTestData : IEnumerable Role = MessageRole.Assistant, Model = AnthropicModels.Claude3Haiku, StopSequence = null, - Usage = new ChatUsage { InputTokens = 472, OutputTokens = 91 }, + Usage = new Usage { InputTokens = 472, OutputTokens = 91 }, StopReason = "tool_use", Content = [ new TextContent("Okay, let's check the weather for San Francisco, CA:"), @@ -54,14 +54,14 @@ public class EventTestData : IEnumerable Type = EventType.MessageStart, Data = new MessageStartEventData() { - Message = new ChatResponse() + Message = new MessageResponse() { Id = "msg_014p7gG3wDgGV9EUtLvnow3U", Type = "message", Role = "assistant", Model = "claude-3-haiku-20240307", StopSequence = null, - Usage = new ChatUsage() + Usage = new Usage() { InputTokens = 472, OutputTokens = 2, @@ -640,7 +640,7 @@ public class EventTestData : IEnumerable StopReason = "tool_use", StopSequence = null, }, - Usage = new ChatUsage() + Usage = new Usage() { OutputTokens = 89, }, diff --git a/tests/AnthropicClient.Tests/Unit/Utils/JsonSchemaGeneratorTestData.cs b/tests/AnthropicClient.Tests/Data/JsonSchemaGeneratorTestData.cs similarity index 99% rename from tests/AnthropicClient.Tests/Unit/Utils/JsonSchemaGeneratorTestData.cs rename to tests/AnthropicClient.Tests/Data/JsonSchemaGeneratorTestData.cs index b7abfce..58706cc 100644 --- a/tests/AnthropicClient.Tests/Unit/Utils/JsonSchemaGeneratorTestData.cs +++ b/tests/AnthropicClient.Tests/Data/JsonSchemaGeneratorTestData.cs @@ -1,6 +1,4 @@ -using System.Text.Json.Nodes; - -namespace AnthropicClient.Tests.Unit.Utils; +namespace AnthropicClient.Tests.Data; public class JsonSchemaGeneratorTestData : IEnumerable { @@ -340,7 +338,7 @@ public class JsonSchemaGeneratorTestData : IEnumerable } }; - // datetime parameter type + // date time parameter type yield return new object[] { Tool.CreateFromFunction(TestToolName,TestToolDescription,(DateTime date) => date), @@ -363,7 +361,7 @@ public class JsonSchemaGeneratorTestData : IEnumerable } }; - // datetimeoffset parameter type + // date time offset parameter type yield return new object[] { Tool.CreateFromFunction(TestToolName,TestToolDescription,(DateTimeOffset date) => date), diff --git a/tests/AnthropicClient.Tests/EndToEnd/AnthropicApiClientTests.cs b/tests/AnthropicClient.Tests/EndToEnd/AnthropicApiClientTests.cs index 0a05083..03b6775 100644 --- a/tests/AnthropicClient.Tests/EndToEnd/AnthropicApiClientTests.cs +++ b/tests/AnthropicClient.Tests/EndToEnd/AnthropicApiClientTests.cs @@ -3,29 +3,29 @@ namespace AnthropicClient.Tests.EndToEnd; public class ClientTests(ConfigurationFixture configFixture) : EndToEndTest(configFixture) { [Fact] - public async Task CreateChatMessage_WhenCalled_ItShouldReturnChatResponse() + public async Task CreateMessageAsync_WhenCalled_ItShouldReturnResponse() { - var request = new ChatMessageRequest( + var request = new MessageRequest( model: AnthropicModels.Claude3Haiku, messages: [new(MessageRole.User, [new TextContent("Hello!")])] ); - var result = await _client.CreateChatMessageAsync(request); + var result = await _client.CreateMessageAsync(request); result.IsSuccess.Should().BeTrue(); - result.Value.Should().BeOfType(); + result.Value.Should().BeOfType(); result.Value.Content.Should().NotBeNullOrEmpty(); } [Fact] - public async Task CreateChatMessage_WhenCalledWithStreamRequest_ItShouldReturnEvents() + public async Task CreateMessageAsync_WhenCalledWithStreamRequest_ItShouldReturnEvents() { - var request = new StreamChatMessageRequest( + var request = new StreamMessageRequest( model: AnthropicModels.Claude3Haiku, messages: [new(MessageRole.User, [new TextContent("Hello!")])] ); - var response = _client.CreateChatMessageAsync(request); + var response = _client.CreateMessageAsync(request); var events = new List(); @@ -38,14 +38,14 @@ public class ClientTests(ConfigurationFixture configFixture) : EndToEndTest(conf } [Fact] - public async Task CreateChatMessage_WhenCalledWithStreamRequest_ItShouldYieldAMessageCompleteEvent() + public async Task CreateMessageAsync_WhenCalledWithStreamRequest_ItShouldYieldAMessageCompleteEvent() { - var request = new StreamChatMessageRequest( + var request = new StreamMessageRequest( model: AnthropicModels.Claude3Haiku, messages: [new(MessageRole.User, [new TextContent("Hello!")])] ); - var response = _client.CreateChatMessageAsync(request); + var response = _client.CreateMessageAsync(request); await foreach (var e in response) { diff --git a/tests/AnthropicClient.Tests/EndToEnd/HttpClientFixture.cs b/tests/AnthropicClient.Tests/EndToEnd/HttpClientFixture.cs deleted file mode 100644 index 53a632e..0000000 --- a/tests/AnthropicClient.Tests/EndToEnd/HttpClientFixture.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace AnthropicClient.Tests.EndToEnd; - -public class HttpClientFixture -{ - public HttpClient HttpClient { get; } - - public HttpClientFixture() - { - HttpClient = new HttpClient(); - } -} \ No newline at end of file diff --git a/tests/AnthropicClient.Tests/Examples/ExampleAttribute.cs b/tests/AnthropicClient.Tests/Examples/ExampleAttribute.cs new file mode 100644 index 0000000..0442062 --- /dev/null +++ b/tests/AnthropicClient.Tests/Examples/ExampleAttribute.cs @@ -0,0 +1,14 @@ +namespace AnthropicClient.Tests.Examples; + +public class ExampleAttribute : FactAttribute +{ + public ExampleAttribute() + { + var skipExamples = Environment.GetEnvironmentVariable("SKIP_EXAMPLES"); + + if (skipExamples == "true") + { + Skip = "Example"; + } + } +} \ No newline at end of file diff --git a/tests/AnthropicClient.Tests/Examples/Examples.cs b/tests/AnthropicClient.Tests/Examples/Examples.cs new file mode 100644 index 0000000..866f98e --- /dev/null +++ b/tests/AnthropicClient.Tests/Examples/Examples.cs @@ -0,0 +1,108 @@ +#pragma warning disable xUnit1004 + +using Xunit.Abstractions; + +namespace AnthropicClient.Tests.Examples; + +public class Examples(ConfigurationFixture config, ITestOutputHelper console) : IClassFixture +{ + private readonly ITestOutputHelper _console = console; + private readonly AnthropicApiClient _client = new(config.AnthropicApiKey, new()); + + [Example] + public async Task CreateMessage() + { + var response = await _client.CreateMessageAsync(new MessageRequest( + AnthropicModels.Claude3Haiku, + [ + new( + MessageRole.User, + [new TextContent("Please write a haiku about the ocean.")] + ) + ] + )); + + if (response.IsSuccess is false) + { + _console.WriteLine($"Failed to create message"); + _console.WriteLine($"Error Type: {0}", response.Error.Error.Type); + _console.WriteLine($"Error Message: {0}", response.Error.Error.Message); + } + + foreach (var content in response.Value.Content) + { + switch (content) + { + case TextContent textContent: + _console.WriteLine(textContent.Text); + break; + } + } + } + + [Example] + public async Task CreateAndStreamMessage() + { + var events = _client.CreateMessageAsync(new StreamMessageRequest( + AnthropicModels.Claude3Haiku, + [ + new( + MessageRole.User, + [new TextContent("Please write a haiku about the ocean.")] + ) + ] + )); + + var msgBuilder = new StringBuilder(); + + await foreach (var e in events) + { + switch (e.Data) + { + case var data when data is ContentDeltaEventData contentData: + switch (contentData.Delta) + { + case var delta when delta is TextDelta textDelta: + msgBuilder.Append(textDelta.Text); + break; + } + break; + } + } + + _console.WriteLine(msgBuilder.ToString()); + } + + [Example] + public async Task CreateStreamMessageAndGetCompleteMessageResponse() + { + var events = _client.CreateMessageAsync(new StreamMessageRequest( + AnthropicModels.Claude3Haiku, + [ + new( + MessageRole.User, + [new TextContent("Please write a haiku about the ocean.")] + ) + ] + )); + + MessageResponse? response = null; + + await foreach (var e in events) + { + switch (e.Data) + { + case var data when data is MessageCompleteEventData msgData: + response = msgData.Message; + break; + } + } + + var textContent = response?.Content + .OfType() + .Aggregate(new StringBuilder(), (sb, c) => sb.Append(c.Text)) + .ToString(); + + _console.WriteLine(textContent); + } +} \ No newline at end of file diff --git a/tests/AnthropicClient.Tests/EndToEnd/ConfigurationFixture.cs b/tests/AnthropicClient.Tests/Fixtures/ConfigurationFixture.cs similarity index 92% rename from tests/AnthropicClient.Tests/EndToEnd/ConfigurationFixture.cs rename to tests/AnthropicClient.Tests/Fixtures/ConfigurationFixture.cs index e487c71..eab468f 100644 --- a/tests/AnthropicClient.Tests/EndToEnd/ConfigurationFixture.cs +++ b/tests/AnthropicClient.Tests/Fixtures/ConfigurationFixture.cs @@ -1,4 +1,4 @@ -namespace AnthropicClient.Tests.EndToEnd; +namespace AnthropicClient.Tests.Fixtures; public class ConfigurationFixture { diff --git a/tests/AnthropicClient.Tests/Integration/AnthropicApiClientTests.cs b/tests/AnthropicClient.Tests/Integration/AnthropicApiClientTests.cs index 21ae076..93bf560 100644 --- a/tests/AnthropicClient.Tests/Integration/AnthropicApiClientTests.cs +++ b/tests/AnthropicClient.Tests/Integration/AnthropicApiClientTests.cs @@ -4,7 +4,7 @@ public class AnthropicApiClientTests : IntegrationTest { [Theory] [ClassData(typeof(ErrorTestData))] - public async Task CreateChatMessageAsync_WhenCalledAndErrorReturned_ItShouldHandleError( + public async Task CreateMessageAsync_WhenCalledAndErrorReturned_ItShouldHandleError( HttpStatusCode statusCode, string content, Type errorType @@ -18,12 +18,12 @@ public class AnthropicApiClientTests : IntegrationTest content ); - var request = new ChatMessageRequest( + var request = new MessageRequest( model: AnthropicModels.Claude3Haiku, messages: [new(MessageRole.User, [new TextContent("Hello!")])] ); - var result = await Client.CreateChatMessageAsync(request); + var result = await Client.CreateMessageAsync(request); result.IsSuccess.Should().BeFalse(); result.Error.Should().BeOfType(); @@ -33,7 +33,7 @@ public class AnthropicApiClientTests : IntegrationTest } [Fact] - public async Task CreateChatMessageAsync_WhenCalledAndMessageCreatedWithTextContent_ItShouldReturnMessage() + public async Task CreateMessageAsync_WhenCalledAndMessageCreatedWithTextContent_ItShouldReturnMessage() { _mockHttpMessageHandler .WhenCreateMessageRequest() @@ -60,15 +60,15 @@ public class AnthropicApiClientTests : IntegrationTest }" ); - var request = new ChatMessageRequest( + var request = new MessageRequest( model: AnthropicModels.Claude35Sonnet, messages: [new(MessageRole.User, [new TextContent("Hello!")])] ); - var result = await Client.CreateChatMessageAsync(request); + var result = await Client.CreateMessageAsync(request); result.IsSuccess.Should().BeTrue(); - result.Value.Should().BeOfType(); + result.Value.Should().BeOfType(); result.Error.Should().BeNull(); var message = result.Value; @@ -90,7 +90,7 @@ public class AnthropicApiClientTests : IntegrationTest } [Fact] - public async Task CreateChatMessageAsync_WhenCalledAndMessageCreatedWithToolUseContentAndToolIsProvided_ItShouldReturnMessageWithToolCall() + public async Task CreateMessageAsync_WhenCalledAndMessageCreatedWithToolUseContentAndToolIsProvided_ItShouldReturnMessageWithToolCall() { _mockHttpMessageHandler .WhenCreateMessageRequest() @@ -121,7 +121,7 @@ public class AnthropicApiClientTests : IntegrationTest var func = (string ticker) => ticker; - var request = new ChatMessageRequest( + var request = new MessageRequest( model: AnthropicModels.Claude35Sonnet, messages: [ new(MessageRole.User, [new TextContent("Hello!")]), @@ -131,10 +131,10 @@ public class AnthropicApiClientTests : IntegrationTest ] ); - var result = await Client.CreateChatMessageAsync(request); + var result = await Client.CreateMessageAsync(request); result.IsSuccess.Should().BeTrue(); - result.Value.Should().BeOfType(); + result.Value.Should().BeOfType(); result.Error.Should().BeNull(); var message = result.Value; @@ -168,7 +168,7 @@ public class AnthropicApiClientTests : IntegrationTest [Fact] - public async Task CreateChatMessageAsync_WhenCalledAndMessageCreatedWithToolUseButNoToolProvided_ItShouldReturnMessageWithoutToolCall() + public async Task CreateMessageAsync_WhenCalledAndMessageCreatedWithToolUseButNoToolProvided_ItShouldReturnMessageWithoutToolCall() { _mockHttpMessageHandler .WhenCreateMessageRequest() @@ -197,17 +197,17 @@ public class AnthropicApiClientTests : IntegrationTest }" ); - var request = new ChatMessageRequest( + var request = new MessageRequest( model: AnthropicModels.Claude35Sonnet, messages: [ new(MessageRole.User, [new TextContent("Hello!")]), ] ); - var result = await Client.CreateChatMessageAsync(request); + var result = await Client.CreateMessageAsync(request); result.IsSuccess.Should().BeTrue(); - result.Value.Should().BeOfType(); + result.Value.Should().BeOfType(); result.Error.Should().BeNull(); var message = result.Value; @@ -236,7 +236,7 @@ public class AnthropicApiClientTests : IntegrationTest [Theory] [ClassData(typeof(EventTestData))] - public async Task CreateChatMessageAsync_WhenCalledAndMessageIsStreamed_ItShouldHandleAllEventTypes(string eventString, AnthropicEvent anthropicEvent) + public async Task CreateMessageAsync_WhenCalledAndMessageIsStreamed_ItShouldHandleAllEventTypes(string eventString, AnthropicEvent anthropicEvent) { _mockHttpMessageHandler .WhenCreateStreamMessageRequest() @@ -246,21 +246,21 @@ public class AnthropicApiClientTests : IntegrationTest new MemoryStream(Encoding.UTF8.GetBytes(eventString)) ); - var request = new StreamChatMessageRequest( + var request = new StreamMessageRequest( model: AnthropicModels.Claude35Sonnet, messages: [ new(MessageRole.User, [new TextContent("Hello!")]), ] ); - var result = Client.CreateChatMessageAsync(request); + var result = Client.CreateMessageAsync(request); var e = await result.FirstOrDefaultAsync(); e.Should().BeEquivalentTo(anthropicEvent); } [Fact] - public async Task CreateChatMessageAsync_WhenCalledAndMessageIsStreamed_ItShouldReturnAllExpectedEvents() + public async Task CreateMessageAsync_WhenCalledAndMessageIsStreamed_ItShouldReturnAllExpectedEvents() { var eventStream = EventTestData.GetEventStream(); var events = EventTestData.GetAllEvents(); @@ -273,14 +273,14 @@ public class AnthropicApiClientTests : IntegrationTest eventStream ); - var request = new StreamChatMessageRequest( + var request = new StreamMessageRequest( model: AnthropicModels.Claude35Sonnet, messages: [ new(MessageRole.User, [new TextContent("Hello!")]), ] ); - var result = Client.CreateChatMessageAsync(request); + var result = Client.CreateMessageAsync(request); var actualEvents = await result.ToListAsync(); @@ -288,7 +288,7 @@ public class AnthropicApiClientTests : IntegrationTest } [Fact] - public async Task CreateChatMessageAsync_WhenCalledMessageIsStreamedAndToolProvide_ItShouldReturnToolCall() + public async Task CreateMessageAsync_WhenCalledMessageIsStreamedAndToolProvide_ItShouldReturnToolCall() { var eventStream = EventTestData.GetEventStream(); @@ -302,7 +302,7 @@ public class AnthropicApiClientTests : IntegrationTest var getWeather = (string location, string unit) => $"The weather in {location} is 72°{unit}"; - var request = new StreamChatMessageRequest( + var request = new StreamMessageRequest( model: AnthropicModels.Claude35Sonnet, messages: [ new(MessageRole.User, [new TextContent("Hello!")]), @@ -312,7 +312,7 @@ public class AnthropicApiClientTests : IntegrationTest ] ); - var result = Client.CreateChatMessageAsync(request); + var result = Client.CreateMessageAsync(request); var msgCompleteEvent = await result .Where(e => e.Type is EventType.MessageComplete) .FirstAsync(); diff --git a/tests/AnthropicClient.Tests/Integration/IntegrationTest.cs b/tests/AnthropicClient.Tests/Integration/IntegrationTest.cs index 29725ef..efc74a9 100644 --- a/tests/AnthropicClient.Tests/Integration/IntegrationTest.cs +++ b/tests/AnthropicClient.Tests/Integration/IntegrationTest.cs @@ -28,13 +28,13 @@ public static class MockHttpMessageHandlerExtensions { return mockHttpMessageHandler .SetupBaseRequest() - .WithJsonContent(r => r.Stream == false, JsonSerializationOptions.DefaultOptions); + .WithJsonContent(r => r.Stream == false, JsonSerializationOptions.DefaultOptions); } public static MockedRequest WhenCreateStreamMessageRequest(this MockHttpMessageHandler mockHttpMessageHandler) { return mockHttpMessageHandler .SetupBaseRequest() - .WithJsonContent(r => r.Stream == true, JsonSerializationOptions.DefaultOptions); + .WithJsonContent(r => r.Stream == true, JsonSerializationOptions.DefaultOptions); } } \ No newline at end of file diff --git a/tests/AnthropicClient.Tests/Unit/Models/MessageCompleteEventDataTests.cs b/tests/AnthropicClient.Tests/Unit/Models/MessageCompleteEventDataTests.cs index 8b456d7..006a7b9 100644 --- a/tests/AnthropicClient.Tests/Unit/Models/MessageCompleteEventDataTests.cs +++ b/tests/AnthropicClient.Tests/Unit/Models/MessageCompleteEventDataTests.cs @@ -5,7 +5,7 @@ public class MessageCompleteEventDataTests [Fact] public void Constructor_WhenCalled_ItShouldInitializeProperties() { - var expectedMessage = new ChatResponse(); + var expectedMessage = new MessageResponse(); var expectedHeaders = new AnthropicHeaders(); var messageCompleteEventData = new MessageCompleteEventData(expectedMessage, expectedHeaders); diff --git a/tests/AnthropicClient.Tests/Unit/Models/MessageDeltaEventDataTests.cs b/tests/AnthropicClient.Tests/Unit/Models/MessageDeltaEventDataTests.cs index b514fbd..0d22412 100644 --- a/tests/AnthropicClient.Tests/Unit/Models/MessageDeltaEventDataTests.cs +++ b/tests/AnthropicClient.Tests/Unit/Models/MessageDeltaEventDataTests.cs @@ -18,7 +18,7 @@ public class MessageDeltaEventDataTests : SerializationTest public void Constructor_WhenCalled_ItShouldInitializeProperties() { var expectedDelta = new MessageDelta("max_tokens", "max_tokens"); - var expectedUsage = new ChatUsage { InputTokens = 1, OutputTokens = 1 }; + var expectedUsage = new Usage { InputTokens = 1, OutputTokens = 1 }; var messageDeltaEventData = new MessageDeltaEventData(expectedDelta, expectedUsage); @@ -30,7 +30,7 @@ public class MessageDeltaEventDataTests : SerializationTest public void JsonSerialization_WhenSerialized_ItShouldHaveExpectedShape() { var expectedDelta = new MessageDelta("max_tokens", "max_tokens"); - var expectedUsage = new ChatUsage { InputTokens = 1, OutputTokens = 1 }; + var expectedUsage = new Usage { InputTokens = 1, OutputTokens = 1 }; var messageDeltaEventData = new MessageDeltaEventData(expectedDelta, expectedUsage); @@ -43,7 +43,7 @@ public class MessageDeltaEventDataTests : SerializationTest public void JsonDeserialization_WhenDeserialized_ItShouldHaveExpectedValues() { var expectedDelta = new MessageDelta("max_tokens", "max_tokens"); - var expectedUsage = new ChatUsage { InputTokens = 1, OutputTokens = 1 }; + var expectedUsage = new Usage { InputTokens = 1, OutputTokens = 1 }; var messageDeltaEventData = Deserialize(_testJson); diff --git a/tests/AnthropicClient.Tests/Unit/Models/ChatMessageRequestTests.cs b/tests/AnthropicClient.Tests/Unit/Models/MessageRequestTests.cs similarity index 58% rename from tests/AnthropicClient.Tests/Unit/Models/ChatMessageRequestTests.cs rename to tests/AnthropicClient.Tests/Unit/Models/MessageRequestTests.cs index 6afea5c..a1ae3b0 100644 --- a/tests/AnthropicClient.Tests/Unit/Models/ChatMessageRequestTests.cs +++ b/tests/AnthropicClient.Tests/Unit/Models/MessageRequestTests.cs @@ -1,6 +1,6 @@ namespace AnthropicClient.Tests.Unit.Models; -public class ChatMessageRequestTests : SerializationTest +public class MessageRequestTests : SerializationTest { private readonly string _testJson = @"{ ""model"": ""claude-3-sonnet-20240229"", @@ -152,7 +152,7 @@ public class ChatMessageRequestTests : SerializationTest public void Constructor_WhenCalled_ItShouldInitializeProperties() { var model = AnthropicModels.Claude3Sonnet; - var messages = new List { new() }; + var messages = new List { new() }; var maxTokens = 512; var system = "test-system"; var metadata = new Dictionary { ["test"] = "test" }; @@ -162,7 +162,7 @@ public class ChatMessageRequestTests : SerializationTest var toolChoice = new AutoToolChoice(); var tools = new List(); - var chatMessageRequest = new ChatMessageRequest( + var messageRequest = new MessageRequest( model: model, messages: messages, maxTokens: maxTokens, @@ -175,23 +175,23 @@ public class ChatMessageRequestTests : SerializationTest tools: tools ); - chatMessageRequest.Model.Should().Be(model); - chatMessageRequest.Messages.Should().BeSameAs(messages); - chatMessageRequest.MaxTokens.Should().Be(maxTokens); - chatMessageRequest.System.Should().Be(system); - chatMessageRequest.Metadata.Should().BeSameAs(metadata); - chatMessageRequest.Temperature.Should().Be(temperature); - chatMessageRequest.TopK.Should().Be(topK); - chatMessageRequest.TopP.Should().Be(topP); - chatMessageRequest.ToolChoice.Should().Be(toolChoice); - chatMessageRequest.Tools.Should().BeSameAs(tools); - chatMessageRequest.Stream.Should().BeFalse(); + messageRequest.Model.Should().Be(model); + messageRequest.Messages.Should().BeSameAs(messages); + messageRequest.MaxTokens.Should().Be(maxTokens); + messageRequest.System.Should().Be(system); + messageRequest.Metadata.Should().BeSameAs(metadata); + messageRequest.Temperature.Should().Be(temperature); + messageRequest.TopK.Should().Be(topK); + messageRequest.TopP.Should().Be(topP); + messageRequest.ToolChoice.Should().Be(toolChoice); + messageRequest.Tools.Should().BeSameAs(tools); + messageRequest.Stream.Should().BeFalse(); } [Fact] public void Constructor_WhenCalledAndModelIsNull_ItShouldThrowArgumentNullException() { - var action = () => new ChatMessageRequest( + var action = () => new MessageRequest( model: null!, messages: [new()] ); @@ -202,7 +202,7 @@ public class ChatMessageRequestTests : SerializationTest [Fact] public void Constructor_WhenCalledAndMessagesIsNull_ItShouldThrowArgumentNullException() { - var action = () => new ChatMessageRequest( + var action = () => new MessageRequest( model: AnthropicModels.Claude3Sonnet, messages: null! ); @@ -213,7 +213,7 @@ public class ChatMessageRequestTests : SerializationTest [Fact] public void Constructor_WhenCalledAndModelIsInvalid_ItShouldThrowArgumentException() { - var action = () => new ChatMessageRequest( + var action = () => new MessageRequest( model: "invalid-model", messages: [new()] ); @@ -224,7 +224,7 @@ public class ChatMessageRequestTests : SerializationTest [Fact] public void Constructor_WhenCalledAndMessagesIsEmpty_ItShouldThrowArgumentException() { - var action = () => new ChatMessageRequest( + var action = () => new MessageRequest( model: AnthropicModels.Claude3Sonnet, messages: [] ); @@ -235,7 +235,7 @@ public class ChatMessageRequestTests : SerializationTest [Fact] public void Constructor_WhenCalledAndMaxTokensIsInvalid_ItShouldThrowArgumentException() { - var action = () => new ChatMessageRequest( + var action = () => new MessageRequest( model: AnthropicModels.Claude3Sonnet, messages: [new()], maxTokens: 0 @@ -249,7 +249,7 @@ public class ChatMessageRequestTests : SerializationTest [InlineData(2)] public void Constructor_WhenCalledAndTemperatureIsInvalid_ItShouldThrowArgumentException(decimal temperature) { - var action = () => new ChatMessageRequest( + var action = () => new MessageRequest( model: AnthropicModels.Claude3Sonnet, messages: [new()], temperature: temperature @@ -261,7 +261,7 @@ public class ChatMessageRequestTests : SerializationTest [Fact] public void JsonSerialization_WhenSerialized_ItShouldHaveExpectedShape() { - var messages = new List() + var messages = new List() { new() { @@ -283,7 +283,7 @@ public class ChatMessageRequestTests : SerializationTest var toolChoice = new AutoToolChoice(); var tools = new List(); - var chatMessageRequest = new ChatMessageRequest( + var messageRequest = new MessageRequest( model: model, messages: messages, maxTokens: maxTokens, @@ -296,7 +296,7 @@ public class ChatMessageRequestTests : SerializationTest tools: tools ); - var actual = Serialize(chatMessageRequest); + var actual = Serialize(messageRequest); JsonAssert.Equal(_testJson, actual); } @@ -304,71 +304,71 @@ public class ChatMessageRequestTests : SerializationTest [Fact] public void JsonDeserialization_WhenDeserialized_ItShouldHaveExpectedShape() { - var chatMessageRequest = Deserialize(_testJson); + var messageRequest = Deserialize(_testJson); - chatMessageRequest!.Model.Should().Be(AnthropicModels.Claude3Sonnet); - chatMessageRequest.System.Should().Be("test-system"); - chatMessageRequest.Messages.Should().HaveCount(1); - chatMessageRequest.MaxTokens.Should().Be(512); - chatMessageRequest.Metadata.Should().HaveCount(1); + messageRequest!.Model.Should().Be(AnthropicModels.Claude3Sonnet); + messageRequest.System.Should().Be("test-system"); + messageRequest.Messages.Should().HaveCount(1); + messageRequest.MaxTokens.Should().Be(512); + messageRequest.Metadata.Should().HaveCount(1); - var testValue = chatMessageRequest.Metadata!.GetValueOrDefault("test")!.ToString(); + var testValue = messageRequest.Metadata!.GetValueOrDefault("test")!.ToString(); testValue.Should().Be("test"); - chatMessageRequest.Temperature.Should().Be(0.5m); - chatMessageRequest.TopK.Should().Be(10); - chatMessageRequest.TopP.Should().Be(0.5m); - chatMessageRequest.ToolChoice.Should().BeOfType(); - chatMessageRequest.ToolChoice!.Type.Should().Be("auto"); - chatMessageRequest.Tools.Should().HaveCount(0); - chatMessageRequest.Stream.Should().BeFalse(); + messageRequest.Temperature.Should().Be(0.5m); + messageRequest.TopK.Should().Be(10); + messageRequest.TopP.Should().Be(0.5m); + messageRequest.ToolChoice.Should().BeOfType(); + messageRequest.ToolChoice!.Type.Should().Be("auto"); + messageRequest.Tools.Should().HaveCount(0); + messageRequest.Stream.Should().BeFalse(); } [Fact] public void JsonDeserialization_WhenDeserializedWithAnyToolChoice_ItShouldHaveExpectedShape() { - var chatMessageRequest = Deserialize(_testJsonWithAnyToolChoice); + var messageRequest = Deserialize(_testJsonWithAnyToolChoice); - chatMessageRequest!.Model.Should().Be(AnthropicModels.Claude3Sonnet); - chatMessageRequest.System.Should().Be("test-system"); - chatMessageRequest.Messages.Should().HaveCount(1); - chatMessageRequest.MaxTokens.Should().Be(512); - chatMessageRequest.Metadata.Should().HaveCount(1); + messageRequest!.Model.Should().Be(AnthropicModels.Claude3Sonnet); + messageRequest.System.Should().Be("test-system"); + messageRequest.Messages.Should().HaveCount(1); + messageRequest.MaxTokens.Should().Be(512); + messageRequest.Metadata.Should().HaveCount(1); - var testValue = chatMessageRequest.Metadata!.GetValueOrDefault("test")!.ToString(); + var testValue = messageRequest.Metadata!.GetValueOrDefault("test")!.ToString(); testValue.Should().Be("test"); - chatMessageRequest.Temperature.Should().Be(0.5m); - chatMessageRequest.TopK.Should().Be(10); - chatMessageRequest.TopP.Should().Be(0.5m); - chatMessageRequest.ToolChoice.Should().BeOfType(); - chatMessageRequest.ToolChoice!.Type.Should().Be("any"); - chatMessageRequest.Tools.Should().HaveCount(0); + messageRequest.Temperature.Should().Be(0.5m); + messageRequest.TopK.Should().Be(10); + messageRequest.TopP.Should().Be(0.5m); + messageRequest.ToolChoice.Should().BeOfType(); + messageRequest.ToolChoice!.Type.Should().Be("any"); + messageRequest.Tools.Should().HaveCount(0); } [Fact] public void JsonDeserialization_WhenDeserializedWithSpecificToolChoice_ItShouldHaveExpectedShape() { - var chatMessageRequest = Deserialize(_testJsonWithSpecificToolChoice); + var messageRequest = Deserialize(_testJsonWithSpecificToolChoice); - chatMessageRequest!.Model.Should().Be(AnthropicModels.Claude3Sonnet); - chatMessageRequest.System.Should().Be("test-system"); - chatMessageRequest.Messages.Should().HaveCount(1); - chatMessageRequest.MaxTokens.Should().Be(512); - chatMessageRequest.Metadata.Should().HaveCount(1); + messageRequest!.Model.Should().Be(AnthropicModels.Claude3Sonnet); + messageRequest.System.Should().Be("test-system"); + messageRequest.Messages.Should().HaveCount(1); + messageRequest.MaxTokens.Should().Be(512); + messageRequest.Metadata.Should().HaveCount(1); - var testValue = chatMessageRequest.Metadata!.GetValueOrDefault("test")!.ToString(); + var testValue = messageRequest.Metadata!.GetValueOrDefault("test")!.ToString(); testValue.Should().Be("test"); - chatMessageRequest.Temperature.Should().Be(0.5m); - chatMessageRequest.TopK.Should().Be(10); - chatMessageRequest.TopP.Should().Be(0.5m); - chatMessageRequest.ToolChoice.Should().BeOfType(); + messageRequest.Temperature.Should().Be(0.5m); + messageRequest.TopK.Should().Be(10); + messageRequest.TopP.Should().Be(0.5m); + messageRequest.ToolChoice.Should().BeOfType(); - var specificToolChoice = chatMessageRequest.ToolChoice as SpecificToolChoice; + var specificToolChoice = messageRequest.ToolChoice as SpecificToolChoice; specificToolChoice!.Type.Should().Be("tool"); specificToolChoice.Name.Should().Be("test-tool"); - chatMessageRequest.Tools.Should().HaveCount(0); + messageRequest.Tools.Should().HaveCount(0); } [Fact] @@ -376,7 +376,7 @@ public class ChatMessageRequestTests : SerializationTest { var json = @"{""model"":""claude-3-sonnet-20240229"",""system"":""test-system"",""messages"":[{""role"":""user"",""content"":[{""text"":""Hello!"",""type"":""text""}]}],""max_tokens"":512,""metadata"":{""test"":""test""},""stop_sequences"":[],""temperature"":0.5,""topK"":10,""topP"":0.5,""tool_choice"":{""type"":""unknown""},""tools"":[{""name"":""test-tool"",""description"":""test-description"",""input_schema"":{""type"":""object"",""properties"":{""test-property"":{""type"":""string"",""description"":""test-description""}},""required"":[""test-property""]}}],""stream"":false}"; - var action = () => Deserialize(json); + var action = () => Deserialize(json); action.Should().Throw(); } @@ -384,27 +384,27 @@ public class ChatMessageRequestTests : SerializationTest [Fact] public void JsonDeserialization_WhenDeserializedWithImageContent_ItShouldHaveExpectedShape() { - var chatMessageRequest = Deserialize(_testJsonWithImageContent); + var messageRequest = Deserialize(_testJsonWithImageContent); - chatMessageRequest!.Model.Should().Be(AnthropicModels.Claude3Sonnet); - chatMessageRequest.System.Should().Be("test-system"); - chatMessageRequest.Messages.Should().HaveCount(1); - chatMessageRequest.MaxTokens.Should().Be(512); - chatMessageRequest.Metadata.Should().HaveCount(1); + messageRequest!.Model.Should().Be(AnthropicModels.Claude3Sonnet); + messageRequest.System.Should().Be("test-system"); + messageRequest.Messages.Should().HaveCount(1); + messageRequest.MaxTokens.Should().Be(512); + messageRequest.Metadata.Should().HaveCount(1); - var testValue = chatMessageRequest.Metadata!.GetValueOrDefault("test")!.ToString(); + var testValue = messageRequest.Metadata!.GetValueOrDefault("test")!.ToString(); testValue.Should().Be("test"); - chatMessageRequest.Temperature.Should().Be(0.5m); - chatMessageRequest.TopK.Should().Be(10); - chatMessageRequest.TopP.Should().Be(0.5m); - chatMessageRequest.ToolChoice.Should().BeOfType(); - chatMessageRequest.ToolChoice!.Type.Should().Be("auto"); - chatMessageRequest.Tools.Should().HaveCount(0); - chatMessageRequest.Messages[0].Content.Should().HaveCount(1); - chatMessageRequest.Messages[0].Content[0].Should().BeOfType(); + messageRequest.Temperature.Should().Be(0.5m); + messageRequest.TopK.Should().Be(10); + messageRequest.TopP.Should().Be(0.5m); + messageRequest.ToolChoice.Should().BeOfType(); + messageRequest.ToolChoice!.Type.Should().Be("auto"); + messageRequest.Tools.Should().HaveCount(0); + messageRequest.Messages[0].Content.Should().HaveCount(1); + messageRequest.Messages[0].Content[0].Should().BeOfType(); - var imageContent = chatMessageRequest.Messages[0].Content[0] as ImageContent; + var imageContent = messageRequest.Messages[0].Content[0] as ImageContent; imageContent!.Type.Should().Be("image"); imageContent.Source.MediaType.Should().Be("image/jpeg"); imageContent.Source.Data.Should().Be("data"); @@ -413,27 +413,27 @@ public class ChatMessageRequestTests : SerializationTest [Fact] public void JsonDeserialization_WhenDeserializedWithToolUseContent_ItShouldHaveExpectedShape() { - var chatMessageRequest = Deserialize(_testJsonWithToolUseContent); + var messageRequest = Deserialize(_testJsonWithToolUseContent); - chatMessageRequest!.Model.Should().Be(AnthropicModels.Claude3Sonnet); - chatMessageRequest.System.Should().Be("test-system"); - chatMessageRequest.Messages.Should().HaveCount(1); - chatMessageRequest.MaxTokens.Should().Be(512); - chatMessageRequest.Metadata.Should().HaveCount(1); + messageRequest!.Model.Should().Be(AnthropicModels.Claude3Sonnet); + messageRequest.System.Should().Be("test-system"); + messageRequest.Messages.Should().HaveCount(1); + messageRequest.MaxTokens.Should().Be(512); + messageRequest.Metadata.Should().HaveCount(1); - var testValue = chatMessageRequest.Metadata!.GetValueOrDefault("test")!.ToString(); + var testValue = messageRequest.Metadata!.GetValueOrDefault("test")!.ToString(); testValue.Should().Be("test"); - chatMessageRequest.Temperature.Should().Be(0.5m); - chatMessageRequest.TopK.Should().Be(10); - chatMessageRequest.TopP.Should().Be(0.5m); - chatMessageRequest.ToolChoice.Should().BeOfType(); - chatMessageRequest.ToolChoice!.Type.Should().Be("auto"); - chatMessageRequest.Tools.Should().HaveCount(0); - chatMessageRequest.Messages[0].Content.Should().HaveCount(1); - chatMessageRequest.Messages[0].Content[0].Should().BeOfType(); + messageRequest.Temperature.Should().Be(0.5m); + messageRequest.TopK.Should().Be(10); + messageRequest.TopP.Should().Be(0.5m); + messageRequest.ToolChoice.Should().BeOfType(); + messageRequest.ToolChoice!.Type.Should().Be("auto"); + messageRequest.Tools.Should().HaveCount(0); + messageRequest.Messages[0].Content.Should().HaveCount(1); + messageRequest.Messages[0].Content[0].Should().BeOfType(); - var toolUseContent = chatMessageRequest.Messages[0].Content[0] as ToolUseContent; + var toolUseContent = messageRequest.Messages[0].Content[0] as ToolUseContent; toolUseContent!.Type.Should().Be("tool_use"); toolUseContent.Name.Should().Be("test-tool"); toolUseContent.Id.Should().Be("test-tool-id"); @@ -444,27 +444,27 @@ public class ChatMessageRequestTests : SerializationTest [Fact] public void JsonDeserialization_WhenDeserializedWithToolResultContent_ItShouldHaveExpectedShape() { - var chatMessageRequest = Deserialize(_testJsonWithToolResultContent); + var messageRequest = Deserialize(_testJsonWithToolResultContent); - chatMessageRequest!.Model.Should().Be(AnthropicModels.Claude3Sonnet); - chatMessageRequest.System.Should().Be("test-system"); - chatMessageRequest.Messages.Should().HaveCount(1); - chatMessageRequest.MaxTokens.Should().Be(512); - chatMessageRequest.Metadata.Should().HaveCount(1); + messageRequest!.Model.Should().Be(AnthropicModels.Claude3Sonnet); + messageRequest.System.Should().Be("test-system"); + messageRequest.Messages.Should().HaveCount(1); + messageRequest.MaxTokens.Should().Be(512); + messageRequest.Metadata.Should().HaveCount(1); - var testValue = chatMessageRequest.Metadata!.GetValueOrDefault("test")!.ToString(); + var testValue = messageRequest.Metadata!.GetValueOrDefault("test")!.ToString(); testValue.Should().Be("test"); - chatMessageRequest.Temperature.Should().Be(0.5m); - chatMessageRequest.TopK.Should().Be(10); - chatMessageRequest.TopP.Should().Be(0.5m); - chatMessageRequest.ToolChoice.Should().BeOfType(); - chatMessageRequest.ToolChoice!.Type.Should().Be("auto"); - chatMessageRequest.Tools.Should().HaveCount(0); - chatMessageRequest.Messages[0].Content.Should().HaveCount(1); - chatMessageRequest.Messages[0].Content[0].Should().BeOfType(); + messageRequest.Temperature.Should().Be(0.5m); + messageRequest.TopK.Should().Be(10); + messageRequest.TopP.Should().Be(0.5m); + messageRequest.ToolChoice.Should().BeOfType(); + messageRequest.ToolChoice!.Type.Should().Be("auto"); + messageRequest.Tools.Should().HaveCount(0); + messageRequest.Messages[0].Content.Should().HaveCount(1); + messageRequest.Messages[0].Content[0].Should().BeOfType(); - var toolResultContent = chatMessageRequest.Messages[0].Content[0] as ToolResultContent; + var toolResultContent = messageRequest.Messages[0].Content[0] as ToolResultContent; toolResultContent!.Type.Should().Be("tool_result"); toolResultContent.ToolUseId.Should().Be("test-tool"); toolResultContent.Content.Should().Be("test-value"); @@ -473,7 +473,7 @@ public class ChatMessageRequestTests : SerializationTest [Fact] public void JsonDeserialization_WhenDeserializedWithUnknownContent_ItShouldThrowJsonException() { - var action = () => Deserialize(_testJsonWithUnknownContent); + var action = () => Deserialize(_testJsonWithUnknownContent); action.Should().Throw(); } diff --git a/tests/AnthropicClient.Tests/Unit/Models/ChatResponseTests.cs b/tests/AnthropicClient.Tests/Unit/Models/MessageResponseTests.cs similarity index 63% rename from tests/AnthropicClient.Tests/Unit/Models/ChatResponseTests.cs rename to tests/AnthropicClient.Tests/Unit/Models/MessageResponseTests.cs index 38a9b78..e2bf053 100644 --- a/tests/AnthropicClient.Tests/Unit/Models/ChatResponseTests.cs +++ b/tests/AnthropicClient.Tests/Unit/Models/MessageResponseTests.cs @@ -1,6 +1,6 @@ namespace AnthropicClient.Tests.Unit.Models; -public class ChatResponseTests : SerializationTest +public class MessageResponseTests : SerializationTest { [Fact] public void Constructor_WhenCalled_ShouldInitializeProperties() @@ -11,7 +11,7 @@ public class ChatResponseTests : SerializationTest var expectedStopReason = "stop reason"; var expectedStopSequence = "stop sequence"; var expectedType = "type"; - var expectedUsage = new ChatUsage + var expectedUsage = new Usage { InputTokens = 1, OutputTokens = 2 @@ -21,7 +21,7 @@ public class ChatResponseTests : SerializationTest new TextContent("text content"), }; - var chatResponse = new ChatResponse + var messageResponse = new MessageResponse { Id = expectedId, Model = expectedModel, @@ -33,14 +33,14 @@ public class ChatResponseTests : SerializationTest Content = expectedContent }; - chatResponse.Id.Should().Be(expectedId); - chatResponse.Model.Should().Be(expectedModel); - chatResponse.Role.Should().Be(expectedRole); - chatResponse.StopReason.Should().Be(expectedStopReason); - chatResponse.StopSequence.Should().Be(expectedStopSequence); - chatResponse.Type.Should().Be(expectedType); - chatResponse.Usage.Should().BeEquivalentTo(expectedUsage); - chatResponse.Content.Should().BeEquivalentTo(expectedContent); + messageResponse.Id.Should().Be(expectedId); + messageResponse.Model.Should().Be(expectedModel); + messageResponse.Role.Should().Be(expectedRole); + messageResponse.StopReason.Should().Be(expectedStopReason); + messageResponse.StopSequence.Should().Be(expectedStopSequence); + messageResponse.Type.Should().Be(expectedType); + messageResponse.Usage.Should().BeEquivalentTo(expectedUsage); + messageResponse.Content.Should().BeEquivalentTo(expectedContent); } [Fact] @@ -59,7 +59,7 @@ public class ChatResponseTests : SerializationTest ] }"; - var chatResponse = new ChatResponse + var messageResponse = new MessageResponse { Id = "id", Model = "model", @@ -67,7 +67,7 @@ public class ChatResponseTests : SerializationTest StopReason = "stop reason", StopSequence = "stop sequence", Type = "type", - Usage = new ChatUsage + Usage = new Usage { InputTokens = 1, OutputTokens = 2 @@ -78,7 +78,7 @@ public class ChatResponseTests : SerializationTest ] }; - var actual = Serialize(chatResponse); + var actual = Serialize(messageResponse); JsonAssert.Equal(expectedJson, actual); } @@ -99,20 +99,20 @@ public class ChatResponseTests : SerializationTest ] }"; - var chatResponse = Deserialize(json); + var messageResponse = Deserialize(json); - chatResponse!.Id.Should().Be("id"); - chatResponse.Model.Should().Be("model"); - chatResponse.Role.Should().Be("role"); - chatResponse.StopReason.Should().Be("stop reason"); - chatResponse.StopSequence.Should().Be("stop sequence"); - chatResponse.Type.Should().Be("type"); - chatResponse.Usage.Should().BeEquivalentTo(new ChatUsage + messageResponse!.Id.Should().Be("id"); + messageResponse.Model.Should().Be("model"); + messageResponse.Role.Should().Be("role"); + messageResponse.StopReason.Should().Be("stop reason"); + messageResponse.StopSequence.Should().Be("stop sequence"); + messageResponse.Type.Should().Be("type"); + messageResponse.Usage.Should().BeEquivalentTo(new Usage { InputTokens = 1, OutputTokens = 2 }); - chatResponse.Content.Should().BeEquivalentTo(new List + messageResponse.Content.Should().BeEquivalentTo(new List { new TextContent("text content"), }); diff --git a/tests/AnthropicClient.Tests/Unit/Models/MessageStartEventDataTests.cs b/tests/AnthropicClient.Tests/Unit/Models/MessageStartEventDataTests.cs index 2df48d1..3a550d8 100644 --- a/tests/AnthropicClient.Tests/Unit/Models/MessageStartEventDataTests.cs +++ b/tests/AnthropicClient.Tests/Unit/Models/MessageStartEventDataTests.cs @@ -22,7 +22,7 @@ public class MessageStartEventDataTests : SerializationTest [Fact] public void Constructor_WhenCalled_ItShouldInitializeProperties() { - var expectedMessage = new ChatResponse + var expectedMessage = new MessageResponse { Id = "msg_1nZdL29xx5MUA1yADyHTEsnR8uuvGzszyY", Type = "message", @@ -31,7 +31,7 @@ public class MessageStartEventDataTests : SerializationTest Model = "claude-3-5-sonnet-20240620", StopReason = string.Empty, StopSequence = string.Empty, - Usage = new ChatUsage { InputTokens = 25, OutputTokens = 1 } + Usage = new Usage { InputTokens = 25, OutputTokens = 1 } }; var messageStartEventData = new MessageStartEventData(expectedMessage); @@ -42,7 +42,7 @@ public class MessageStartEventDataTests : SerializationTest [Fact] public void JsonSerialization_WhenSerialized_ItShouldHaveExpectedShape() { - var expectedMessage = new ChatResponse + var expectedMessage = new MessageResponse { Id = "msg_1nZdL29xx5MUA1yADyHTEsnR8uuvGzszyY", Type = "message", @@ -51,7 +51,7 @@ public class MessageStartEventDataTests : SerializationTest Model = "claude-3-5-sonnet-20240620", StopReason = string.Empty, StopSequence = string.Empty, - Usage = new ChatUsage { InputTokens = 25, OutputTokens = 1 } + Usage = new Usage { InputTokens = 25, OutputTokens = 1 } }; var messageStartEventData = new MessageStartEventData(expectedMessage); @@ -64,7 +64,7 @@ public class MessageStartEventDataTests : SerializationTest [Fact] public void JsonDeserialization_WhenDeserialized_ItShouldHaveExpectedValues() { - var expectedMessage = new ChatResponse + var expectedMessage = new MessageResponse { Id = "msg_1nZdL29xx5MUA1yADyHTEsnR8uuvGzszyY", Type = "message", @@ -73,7 +73,7 @@ public class MessageStartEventDataTests : SerializationTest Model = "claude-3-5-sonnet-20240620", StopReason = string.Empty, StopSequence = string.Empty, - Usage = new ChatUsage { InputTokens = 25, OutputTokens = 1 } + Usage = new Usage { InputTokens = 25, OutputTokens = 1 } }; var messageStartEventData = Deserialize(_testJson); diff --git a/tests/AnthropicClient.Tests/Unit/Models/ChatMessageTests.cs b/tests/AnthropicClient.Tests/Unit/Models/MessageTests.cs similarity index 62% rename from tests/AnthropicClient.Tests/Unit/Models/ChatMessageTests.cs rename to tests/AnthropicClient.Tests/Unit/Models/MessageTests.cs index 7554db7..47dd82b 100644 --- a/tests/AnthropicClient.Tests/Unit/Models/ChatMessageTests.cs +++ b/tests/AnthropicClient.Tests/Unit/Models/MessageTests.cs @@ -1,6 +1,6 @@ namespace AnthropicClient.Tests.Unit.Models; -public class ChatMessageTests : SerializationTest +public class MessageTests : SerializationTest { private readonly string _testJson = @"{ ""role"": ""assistant"", @@ -15,10 +15,10 @@ public class ChatMessageTests : SerializationTest var role = "assistant"; var content = new List { new TextContent("text") }; - var chatMessage = new ChatMessage(role, content); + var message = new Message(role, content); - chatMessage.Role.Should().Be(role); - chatMessage.Content.Should().BeSameAs(content); + message.Role.Should().Be(role); + message.Content.Should().BeSameAs(content); } [Fact] @@ -26,7 +26,7 @@ public class ChatMessageTests : SerializationTest { var content = new List { new TextContent("text") }; - var action = () => new ChatMessage(null!, content); + var action = () => new Message(null!, content); action.Should().Throw(); } @@ -36,7 +36,7 @@ public class ChatMessageTests : SerializationTest { var role = "assistant"; - var action = () => new ChatMessage(role, null!); + var action = () => new Message(role, null!); action.Should().Throw(); } @@ -47,7 +47,7 @@ public class ChatMessageTests : SerializationTest var role = "invalid"; var content = new List { new TextContent("text") }; - var action = () => new ChatMessage(role, content); + var action = () => new Message(role, content); action.Should().Throw(); } @@ -57,22 +57,22 @@ public class ChatMessageTests : SerializationTest { var role = "assistant"; var content = new List { new TextContent("text") }; - var chatMessage = new ChatMessage(role, content); + var message = new Message(role, content); - var actual = Serialize(chatMessage); + var actual = Serialize(message); JsonAssert.Equal(_testJson, actual); } [Fact] - public void JsonDeserialization_WhenDeserialized_ItShouldReturnChatMessage() + public void JsonDeserialization_WhenDeserialized_ItShouldReturnMessage() { - var chatMessage = Deserialize(_testJson); + var message = Deserialize(_testJson); - chatMessage.Should().NotBeNull(); - chatMessage!.Role.Should().Be("assistant"); - chatMessage.Content.Should().HaveCount(1); - chatMessage.Content[0].Should().BeOfType(); - chatMessage.Content[0].As().Text.Should().Be("text"); + message.Should().NotBeNull(); + message!.Role.Should().Be("assistant"); + message.Content.Should().HaveCount(1); + message.Content[0].Should().BeOfType(); + message.Content[0].As().Text.Should().Be("text"); } } \ No newline at end of file diff --git a/tests/AnthropicClient.Tests/Unit/Models/StreamChatMessageRequestTests.cs b/tests/AnthropicClient.Tests/Unit/Models/StreamMessageRequestTests.cs similarity index 63% rename from tests/AnthropicClient.Tests/Unit/Models/StreamChatMessageRequestTests.cs rename to tests/AnthropicClient.Tests/Unit/Models/StreamMessageRequestTests.cs index c2fd576..16c1274 100644 --- a/tests/AnthropicClient.Tests/Unit/Models/StreamChatMessageRequestTests.cs +++ b/tests/AnthropicClient.Tests/Unit/Models/StreamMessageRequestTests.cs @@ -1,6 +1,6 @@ namespace AnthropicClient.Tests.Unit.Models; -public class StreamChatMessageRequestTests : SerializationTest +public class StreamMessageRequestTests : SerializationTest { private readonly string _testJson = @"{ ""model"": ""claude-3-sonnet-20240229"", @@ -23,7 +23,7 @@ public class StreamChatMessageRequestTests : SerializationTest public void Constructor_WhenCalled_ItShouldInitializeProperties() { var model = AnthropicModels.Claude3Sonnet; - var messages = new List { new() }; + var messages = new List { new() }; var maxTokens = 512; var system = "test-system"; var metadata = new Dictionary { ["test"] = "test" }; @@ -33,7 +33,7 @@ public class StreamChatMessageRequestTests : SerializationTest var toolChoice = new AutoToolChoice(); var tools = new List(); - var chatMessageRequest = new StreamChatMessageRequest( + var messageRequest = new StreamMessageRequest( model: model, messages: messages, maxTokens: maxTokens, @@ -46,23 +46,23 @@ public class StreamChatMessageRequestTests : SerializationTest tools: tools ); - chatMessageRequest.Model.Should().Be(model); - chatMessageRequest.Messages.Should().BeSameAs(messages); - chatMessageRequest.MaxTokens.Should().Be(maxTokens); - chatMessageRequest.System.Should().Be(system); - chatMessageRequest.Metadata.Should().BeSameAs(metadata); - chatMessageRequest.Temperature.Should().Be(temperature); - chatMessageRequest.TopK.Should().Be(topK); - chatMessageRequest.TopP.Should().Be(topP); - chatMessageRequest.ToolChoice.Should().Be(toolChoice); - chatMessageRequest.Tools.Should().BeSameAs(tools); - chatMessageRequest.Stream.Should().BeTrue(); + messageRequest.Model.Should().Be(model); + messageRequest.Messages.Should().BeSameAs(messages); + messageRequest.MaxTokens.Should().Be(maxTokens); + messageRequest.System.Should().Be(system); + messageRequest.Metadata.Should().BeSameAs(metadata); + messageRequest.Temperature.Should().Be(temperature); + messageRequest.TopK.Should().Be(topK); + messageRequest.TopP.Should().Be(topP); + messageRequest.ToolChoice.Should().Be(toolChoice); + messageRequest.Tools.Should().BeSameAs(tools); + messageRequest.Stream.Should().BeTrue(); } [Fact] public void Constructor_WhenCalledAndModelIsNull_ItShouldThrowArgumentNullException() { - var action = () => new StreamChatMessageRequest( + var action = () => new StreamMessageRequest( model: null!, messages: [new()] ); @@ -73,7 +73,7 @@ public class StreamChatMessageRequestTests : SerializationTest [Fact] public void Constructor_WhenCalledAndMessagesIsNull_ItShouldThrowArgumentNullException() { - var action = () => new StreamChatMessageRequest( + var action = () => new StreamMessageRequest( model: AnthropicModels.Claude3Sonnet, messages: null! ); @@ -84,7 +84,7 @@ public class StreamChatMessageRequestTests : SerializationTest [Fact] public void Constructor_WhenCalledAndModelIsInvalid_ItShouldThrowArgumentException() { - var action = () => new StreamChatMessageRequest( + var action = () => new StreamMessageRequest( model: "invalid-model", messages: [new()] ); @@ -95,7 +95,7 @@ public class StreamChatMessageRequestTests : SerializationTest [Fact] public void Constructor_WhenCalledAndMessagesIsEmpty_ItShouldThrowArgumentException() { - var action = () => new StreamChatMessageRequest( + var action = () => new StreamMessageRequest( model: AnthropicModels.Claude3Sonnet, messages: [] ); @@ -106,7 +106,7 @@ public class StreamChatMessageRequestTests : SerializationTest [Fact] public void Constructor_WhenCalledAndMaxTokensIsInvalid_ItShouldThrowArgumentException() { - var action = () => new StreamChatMessageRequest( + var action = () => new StreamMessageRequest( model: AnthropicModels.Claude3Sonnet, messages: [new()], maxTokens: 0 @@ -120,7 +120,7 @@ public class StreamChatMessageRequestTests : SerializationTest [InlineData(2)] public void Constructor_WhenCalledAndTemperatureIsInvalid_ItShouldThrowArgumentException(decimal temperature) { - var action = () => new StreamChatMessageRequest( + var action = () => new StreamMessageRequest( model: AnthropicModels.Claude3Sonnet, messages: [new()], temperature: temperature @@ -132,7 +132,7 @@ public class StreamChatMessageRequestTests : SerializationTest [Fact] public void JsonSerialization_WhenSerialized_ItShouldHaveExpectedShape() { - var messages = new List() + var messages = new List() { new() { @@ -154,7 +154,7 @@ public class StreamChatMessageRequestTests : SerializationTest var toolChoice = new AutoToolChoice(); var tools = new List(); - var chatMessageRequest = new StreamChatMessageRequest( + var messageRequest = new StreamMessageRequest( model: model, messages: messages, maxTokens: maxTokens, @@ -167,7 +167,7 @@ public class StreamChatMessageRequestTests : SerializationTest tools: tools ); - var actual = Serialize(chatMessageRequest); + var actual = Serialize(messageRequest); JsonAssert.Equal(_testJson, actual); } @@ -175,23 +175,23 @@ public class StreamChatMessageRequestTests : SerializationTest [Fact] public void JsonDeserialization_WhenDeserialized_ItShouldHaveExpectedShape() { - var chatMessageRequest = Deserialize(_testJson); + var messageRequest = Deserialize(_testJson); - chatMessageRequest!.Model.Should().Be(AnthropicModels.Claude3Sonnet); - chatMessageRequest.System.Should().Be("test-system"); - chatMessageRequest.Messages.Should().HaveCount(1); - chatMessageRequest.MaxTokens.Should().Be(512); - chatMessageRequest.Metadata.Should().HaveCount(1); + messageRequest!.Model.Should().Be(AnthropicModels.Claude3Sonnet); + messageRequest.System.Should().Be("test-system"); + messageRequest.Messages.Should().HaveCount(1); + messageRequest.MaxTokens.Should().Be(512); + messageRequest.Metadata.Should().HaveCount(1); - var testValue = chatMessageRequest.Metadata!.GetValueOrDefault("test")!.ToString(); + var testValue = messageRequest.Metadata!.GetValueOrDefault("test")!.ToString(); testValue.Should().Be("test"); - chatMessageRequest.Temperature.Should().Be(0.5m); - chatMessageRequest.TopK.Should().Be(10); - chatMessageRequest.TopP.Should().Be(0.5m); - chatMessageRequest.ToolChoice.Should().BeOfType(); - chatMessageRequest.ToolChoice!.Type.Should().Be("auto"); - chatMessageRequest.Tools.Should().HaveCount(0); - chatMessageRequest.Stream.Should().BeTrue(); + messageRequest.Temperature.Should().Be(0.5m); + messageRequest.TopK.Should().Be(10); + messageRequest.TopP.Should().Be(0.5m); + messageRequest.ToolChoice.Should().BeOfType(); + messageRequest.ToolChoice!.Type.Should().Be("auto"); + messageRequest.Tools.Should().HaveCount(0); + messageRequest.Stream.Should().BeTrue(); } } \ No newline at end of file diff --git a/tests/AnthropicClient.Tests/Unit/Models/ChatUsageTests.cs b/tests/AnthropicClient.Tests/Unit/Models/UsageTests.cs similarity index 63% rename from tests/AnthropicClient.Tests/Unit/Models/ChatUsageTests.cs rename to tests/AnthropicClient.Tests/Unit/Models/UsageTests.cs index aae5572..345527a 100644 --- a/tests/AnthropicClient.Tests/Unit/Models/ChatUsageTests.cs +++ b/tests/AnthropicClient.Tests/Unit/Models/UsageTests.cs @@ -1,6 +1,6 @@ namespace AnthropicClient.Tests.Unit.Models; -public class ChatUsageTests : SerializationTest +public class UsageTests : SerializationTest { [Fact] public void Constructor_WhenCalled_ShouldInitializeProperties() @@ -8,14 +8,14 @@ public class ChatUsageTests : SerializationTest var expectedInputTokens = 1; var expectedOutputTokens = 2; - var chatUsage = new ChatUsage + var usage = new Usage { InputTokens = expectedInputTokens, OutputTokens = expectedOutputTokens }; - chatUsage.InputTokens.Should().Be(expectedInputTokens); - chatUsage.OutputTokens.Should().Be(expectedOutputTokens); + usage.InputTokens.Should().Be(expectedInputTokens); + usage.OutputTokens.Should().Be(expectedOutputTokens); } [Fact] @@ -23,13 +23,13 @@ public class ChatUsageTests : SerializationTest { var expectedJson = @"{ ""input_tokens"": 1, ""output_tokens"": 2 }"; - var chatUsage = new ChatUsage + var usage = new Usage { InputTokens = 1, OutputTokens = 2 }; - var actual = Serialize(chatUsage); + var actual = Serialize(usage); JsonAssert.Equal(expectedJson, actual); } @@ -39,9 +39,9 @@ public class ChatUsageTests : SerializationTest { var json = @"{ ""input_tokens"": 1, ""output_tokens"": 2 }"; - var chatUsage = Deserialize(json); + var usage = Deserialize(json); - chatUsage!.InputTokens.Should().Be(1); - chatUsage.OutputTokens.Should().Be(2); + usage!.InputTokens.Should().Be(1); + usage.OutputTokens.Should().Be(2); } } \ No newline at end of file diff --git a/tests/AnthropicClient.Tests/Unit/Utils/JsonSchemaGeneratorTests.cs b/tests/AnthropicClient.Tests/Unit/Utils/JsonSchemaGeneratorTests.cs index 18b5074..4e3b067 100644 --- a/tests/AnthropicClient.Tests/Unit/Utils/JsonSchemaGeneratorTests.cs +++ b/tests/AnthropicClient.Tests/Unit/Utils/JsonSchemaGeneratorTests.cs @@ -1,5 +1,3 @@ -using System.Text.Json.Nodes; - namespace AnthropicClient.Tests.Unit.Utils; public class JsonSchemaGeneratorTests diff --git a/tests/AnthropicClient.Tests/Usings.cs b/tests/AnthropicClient.Tests/Usings.cs index 69bde4f..57054e6 100644 --- a/tests/AnthropicClient.Tests/Usings.cs +++ b/tests/AnthropicClient.Tests/Usings.cs @@ -1,10 +1,13 @@ global using System.Text; global using System.Text.Json; +global using System.Text.Json.Nodes; global using System.Text.Json.JsonDiffPatch.Xunit; global using AnthropicClient.Models; global using AnthropicClient.Utils; global using AnthropicClient.Json; +global using AnthropicClient.Tests.Data; +global using AnthropicClient.Tests.Fixtures; global using FluentAssertions; @@ -12,4 +15,4 @@ global using Microsoft.Extensions.Configuration; global using RichardSzalay.MockHttp; global using System.Net; -global using System.Collections; \ No newline at end of file +global using System.Collections;