Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5ab8297a83 | ||
|
|
d27538b656 | ||
|
|
a16b64d5a0 | ||
|
|
48b2716a61 | ||
|
|
641eaf30a4 | ||
|
|
30c0116113 | ||
|
|
9b714df12f | ||
|
|
f39e76cfa6 | ||
|
|
f970b07ed7 | ||
|
|
93761b8e7e | ||
|
|
b3d4ac3faa | ||
|
|
3b47ed3fb4 | ||
|
|
0a692d1b9a |
@@ -95,6 +95,14 @@ public class AnthropicApiClient : IAnthropicApiClient
|
||||
public async IAsyncEnumerable<AnthropicEvent> CreateMessageAsync(StreamMessageRequest request)
|
||||
{
|
||||
var response = await SendRequestAsync(request);
|
||||
|
||||
if (response.IsSuccessStatusCode is false)
|
||||
{
|
||||
var error = Deserialize<AnthropicError>(await response.Content.ReadAsStringAsync()) ?? new AnthropicError();
|
||||
yield return new AnthropicEvent(EventType.Error, new ErrorEventData(error.Error));
|
||||
yield break;
|
||||
}
|
||||
|
||||
var anthropicHeaders = new AnthropicHeaders(response.Headers);
|
||||
|
||||
using var responseContent = await response.Content.ReadAsStreamAsync();
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<PackageId>AnthropicClient</PackageId>
|
||||
<Version>0.0.3</Version>
|
||||
<Version>0.0.4</Version>
|
||||
<Authors>Stevan Freeborn</Authors>
|
||||
<Description>Anthropic Client Library</Description>
|
||||
<PackageProjectUrl>https://anthropicclient.stevanfreeborn.com/</PackageProjectUrl>
|
||||
|
||||
@@ -2,6 +2,27 @@
|
||||
|
||||
All notable changes to this project will be documented in this file. See [versionize](https://github.com/versionize/versionize) for commit guidelines.
|
||||
|
||||
<a name="0.0.4"></a>
|
||||
## [0.0.4](https://www.github.com/StevanFreeborn/anthropic-client/releases/tag/v0.0.4) (2024-07-20)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add end to end test for vision ([b3d4ac3](https://www.github.com/StevanFreeborn/anthropic-client/commit/b3d4ac3faa94086369a02b0a47a19db9441b274e))
|
||||
* add missing type property to image source model ([0a692d1](https://www.github.com/StevanFreeborn/anthropic-client/commit/0a692d1b9a5f39869aef05892f0801fefe31f50e))
|
||||
* yield error when initial stream response is not successful ([3b47ed3](https://www.github.com/StevanFreeborn/anthropic-client/commit/3b47ed3fb4e8b30a73a8297e6485985fee7c129a))
|
||||
|
||||
### Other
|
||||
|
||||
* Merge pull request #11 from StevanFreeborn/stevanfreeborn/fix/missing-type-property-and-response-handling ([d27538b](https://www.github.com/StevanFreeborn/anthropic-client/commit/d27538b65620333ade65e920110a21acd829123f))
|
||||
* add missing type property ([f970b07](https://www.github.com/StevanFreeborn/anthropic-client/commit/f970b07ed79979b94961c93a192b57fc12a81097))
|
||||
* debug ci failure ([641eaf3](https://www.github.com/StevanFreeborn/anthropic-client/commit/641eaf30a476cece3cc8bb37f147aa42625992d2))
|
||||
* debug ci failure ([30c0116](https://www.github.com/StevanFreeborn/anthropic-client/commit/30c01161136949471d661284fa86838ac663ec87))
|
||||
* debug CI failure ([48b2716](https://www.github.com/StevanFreeborn/anthropic-client/commit/48b2716a61894c471bd2b703254ceb7126354f5e))
|
||||
* debug CI failure ([9b714df](https://www.github.com/StevanFreeborn/anthropic-client/commit/9b714df12f1d7a6e2e500726ac10a345e4c66054))
|
||||
* debug test failure in CI ([f39e76c](https://www.github.com/StevanFreeborn/anthropic-client/commit/f39e76cfa64f649e9297bab319ef6ddd58995e0e))
|
||||
* run format ([93761b8](https://www.github.com/StevanFreeborn/anthropic-client/commit/93761b8e7e0ac67f205abd93f6e9db9465924b02))
|
||||
* use base64 text file ([a16b64d](https://www.github.com/StevanFreeborn/anthropic-client/commit/a16b64d5a079f65884a8f3a2cf15be4da06f73d3))
|
||||
|
||||
<a name="0.0.3"></a>
|
||||
## [0.0.3](https://www.github.com/StevanFreeborn/anthropic-client/releases/tag/v0.0.3) (2024-07-11)
|
||||
|
||||
|
||||
@@ -20,6 +20,11 @@ public class ImageSource
|
||||
/// </summary>
|
||||
public string Data { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of encoding of the image data.
|
||||
/// </summary>
|
||||
public string Type { get; init; } = "base64";
|
||||
|
||||
[JsonConstructor]
|
||||
internal ImageSource()
|
||||
{
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<IsPackable>false</IsPackable>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
<NoWarn>$(NoWarn);IDE0039</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -53,5 +54,11 @@
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="Files/**">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
using Xunit.Abstractions;
|
||||
using Xunit.Sdk;
|
||||
|
||||
namespace AnthropicClient.Tests.EndToEnd;
|
||||
|
||||
public class ClientTests(ConfigurationFixture configFixture) : EndToEndTest(configFixture)
|
||||
@@ -56,4 +59,40 @@ public class ClientTests(ConfigurationFixture configFixture) : EndToEndTest(conf
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateMessageAsync_WhenImageIsSent_ItShouldReturnResponse()
|
||||
{
|
||||
var imagePath = Path.Combine(Directory.GetCurrentDirectory(), "Files", "base64-elephant.txt");
|
||||
var mediaType = "image/jpeg";
|
||||
var base64Data = await File.ReadAllTextAsync(imagePath);
|
||||
|
||||
var request = new MessageRequest(
|
||||
model: AnthropicModels.Claude3Haiku,
|
||||
messages: [
|
||||
new(MessageRole.User, [
|
||||
new ImageContent(mediaType, base64Data),
|
||||
new TextContent("What is in this image?")
|
||||
]),
|
||||
]
|
||||
);
|
||||
|
||||
var result = await _client.CreateMessageAsync(request);
|
||||
|
||||
result.IsSuccess.Should().BeTrue();
|
||||
result.Value.Should().BeOfType<MessageResponse>();
|
||||
result.Value.Content.Should().NotBeNullOrEmpty();
|
||||
|
||||
var text = result.Value.Content.Aggregate("", (acc, content) =>
|
||||
{
|
||||
if (content is TextContent textContent)
|
||||
{
|
||||
acc += textContent.Text;
|
||||
}
|
||||
|
||||
return acc;
|
||||
});
|
||||
|
||||
text.Should().Contain("elephant");
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
@@ -321,4 +321,42 @@ public class AnthropicApiClientTests : IntegrationTest
|
||||
toolCallResult.IsSuccess.Should().BeTrue();
|
||||
toolCallResult.Value.Should().Be(getWeather("San Francisco, CA", "fahrenheit"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateMessageAsync_WhenCalledMessageIsStreamAndRequestFails_ItShouldReturnErrorEvent()
|
||||
{
|
||||
_mockHttpMessageHandler
|
||||
.WhenCreateStreamMessageRequest()
|
||||
.Respond(
|
||||
HttpStatusCode.BadRequest,
|
||||
"application/json",
|
||||
@"{
|
||||
""type"": ""error"",
|
||||
""error"": {
|
||||
""type"": ""invalid_request_error"",
|
||||
""message"": ""messages: roles must alternate between user and assistant, but found multiple user roles in a row""
|
||||
}
|
||||
}"
|
||||
);
|
||||
|
||||
var request = new StreamMessageRequest(
|
||||
model: AnthropicModels.Claude35Sonnet,
|
||||
messages: [
|
||||
new(MessageRole.User, [new TextContent("Hello!")]),
|
||||
new(MessageRole.User, [new TextContent("Hello!")])
|
||||
]
|
||||
);
|
||||
|
||||
var result = Client.CreateMessageAsync(request);
|
||||
var events = await result.ToListAsync();
|
||||
|
||||
events.Should().HaveCount(1);
|
||||
events[0].Type.Should().Be(EventType.Error);
|
||||
events[0].Data.Should().BeOfType<ErrorEventData>();
|
||||
events[0].Data.Should().BeEquivalentTo(new ErrorEventData(
|
||||
new InvalidRequestError(
|
||||
"messages: roles must alternate between user and assistant, but found multiple user roles in a row"
|
||||
)
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,8 @@ public class ImageContentTests : SerializationTest
|
||||
private readonly string _testJson = @"{
|
||||
""source"": {
|
||||
""media_type"": ""image/png"",
|
||||
""data"": ""data""
|
||||
""data"": ""data"",
|
||||
""type"": ""base64""
|
||||
},
|
||||
""type"": ""image""
|
||||
}";
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
namespace AnthropicClient.Tests.Unit.Models;
|
||||
|
||||
public class ImageSourceTests
|
||||
{
|
||||
[Fact]
|
||||
public void Constructor_WhenCalledWithValidArguments_ItShouldSetProperties()
|
||||
{
|
||||
var mediaType = "image/jpeg";
|
||||
var data = "base64data";
|
||||
|
||||
var imageSource = new ImageSource(mediaType, data);
|
||||
|
||||
imageSource.MediaType.Should().Be(mediaType);
|
||||
imageSource.Data.Should().Be(data);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_WhenCalledWithInvalidMediaType_ItShouldThrowArgumentException()
|
||||
{
|
||||
var mediaType = "invalid";
|
||||
var data = "base64data";
|
||||
|
||||
var action = () => new ImageSource(mediaType, data);
|
||||
|
||||
action.Should().Throw<ArgumentException>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_WhenCalledWithNullMediaType_ItShouldThrowArgumentNullException()
|
||||
{
|
||||
string? mediaType = null;
|
||||
var data = "base64data";
|
||||
|
||||
var action = () => new ImageSource(mediaType!, data);
|
||||
|
||||
action.Should().Throw<ArgumentNullException>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_WhenCalledWithNullData_ItShouldThrowArgumentNullException()
|
||||
{
|
||||
var mediaType = "image/jpeg";
|
||||
string? data = null;
|
||||
|
||||
var action = () => new ImageSource(mediaType, data!);
|
||||
|
||||
action.Should().Throw<ArgumentNullException>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_WhenCalled_ItShouldHaveTypePropertySetToBase64()
|
||||
{
|
||||
var mediaType = "image/jpeg";
|
||||
var data = "base64data";
|
||||
|
||||
var imageSource = new ImageSource(mediaType, data);
|
||||
|
||||
imageSource.Type.Should().Be("base64");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user