tests: work on adding integration tests
This commit is contained in:
@@ -213,6 +213,12 @@ public class AnthropicApiClient : IAnthropicApiClient
|
|||||||
|
|
||||||
if (line is null)
|
if (line is null)
|
||||||
{
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(currentEvent.Type) is false)
|
||||||
|
{
|
||||||
|
yield return currentEvent;
|
||||||
|
currentEvent = new AnthropicEvent();
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,13 +26,13 @@ public class ChatResponse
|
|||||||
/// Gets the stop reason of the chat response.
|
/// Gets the stop reason of the chat response.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonPropertyName("stop_reason")]
|
[JsonPropertyName("stop_reason")]
|
||||||
public string StopReason { get; init; } = string.Empty;
|
public string? StopReason { get; init; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the stop sequence of the chat response.
|
/// Gets the stop sequence of the chat response.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonPropertyName("stop_sequence")]
|
[JsonPropertyName("stop_sequence")]
|
||||||
public string StopSequence { get; init; } = string.Empty;
|
public string? StopSequence { get; init; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the type of the chat response.
|
/// Gets the type of the chat response.
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
||||||
<PackageReference Include="RichardSzalay.MockHttp" Version="7.0.0" />
|
<PackageReference Include="RichardSzalay.MockHttp" Version="7.0.0" />
|
||||||
|
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
|
||||||
<PackageReference Include="SystemTextJson.JsonDiffPatch.Xunit" Version="2.0.0" />
|
<PackageReference Include="SystemTextJson.JsonDiffPatch.Xunit" Version="2.0.0" />
|
||||||
<PackageReference Include="xunit" Version="2.5.3" />
|
<PackageReference Include="xunit" Version="2.5.3" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" />
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" />
|
||||||
|
|||||||
@@ -31,4 +31,231 @@ public class AnthropicApiClientTests : IntegrationTest
|
|||||||
var actualErrorType = result.Error.Error!.GetType();
|
var actualErrorType = result.Error.Error!.GetType();
|
||||||
actualErrorType.Should().Be(errorType);
|
actualErrorType.Should().Be(errorType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task CreateChatMessageAsync_WhenCalledAndMessageCreatedWithTextContent_ItShouldReturnMessage()
|
||||||
|
{
|
||||||
|
_mockHttpMessageHandler
|
||||||
|
.WhenCreateMessageRequest()
|
||||||
|
.Respond(
|
||||||
|
HttpStatusCode.OK,
|
||||||
|
"application/json",
|
||||||
|
@"{
|
||||||
|
""content"": [
|
||||||
|
{
|
||||||
|
""text"": ""Hi! My name is Claude."",
|
||||||
|
""type"": ""text""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
""id"": ""msg_013Zva2CMHLNnXjNJJKqJ2EF"",
|
||||||
|
""model"": ""claude-3-5-sonnet-20240620"",
|
||||||
|
""role"": ""assistant"",
|
||||||
|
""stop_reason"": ""end_turn"",
|
||||||
|
""stop_sequence"": null,
|
||||||
|
""type"": ""message"",
|
||||||
|
""usage"": {
|
||||||
|
""input_tokens"": 10,
|
||||||
|
""output_tokens"": 25
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
);
|
||||||
|
|
||||||
|
var request = new ChatMessageRequest(
|
||||||
|
model: AnthropicModels.Claude35Sonnet,
|
||||||
|
messages: [new(MessageRole.User, [new TextContent("Hello!")])]
|
||||||
|
);
|
||||||
|
|
||||||
|
var result = await Client.CreateChatMessageAsync(request);
|
||||||
|
|
||||||
|
result.IsSuccess.Should().BeTrue();
|
||||||
|
result.Value.Should().BeOfType<ChatResponse>();
|
||||||
|
result.Error.Should().BeNull();
|
||||||
|
|
||||||
|
var message = result.Value;
|
||||||
|
message.Id.Should().Be("msg_013Zva2CMHLNnXjNJJKqJ2EF");
|
||||||
|
message.Model.Should().Be("claude-3-5-sonnet-20240620");
|
||||||
|
message.Role.Should().Be("assistant");
|
||||||
|
message.StopReason.Should().Be("end_turn");
|
||||||
|
message.StopSequence.Should().BeNull();
|
||||||
|
message.Type.Should().Be("message");
|
||||||
|
message.Usage.InputTokens.Should().Be(10);
|
||||||
|
message.Usage.OutputTokens.Should().Be(25);
|
||||||
|
message.Content.Should().HaveCount(1);
|
||||||
|
message.ToolCall.Should().BeNull();
|
||||||
|
|
||||||
|
var textContent = message.Content[0];
|
||||||
|
textContent.Should().BeOfType<TextContent>();
|
||||||
|
textContent.As<TextContent>().Text.Should().Be("Hi! My name is Claude.");
|
||||||
|
textContent.As<TextContent>().Type.Should().Be("text");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task CreateChatMessageAsync_WhenCalledAndMessageCreatedWithToolUseContentAndToolIsProvided_ItShouldReturnMessageWithToolCall()
|
||||||
|
{
|
||||||
|
_mockHttpMessageHandler
|
||||||
|
.WhenCreateMessageRequest()
|
||||||
|
.Respond(
|
||||||
|
HttpStatusCode.OK,
|
||||||
|
"application/json",
|
||||||
|
@"{
|
||||||
|
""content"": [
|
||||||
|
{
|
||||||
|
""id"": ""toolu_01D7FLrfh4GYq7yT1ULFeyMV"",
|
||||||
|
""name"": ""get_stock_price"",
|
||||||
|
""input"": { ""ticker"": ""^GSPC"" },
|
||||||
|
""type"": ""tool_use""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
""id"": ""msg_01D7FLrfh4GYq7yT1ULFeyMV"",
|
||||||
|
""model"": ""claude-3-5-sonnet-20240620"",
|
||||||
|
""role"": ""assistant"",
|
||||||
|
""stop_reason"": ""end_turn"",
|
||||||
|
""stop_sequence"": null,
|
||||||
|
""type"": ""message"",
|
||||||
|
""usage"": {
|
||||||
|
""input_tokens"": 10,
|
||||||
|
""output_tokens"": 25
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
);
|
||||||
|
|
||||||
|
var func = (string ticker) => ticker;
|
||||||
|
|
||||||
|
var request = new ChatMessageRequest(
|
||||||
|
model: AnthropicModels.Claude35Sonnet,
|
||||||
|
messages: [
|
||||||
|
new(MessageRole.User, [new TextContent("Hello!")]),
|
||||||
|
],
|
||||||
|
tools: [
|
||||||
|
Tool.CreateFromFunction("get_stock_price", "Gets the stock price for a given ticker", func)
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
var result = await Client.CreateChatMessageAsync(request);
|
||||||
|
|
||||||
|
result.IsSuccess.Should().BeTrue();
|
||||||
|
result.Value.Should().BeOfType<ChatResponse>();
|
||||||
|
result.Error.Should().BeNull();
|
||||||
|
|
||||||
|
var message = result.Value;
|
||||||
|
message.Id.Should().Be("msg_01D7FLrfh4GYq7yT1ULFeyMV");
|
||||||
|
message.Model.Should().Be("claude-3-5-sonnet-20240620");
|
||||||
|
message.Role.Should().Be("assistant");
|
||||||
|
message.StopReason.Should().Be("end_turn");
|
||||||
|
message.StopSequence.Should().BeNull();
|
||||||
|
message.Type.Should().Be("message");
|
||||||
|
message.Usage.InputTokens.Should().Be(10);
|
||||||
|
message.Usage.OutputTokens.Should().Be(25);
|
||||||
|
message.Content.Should().HaveCount(1);
|
||||||
|
message.ToolCall.Should().NotBeNull();
|
||||||
|
|
||||||
|
var toolUseContent = message.Content[0];
|
||||||
|
toolUseContent.Should().BeOfType<ToolUseContent>();
|
||||||
|
|
||||||
|
toolUseContent.As<ToolUseContent>().Id.Should().Be("toolu_01D7FLrfh4GYq7yT1ULFeyMV");
|
||||||
|
toolUseContent.As<ToolUseContent>().Name.Should().Be("get_stock_price");
|
||||||
|
toolUseContent.As<ToolUseContent>().Type.Should().Be("tool_use");
|
||||||
|
|
||||||
|
var input = toolUseContent.As<ToolUseContent>().Input;
|
||||||
|
var ticker = input.GetValueOrDefault("ticker");
|
||||||
|
ticker!.ToString().Should().Be("^GSPC");
|
||||||
|
|
||||||
|
var toolCallResult = await message.ToolCall!.InvokeAsync();
|
||||||
|
toolCallResult.IsSuccess.Should().BeTrue();
|
||||||
|
toolCallResult.Value!.ToString().Should().Be("^GSPC");
|
||||||
|
toolCallResult.Error.Should().BeNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task CreateChatMessageAsync_WhenCalledAndMessageCreatedWithToolUseButNoToolProvided_ItShouldReturnMessageWithoutToolCall()
|
||||||
|
{
|
||||||
|
_mockHttpMessageHandler
|
||||||
|
.WhenCreateMessageRequest()
|
||||||
|
.Respond(
|
||||||
|
HttpStatusCode.OK,
|
||||||
|
"application/json",
|
||||||
|
@"{
|
||||||
|
""content"": [
|
||||||
|
{
|
||||||
|
""id"": ""toolu_01D7FLrfh4GYq7yT1ULFeyMV"",
|
||||||
|
""name"": ""get_stock_price"",
|
||||||
|
""input"": { ""ticker"": ""^GSPC"" },
|
||||||
|
""type"": ""tool_use""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
""id"": ""msg_01D7FLrfh4GYq7yT1ULFeyMV"",
|
||||||
|
""model"": ""claude-3-5-sonnet-20240620"",
|
||||||
|
""role"": ""assistant"",
|
||||||
|
""stop_reason"": ""end_turn"",
|
||||||
|
""stop_sequence"": null,
|
||||||
|
""type"": ""message"",
|
||||||
|
""usage"": {
|
||||||
|
""input_tokens"": 10,
|
||||||
|
""output_tokens"": 25
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
);
|
||||||
|
|
||||||
|
var request = new ChatMessageRequest(
|
||||||
|
model: AnthropicModels.Claude35Sonnet,
|
||||||
|
messages: [
|
||||||
|
new(MessageRole.User, [new TextContent("Hello!")]),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
var result = await Client.CreateChatMessageAsync(request);
|
||||||
|
|
||||||
|
result.IsSuccess.Should().BeTrue();
|
||||||
|
result.Value.Should().BeOfType<ChatResponse>();
|
||||||
|
result.Error.Should().BeNull();
|
||||||
|
|
||||||
|
var message = result.Value;
|
||||||
|
message.Id.Should().Be("msg_01D7FLrfh4GYq7yT1ULFeyMV");
|
||||||
|
message.Model.Should().Be("claude-3-5-sonnet-20240620");
|
||||||
|
message.Role.Should().Be("assistant");
|
||||||
|
message.StopReason.Should().Be("end_turn");
|
||||||
|
message.StopSequence.Should().BeNull();
|
||||||
|
message.Type.Should().Be("message");
|
||||||
|
message.Usage.InputTokens.Should().Be(10);
|
||||||
|
message.Usage.OutputTokens.Should().Be(25);
|
||||||
|
message.Content.Should().HaveCount(1);
|
||||||
|
message.ToolCall.Should().BeNull();
|
||||||
|
|
||||||
|
var toolUseContent = message.Content[0];
|
||||||
|
toolUseContent.Should().BeOfType<ToolUseContent>();
|
||||||
|
|
||||||
|
toolUseContent.As<ToolUseContent>().Id.Should().Be("toolu_01D7FLrfh4GYq7yT1ULFeyMV");
|
||||||
|
toolUseContent.As<ToolUseContent>().Name.Should().Be("get_stock_price");
|
||||||
|
toolUseContent.As<ToolUseContent>().Type.Should().Be("tool_use");
|
||||||
|
|
||||||
|
var input = toolUseContent.As<ToolUseContent>().Input;
|
||||||
|
var ticker = input.GetValueOrDefault("ticker");
|
||||||
|
ticker!.ToString().Should().Be("^GSPC");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[ClassData(typeof(EventTestData))]
|
||||||
|
public async Task CreateChatMessageAsync_WhenCalledAndMessageIsStreamed_ItShouldReturnAllEvents(string eventString, AnthropicEvent anthropicEvent)
|
||||||
|
{
|
||||||
|
_mockHttpMessageHandler
|
||||||
|
.WhenCreateStreamMessageRequest()
|
||||||
|
.Respond(
|
||||||
|
HttpStatusCode.OK,
|
||||||
|
"text/event-stream",
|
||||||
|
new MemoryStream(Encoding.UTF8.GetBytes(eventString))
|
||||||
|
);
|
||||||
|
|
||||||
|
var request = new StreamChatMessageRequest(
|
||||||
|
model: AnthropicModels.Claude35Sonnet,
|
||||||
|
messages: [
|
||||||
|
new(MessageRole.User, [new TextContent("Hello!")]),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
var result = Client.CreateChatMessageAsync(request);
|
||||||
|
var e = await result.FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
e.Should().BeEquivalentTo(anthropicEvent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,385 @@
|
|||||||
|
namespace AnthropicClient.Tests.Integration;
|
||||||
|
|
||||||
|
public class EventTestData : IEnumerable<object[]>
|
||||||
|
{
|
||||||
|
public IEnumerator<object[]> GetEnumerator()
|
||||||
|
{
|
||||||
|
yield return new object[]
|
||||||
|
{
|
||||||
|
"""
|
||||||
|
event: message_start
|
||||||
|
data: {"type":"message_start","message":{"id":"msg_014p7gG3wDgGV9EUtLvnow3U","type":"message","role":"assistant","model":"claude-3-haiku-20240307","stop_sequence":null,"usage":{"input_tokens":472,"output_tokens":2},"content":[],"stop_reason":null}}
|
||||||
|
""",
|
||||||
|
new AnthropicEvent()
|
||||||
|
{
|
||||||
|
Type = EventType.MessageStart,
|
||||||
|
Data = new MessageStartEventData()
|
||||||
|
{
|
||||||
|
Message = new ChatResponse()
|
||||||
|
{
|
||||||
|
Id = "msg_014p7gG3wDgGV9EUtLvnow3U",
|
||||||
|
Type = "message",
|
||||||
|
Role = "assistant",
|
||||||
|
Model = "claude-3-haiku-20240307",
|
||||||
|
StopSequence = null,
|
||||||
|
Usage = new ChatUsage()
|
||||||
|
{
|
||||||
|
InputTokens = 472,
|
||||||
|
OutputTokens = 2,
|
||||||
|
},
|
||||||
|
Content = [],
|
||||||
|
StopReason = null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
yield return new object[]
|
||||||
|
{
|
||||||
|
"""
|
||||||
|
event: content_block_start
|
||||||
|
data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}
|
||||||
|
""",
|
||||||
|
new AnthropicEvent()
|
||||||
|
{
|
||||||
|
Type = EventType.ContentBlockStart,
|
||||||
|
Data = new ContentStartEventData()
|
||||||
|
{
|
||||||
|
Index = 0,
|
||||||
|
ContentBlock = new TextContent()
|
||||||
|
{
|
||||||
|
Type = "text",
|
||||||
|
Text = "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
yield return new object[]
|
||||||
|
{
|
||||||
|
"""
|
||||||
|
event: ping
|
||||||
|
data: {"type": "ping"}
|
||||||
|
""",
|
||||||
|
new AnthropicEvent()
|
||||||
|
{
|
||||||
|
Type = EventType.Ping,
|
||||||
|
Data = new PingEventData(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
yield return new object[]
|
||||||
|
{
|
||||||
|
"""
|
||||||
|
event: content_block_delta
|
||||||
|
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Okay"}}
|
||||||
|
""",
|
||||||
|
new AnthropicEvent()
|
||||||
|
{
|
||||||
|
Type = EventType.ContentBlockDelta,
|
||||||
|
Data = new ContentDeltaEventData()
|
||||||
|
{
|
||||||
|
Index = 0,
|
||||||
|
Delta = new TextDelta()
|
||||||
|
{
|
||||||
|
Type = "text_delta",
|
||||||
|
Text = "Okay",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
yield return new object[]
|
||||||
|
{
|
||||||
|
"""
|
||||||
|
event: content_block_delta
|
||||||
|
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":","}}
|
||||||
|
""",
|
||||||
|
new AnthropicEvent()
|
||||||
|
{
|
||||||
|
Type = EventType.ContentBlockDelta,
|
||||||
|
Data = new ContentDeltaEventData()
|
||||||
|
{
|
||||||
|
Index = 0,
|
||||||
|
Delta = new TextDelta()
|
||||||
|
{
|
||||||
|
Type = "text_delta",
|
||||||
|
Text = ",",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
yield return new object[]
|
||||||
|
{
|
||||||
|
"""
|
||||||
|
event: content_block_delta
|
||||||
|
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" let"}}
|
||||||
|
""",
|
||||||
|
new AnthropicEvent()
|
||||||
|
{
|
||||||
|
Type = EventType.ContentBlockDelta,
|
||||||
|
Data = new ContentDeltaEventData()
|
||||||
|
{
|
||||||
|
Index = 0,
|
||||||
|
Delta = new TextDelta()
|
||||||
|
{
|
||||||
|
Type = "text_delta",
|
||||||
|
Text = " let",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
yield return new object[]
|
||||||
|
{
|
||||||
|
"""
|
||||||
|
event: content_block_delta
|
||||||
|
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"'s"}}
|
||||||
|
""",
|
||||||
|
new AnthropicEvent()
|
||||||
|
{
|
||||||
|
Type = EventType.ContentBlockDelta,
|
||||||
|
Data = new ContentDeltaEventData()
|
||||||
|
{
|
||||||
|
Index = 0,
|
||||||
|
Delta = new TextDelta()
|
||||||
|
{
|
||||||
|
Type = "text_delta",
|
||||||
|
Text = "'s",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
yield return new object[]
|
||||||
|
{
|
||||||
|
"""
|
||||||
|
event: content_block_delta
|
||||||
|
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" check"}}
|
||||||
|
""",
|
||||||
|
new AnthropicEvent()
|
||||||
|
{
|
||||||
|
Type = EventType.ContentBlockDelta,
|
||||||
|
Data = new ContentDeltaEventData()
|
||||||
|
{
|
||||||
|
Index = 0,
|
||||||
|
Delta = new TextDelta()
|
||||||
|
{
|
||||||
|
Type = "text_delta",
|
||||||
|
Text = " check",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
yield return new object[]
|
||||||
|
{
|
||||||
|
"""
|
||||||
|
event: content_block_delta
|
||||||
|
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" the"}}
|
||||||
|
""",
|
||||||
|
new AnthropicEvent()
|
||||||
|
{
|
||||||
|
Type = EventType.ContentBlockDelta,
|
||||||
|
Data = new ContentDeltaEventData()
|
||||||
|
{
|
||||||
|
Index = 0,
|
||||||
|
Delta = new TextDelta()
|
||||||
|
{
|
||||||
|
Type = "text_delta",
|
||||||
|
Text = " the",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
yield return new object[]
|
||||||
|
{
|
||||||
|
"""
|
||||||
|
event: content_block_delta
|
||||||
|
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" weather"}}
|
||||||
|
""",
|
||||||
|
new AnthropicEvent()
|
||||||
|
{
|
||||||
|
Type = EventType.ContentBlockDelta,
|
||||||
|
Data = new ContentDeltaEventData()
|
||||||
|
{
|
||||||
|
Index = 0,
|
||||||
|
Delta = new TextDelta()
|
||||||
|
{
|
||||||
|
Type = "text_delta",
|
||||||
|
Text = " weather",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
yield return new object[]
|
||||||
|
{
|
||||||
|
"""
|
||||||
|
event: content_block_delta
|
||||||
|
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" for"}}
|
||||||
|
""",
|
||||||
|
new AnthropicEvent()
|
||||||
|
{
|
||||||
|
Type = EventType.ContentBlockDelta,
|
||||||
|
Data = new ContentDeltaEventData()
|
||||||
|
{
|
||||||
|
Index = 0,
|
||||||
|
Delta = new TextDelta()
|
||||||
|
{
|
||||||
|
Type = "text_delta",
|
||||||
|
Text = " for",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
yield return new object[]
|
||||||
|
{
|
||||||
|
"""
|
||||||
|
event: content_block_delta
|
||||||
|
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" San"}}
|
||||||
|
""",
|
||||||
|
new AnthropicEvent()
|
||||||
|
{
|
||||||
|
Type = EventType.ContentBlockDelta,
|
||||||
|
Data = new ContentDeltaEventData()
|
||||||
|
{
|
||||||
|
Index = 0,
|
||||||
|
Delta = new TextDelta()
|
||||||
|
{
|
||||||
|
Type = "text_delta",
|
||||||
|
Text = " San",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
yield return new object[]
|
||||||
|
{
|
||||||
|
"""
|
||||||
|
event: content_block_delta
|
||||||
|
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" Francisco"}}
|
||||||
|
""",
|
||||||
|
new AnthropicEvent()
|
||||||
|
{
|
||||||
|
Type = EventType.ContentBlockDelta,
|
||||||
|
Data = new ContentDeltaEventData()
|
||||||
|
{
|
||||||
|
Index = 0,
|
||||||
|
Delta = new TextDelta()
|
||||||
|
{
|
||||||
|
Type = "text_delta",
|
||||||
|
Text = " Francisco",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
yield return new object[]
|
||||||
|
{
|
||||||
|
"""
|
||||||
|
event: content_block_delta
|
||||||
|
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":","}}
|
||||||
|
""",
|
||||||
|
new AnthropicEvent()
|
||||||
|
{
|
||||||
|
Type = EventType.ContentBlockDelta,
|
||||||
|
Data = new ContentDeltaEventData()
|
||||||
|
{
|
||||||
|
Index = 0,
|
||||||
|
Delta = new TextDelta()
|
||||||
|
{
|
||||||
|
Type = "text_delta",
|
||||||
|
Text = " ,",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
yield return new object[]
|
||||||
|
{
|
||||||
|
"""
|
||||||
|
event: content_block_delta
|
||||||
|
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" CA"}}
|
||||||
|
""",
|
||||||
|
new AnthropicEvent()
|
||||||
|
{
|
||||||
|
Type = EventType.ContentBlockDelta,
|
||||||
|
Data = new ContentDeltaEventData()
|
||||||
|
{
|
||||||
|
Index = 0,
|
||||||
|
Delta = new TextDelta()
|
||||||
|
{
|
||||||
|
Type = "text_delta",
|
||||||
|
Text = " CA",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
yield return new object[]
|
||||||
|
{
|
||||||
|
"""
|
||||||
|
event: content_block_delta
|
||||||
|
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":":"}}
|
||||||
|
""",
|
||||||
|
new AnthropicEvent()
|
||||||
|
{
|
||||||
|
Type = EventType.ContentBlockDelta,
|
||||||
|
Data = new ContentDeltaEventData()
|
||||||
|
{
|
||||||
|
Index = 0,
|
||||||
|
Delta = new TextDelta()
|
||||||
|
{
|
||||||
|
Type = "text_delta",
|
||||||
|
Text = " :",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
yield return new object[]
|
||||||
|
{
|
||||||
|
"""
|
||||||
|
event: content_block_stop
|
||||||
|
data: {"type":"content_block_stop","index":0}
|
||||||
|
""",
|
||||||
|
new AnthropicEvent()
|
||||||
|
{
|
||||||
|
Type = EventType.ContentBlockStop,
|
||||||
|
Data = new ContentStopEventData()
|
||||||
|
{
|
||||||
|
Index = 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
yield return new object[]
|
||||||
|
{
|
||||||
|
"""
|
||||||
|
event: content_block_start
|
||||||
|
data: {"type":"content_block_start","index":1,"content_block":{"type":"tool_use","id":"toolu_01T1x1fJ34qAmk2tNTrN7Up6","name":"get_weather","input":{}}}
|
||||||
|
""",
|
||||||
|
new AnthropicEvent()
|
||||||
|
{
|
||||||
|
Type = EventType.ContentBlockStart,
|
||||||
|
Data = new ContentStartEventData()
|
||||||
|
{
|
||||||
|
Index = 1,
|
||||||
|
ContentBlock = new ToolUseContent()
|
||||||
|
{
|
||||||
|
Type = "tool_use",
|
||||||
|
Id = "toolu_01T1x1fJ34qAmk2tNTrN7Up6",
|
||||||
|
Name = "get_weather",
|
||||||
|
Input = [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||||
|
}
|
||||||
@@ -28,13 +28,13 @@ public static class MockHttpMessageHandlerExtensions
|
|||||||
{
|
{
|
||||||
return mockHttpMessageHandler
|
return mockHttpMessageHandler
|
||||||
.SetupBaseRequest()
|
.SetupBaseRequest()
|
||||||
.WithJsonContent<ChatMessageRequest>(r => r.Stream == false);
|
.WithJsonContent<ChatMessageRequest>(r => r.Stream == false, JsonSerializationOptions.DefaultOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MockedRequest WhenCreateStreamMessageRequest(this MockHttpMessageHandler mockHttpMessageHandler)
|
public static MockedRequest WhenCreateStreamMessageRequest(this MockHttpMessageHandler mockHttpMessageHandler)
|
||||||
{
|
{
|
||||||
return mockHttpMessageHandler
|
return mockHttpMessageHandler
|
||||||
.SetupBaseRequest()
|
.SetupBaseRequest()
|
||||||
.WithJsonContent<ChatMessageRequest>(r => r.Stream == true);
|
.WithJsonContent<StreamChatMessageRequest>(r => r.Stream == true, JsonSerializationOptions.DefaultOptions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
|
global using System.Text;
|
||||||
global using System.Text.Json;
|
global using System.Text.Json;
|
||||||
global using System.Text.Json.JsonDiffPatch.Xunit;
|
global using System.Text.Json.JsonDiffPatch.Xunit;
|
||||||
|
|
||||||
global using AnthropicClient.Models;
|
global using AnthropicClient.Models;
|
||||||
global using AnthropicClient.Utils;
|
global using AnthropicClient.Utils;
|
||||||
|
global using AnthropicClient.Json;
|
||||||
|
|
||||||
global using FluentAssertions;
|
global using FluentAssertions;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user