tests: add missing tests for streaming chat message
This commit is contained in:
@@ -172,6 +172,13 @@ public class AnthropicApiClient : IAnthropicApiClient
|
|||||||
chatResponse is not null
|
chatResponse is not null
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
var existingUsage = chatResponse.Usage;
|
||||||
|
var newUsage = new ChatUsage()
|
||||||
|
{
|
||||||
|
InputTokens = existingUsage.InputTokens + msgDeltaData.Usage.InputTokens,
|
||||||
|
OutputTokens = existingUsage.OutputTokens + msgDeltaData.Usage.OutputTokens,
|
||||||
|
};
|
||||||
|
|
||||||
chatResponse = new ChatResponse()
|
chatResponse = new ChatResponse()
|
||||||
{
|
{
|
||||||
Id = chatResponse.Id,
|
Id = chatResponse.Id,
|
||||||
@@ -180,7 +187,7 @@ public class AnthropicApiClient : IAnthropicApiClient
|
|||||||
StopReason = msgDeltaData.Delta.StopReason,
|
StopReason = msgDeltaData.Delta.StopReason,
|
||||||
StopSequence = msgDeltaData.Delta.StopSequence,
|
StopSequence = msgDeltaData.Delta.StopSequence,
|
||||||
Type = chatResponse.Type,
|
Type = chatResponse.Type,
|
||||||
Usage = msgDeltaData.Usage,
|
Usage = newUsage,
|
||||||
Content = chatResponse.Content,
|
Content = chatResponse.Content,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace AnthropicClient.Models;
|
namespace AnthropicClient.Models;
|
||||||
|
|
||||||
public abstract class EventData
|
public abstract class EventData
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace AnthropicClient.Models;
|
namespace AnthropicClient.Models;
|
||||||
|
|
||||||
public class MessageCompleteEventData : EventData
|
public class MessageCompleteEventData : EventData
|
||||||
{
|
{
|
||||||
public AnthropicHeaders Headers { get; init; } = new();
|
public AnthropicHeaders Headers { get; init; }
|
||||||
public ChatResponse Message { get; init; } = new();
|
public ChatResponse Message { get; init; }
|
||||||
|
|
||||||
public MessageCompleteEventData(ChatResponse message, AnthropicHeaders headers) : base(EventType.MessageComplete)
|
public MessageCompleteEventData(ChatResponse message, AnthropicHeaders headers) : base(EventType.MessageComplete)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,372 @@
|
|||||||
|
namespace AnthropicClient.Tests.Unit.Models;
|
||||||
|
|
||||||
|
public class AnthropicEventTests : SerializationTest
|
||||||
|
{
|
||||||
|
private readonly string _testMessageStartJson = @"{
|
||||||
|
""type"": ""message_start"",
|
||||||
|
""data"": {
|
||||||
|
""message"": {
|
||||||
|
""id"": ""msg_014p7gG3wDgGV9EUtLvnow3U"",
|
||||||
|
""type"": ""message"",
|
||||||
|
""role"": ""assistant"",
|
||||||
|
""model"": ""claude-3-haiku-20240307"",
|
||||||
|
""stop_sequence"": """",
|
||||||
|
""usage"": {
|
||||||
|
""input_tokens"": 472,
|
||||||
|
""output_tokens"": 2
|
||||||
|
},
|
||||||
|
""content"": [],
|
||||||
|
""stop_reason"": """"
|
||||||
|
},
|
||||||
|
""type"": ""message_start""
|
||||||
|
}
|
||||||
|
}";
|
||||||
|
|
||||||
|
private readonly string _testContentBlockStartJson = @"{
|
||||||
|
""type"": ""content_block_start"",
|
||||||
|
""data"": {
|
||||||
|
""index"": 0,
|
||||||
|
""content_block"": {
|
||||||
|
""type"": ""text"",
|
||||||
|
""text"": ""Okay""
|
||||||
|
},
|
||||||
|
""type"": ""content_block_start""
|
||||||
|
}
|
||||||
|
}";
|
||||||
|
|
||||||
|
private readonly string _testPingJson = @"{
|
||||||
|
""type"": ""ping"",
|
||||||
|
""data"": {
|
||||||
|
""type"": ""ping""
|
||||||
|
}
|
||||||
|
}";
|
||||||
|
|
||||||
|
private readonly string _testContentBlockDeltaJson = @"{
|
||||||
|
""type"": ""content_block_delta"",
|
||||||
|
""data"": {
|
||||||
|
""index"": 0,
|
||||||
|
""delta"": {
|
||||||
|
""type"": ""text_delta"",
|
||||||
|
""text"": ""Okay""
|
||||||
|
},
|
||||||
|
""type"": ""content_block_delta""
|
||||||
|
}
|
||||||
|
}";
|
||||||
|
|
||||||
|
private readonly string _testContentBlockStopJson = @"{
|
||||||
|
""type"": ""content_block_stop"",
|
||||||
|
""data"": {
|
||||||
|
""index"": 0,
|
||||||
|
""type"": ""content_block_stop""
|
||||||
|
}
|
||||||
|
}";
|
||||||
|
|
||||||
|
private readonly string _testMessageDeltaJson = @"{
|
||||||
|
""type"": ""message_delta"",
|
||||||
|
""data"": {
|
||||||
|
""delta"": {
|
||||||
|
""stop_reason"": ""tool_use"",
|
||||||
|
""stop_sequence"": """"
|
||||||
|
},
|
||||||
|
""usage"": {
|
||||||
|
""output_tokens"": 89,
|
||||||
|
""input_tokens"": 0
|
||||||
|
},
|
||||||
|
""type"": ""message_delta""
|
||||||
|
}
|
||||||
|
}";
|
||||||
|
|
||||||
|
private readonly string _testMessageStopJson = @"{
|
||||||
|
""type"": ""message_stop"",
|
||||||
|
""data"": {
|
||||||
|
""type"": ""message_stop""
|
||||||
|
}
|
||||||
|
}";
|
||||||
|
|
||||||
|
private readonly string _testErrorJson = @"{
|
||||||
|
""type"": ""error"",
|
||||||
|
""data"": {
|
||||||
|
""error"": {
|
||||||
|
""type"": ""api_error"",
|
||||||
|
""message"": ""An error occurred.""
|
||||||
|
},
|
||||||
|
""type"": ""error""
|
||||||
|
}
|
||||||
|
}";
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_WhenCalled_ItShouldInitializeProperties()
|
||||||
|
{
|
||||||
|
var expectedType = EventType.MessageStart;
|
||||||
|
var expected = new MessageStartEventData
|
||||||
|
{
|
||||||
|
Type = EventType.MessageStart,
|
||||||
|
Message = new()
|
||||||
|
};
|
||||||
|
|
||||||
|
var actual = new AnthropicEvent(expectedType, expected);
|
||||||
|
|
||||||
|
actual.Type.Should().Be(expectedType);
|
||||||
|
actual.Data.Should().BeSameAs(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JsonSerialization_WhenSerialized_ItShouldHaveExpectedShape()
|
||||||
|
{
|
||||||
|
var eventData = new MessageStartEventData
|
||||||
|
{
|
||||||
|
Type = EventType.MessageStart,
|
||||||
|
Message = new()
|
||||||
|
{
|
||||||
|
Id = "msg_014p7gG3wDgGV9EUtLvnow3U",
|
||||||
|
Type = "message",
|
||||||
|
Role = "assistant",
|
||||||
|
Model = "claude-3-haiku-20240307",
|
||||||
|
StopSequence = string.Empty,
|
||||||
|
Usage = new()
|
||||||
|
{
|
||||||
|
InputTokens = 472,
|
||||||
|
OutputTokens = 2
|
||||||
|
},
|
||||||
|
Content = [],
|
||||||
|
StopReason = string.Empty
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var e = new AnthropicEvent(EventType.MessageStart, eventData);
|
||||||
|
|
||||||
|
var actual = Serialize(e);
|
||||||
|
|
||||||
|
JsonAssert.Equal(_testMessageStartJson, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JsonDeserialization_WhenMessageStartEventDeserialized_ItShouldHaveBeExpectedType()
|
||||||
|
{
|
||||||
|
var expected = new MessageStartEventData
|
||||||
|
{
|
||||||
|
Type = EventType.MessageStart,
|
||||||
|
Message = new()
|
||||||
|
{
|
||||||
|
Id = "msg_014p7gG3wDgGV9EUtLvnow3U",
|
||||||
|
Type = "message",
|
||||||
|
Role = "assistant",
|
||||||
|
Model = "claude-3-haiku-20240307",
|
||||||
|
StopSequence = string.Empty,
|
||||||
|
Usage = new()
|
||||||
|
{
|
||||||
|
InputTokens = 472,
|
||||||
|
OutputTokens = 2
|
||||||
|
},
|
||||||
|
Content = [],
|
||||||
|
StopReason = string.Empty
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var actual = Deserialize<AnthropicEvent>(_testMessageStartJson);
|
||||||
|
|
||||||
|
actual!.Type.Should().Be(expected.Type);
|
||||||
|
actual.Data.Should().BeEquivalentTo(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JsonSerialization_WhenContentBlockStartEventSerialized_ItShouldHaveExpectedShape()
|
||||||
|
{
|
||||||
|
var eventData = new ContentStartEventData(0, new TextContent("Okay"));
|
||||||
|
|
||||||
|
var e = new AnthropicEvent(EventType.ContentBlockStart, eventData);
|
||||||
|
|
||||||
|
var actual = Serialize(e);
|
||||||
|
|
||||||
|
JsonAssert.Equal(_testContentBlockStartJson, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JsonDeserialization_WhenContentBlockStartEventDeserialized_ItShouldHaveExpectedType()
|
||||||
|
{
|
||||||
|
var expected = new ContentStartEventData(0, new TextContent("Okay"));
|
||||||
|
|
||||||
|
var actual = Deserialize<AnthropicEvent>(_testContentBlockStartJson);
|
||||||
|
|
||||||
|
actual!.Type.Should().Be(expected.Type);
|
||||||
|
actual.Data.Should().BeEquivalentTo(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JsonSerialization_WhenPingEventSerialized_ItShouldHaveExpectedShape()
|
||||||
|
{
|
||||||
|
var eventData = new PingEventData();
|
||||||
|
|
||||||
|
var e = new AnthropicEvent(EventType.Ping, eventData);
|
||||||
|
|
||||||
|
var actual = Serialize(e);
|
||||||
|
|
||||||
|
JsonAssert.Equal(_testPingJson, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JsonDeserialization_WhenPingEventDeserialized_ItShouldHaveExpectedType()
|
||||||
|
{
|
||||||
|
var expected = new PingEventData();
|
||||||
|
|
||||||
|
var actual = Deserialize<AnthropicEvent>(_testPingJson);
|
||||||
|
|
||||||
|
actual!.Type.Should().Be(expected.Type);
|
||||||
|
actual.Data.Should().BeEquivalentTo(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JsonSerialization_WhenContentBlockDeltaEventSerialized_ItShouldHaveExpectedShape()
|
||||||
|
{
|
||||||
|
var eventData = new ContentDeltaEventData(0, new TextDelta("Okay"));
|
||||||
|
|
||||||
|
var e = new AnthropicEvent(EventType.ContentBlockDelta, eventData);
|
||||||
|
|
||||||
|
var actual = Serialize(e);
|
||||||
|
|
||||||
|
JsonAssert.Equal(_testContentBlockDeltaJson, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JsonDeserialization_WhenContentBlockDeltaEventDeserialized_ItShouldHaveExpectedType()
|
||||||
|
{
|
||||||
|
var expected = new ContentDeltaEventData(0, new TextDelta("Okay"));
|
||||||
|
|
||||||
|
var actual = Deserialize<AnthropicEvent>(_testContentBlockDeltaJson);
|
||||||
|
|
||||||
|
actual!.Type.Should().Be(expected.Type);
|
||||||
|
actual.Data.Should().BeEquivalentTo(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JsonSerialization_WhenContentBlockStopEventSerialized_ItShouldHaveExpectedShape()
|
||||||
|
{
|
||||||
|
var eventData = new ContentStopEventData(0);
|
||||||
|
|
||||||
|
var e = new AnthropicEvent(EventType.ContentBlockStop, eventData);
|
||||||
|
|
||||||
|
var actual = Serialize(e);
|
||||||
|
|
||||||
|
JsonAssert.Equal(_testContentBlockStopJson, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JsonDeserialization_WhenContentBlockStopEventDeserialized_ItShouldHaveExpectedType()
|
||||||
|
{
|
||||||
|
var expected = new ContentStopEventData(0);
|
||||||
|
|
||||||
|
var actual = Deserialize<AnthropicEvent>(_testContentBlockStopJson);
|
||||||
|
|
||||||
|
actual!.Type.Should().Be(expected.Type);
|
||||||
|
actual.Data.Should().BeEquivalentTo(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JsonSerialization_WhenMessageDeltaEventSerialized_ItShouldHaveExpectedShape()
|
||||||
|
{
|
||||||
|
var eventData = new MessageDeltaEventData
|
||||||
|
{
|
||||||
|
Type = EventType.MessageDelta,
|
||||||
|
Delta = new()
|
||||||
|
{
|
||||||
|
StopReason = "tool_use",
|
||||||
|
StopSequence = string.Empty,
|
||||||
|
},
|
||||||
|
Usage = new()
|
||||||
|
{
|
||||||
|
OutputTokens = 89,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var e = new AnthropicEvent(EventType.MessageDelta, eventData);
|
||||||
|
|
||||||
|
var actual = Serialize(e);
|
||||||
|
|
||||||
|
JsonAssert.Equal(_testMessageDeltaJson, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JsonDeserialization_WhenMessageDeltaEventDeserialized_ItShouldHaveExpectedType()
|
||||||
|
{
|
||||||
|
var expected = new MessageDeltaEventData
|
||||||
|
{
|
||||||
|
Type = EventType.MessageDelta,
|
||||||
|
Delta = new()
|
||||||
|
{
|
||||||
|
StopReason = "tool_use",
|
||||||
|
StopSequence = string.Empty,
|
||||||
|
},
|
||||||
|
Usage = new()
|
||||||
|
{
|
||||||
|
OutputTokens = 89,
|
||||||
|
InputTokens = 0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var actual = Deserialize<AnthropicEvent>(_testMessageDeltaJson);
|
||||||
|
|
||||||
|
actual!.Type.Should().Be(expected.Type);
|
||||||
|
actual.Data.Should().BeEquivalentTo(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JsonSerialization_WhenMessageStopEventSerialized_ItShouldHaveExpectedShape()
|
||||||
|
{
|
||||||
|
var eventData = new MessageStopEventData();
|
||||||
|
|
||||||
|
var e = new AnthropicEvent(EventType.MessageStop, eventData);
|
||||||
|
|
||||||
|
var actual = Serialize(e);
|
||||||
|
|
||||||
|
JsonAssert.Equal(_testMessageStopJson, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JsonDeserialization_WhenMessageStopEventDeserialized_ItShouldHaveExpectedType()
|
||||||
|
{
|
||||||
|
var expected = new MessageStopEventData();
|
||||||
|
|
||||||
|
var actual = Deserialize<AnthropicEvent>(_testMessageStopJson);
|
||||||
|
|
||||||
|
actual!.Type.Should().Be(expected.Type);
|
||||||
|
actual.Data.Should().BeEquivalentTo(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JsonSerialization_WhenErrorEventSerialized_ItShouldHaveExpectedShape()
|
||||||
|
{
|
||||||
|
var eventData = new ErrorEventData(new ApiError("An error occurred."));
|
||||||
|
|
||||||
|
var e = new AnthropicEvent(EventType.Error, eventData);
|
||||||
|
|
||||||
|
var actual = Serialize(e);
|
||||||
|
|
||||||
|
JsonAssert.Equal(_testErrorJson, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JsonDeserialization_WhenErrorEventDeserialized_ItShouldHaveExpectedType()
|
||||||
|
{
|
||||||
|
var expected = new ErrorEventData(new ApiError("An error occurred."));
|
||||||
|
|
||||||
|
var actual = Deserialize<AnthropicEvent>(_testErrorJson);
|
||||||
|
|
||||||
|
actual!.Type.Should().Be(expected.Type);
|
||||||
|
actual.Data.Should().BeEquivalentTo(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JsonDeserialization_WhenTypeUnknown_ItShouldThrow()
|
||||||
|
{
|
||||||
|
var json = @"{
|
||||||
|
""type"": ""unknown"",
|
||||||
|
""data"": {
|
||||||
|
""type"": ""unknown""
|
||||||
|
}
|
||||||
|
}";
|
||||||
|
|
||||||
|
Action act = () => Deserialize<AnthropicEvent>(json);
|
||||||
|
|
||||||
|
act.Should().Throw<JsonException>();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
namespace AnthropicClient.Tests.Unit.Models;
|
||||||
|
|
||||||
|
public class ContentDeltaEventDataTests : SerializationTest
|
||||||
|
{
|
||||||
|
private readonly string _testJsonWithTextDelta = @"{
|
||||||
|
""type"": ""content_block_delta"",
|
||||||
|
""index"": 0,
|
||||||
|
""delta"": {
|
||||||
|
""type"": ""text_delta"",
|
||||||
|
""text"": ""Hello World!""
|
||||||
|
}
|
||||||
|
}";
|
||||||
|
|
||||||
|
private readonly string _testJsonWithInputDelta = @"{
|
||||||
|
""type"": ""content_block_delta"",
|
||||||
|
""index"": 0,
|
||||||
|
""delta"": {
|
||||||
|
""type"": ""input_json_delta"",
|
||||||
|
""partial_json"": ""Hello""
|
||||||
|
}
|
||||||
|
}";
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_WhenCalled_ItShouldInitializeProperties()
|
||||||
|
{
|
||||||
|
var index = 0;
|
||||||
|
var text = "Hello World!";
|
||||||
|
var textDelta = new TextDelta(text);
|
||||||
|
var contentDeltaEventData = new ContentDeltaEventData(index, textDelta);
|
||||||
|
|
||||||
|
contentDeltaEventData.Type.Should().Be("content_block_delta");
|
||||||
|
contentDeltaEventData.Delta.Should().BeSameAs(textDelta);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JsonSerialization_WhenSerializedWithTextDelta_ItShouldHaveExpectedShape()
|
||||||
|
{
|
||||||
|
var index = 0;
|
||||||
|
var text = "Hello World!";
|
||||||
|
var textDelta = new TextDelta(text);
|
||||||
|
var contentDeltaEventData = new ContentDeltaEventData(index, textDelta);
|
||||||
|
|
||||||
|
var actual = Serialize(contentDeltaEventData);
|
||||||
|
|
||||||
|
JsonAssert.Equal(_testJsonWithTextDelta, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JsonDeserialization_WhenDeserializedWithTextDelta_ItShouldHaveExpectedProperties()
|
||||||
|
{
|
||||||
|
var index = 0;
|
||||||
|
var text = "Hello World!";
|
||||||
|
var textDelta = new TextDelta(text);
|
||||||
|
var contentDeltaEventData = new ContentDeltaEventData(index, textDelta);
|
||||||
|
|
||||||
|
var actual = Deserialize<ContentDeltaEventData>(_testJsonWithTextDelta);
|
||||||
|
|
||||||
|
actual.Should().BeEquivalentTo(contentDeltaEventData);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JsonSerialization_WhenSerializedWithInputDelta_ItShouldHaveExpectedShape()
|
||||||
|
{
|
||||||
|
var index = 0;
|
||||||
|
var input = "Hello";
|
||||||
|
var inputDelta = new JsonDelta(input);
|
||||||
|
var contentDeltaEventData = new ContentDeltaEventData(index, inputDelta);
|
||||||
|
|
||||||
|
var actual = Serialize(contentDeltaEventData);
|
||||||
|
|
||||||
|
JsonAssert.Equal(_testJsonWithInputDelta, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JsonDeserialization_WhenDeserializedWithInputDelta_ItShouldHaveExpectedProperties()
|
||||||
|
{
|
||||||
|
var index = 0;
|
||||||
|
var input = "Hello";
|
||||||
|
var inputDelta = new JsonDelta(input);
|
||||||
|
var contentDeltaEventData = new ContentDeltaEventData(index, inputDelta);
|
||||||
|
|
||||||
|
var actual = Deserialize<ContentDeltaEventData>(_testJsonWithInputDelta);
|
||||||
|
|
||||||
|
actual.Should().BeEquivalentTo(contentDeltaEventData);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JsonDeserialization_WhenDeserializedWithUnknownDeltaType_ItShouldThrowJsonException()
|
||||||
|
{
|
||||||
|
var json = @"{
|
||||||
|
""type"": ""content_block_delta"",
|
||||||
|
""index"": 0,
|
||||||
|
""delta"": {
|
||||||
|
""type"": ""unknown_delta"",
|
||||||
|
""text"": ""Hello World!""
|
||||||
|
}
|
||||||
|
}";
|
||||||
|
|
||||||
|
var action = () => Deserialize<ContentDeltaEventData>(json);
|
||||||
|
|
||||||
|
action.Should().Throw<JsonException>();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
namespace AnthropicClient.Tests.Unit.Models;
|
||||||
|
|
||||||
|
public class ContentDeltaTypeTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void TextDelta_WhenCalled_ItShouldReturnExpectedValue()
|
||||||
|
{
|
||||||
|
var expected = "text_delta";
|
||||||
|
|
||||||
|
var actual = ContentDeltaType.TextDelta;
|
||||||
|
|
||||||
|
actual.Should().Be(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JsonDelta_WhenCalled_ItShouldReturnExpectedValue()
|
||||||
|
{
|
||||||
|
var expected = "input_json_delta";
|
||||||
|
|
||||||
|
var actual = ContentDeltaType.JsonDelta;
|
||||||
|
|
||||||
|
actual.Should().Be(expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
namespace AnthropicClient.Tests.Unit.Models;
|
||||||
|
|
||||||
|
public class ContentStartEventDataTests : SerializationTest
|
||||||
|
{
|
||||||
|
private readonly string _testJsonWithTextBlock = @"{
|
||||||
|
""type"": ""content_block_start"",
|
||||||
|
""index"": 0,
|
||||||
|
""content_block"": {
|
||||||
|
""type"": ""text"",
|
||||||
|
""text"": ""Hello World!""
|
||||||
|
}
|
||||||
|
}";
|
||||||
|
|
||||||
|
private readonly string _testJsonWithToolUseBlock = @"{
|
||||||
|
""type"": ""content_block_start"",
|
||||||
|
""index"": 0,
|
||||||
|
""content_block"": {
|
||||||
|
""type"": ""tool_use"",
|
||||||
|
""id"": ""toolu_01T1x1fJ34qAmk2tNTrN7Up6"",
|
||||||
|
""name"": ""get_weather"",
|
||||||
|
""input"": {}}
|
||||||
|
}";
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_WhenCalled_ItShouldInitializeProperties()
|
||||||
|
{
|
||||||
|
var index = 0;
|
||||||
|
var textContent = new TextContent("Hello World!");
|
||||||
|
var contentStartEventData = new ContentStartEventData(index, textContent);
|
||||||
|
|
||||||
|
contentStartEventData.Type.Should().Be("content_block_start");
|
||||||
|
contentStartEventData.Index.Should().Be(index);
|
||||||
|
contentStartEventData.ContentBlock.Should().BeSameAs(textContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JsonSerialization_WhenSerializedWithTextContent_ItShouldHaveExpectedShape()
|
||||||
|
{
|
||||||
|
var index = 0;
|
||||||
|
var textContent = new TextContent("Hello World!");
|
||||||
|
var contentStartEventData = new ContentStartEventData(index, textContent);
|
||||||
|
|
||||||
|
var actual = Serialize(contentStartEventData);
|
||||||
|
|
||||||
|
JsonAssert.Equal(_testJsonWithTextBlock, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JsonDeserialization_WhenDeserializedWithTextContent_ItShouldHaveExpectedProperties()
|
||||||
|
{
|
||||||
|
var index = 0;
|
||||||
|
var textContent = new TextContent("Hello World!");
|
||||||
|
var contentStartEventData = new ContentStartEventData(index, textContent);
|
||||||
|
|
||||||
|
var actual = Deserialize<ContentStartEventData>(_testJsonWithTextBlock);
|
||||||
|
|
||||||
|
actual.Should().BeEquivalentTo(contentStartEventData);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JsonSerialization_WhenSerializedWithToolUseContent_ItShouldHaveExpectedShape()
|
||||||
|
{
|
||||||
|
var index = 0;
|
||||||
|
var toolUseContent = new ToolUseContent()
|
||||||
|
{
|
||||||
|
Id = "toolu_01T1x1fJ34qAmk2tNTrN7Up6",
|
||||||
|
Name = "get_weather",
|
||||||
|
Input = []
|
||||||
|
};
|
||||||
|
|
||||||
|
var contentStartEventData = new ContentStartEventData(index, toolUseContent);
|
||||||
|
|
||||||
|
var actual = Serialize(contentStartEventData);
|
||||||
|
|
||||||
|
JsonAssert.Equal(_testJsonWithToolUseBlock, actual);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
namespace AnthropicClient.Tests.Unit.Models;
|
||||||
|
|
||||||
|
public class ContentStopEventDataTests : SerializationTest
|
||||||
|
{
|
||||||
|
private readonly string _testJson = @"{
|
||||||
|
""type"": ""content_block_stop"",
|
||||||
|
""index"": 0
|
||||||
|
}";
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_WhenCalled_ItShouldInitializeProperties()
|
||||||
|
{
|
||||||
|
var index = 0;
|
||||||
|
var contentStopEventData = new ContentStopEventData(index);
|
||||||
|
|
||||||
|
contentStopEventData.Type.Should().Be("content_block_stop");
|
||||||
|
contentStopEventData.Index.Should().Be(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JsonSerialization_WhenSerialized_ItShouldHaveExpectedShape()
|
||||||
|
{
|
||||||
|
var index = 0;
|
||||||
|
var contentStopEventData = new ContentStopEventData(index);
|
||||||
|
|
||||||
|
var actual = Serialize(contentStopEventData);
|
||||||
|
|
||||||
|
JsonAssert.Equal(_testJson, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JsonDeserialization_WhenDeserialized_ItShouldHaveExpectedProperties()
|
||||||
|
{
|
||||||
|
var index = 0;
|
||||||
|
var contentStopEventData = new ContentStopEventData(index);
|
||||||
|
|
||||||
|
var actual = Deserialize<ContentStopEventData>(_testJson);
|
||||||
|
|
||||||
|
actual.Should().BeEquivalentTo(contentStopEventData);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
namespace AnthropicClient.Tests.Unit.Models;
|
||||||
|
|
||||||
|
public class ErrorEventDataTests : SerializationTest
|
||||||
|
{
|
||||||
|
private readonly string _testJson = @"{
|
||||||
|
""type"": ""error"",
|
||||||
|
""error"": {
|
||||||
|
""type"": ""api_error"",
|
||||||
|
""message"": ""An error occurred.""
|
||||||
|
}
|
||||||
|
}";
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_WhenCalled_ItShouldInitializeProperties()
|
||||||
|
{
|
||||||
|
var error = new ApiError("An error occurred.");
|
||||||
|
var errorEventData = new ErrorEventData(error);
|
||||||
|
|
||||||
|
errorEventData.Type.Should().Be("error");
|
||||||
|
errorEventData.Error.Should().BeSameAs(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JsonSerialization_WhenSerialized_ItShouldHaveExpectedShape()
|
||||||
|
{
|
||||||
|
var error = new ApiError("An error occurred.");
|
||||||
|
var errorEventData = new ErrorEventData(error);
|
||||||
|
|
||||||
|
var actual = Serialize(errorEventData);
|
||||||
|
|
||||||
|
JsonAssert.Equal(_testJson, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JsonDeserialization_WhenDeserialized_ItShouldHaveExpectedProperties()
|
||||||
|
{
|
||||||
|
var error = new ApiError("An error occurred.");
|
||||||
|
var errorEventData = new ErrorEventData(error);
|
||||||
|
|
||||||
|
var actual = Deserialize<ErrorEventData>(_testJson);
|
||||||
|
|
||||||
|
actual.Should().BeEquivalentTo(errorEventData);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
namespace AnthropicClient.Tests.Unit.Models;
|
||||||
|
|
||||||
|
public class JsonDeltaTests : SerializationTest
|
||||||
|
{
|
||||||
|
private readonly string _testJson = @"{
|
||||||
|
""type"": ""input_json_delta"",
|
||||||
|
""partial_json"": ""{\""key\"": \""value\""}""
|
||||||
|
}";
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_WhenCalled_ItShouldInitializeProperties()
|
||||||
|
{
|
||||||
|
var partialJson = "{\"key\": \"value\"}";
|
||||||
|
|
||||||
|
var jsonDelta = new JsonDelta(partialJson);
|
||||||
|
|
||||||
|
jsonDelta.Type.Should().Be("input_json_delta");
|
||||||
|
jsonDelta.PartialJson.Should().Be(partialJson);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JsonSerialization_WhenSerialized_ItShouldHaveExpectedShape()
|
||||||
|
{
|
||||||
|
var jsonDelta = new JsonDelta("{\"key\": \"value\"}");
|
||||||
|
|
||||||
|
var actual = Serialize(jsonDelta);
|
||||||
|
|
||||||
|
JsonAssert.Equal(_testJson, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JsonDeserialization_WhenDeserialized_ItShouldHaveExpectedProperties()
|
||||||
|
{
|
||||||
|
var expected = new JsonDelta("{\"key\": \"value\"}");
|
||||||
|
|
||||||
|
var actual = Deserialize<JsonDelta>(_testJson);
|
||||||
|
|
||||||
|
actual.Should().BeEquivalentTo(expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
namespace AnthropicClient.Tests.Unit.Models;
|
||||||
|
|
||||||
|
public class MessageCompleteEventDataTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_WhenCalled_ItShouldInitializeProperties()
|
||||||
|
{
|
||||||
|
var expectedMessage = new ChatResponse();
|
||||||
|
var expectedHeaders = new AnthropicHeaders();
|
||||||
|
|
||||||
|
var messageCompleteEventData = new MessageCompleteEventData(expectedMessage, expectedHeaders);
|
||||||
|
|
||||||
|
messageCompleteEventData.Message.Should().BeSameAs(expectedMessage);
|
||||||
|
messageCompleteEventData.Headers.Should().BeSameAs(expectedHeaders);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
namespace AnthropicClient.Tests.Unit.Models;
|
||||||
|
|
||||||
|
public class MessageDeltaEventDataTests : SerializationTest
|
||||||
|
{
|
||||||
|
private readonly string _testJson = @"{
|
||||||
|
""type"": ""message_delta"",
|
||||||
|
""delta"": {
|
||||||
|
""stop_reason"": ""max_tokens"",
|
||||||
|
""stop_sequence"": ""max_tokens""
|
||||||
|
},
|
||||||
|
""usage"": {
|
||||||
|
""input_tokens"": 1,
|
||||||
|
""output_tokens"": 1
|
||||||
|
}
|
||||||
|
}";
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_WhenCalled_ItShouldInitializeProperties()
|
||||||
|
{
|
||||||
|
var expectedDelta = new MessageDelta("max_tokens", "max_tokens");
|
||||||
|
var expectedUsage = new ChatUsage { InputTokens = 1, OutputTokens = 1 };
|
||||||
|
|
||||||
|
var messageDeltaEventData = new MessageDeltaEventData(expectedDelta, expectedUsage);
|
||||||
|
|
||||||
|
messageDeltaEventData.Delta.Should().BeSameAs(expectedDelta);
|
||||||
|
messageDeltaEventData.Usage.Should().BeSameAs(expectedUsage);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JsonSerialization_WhenSerialized_ItShouldHaveExpectedShape()
|
||||||
|
{
|
||||||
|
var expectedDelta = new MessageDelta("max_tokens", "max_tokens");
|
||||||
|
var expectedUsage = new ChatUsage { InputTokens = 1, OutputTokens = 1 };
|
||||||
|
|
||||||
|
var messageDeltaEventData = new MessageDeltaEventData(expectedDelta, expectedUsage);
|
||||||
|
|
||||||
|
var actualJson = Serialize(messageDeltaEventData);
|
||||||
|
|
||||||
|
JsonAssert.Equal(_testJson, actualJson);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JsonDeserialization_WhenDeserialized_ItShouldHaveExpectedValues()
|
||||||
|
{
|
||||||
|
var expectedDelta = new MessageDelta("max_tokens", "max_tokens");
|
||||||
|
var expectedUsage = new ChatUsage { InputTokens = 1, OutputTokens = 1 };
|
||||||
|
|
||||||
|
var messageDeltaEventData = Deserialize<MessageDeltaEventData>(_testJson);
|
||||||
|
|
||||||
|
messageDeltaEventData!.Delta.Should().BeEquivalentTo(expectedDelta);
|
||||||
|
messageDeltaEventData.Usage.Should().BeEquivalentTo(expectedUsage);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
namespace AnthropicClient.Tests.Unit.Models;
|
||||||
|
|
||||||
|
public class MessageDeltaTests : SerializationTest
|
||||||
|
{
|
||||||
|
private readonly string _testJson = @"{
|
||||||
|
""stop_reason"": ""max_tokens"",
|
||||||
|
""stop_sequence"": ""max_tokens""
|
||||||
|
}";
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_WhenCalled_ItShouldInitializeProperties()
|
||||||
|
{
|
||||||
|
var expectedStopReason = "max_tokens";
|
||||||
|
var expectedStopSequence = "max_tokens";
|
||||||
|
|
||||||
|
var messageDelta = new MessageDelta(expectedStopReason, expectedStopSequence);
|
||||||
|
|
||||||
|
messageDelta.StopReason.Should().Be(expectedStopReason);
|
||||||
|
messageDelta.StopSequence.Should().Be(expectedStopSequence);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JsonSerialization_WhenSerialized_ItShouldHaveExpectedShape()
|
||||||
|
{
|
||||||
|
var messageDelta = new MessageDelta("max_tokens", "max_tokens");
|
||||||
|
|
||||||
|
var actualJson = Serialize(messageDelta);
|
||||||
|
|
||||||
|
JsonAssert.Equal(_testJson, actualJson);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JsonDeserialization_WhenDeserialized_ItShouldHaveExpectedProperties()
|
||||||
|
{
|
||||||
|
var expectedStopReason = "max_tokens";
|
||||||
|
var expectedStopSequence = "max_tokens";
|
||||||
|
|
||||||
|
var messageDelta = Deserialize<MessageDelta>(_testJson);
|
||||||
|
|
||||||
|
messageDelta!.StopReason.Should().Be(expectedStopReason);
|
||||||
|
messageDelta.StopSequence.Should().Be(expectedStopSequence);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
namespace AnthropicClient.Tests.Unit.Models;
|
||||||
|
|
||||||
|
public class MessageStartEventDataTests : SerializationTest
|
||||||
|
{
|
||||||
|
private readonly string _testJson = @"{
|
||||||
|
""type"": ""message_start"",
|
||||||
|
""message"": {
|
||||||
|
""id"": ""msg_1nZdL29xx5MUA1yADyHTEsnR8uuvGzszyY"",
|
||||||
|
""type"": ""message"",
|
||||||
|
""role"": ""assistant"",
|
||||||
|
""content"": [],
|
||||||
|
""model"": ""claude-3-5-sonnet-20240620"",
|
||||||
|
""stop_reason"": """",
|
||||||
|
""stop_sequence"": """",
|
||||||
|
""usage"": {
|
||||||
|
""input_tokens"": 25,
|
||||||
|
""output_tokens"": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}";
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_WhenCalled_ItShouldInitializeProperties()
|
||||||
|
{
|
||||||
|
var expectedMessage = new ChatResponse
|
||||||
|
{
|
||||||
|
Id = "msg_1nZdL29xx5MUA1yADyHTEsnR8uuvGzszyY",
|
||||||
|
Type = "message",
|
||||||
|
Role = "assistant",
|
||||||
|
Content = [],
|
||||||
|
Model = "claude-3-5-sonnet-20240620",
|
||||||
|
StopReason = string.Empty,
|
||||||
|
StopSequence = string.Empty,
|
||||||
|
Usage = new ChatUsage { InputTokens = 25, OutputTokens = 1 }
|
||||||
|
};
|
||||||
|
|
||||||
|
var messageStartEventData = new MessageStartEventData(expectedMessage);
|
||||||
|
|
||||||
|
messageStartEventData.Message.Should().BeSameAs(expectedMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JsonSerialization_WhenSerialized_ItShouldHaveExpectedShape()
|
||||||
|
{
|
||||||
|
var expectedMessage = new ChatResponse
|
||||||
|
{
|
||||||
|
Id = "msg_1nZdL29xx5MUA1yADyHTEsnR8uuvGzszyY",
|
||||||
|
Type = "message",
|
||||||
|
Role = "assistant",
|
||||||
|
Content = [],
|
||||||
|
Model = "claude-3-5-sonnet-20240620",
|
||||||
|
StopReason = string.Empty,
|
||||||
|
StopSequence = string.Empty,
|
||||||
|
Usage = new ChatUsage { InputTokens = 25, OutputTokens = 1 }
|
||||||
|
};
|
||||||
|
|
||||||
|
var messageStartEventData = new MessageStartEventData(expectedMessage);
|
||||||
|
|
||||||
|
var actualJson = Serialize(messageStartEventData);
|
||||||
|
|
||||||
|
JsonAssert.Equal(_testJson, actualJson);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JsonDeserialization_WhenDeserialized_ItShouldHaveExpectedValues()
|
||||||
|
{
|
||||||
|
var expectedMessage = new ChatResponse
|
||||||
|
{
|
||||||
|
Id = "msg_1nZdL29xx5MUA1yADyHTEsnR8uuvGzszyY",
|
||||||
|
Type = "message",
|
||||||
|
Role = "assistant",
|
||||||
|
Content = [],
|
||||||
|
Model = "claude-3-5-sonnet-20240620",
|
||||||
|
StopReason = string.Empty,
|
||||||
|
StopSequence = string.Empty,
|
||||||
|
Usage = new ChatUsage { InputTokens = 25, OutputTokens = 1 }
|
||||||
|
};
|
||||||
|
|
||||||
|
var messageStartEventData = Deserialize<MessageStartEventData>(_testJson);
|
||||||
|
|
||||||
|
messageStartEventData!.Message.Should().BeEquivalentTo(expectedMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,237 @@
|
|||||||
|
namespace AnthropicClient.Tests.Unit.Models;
|
||||||
|
|
||||||
|
public class StreamChatMessageRequestTests : SerializationTest
|
||||||
|
{
|
||||||
|
private readonly string _testJson = @"{
|
||||||
|
""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"":""auto"" },
|
||||||
|
""tools"": [
|
||||||
|
{
|
||||||
|
""name"": ""test-tool"",
|
||||||
|
""description"": ""test-description"",
|
||||||
|
""input_schema"": {
|
||||||
|
""type"": ""object"",
|
||||||
|
""properties"": {
|
||||||
|
""test-property"": {
|
||||||
|
""type"": ""string"",
|
||||||
|
""description"": ""test-description""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
""required"": [""test-property""]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
""stream"": true
|
||||||
|
}";
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_WhenCalled_ItShouldInitializeProperties()
|
||||||
|
{
|
||||||
|
var model = AnthropicModels.Claude3Sonnet;
|
||||||
|
var messages = new List<ChatMessage> { new() };
|
||||||
|
var maxTokens = 512;
|
||||||
|
var system = "test-system";
|
||||||
|
var metadata = new Dictionary<string, object> { ["test"] = "test" };
|
||||||
|
var temperature = 0.5m;
|
||||||
|
var topK = 10;
|
||||||
|
var topP = 0.5m;
|
||||||
|
var toolChoice = new AutoToolChoice();
|
||||||
|
var tools = new List<Tool> { new() };
|
||||||
|
|
||||||
|
var chatMessageRequest = new StreamChatMessageRequest(
|
||||||
|
model: model,
|
||||||
|
messages: messages,
|
||||||
|
maxTokens: maxTokens,
|
||||||
|
system: system,
|
||||||
|
metadata: metadata,
|
||||||
|
temperature: temperature,
|
||||||
|
topK: topK,
|
||||||
|
topP: topP,
|
||||||
|
toolChoice: toolChoice,
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_WhenCalledAndModelIsNull_ItShouldThrowArgumentNullException()
|
||||||
|
{
|
||||||
|
var action = () => new StreamChatMessageRequest(
|
||||||
|
model: null!,
|
||||||
|
messages: [new()]
|
||||||
|
);
|
||||||
|
|
||||||
|
action.Should().Throw<ArgumentNullException>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_WhenCalledAndMessagesIsNull_ItShouldThrowArgumentNullException()
|
||||||
|
{
|
||||||
|
var action = () => new StreamChatMessageRequest(
|
||||||
|
model: AnthropicModels.Claude3Sonnet,
|
||||||
|
messages: null!
|
||||||
|
);
|
||||||
|
|
||||||
|
action.Should().Throw<ArgumentNullException>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_WhenCalledAndModelIsInvalid_ItShouldThrowArgumentException()
|
||||||
|
{
|
||||||
|
var action = () => new StreamChatMessageRequest(
|
||||||
|
model: "invalid-model",
|
||||||
|
messages: [new()]
|
||||||
|
);
|
||||||
|
|
||||||
|
action.Should().Throw<ArgumentException>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_WhenCalledAndMessagesIsEmpty_ItShouldThrowArgumentException()
|
||||||
|
{
|
||||||
|
var action = () => new StreamChatMessageRequest(
|
||||||
|
model: AnthropicModels.Claude3Sonnet,
|
||||||
|
messages: []
|
||||||
|
);
|
||||||
|
|
||||||
|
action.Should().Throw<ArgumentException>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_WhenCalledAndMaxTokensIsInvalid_ItShouldThrowArgumentException()
|
||||||
|
{
|
||||||
|
var action = () => new StreamChatMessageRequest(
|
||||||
|
model: AnthropicModels.Claude3Sonnet,
|
||||||
|
messages: [new()],
|
||||||
|
maxTokens: 0
|
||||||
|
);
|
||||||
|
|
||||||
|
action.Should().Throw<ArgumentException>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(-1)]
|
||||||
|
[InlineData(2)]
|
||||||
|
public void Constructor_WhenCalledAndTemperatureIsInvalid_ItShouldThrowArgumentException(decimal temperature)
|
||||||
|
{
|
||||||
|
var action = () => new StreamChatMessageRequest(
|
||||||
|
model: AnthropicModels.Claude3Sonnet,
|
||||||
|
messages: [new()],
|
||||||
|
temperature: temperature
|
||||||
|
);
|
||||||
|
|
||||||
|
action.Should().Throw<ArgumentException>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JsonSerialization_WhenSerialized_ItShouldHaveExpectedShape()
|
||||||
|
{
|
||||||
|
var messages = new List<ChatMessage>()
|
||||||
|
{
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Role = MessageRole.User,
|
||||||
|
Content = [new TextContent("Hello!")]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var model = AnthropicModels.Claude3Sonnet;
|
||||||
|
var maxTokens = 512;
|
||||||
|
var system = "test-system";
|
||||||
|
var metadata = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
["test"] = "test"
|
||||||
|
};
|
||||||
|
var temperature = 0.5m;
|
||||||
|
var topK = 10;
|
||||||
|
var topP = 0.5m;
|
||||||
|
var toolChoice = new AutoToolChoice();
|
||||||
|
var tools = new List<Tool>
|
||||||
|
{
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Name = "test-tool",
|
||||||
|
Description = "test-description",
|
||||||
|
InputSchema = new InputSchema(
|
||||||
|
properties: new Dictionary<string, InputProperty>
|
||||||
|
{
|
||||||
|
["test-property"] = new InputProperty(
|
||||||
|
type: "string",
|
||||||
|
description: "test-description"
|
||||||
|
)
|
||||||
|
},
|
||||||
|
required: ["test-property"]
|
||||||
|
),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var chatMessageRequest = new StreamChatMessageRequest(
|
||||||
|
model: model,
|
||||||
|
messages: messages,
|
||||||
|
maxTokens: maxTokens,
|
||||||
|
system: system,
|
||||||
|
metadata: metadata,
|
||||||
|
temperature: temperature,
|
||||||
|
topK: topK,
|
||||||
|
topP: topP,
|
||||||
|
toolChoice: toolChoice,
|
||||||
|
tools: tools
|
||||||
|
);
|
||||||
|
|
||||||
|
var actual = Serialize(chatMessageRequest);
|
||||||
|
|
||||||
|
JsonAssert.Equal(_testJson, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JsonDeserialization_WhenDeserialized_ItShouldHaveExpectedShape()
|
||||||
|
{
|
||||||
|
var chatMessageRequest = Deserialize<StreamChatMessageRequest>(_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);
|
||||||
|
|
||||||
|
var testValue = chatMessageRequest.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<AutoToolChoice>();
|
||||||
|
chatMessageRequest.ToolChoice!.Type.Should().Be("auto");
|
||||||
|
chatMessageRequest.Tools.Should().HaveCount(1);
|
||||||
|
chatMessageRequest.Tools![0].Name.Should().Be("test-tool");
|
||||||
|
chatMessageRequest.Tools[0].Description.Should().Be("test-description");
|
||||||
|
chatMessageRequest.Tools[0].InputSchema.Type.Should().Be("object");
|
||||||
|
chatMessageRequest.Tools[0].InputSchema.Properties.Should().HaveCount(1);
|
||||||
|
chatMessageRequest.Tools[0].InputSchema.Properties["test-property"].Type.Should().Be("string");
|
||||||
|
chatMessageRequest.Tools[0].InputSchema.Properties["test-property"].Description.Should().Be("test-description");
|
||||||
|
chatMessageRequest.Tools[0].InputSchema.Required.Should().HaveCount(1);
|
||||||
|
chatMessageRequest.Tools[0].InputSchema.Required[0].Should().Be("test-property");
|
||||||
|
chatMessageRequest.Stream.Should().BeTrue();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
namespace AnthropicClient.Tests.Unit.Models;
|
||||||
|
|
||||||
|
public class TextDeltaTests : SerializationTest
|
||||||
|
{
|
||||||
|
private readonly string _testJson = @"{
|
||||||
|
""type"": ""text_delta"",
|
||||||
|
""text"": ""Hello World!""
|
||||||
|
}";
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_WhenCalled_ItShouldInitializeProperties()
|
||||||
|
{
|
||||||
|
var text = "Hello World!";
|
||||||
|
|
||||||
|
var textDelta = new TextDelta(text);
|
||||||
|
|
||||||
|
textDelta.Type.Should().Be("text_delta");
|
||||||
|
textDelta.Text.Should().Be(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JsonSerialization_WhenSerialized_ItShouldHaveExpectedShape()
|
||||||
|
{
|
||||||
|
var textDelta = new TextDelta("Hello World!");
|
||||||
|
|
||||||
|
var actual = Serialize(textDelta);
|
||||||
|
|
||||||
|
JsonAssert.Equal(_testJson, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JsonDeserialization_WhenDeserialized_ItShouldHaveExpectedProperties()
|
||||||
|
{
|
||||||
|
var expected = new TextDelta("Hello World!");
|
||||||
|
|
||||||
|
var actual = Deserialize<TextDelta>(_testJson);
|
||||||
|
|
||||||
|
actual.Should().BeEquivalentTo(expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user