2025-01-09 23:17:41 -06:00
|
|
|
using AnthropicClient.Tests.Files;
|
|
|
|
|
|
2024-06-27 23:26:53 -05:00
|
|
|
namespace AnthropicClient.Tests.EndToEnd;
|
|
|
|
|
|
2025-07-14 23:01:23 -05:00
|
|
|
public class AnthropicApiClientTests(ConfigurationFixture configFixture) : EndToEndTest(configFixture), IAsyncLifetime
|
2024-06-27 23:26:53 -05:00
|
|
|
{
|
2025-07-14 23:01:23 -05:00
|
|
|
private readonly List<string> _filesToDelete = [];
|
|
|
|
|
|
|
|
|
|
public Task InitializeAsync()
|
|
|
|
|
{
|
|
|
|
|
return Task.CompletedTask;
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-27 23:26:53 -05:00
|
|
|
[Fact]
|
2024-07-03 16:57:27 -05:00
|
|
|
public async Task CreateMessageAsync_WhenCalled_ItShouldReturnResponse()
|
2024-06-27 23:26:53 -05:00
|
|
|
{
|
2024-07-03 16:57:27 -05:00
|
|
|
var request = new MessageRequest(
|
2024-06-27 23:26:53 -05:00
|
|
|
model: AnthropicModels.Claude3Haiku,
|
|
|
|
|
messages: [new(MessageRole.User, [new TextContent("Hello!")])]
|
|
|
|
|
);
|
|
|
|
|
|
2024-07-03 16:57:27 -05:00
|
|
|
var result = await _client.CreateMessageAsync(request);
|
2024-06-27 23:26:53 -05:00
|
|
|
|
|
|
|
|
result.IsSuccess.Should().BeTrue();
|
2024-07-03 16:57:27 -05:00
|
|
|
result.Value.Should().BeOfType<MessageResponse>();
|
2024-07-01 12:09:51 -05:00
|
|
|
result.Value.Content.Should().NotBeNullOrEmpty();
|
2024-06-27 23:26:53 -05:00
|
|
|
}
|
2024-06-28 10:54:00 -05:00
|
|
|
|
|
|
|
|
[Fact]
|
2024-07-03 16:57:27 -05:00
|
|
|
public async Task CreateMessageAsync_WhenCalledWithStreamRequest_ItShouldReturnEvents()
|
2024-06-28 10:54:00 -05:00
|
|
|
{
|
2024-07-03 16:57:27 -05:00
|
|
|
var request = new StreamMessageRequest(
|
2024-06-28 10:54:00 -05:00
|
|
|
model: AnthropicModels.Claude3Haiku,
|
|
|
|
|
messages: [new(MessageRole.User, [new TextContent("Hello!")])]
|
|
|
|
|
);
|
|
|
|
|
|
2024-07-03 16:57:27 -05:00
|
|
|
var response = _client.CreateMessageAsync(request);
|
2024-07-01 22:46:02 -05:00
|
|
|
|
2024-06-28 10:54:00 -05:00
|
|
|
var events = new List<AnthropicEvent>();
|
|
|
|
|
|
|
|
|
|
await foreach (var e in response)
|
|
|
|
|
{
|
|
|
|
|
events.Add(e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
events.Should().NotBeEmpty();
|
|
|
|
|
}
|
2024-06-28 16:30:09 -05:00
|
|
|
|
|
|
|
|
[Fact]
|
2024-07-03 16:57:27 -05:00
|
|
|
public async Task CreateMessageAsync_WhenCalledWithStreamRequest_ItShouldYieldAMessageCompleteEvent()
|
2024-06-28 16:30:09 -05:00
|
|
|
{
|
2024-07-03 16:57:27 -05:00
|
|
|
var request = new StreamMessageRequest(
|
2024-06-28 16:30:09 -05:00
|
|
|
model: AnthropicModels.Claude3Haiku,
|
|
|
|
|
messages: [new(MessageRole.User, [new TextContent("Hello!")])]
|
|
|
|
|
);
|
|
|
|
|
|
2024-07-03 16:57:27 -05:00
|
|
|
var response = _client.CreateMessageAsync(request);
|
2024-07-01 22:46:02 -05:00
|
|
|
|
2024-06-28 16:30:09 -05:00
|
|
|
await foreach (var e in response)
|
|
|
|
|
{
|
|
|
|
|
if (e.Data is MessageCompleteEventData messageCompleteData)
|
|
|
|
|
{
|
|
|
|
|
messageCompleteData.Message.Should().NotBeNull();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-07-19 21:31:49 -05:00
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public async Task CreateMessageAsync_WhenImageIsSent_ItShouldReturnResponse()
|
|
|
|
|
{
|
2025-01-09 23:17:41 -06:00
|
|
|
var imagePath = TestFileHelper.GetTestFilePath("elephant.jpg");
|
2024-07-19 21:31:49 -05:00
|
|
|
var mediaType = "image/jpeg";
|
2024-07-19 22:47:47 -05:00
|
|
|
var bytes = await File.ReadAllBytesAsync(imagePath);
|
|
|
|
|
var base64Data = Convert.ToBase64String(bytes);
|
2024-07-19 21:31:49 -05:00
|
|
|
|
|
|
|
|
var request = new MessageRequest(
|
|
|
|
|
model: AnthropicModels.Claude3Haiku,
|
|
|
|
|
messages: [
|
|
|
|
|
new(MessageRole.User, [
|
2024-07-19 22:38:47 -05:00
|
|
|
new ImageContent(mediaType, base64Data),
|
2024-07-19 21:31:49 -05:00
|
|
|
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();
|
2024-07-19 21:38:52 -05:00
|
|
|
|
2025-06-10 23:54:43 -05:00
|
|
|
var text = result.Value.Content.Aggregate("", static (acc, content) =>
|
2024-07-19 21:31:49 -05:00
|
|
|
{
|
|
|
|
|
if (content is TextContent textContent)
|
|
|
|
|
{
|
|
|
|
|
acc += textContent.Text;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return acc;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
text.Should().Contain("elephant");
|
|
|
|
|
}
|
2024-08-18 12:54:13 -05:00
|
|
|
|
2025-07-14 23:01:23 -05:00
|
|
|
[Fact]
|
|
|
|
|
public async Task CreateMessageAsync_WhenImageIsSentAsUrl_ItShouldReturnResponse()
|
|
|
|
|
{
|
|
|
|
|
var request = new MessageRequest(
|
|
|
|
|
model: AnthropicModels.Claude3Haiku,
|
|
|
|
|
messages: [
|
|
|
|
|
new(MessageRole.User, [
|
|
|
|
|
new ImageContent(new UrlSource("https://ftp.stevanfreeborn.com/share/anthropic-client/ant.jpg")),
|
|
|
|
|
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("", static (acc, content) =>
|
|
|
|
|
{
|
|
|
|
|
if (content is TextContent textContent)
|
|
|
|
|
{
|
|
|
|
|
acc += textContent.Text;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return acc;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
text.Should().Contain("ant");
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-18 12:54:13 -05:00
|
|
|
[Fact]
|
|
|
|
|
public async Task CreateMessageAsync_WhenSystemMessagesContainCacheControl_ItShouldUseCache()
|
|
|
|
|
{
|
2025-01-09 23:17:41 -06:00
|
|
|
var storyPath = TestFileHelper.GetTestFilePath("story.txt");
|
2024-08-18 12:54:13 -05:00
|
|
|
var storyText = await File.ReadAllTextAsync(storyPath);
|
|
|
|
|
|
|
|
|
|
var request = new MessageRequest(
|
|
|
|
|
model: AnthropicModels.Claude3Haiku,
|
|
|
|
|
systemMessages: [
|
|
|
|
|
new("You are a helpful assistant who can answer questions about the following text:"),
|
|
|
|
|
new(storyText, new EphemeralCacheControl())
|
|
|
|
|
],
|
|
|
|
|
messages: [
|
|
|
|
|
new(MessageRole.User, [
|
|
|
|
|
new TextContent("Give me a one sentence summary of this story.")
|
|
|
|
|
]),
|
|
|
|
|
]
|
|
|
|
|
);
|
|
|
|
|
|
2025-01-02 20:47:35 -06:00
|
|
|
var resultOne = await _client.CreateMessageAsync(request);
|
2024-08-18 12:54:13 -05:00
|
|
|
|
|
|
|
|
resultOne.IsSuccess.Should().BeTrue();
|
|
|
|
|
resultOne.Value.Should().BeOfType<MessageResponse>();
|
|
|
|
|
resultOne.Value.Content.Should().NotBeNullOrEmpty();
|
2025-06-10 23:54:43 -05:00
|
|
|
resultOne.Value.Usage.Should().Match<Usage>(static u => u.CacheCreationInputTokens > 0 || u.CacheReadInputTokens > 0);
|
2024-08-18 12:54:13 -05:00
|
|
|
|
|
|
|
|
request.Messages.Add(new(MessageRole.Assistant, resultOne.Value.Content));
|
|
|
|
|
request.Messages.Add(new(MessageRole.User, [new TextContent("What is the main theme of this story?")]));
|
|
|
|
|
|
2025-07-14 23:01:23 -05:00
|
|
|
var resultTwo = await _client.CreateMessageAsync(request);
|
2024-08-18 12:54:13 -05:00
|
|
|
|
|
|
|
|
resultTwo.IsSuccess.Should().BeTrue();
|
|
|
|
|
resultTwo.Value.Should().BeOfType<MessageResponse>();
|
|
|
|
|
resultTwo.Value.Content.Should().NotBeNullOrEmpty();
|
|
|
|
|
resultTwo.Value.Usage.CacheReadInputTokens.Should().BeGreaterThan(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public async Task CreateMessageAsync_WhenMessagesContainCacheControl_ItShouldUseCache()
|
|
|
|
|
{
|
2025-01-09 23:17:41 -06:00
|
|
|
var storyPath = TestFileHelper.GetTestFilePath("story.txt");
|
2024-08-18 12:54:13 -05:00
|
|
|
var storyText = await File.ReadAllTextAsync(storyPath);
|
|
|
|
|
|
|
|
|
|
var request = new MessageRequest(
|
|
|
|
|
model: AnthropicModels.Claude3Haiku,
|
|
|
|
|
messages: [
|
|
|
|
|
new(MessageRole.User, [
|
|
|
|
|
new TextContent("Give me a one sentence summary of this story."),
|
|
|
|
|
new TextContent(storyText, new EphemeralCacheControl())
|
|
|
|
|
]),
|
|
|
|
|
]
|
|
|
|
|
);
|
|
|
|
|
|
2025-07-14 23:01:23 -05:00
|
|
|
var resultOne = await _client.CreateMessageAsync(request);
|
2024-08-18 12:54:13 -05:00
|
|
|
|
|
|
|
|
resultOne.IsSuccess.Should().BeTrue();
|
|
|
|
|
resultOne.Value.Should().BeOfType<MessageResponse>();
|
|
|
|
|
resultOne.Value.Content.Should().NotBeNullOrEmpty();
|
2025-06-10 23:54:43 -05:00
|
|
|
resultOne.Value.Usage.Should().Match<Usage>(static u => u.CacheCreationInputTokens > 0 || u.CacheReadInputTokens > 0);
|
2024-08-18 12:54:13 -05:00
|
|
|
|
|
|
|
|
request.Messages.Add(new(MessageRole.Assistant, resultOne.Value.Content));
|
|
|
|
|
request.Messages.Add(new(MessageRole.User, [new TextContent("What is the main theme of this story?")]));
|
|
|
|
|
|
2025-07-14 23:01:23 -05:00
|
|
|
var resultTwo = await _client.CreateMessageAsync(request);
|
2024-08-18 12:54:13 -05:00
|
|
|
|
|
|
|
|
resultTwo.IsSuccess.Should().BeTrue();
|
|
|
|
|
resultTwo.Value.Should().BeOfType<MessageResponse>();
|
|
|
|
|
resultTwo.Value.Content.Should().NotBeNullOrEmpty();
|
|
|
|
|
resultTwo.Value.Usage.CacheReadInputTokens.Should().BeGreaterThan(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public async Task CreateMessageAsync_WhenToolsContainCacheControl_ItShouldUseCache()
|
|
|
|
|
{
|
|
|
|
|
var func = (string ticker) => ticker;
|
|
|
|
|
|
|
|
|
|
var tools = Enumerable
|
|
|
|
|
.Range(0, 50)
|
|
|
|
|
.Select(i => Tool.CreateFromFunction($"tool-{i}", $"Tool {i}", func))
|
|
|
|
|
.ToList();
|
|
|
|
|
|
|
|
|
|
tools.Last().CacheControl = new EphemeralCacheControl();
|
|
|
|
|
|
|
|
|
|
var request = new MessageRequest(
|
|
|
|
|
model: AnthropicModels.Claude3Haiku,
|
|
|
|
|
messages: [
|
|
|
|
|
new(MessageRole.User, [
|
|
|
|
|
new TextContent("Hi could you tell me your name?"),
|
|
|
|
|
]),
|
|
|
|
|
],
|
|
|
|
|
tools: tools
|
|
|
|
|
);
|
|
|
|
|
|
2025-07-14 23:01:23 -05:00
|
|
|
var resultOne = await _client.CreateMessageAsync(request);
|
2024-08-18 12:54:13 -05:00
|
|
|
|
|
|
|
|
resultOne.IsSuccess.Should().BeTrue();
|
|
|
|
|
resultOne.Value.Should().BeOfType<MessageResponse>();
|
|
|
|
|
resultOne.Value.Content.Should().NotBeNullOrEmpty();
|
|
|
|
|
resultOne.Value.Usage.Should().Match<Usage>(u => u.CacheCreationInputTokens > 0 || u.CacheReadInputTokens > 0);
|
|
|
|
|
|
|
|
|
|
request.Messages.Add(new(MessageRole.Assistant, resultOne.Value.Content));
|
|
|
|
|
request.Messages.Add(new(MessageRole.User, [new TextContent("Could you tell me the stock price for AAPL?")]));
|
|
|
|
|
|
2025-07-14 23:01:23 -05:00
|
|
|
var resultTwo = await _client.CreateMessageAsync(request);
|
2024-08-18 12:54:13 -05:00
|
|
|
|
|
|
|
|
resultTwo.IsSuccess.Should().BeTrue();
|
|
|
|
|
resultTwo.Value.Should().BeOfType<MessageResponse>();
|
|
|
|
|
resultTwo.Value.Content.Should().NotBeNullOrEmpty();
|
|
|
|
|
resultTwo.Value.Usage.CacheReadInputTokens.Should().BeGreaterThan(0);
|
|
|
|
|
}
|
2024-11-21 13:43:41 -06:00
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public async Task CreateMessageAsync_WhenProvidedWithPDF_ItShouldReturnResponse()
|
|
|
|
|
{
|
2025-01-09 23:17:41 -06:00
|
|
|
var pdfPath = TestFileHelper.GetTestFilePath("addendum.pdf");
|
2024-11-21 13:43:41 -06:00
|
|
|
var bytes = await File.ReadAllBytesAsync(pdfPath);
|
|
|
|
|
var base64Data = Convert.ToBase64String(bytes);
|
|
|
|
|
|
|
|
|
|
var request = new MessageRequest(
|
|
|
|
|
model: AnthropicModels.Claude35Sonnet,
|
|
|
|
|
messages: [
|
|
|
|
|
new(MessageRole.User, [new TextContent("What is the title of this paper?")]),
|
|
|
|
|
new(MessageRole.User, [new DocumentContent("application/pdf", base64Data)])
|
|
|
|
|
]
|
|
|
|
|
);
|
|
|
|
|
|
2025-07-14 23:01:23 -05:00
|
|
|
var result = await _client.CreateMessageAsync(request);
|
2024-11-21 13:43:41 -06:00
|
|
|
|
|
|
|
|
result.IsSuccess.Should().BeTrue();
|
|
|
|
|
result.Value.Should().BeOfType<MessageResponse>();
|
|
|
|
|
result.Value.Content.Should().NotBeNullOrEmpty();
|
|
|
|
|
|
2025-06-10 23:54:43 -05:00
|
|
|
var text = result.Value.Content.Aggregate("", static (acc, content) =>
|
2024-11-21 13:43:41 -06:00
|
|
|
{
|
|
|
|
|
if (content is TextContent textContent)
|
|
|
|
|
{
|
|
|
|
|
acc += textContent.Text;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return acc;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
text.Should().Contain("Model Card Addendum: Claude 3.5 Haiku and Upgraded Claude 3.5 Sonnet");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public async Task CreateMessageAsync_WhenProvidedWithPDFWithCacheControl_ItShouldUseCache()
|
|
|
|
|
{
|
2025-01-09 23:17:41 -06:00
|
|
|
var pdfPath = TestFileHelper.GetTestFilePath("addendum.pdf");
|
2024-11-21 13:43:41 -06:00
|
|
|
var bytes = await File.ReadAllBytesAsync(pdfPath);
|
|
|
|
|
var base64Data = Convert.ToBase64String(bytes);
|
|
|
|
|
|
|
|
|
|
var request = new MessageRequest(
|
|
|
|
|
model: AnthropicModels.Claude35Sonnet,
|
|
|
|
|
messages: [
|
|
|
|
|
new(MessageRole.User, [
|
|
|
|
|
new DocumentContent("application/pdf", base64Data, new EphemeralCacheControl()),
|
|
|
|
|
new TextContent("What is the title of this paper?")
|
|
|
|
|
]),
|
|
|
|
|
]
|
|
|
|
|
);
|
|
|
|
|
|
2025-07-14 23:01:23 -05:00
|
|
|
var resultOne = await _client.CreateMessageAsync(request);
|
2024-11-21 13:43:41 -06:00
|
|
|
|
|
|
|
|
resultOne.IsSuccess.Should().BeTrue();
|
|
|
|
|
resultOne.Value.Should().BeOfType<MessageResponse>();
|
|
|
|
|
resultOne.Value.Content.Should().NotBeNullOrEmpty();
|
2025-06-10 23:54:43 -05:00
|
|
|
resultOne.Value.Usage.Should().Match<Usage>(static u => u.CacheCreationInputTokens > 0 || u.CacheReadInputTokens > 0);
|
2024-11-21 13:43:41 -06:00
|
|
|
|
|
|
|
|
request.Messages.Add(new(MessageRole.Assistant, resultOne.Value.Content));
|
|
|
|
|
request.Messages.Add(new(MessageRole.User, [new TextContent("What is the main theme of this paper?")]));
|
|
|
|
|
|
2025-07-14 23:01:23 -05:00
|
|
|
var resultTwo = await _client.CreateMessageAsync(request);
|
2024-11-21 13:43:41 -06:00
|
|
|
|
|
|
|
|
resultTwo.IsSuccess.Should().BeTrue();
|
|
|
|
|
resultTwo.Value.Should().BeOfType<MessageResponse>();
|
|
|
|
|
resultTwo.Value.Content.Should().NotBeNullOrEmpty();
|
|
|
|
|
resultTwo.Value.Usage.CacheReadInputTokens.Should().BeGreaterThan(0);
|
|
|
|
|
}
|
2025-01-01 20:13:01 -06:00
|
|
|
|
2025-06-10 23:34:33 -05:00
|
|
|
[Fact]
|
2025-06-14 13:44:10 -05:00
|
|
|
public async Task CreateMessageAsync_WhenCitationsAreEnabledForTextDocumentSource_ItShouldReturnCitationsInResponse()
|
2025-06-10 23:34:33 -05:00
|
|
|
{
|
|
|
|
|
var request = new MessageRequest(
|
2025-06-14 13:44:10 -05:00
|
|
|
model: AnthropicModels.Claude35HaikuLatest,
|
2025-06-10 23:34:33 -05:00
|
|
|
messages: [
|
|
|
|
|
new(
|
|
|
|
|
MessageRole.User,
|
|
|
|
|
[
|
|
|
|
|
new DocumentContent(
|
2025-06-14 13:44:10 -05:00
|
|
|
new TextSource("The grass is green. The sky is blue.")
|
2025-06-10 23:34:33 -05:00
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
Title = "My Document",
|
|
|
|
|
Context = "This is a trustworthy document.",
|
|
|
|
|
Citations = new() { Enabled = true }
|
|
|
|
|
},
|
|
|
|
|
new TextContent("What color is the grass and sky?"),
|
|
|
|
|
]
|
|
|
|
|
)
|
|
|
|
|
]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
var result = await _client.CreateMessageAsync(request);
|
|
|
|
|
|
|
|
|
|
result.IsSuccess.Should().BeTrue();
|
2025-06-14 13:44:10 -05:00
|
|
|
|
|
|
|
|
var citations = result.Value
|
|
|
|
|
.Content
|
|
|
|
|
.OfType<TextContent>()
|
2025-06-14 22:24:44 -05:00
|
|
|
.SelectMany(static c => c.Citations is null ? [] : c.Citations);
|
2025-06-14 13:44:10 -05:00
|
|
|
|
|
|
|
|
citations.OfType<CharacterLocationCitation>().Should().NotBeEmpty();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public async Task CreateMessageAsync_WhenCitationsAreEnabledForPDFDocumentSource_ItShouldReturnCitationsInResponse()
|
|
|
|
|
{
|
|
|
|
|
var pdfPath = TestFileHelper.GetTestFilePath("addendum.pdf");
|
|
|
|
|
var bytes = await File.ReadAllBytesAsync(pdfPath);
|
|
|
|
|
var base64Data = Convert.ToBase64String(bytes);
|
|
|
|
|
|
|
|
|
|
var request = new MessageRequest(
|
|
|
|
|
model: AnthropicModels.Claude35HaikuLatest,
|
|
|
|
|
messages: [
|
|
|
|
|
new(
|
|
|
|
|
MessageRole.User,
|
|
|
|
|
[
|
|
|
|
|
new DocumentContent("application/pdf", base64Data)
|
|
|
|
|
{
|
|
|
|
|
Title = "My PDF Document",
|
|
|
|
|
Context = "This is a trustworthy document.",
|
|
|
|
|
Citations = new() { Enabled = true }
|
|
|
|
|
},
|
|
|
|
|
new TextContent("What is the title of this paper?"),
|
|
|
|
|
]
|
|
|
|
|
)
|
|
|
|
|
]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
var result = await _client.CreateMessageAsync(request);
|
|
|
|
|
|
|
|
|
|
result.IsSuccess.Should().BeTrue();
|
|
|
|
|
|
|
|
|
|
var citations = result.Value
|
|
|
|
|
.Content
|
|
|
|
|
.OfType<TextContent>()
|
|
|
|
|
.SelectMany(static c => c.Citations is null ? [] : c.Citations);
|
|
|
|
|
|
|
|
|
|
citations.OfType<PageLocationCitation>().Should().NotBeEmpty();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public async Task CreateMessageAsync_WhenCitationsAreEnabledForCustomDocumentSource_ItShouldReturnCitationsInResponse()
|
|
|
|
|
{
|
|
|
|
|
var request = new MessageRequest(
|
|
|
|
|
model: AnthropicModels.Claude35HaikuLatest,
|
|
|
|
|
messages: [
|
|
|
|
|
new(
|
|
|
|
|
MessageRole.User,
|
|
|
|
|
[
|
|
|
|
|
new DocumentContent(
|
|
|
|
|
new CustomSource([
|
|
|
|
|
new TextContent("The grass is green. The sky is blue.")
|
|
|
|
|
])
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
Title = "My Custom Document",
|
|
|
|
|
Context = "This is a trustworthy document.",
|
|
|
|
|
Citations = new() { Enabled = true }
|
|
|
|
|
},
|
|
|
|
|
new TextContent("What color is the grass and sky?"),
|
|
|
|
|
]
|
|
|
|
|
)
|
|
|
|
|
]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
var result = await _client.CreateMessageAsync(request);
|
|
|
|
|
|
|
|
|
|
result.IsSuccess.Should().BeTrue();
|
|
|
|
|
|
|
|
|
|
var citations = result.Value
|
|
|
|
|
.Content
|
|
|
|
|
.OfType<TextContent>()
|
|
|
|
|
.SelectMany(static c => c.Citations is null ? [] : c.Citations);
|
|
|
|
|
|
|
|
|
|
citations.OfType<ContentBlockLocationCitation>().Should().NotBeEmpty();
|
2025-06-10 23:34:33 -05:00
|
|
|
}
|
|
|
|
|
|
2025-07-14 23:01:23 -05:00
|
|
|
[Fact]
|
|
|
|
|
public async Task CreateMessageAsync_WhenCitationsAreEnabledForFileSource_ItShouldReturnCitationsInResponse()
|
|
|
|
|
{
|
|
|
|
|
var fileName = "story.txt";
|
|
|
|
|
var fileType = "text/plain";
|
|
|
|
|
var filePath = TestFileHelper.GetTestFilePath("story.txt");
|
|
|
|
|
var fileContent = await File.ReadAllBytesAsync(filePath);
|
|
|
|
|
var createFileRequest = new CreateFileRequest(fileContent, fileName, fileType);
|
|
|
|
|
|
|
|
|
|
var httpClient = new HttpClient();
|
|
|
|
|
httpClient.DefaultRequestHeaders.Add("anthropic-beta", "files-api-2025-04-14");
|
|
|
|
|
var client = CreateClient(httpClient);
|
|
|
|
|
|
|
|
|
|
var createdFile = await client.CreateFileAsync(createFileRequest);
|
|
|
|
|
|
|
|
|
|
var request = new MessageRequest(
|
|
|
|
|
model: AnthropicModels.Claude35HaikuLatest,
|
|
|
|
|
messages: [
|
|
|
|
|
new(
|
|
|
|
|
MessageRole.User,
|
|
|
|
|
[
|
|
|
|
|
new DocumentContent(new FileSource(createdFile.Value.Id))
|
|
|
|
|
{
|
|
|
|
|
Title = "A Story",
|
|
|
|
|
Context = "This is a trustworthy document.",
|
|
|
|
|
Citations = new() { Enabled = true }
|
|
|
|
|
},
|
|
|
|
|
new TextContent("Can you tell me what the title of this story is?"),
|
|
|
|
|
]
|
|
|
|
|
)
|
|
|
|
|
]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
var result = await client.CreateMessageAsync(request);
|
|
|
|
|
|
|
|
|
|
result.IsSuccess.Should().BeTrue();
|
|
|
|
|
|
|
|
|
|
var textContents = result.Value.Content.OfType<TextContent>();
|
|
|
|
|
|
|
|
|
|
var messageContent = textContents.Aggregate(new StringBuilder(), (sb, content) =>
|
|
|
|
|
{
|
|
|
|
|
sb.Append(content.Text);
|
|
|
|
|
return sb;
|
|
|
|
|
});
|
|
|
|
|
messageContent.ToString().Should().MatchRegex("The Forgotten Lighthouse");
|
|
|
|
|
|
|
|
|
|
var citations = textContents.SelectMany(static c => c.Citations is null ? [] : c.Citations);
|
|
|
|
|
citations.OfType<CharacterLocationCitation>().Should().NotBeEmpty();
|
|
|
|
|
|
|
|
|
|
_filesToDelete.Add(createdFile.Value.Id);
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-14 22:24:44 -05:00
|
|
|
[Fact]
|
|
|
|
|
public async Task CreateMessageAsync_WhenStreamingAndCitationsAreEnabledForTextDocumentSource_ItShouldReturnCitationsInResponse()
|
|
|
|
|
{
|
|
|
|
|
var request = new StreamMessageRequest(
|
|
|
|
|
model: AnthropicModels.Claude35HaikuLatest,
|
|
|
|
|
messages: [
|
|
|
|
|
new(
|
|
|
|
|
MessageRole.User,
|
|
|
|
|
[
|
|
|
|
|
new DocumentContent(
|
|
|
|
|
new TextSource("The grass is green. The sky is blue.")
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
Title = "My Document",
|
|
|
|
|
Context = "This is a trustworthy document.",
|
|
|
|
|
Citations = new() { Enabled = true }
|
|
|
|
|
},
|
|
|
|
|
new TextContent("What color is the grass and sky?"),
|
|
|
|
|
]
|
|
|
|
|
)
|
|
|
|
|
]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
var result = _client.CreateMessageAsync(request);
|
|
|
|
|
|
|
|
|
|
var messageCompleteEvent = await result
|
|
|
|
|
.Where(e => e.Type is EventType.MessageComplete)
|
|
|
|
|
.FirstAsync();
|
|
|
|
|
|
|
|
|
|
var citations = messageCompleteEvent.Data
|
|
|
|
|
.As<MessageCompleteEventData>()
|
|
|
|
|
.Message
|
|
|
|
|
.Content
|
|
|
|
|
.OfType<TextContent>()
|
|
|
|
|
.SelectMany(static c => c.Citations is null ? [] : c.Citations);
|
|
|
|
|
|
|
|
|
|
citations.OfType<CharacterLocationCitation>().Should().NotBeEmpty();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public async Task CreateMessageAsync_WhenStreamingAndCitationsAreEnabledForPDFDocumentSource_ItShouldReturnCitationsInResponse()
|
|
|
|
|
{
|
|
|
|
|
var pdfPath = TestFileHelper.GetTestFilePath("addendum.pdf");
|
|
|
|
|
var bytes = await File.ReadAllBytesAsync(pdfPath);
|
|
|
|
|
var base64Data = Convert.ToBase64String(bytes);
|
|
|
|
|
|
|
|
|
|
var request = new StreamMessageRequest(
|
|
|
|
|
model: AnthropicModels.Claude35HaikuLatest,
|
|
|
|
|
messages: [
|
|
|
|
|
new(
|
|
|
|
|
MessageRole.User,
|
|
|
|
|
[
|
|
|
|
|
new DocumentContent("application/pdf", base64Data)
|
|
|
|
|
{
|
|
|
|
|
Title = "My PDF Document",
|
|
|
|
|
Context = "This is a trustworthy document.",
|
|
|
|
|
Citations = new() { Enabled = true }
|
|
|
|
|
},
|
|
|
|
|
new TextContent("What is the title of this paper?"),
|
|
|
|
|
]
|
|
|
|
|
)
|
|
|
|
|
]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
var result = _client.CreateMessageAsync(request);
|
|
|
|
|
|
|
|
|
|
var messageCompleteEvent = await result
|
|
|
|
|
.Where(e => e.Type is EventType.MessageComplete)
|
|
|
|
|
.FirstAsync();
|
|
|
|
|
|
|
|
|
|
var citations = messageCompleteEvent.Data
|
|
|
|
|
.As<MessageCompleteEventData>()
|
|
|
|
|
.Message
|
|
|
|
|
.Content
|
|
|
|
|
.OfType<TextContent>()
|
|
|
|
|
.SelectMany(static c => c.Citations is null ? [] : c.Citations);
|
|
|
|
|
|
|
|
|
|
citations.OfType<PageLocationCitation>().Should().NotBeEmpty();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public async Task CreateMessageAsync_WhenStreamingAndCitationsAreEnabledForCustomDocumentSource_ItShouldReturnCitationsInResponse()
|
|
|
|
|
{
|
|
|
|
|
var request = new StreamMessageRequest(
|
|
|
|
|
model: AnthropicModels.Claude35HaikuLatest,
|
|
|
|
|
messages: [
|
|
|
|
|
new(
|
|
|
|
|
MessageRole.User,
|
|
|
|
|
[
|
|
|
|
|
new DocumentContent(
|
|
|
|
|
new CustomSource([
|
|
|
|
|
new TextContent("The grass is green. The sky is blue.")
|
|
|
|
|
])
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
Title = "My Custom Document",
|
|
|
|
|
Context = "This is a trustworthy document.",
|
|
|
|
|
Citations = new() { Enabled = true }
|
|
|
|
|
},
|
|
|
|
|
new TextContent("What color is the grass and sky?"),
|
|
|
|
|
]
|
|
|
|
|
)
|
|
|
|
|
]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
var result = _client.CreateMessageAsync(request);
|
|
|
|
|
|
|
|
|
|
var messageCompleteEvent = await result
|
|
|
|
|
.Where(e => e.Type is EventType.MessageComplete)
|
|
|
|
|
.FirstAsync();
|
|
|
|
|
|
|
|
|
|
var citations = messageCompleteEvent.Data
|
|
|
|
|
.As<MessageCompleteEventData>()
|
|
|
|
|
.Message
|
|
|
|
|
.Content
|
|
|
|
|
.OfType<TextContent>()
|
|
|
|
|
.SelectMany(static c => c.Citations is null ? [] : c.Citations);
|
|
|
|
|
|
|
|
|
|
citations.OfType<ContentBlockLocationCitation>().Should().NotBeEmpty();
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-14 23:01:23 -05:00
|
|
|
[Fact]
|
|
|
|
|
public async Task CreateMessageAsync_WhenStreamingAndCitationsAreEnabledForFileSource_ItShouldReturnCitationsInResponse()
|
|
|
|
|
{
|
|
|
|
|
var fileName = "story.txt";
|
|
|
|
|
var fileType = "text/plain";
|
|
|
|
|
var filePath = TestFileHelper.GetTestFilePath("story.txt");
|
|
|
|
|
var fileContent = await File.ReadAllBytesAsync(filePath);
|
|
|
|
|
var createFileRequest = new CreateFileRequest(fileContent, fileName, fileType);
|
|
|
|
|
|
|
|
|
|
using var httpClient = new HttpClient();
|
|
|
|
|
httpClient.DefaultRequestHeaders.Add("anthropic-beta", "files-api-2025-04-14");
|
|
|
|
|
var client = CreateClient(httpClient);
|
|
|
|
|
|
|
|
|
|
var createdFile = await client.CreateFileAsync(createFileRequest);
|
|
|
|
|
|
|
|
|
|
var request = new StreamMessageRequest(
|
|
|
|
|
model: AnthropicModels.Claude35HaikuLatest,
|
|
|
|
|
messages: [
|
|
|
|
|
new(
|
|
|
|
|
MessageRole.User,
|
|
|
|
|
[
|
|
|
|
|
new DocumentContent(new FileSource(createdFile.Value.Id))
|
|
|
|
|
{
|
|
|
|
|
Title = "A Story",
|
|
|
|
|
Context = "This is a trustworthy document.",
|
|
|
|
|
Citations = new() { Enabled = true }
|
|
|
|
|
},
|
|
|
|
|
new TextContent("Can you tell me what the title of this story is?"),
|
|
|
|
|
]
|
|
|
|
|
)
|
|
|
|
|
]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
var result = client.CreateMessageAsync(request);
|
|
|
|
|
|
|
|
|
|
var messageCompleteEvent = await result
|
|
|
|
|
.Where(e => e.Type is EventType.MessageComplete)
|
|
|
|
|
.FirstAsync();
|
|
|
|
|
|
|
|
|
|
var textContents = messageCompleteEvent.Data
|
|
|
|
|
.As<MessageCompleteEventData>()
|
|
|
|
|
.Message
|
|
|
|
|
.Content
|
|
|
|
|
.OfType<TextContent>();
|
|
|
|
|
|
|
|
|
|
var messageContent = textContents.Aggregate(new StringBuilder(), (sb, content) =>
|
|
|
|
|
{
|
|
|
|
|
sb.Append(content.Text);
|
|
|
|
|
return sb;
|
|
|
|
|
});
|
|
|
|
|
messageContent.ToString().Should().MatchRegex("The Forgotten Lighthouse");
|
|
|
|
|
|
|
|
|
|
var citations = textContents.SelectMany(static c => c.Citations is null ? [] : c.Citations);
|
|
|
|
|
citations.OfType<CharacterLocationCitation>().Should().NotBeEmpty();
|
|
|
|
|
|
|
|
|
|
_filesToDelete.Add(createdFile.Value.Id);
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-01 20:13:01 -06:00
|
|
|
[Fact]
|
|
|
|
|
public async Task CountMessageTokensAsync_WhenCalled_ItShouldReturnResponse()
|
|
|
|
|
{
|
|
|
|
|
var request = new CountMessageTokensRequest(
|
|
|
|
|
model: AnthropicModels.Claude3Haiku,
|
|
|
|
|
messages: [
|
|
|
|
|
new(MessageRole.User, [new TextContent("Hello!")])
|
|
|
|
|
]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
var result = await _client.CountMessageTokensAsync(request);
|
|
|
|
|
|
|
|
|
|
result.IsSuccess.Should().BeTrue();
|
|
|
|
|
result.Value.Should().BeOfType<TokenCountResponse>();
|
|
|
|
|
result.Value.InputTokens.Should().BeGreaterThan(0);
|
|
|
|
|
}
|
2025-01-05 21:20:27 -06:00
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public async Task ListModelsAsync_WhenCalled_ItShouldReturnResponse()
|
|
|
|
|
{
|
|
|
|
|
var result = await _client.ListModelsAsync();
|
|
|
|
|
|
|
|
|
|
result.IsSuccess.Should().BeTrue();
|
|
|
|
|
result.Value.Should().BeOfType<Page<AnthropicModel>>();
|
|
|
|
|
result.Value.Data.Should().HaveCountGreaterThan(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public async Task ListModelsAsync_WhenCalledWithPagination_ItShouldReturnResponse()
|
|
|
|
|
{
|
|
|
|
|
var result = await _client.ListModelsAsync(new PagingRequest(limit: 1));
|
|
|
|
|
|
|
|
|
|
result.IsSuccess.Should().BeTrue();
|
|
|
|
|
result.Value.Should().BeOfType<Page<AnthropicModel>>();
|
|
|
|
|
result.Value.Data.Should().HaveCount(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public async Task ListAllModelsAsync_WhenCalled_ItShouldReturnResponse()
|
|
|
|
|
{
|
|
|
|
|
var responses = await _client.ListAllModelsAsync(limit: 1).ToListAsync();
|
|
|
|
|
|
|
|
|
|
responses.Should().HaveCountGreaterThan(0);
|
|
|
|
|
}
|
2025-01-05 21:36:34 -06:00
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public async Task GetModelAsync_WhenCalled_ItShouldReturnResponse()
|
|
|
|
|
{
|
|
|
|
|
var result = await _client.GetModelAsync(AnthropicModels.Claude3Haiku);
|
|
|
|
|
|
|
|
|
|
result.IsSuccess.Should().BeTrue();
|
|
|
|
|
result.Value.Should().BeOfType<AnthropicModel>();
|
|
|
|
|
result.Value.Id.Should().Be(AnthropicModels.Claude3Haiku);
|
|
|
|
|
}
|
2025-01-08 22:33:27 -06:00
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public async Task CreateMessageBatchAsync_WhenCalled_ItShouldReturnResponse()
|
|
|
|
|
{
|
|
|
|
|
var request = new MessageBatchRequest([
|
|
|
|
|
new(
|
|
|
|
|
Guid.NewGuid().ToString(),
|
|
|
|
|
new(
|
|
|
|
|
model: AnthropicModels.Claude3Haiku,
|
|
|
|
|
messages: [new(MessageRole.User, [new TextContent("Hello!")])]
|
|
|
|
|
)
|
|
|
|
|
),
|
|
|
|
|
]);
|
2025-01-09 23:17:41 -06:00
|
|
|
|
2025-01-08 22:33:27 -06:00
|
|
|
var result = await _client.CreateMessageBatchAsync(request);
|
2025-01-09 23:17:41 -06:00
|
|
|
|
2025-01-08 22:33:27 -06:00
|
|
|
result.IsSuccess.Should().BeTrue();
|
|
|
|
|
result.Value.Should().BeOfType<MessageBatchResponse>();
|
|
|
|
|
result.Value.Id.Should().NotBeNullOrEmpty();
|
|
|
|
|
}
|
2025-01-08 23:15:24 -06:00
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public async Task GetMessageBatchAsync_WhenCalled_ItShouldReturnResponse()
|
|
|
|
|
{
|
|
|
|
|
var request = new MessageBatchRequest([
|
|
|
|
|
new(
|
|
|
|
|
Guid.NewGuid().ToString(),
|
|
|
|
|
new(
|
|
|
|
|
model: AnthropicModels.Claude3Haiku,
|
|
|
|
|
messages: [new(MessageRole.User, [new TextContent("Hello!")])]
|
|
|
|
|
)
|
|
|
|
|
),
|
|
|
|
|
]);
|
2025-01-09 23:17:41 -06:00
|
|
|
|
2025-01-08 23:15:24 -06:00
|
|
|
var createResult = await _client.CreateMessageBatchAsync(request);
|
|
|
|
|
var getResult = await _client.GetMessageBatchAsync(createResult.Value.Id);
|
|
|
|
|
|
|
|
|
|
getResult.IsSuccess.Should().BeTrue();
|
|
|
|
|
getResult.Value.Should().BeOfType<MessageBatchResponse>();
|
|
|
|
|
getResult.Value.Id.Should().Be(createResult.Value.Id);
|
|
|
|
|
}
|
2025-01-12 13:46:05 -06:00
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public async Task ListMessageBatchesAsync_WhenCalled_ItShouldReturnResponse()
|
|
|
|
|
{
|
|
|
|
|
var request = new MessageBatchRequest([
|
|
|
|
|
new(
|
|
|
|
|
Guid.NewGuid().ToString(),
|
|
|
|
|
new(
|
|
|
|
|
model: AnthropicModels.Claude3Haiku,
|
|
|
|
|
messages: [new(MessageRole.User, [new TextContent("Hello!")])]
|
|
|
|
|
)
|
|
|
|
|
),
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
var createResult = await _client.CreateMessageBatchAsync(request);
|
|
|
|
|
var result = await _client.ListMessageBatchesAsync();
|
|
|
|
|
|
|
|
|
|
result.IsSuccess.Should().BeTrue();
|
|
|
|
|
result.Value.Should().BeOfType<Page<MessageBatchResponse>>();
|
|
|
|
|
result.Value.Data.Should().HaveCountGreaterThan(0);
|
|
|
|
|
result.Value.Data.Should().ContainSingle(b => b.Id == createResult.Value.Id);
|
|
|
|
|
}
|
2025-01-13 12:42:23 -06:00
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public async Task ListAllMessageBatchesAsync_WhenCalled_ItShouldReturnResponse()
|
|
|
|
|
{
|
|
|
|
|
var createRequest = (string id) => new MessageBatchRequest([
|
|
|
|
|
new(
|
|
|
|
|
id,
|
|
|
|
|
new(
|
|
|
|
|
model: AnthropicModels.Claude3Haiku,
|
|
|
|
|
messages: [new(MessageRole.User, [new TextContent("Hello!")])]
|
|
|
|
|
)
|
|
|
|
|
),
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
var requestNumberOne = createRequest(Guid.NewGuid().ToString());
|
|
|
|
|
var requestNumberTwo = createRequest(Guid.NewGuid().ToString());
|
|
|
|
|
|
|
|
|
|
var createResultOne = await _client.CreateMessageBatchAsync(requestNumberOne);
|
|
|
|
|
var createResultTwo = await _client.CreateMessageBatchAsync(requestNumberTwo);
|
|
|
|
|
|
|
|
|
|
var responses = await _client.ListAllMessageBatchesAsync(limit: 1).ToListAsync();
|
|
|
|
|
|
|
|
|
|
responses.Should().HaveCountGreaterThan(2);
|
|
|
|
|
|
|
|
|
|
var batches = responses
|
|
|
|
|
.Where(r => r.IsSuccess)
|
|
|
|
|
.Select(r => r.Value)
|
|
|
|
|
.SelectMany(r => r.Data);
|
|
|
|
|
|
|
|
|
|
batches.Should().ContainSingle(b => b.Id == createResultOne.Value.Id);
|
|
|
|
|
batches.Should().ContainSingle(b => b.Id == createResultTwo.Value.Id);
|
|
|
|
|
}
|
2025-01-14 12:57:52 -06:00
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public async Task CancelMessageBatchAsync_WhenCalled_ItShouldReturnResponse()
|
|
|
|
|
{
|
|
|
|
|
var request = new MessageBatchRequest([
|
|
|
|
|
new(
|
|
|
|
|
Guid.NewGuid().ToString(),
|
|
|
|
|
new(
|
|
|
|
|
model: AnthropicModels.Claude3Haiku,
|
|
|
|
|
messages: [new(MessageRole.User, [new TextContent("Hello!")])]
|
|
|
|
|
)
|
|
|
|
|
),
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
var createResult = await _client.CreateMessageBatchAsync(request);
|
|
|
|
|
var result = await _client.CancelMessageBatchAsync(createResult.Value.Id);
|
|
|
|
|
|
|
|
|
|
result.IsSuccess.Should().BeTrue();
|
|
|
|
|
result.Value.Should().BeOfType<MessageBatchResponse>();
|
|
|
|
|
result.Value.Id.Should().Be(createResult.Value.Id);
|
|
|
|
|
result.Value.ProcessingStatus.Should().Be(MessageBatchStatus.Canceling);
|
|
|
|
|
}
|
2025-07-12 22:13:10 -05:00
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public async Task CreateFileAsync_WhenCalled_ItShouldReturnResponse()
|
|
|
|
|
{
|
|
|
|
|
var fileName = "story.txt";
|
|
|
|
|
var fileType = "text/plain";
|
|
|
|
|
var filePath = TestFileHelper.GetTestFilePath("story.txt");
|
|
|
|
|
var fileContent = await File.ReadAllBytesAsync(filePath);
|
|
|
|
|
var request = new CreateFileRequest(fileContent, fileName, fileType);
|
|
|
|
|
|
2025-07-14 23:01:23 -05:00
|
|
|
using var httpClient = new HttpClient();
|
2025-07-12 22:13:10 -05:00
|
|
|
httpClient.DefaultRequestHeaders.Add("anthropic-beta", "files-api-2025-04-14");
|
|
|
|
|
var client = CreateClient(httpClient);
|
|
|
|
|
|
|
|
|
|
var result = await client.CreateFileAsync(request);
|
|
|
|
|
|
|
|
|
|
result.IsSuccess.Should().BeTrue();
|
|
|
|
|
result.Value.Should().BeOfType<AnthropicFile>();
|
|
|
|
|
result.Value.Name.Should().Be(fileName);
|
|
|
|
|
result.Value.MimeType.Should().Be(fileType);
|
2025-07-14 23:01:23 -05:00
|
|
|
|
|
|
|
|
_filesToDelete.Add(result.Value.Id);
|
2025-07-12 22:13:10 -05:00
|
|
|
}
|
2025-07-13 21:12:37 -05:00
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public async Task ListFilesAsync_WhenCalled_ItShouldReturnPageOfFiles()
|
|
|
|
|
{
|
2025-07-14 23:01:23 -05:00
|
|
|
using var httpClient = new HttpClient();
|
2025-07-13 21:12:37 -05:00
|
|
|
httpClient.DefaultRequestHeaders.Add("anthropic-beta", "files-api-2025-04-14");
|
|
|
|
|
var client = CreateClient(httpClient);
|
|
|
|
|
|
|
|
|
|
var fileBytes = await File.ReadAllBytesAsync(TestFileHelper.GetTestFilePath("story.txt"));
|
|
|
|
|
var createFileRequest = new CreateFileRequest(fileBytes, "story.txt", "text/plain");
|
|
|
|
|
var createdFile = await client.CreateFileAsync(createFileRequest);
|
|
|
|
|
|
|
|
|
|
var result = await client.ListFilesAsync();
|
|
|
|
|
|
|
|
|
|
result.IsSuccess.Should().BeTrue();
|
|
|
|
|
result.Value.Should().BeOfType<Page<AnthropicFile>>();
|
|
|
|
|
result.Value.Data.Should().ContainSingle(f => f.Id == createdFile.Value.Id);
|
2025-07-14 23:01:23 -05:00
|
|
|
|
|
|
|
|
_filesToDelete.Add(createdFile.Value.Id);
|
2025-07-13 21:12:37 -05:00
|
|
|
}
|
2025-07-13 22:33:11 -05:00
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public async Task ListAllFilesAsync_WhenCalled_ItShouldReturnAllFiles()
|
|
|
|
|
{
|
2025-07-14 23:01:23 -05:00
|
|
|
using var httpClient = new HttpClient();
|
2025-07-13 22:33:11 -05:00
|
|
|
httpClient.DefaultRequestHeaders.Add("anthropic-beta", "files-api-2025-04-14");
|
|
|
|
|
var client = CreateClient(httpClient);
|
|
|
|
|
|
|
|
|
|
var fileBytes = await File.ReadAllBytesAsync(TestFileHelper.GetTestFilePath("story.txt"));
|
|
|
|
|
var createFileRequest = new CreateFileRequest(fileBytes, "story.txt", "text/plain");
|
|
|
|
|
var createdFile = await client.CreateFileAsync(createFileRequest);
|
|
|
|
|
|
|
|
|
|
var responses = await client.ListAllFilesAsync(limit: 1).ToListAsync();
|
|
|
|
|
|
|
|
|
|
responses.Should().HaveCountGreaterThan(0);
|
2025-07-14 23:01:23 -05:00
|
|
|
responses.Select(r => r.Value)
|
|
|
|
|
.SelectMany(p => p.Data)
|
|
|
|
|
.Should()
|
|
|
|
|
.ContainSingle(f => f.Id == createdFile.Value.Id);
|
|
|
|
|
|
|
|
|
|
_filesToDelete.Add(createdFile.Value.Id);
|
2025-07-13 22:33:11 -05:00
|
|
|
}
|
2025-07-14 18:46:32 -05:00
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public async Task GetFileInfoAsync_WhenCalled_ItShouldReturnFile()
|
|
|
|
|
{
|
2025-07-14 23:01:23 -05:00
|
|
|
using var httpClient = new HttpClient();
|
2025-07-14 18:46:32 -05:00
|
|
|
httpClient.DefaultRequestHeaders.Add("anthropic-beta", "files-api-2025-04-14");
|
|
|
|
|
var client = CreateClient(httpClient);
|
|
|
|
|
|
|
|
|
|
var fileBytes = await File.ReadAllBytesAsync(TestFileHelper.GetTestFilePath("story.txt"));
|
|
|
|
|
var createFileRequest = new CreateFileRequest(fileBytes, "story.txt", "text/plain");
|
|
|
|
|
var createdFile = await client.CreateFileAsync(createFileRequest);
|
|
|
|
|
|
|
|
|
|
var result = await client.GetFileInfoAsync(createdFile.Value.Id);
|
|
|
|
|
|
|
|
|
|
result.IsSuccess.Should().BeTrue();
|
|
|
|
|
result.Value.Should().BeOfType<AnthropicFile>();
|
|
|
|
|
result.Value.Id.Should().Be(createdFile.Value.Id);
|
2025-07-14 23:01:23 -05:00
|
|
|
|
|
|
|
|
_filesToDelete.Add(createdFile.Value.Id);
|
2025-07-14 18:46:32 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
public async Task DeleteFileAsync_WhenCalled_ItShouldReturnDeleteResponse()
|
|
|
|
|
{
|
2025-07-14 23:01:23 -05:00
|
|
|
using var httpClient = new HttpClient();
|
2025-07-14 18:46:32 -05:00
|
|
|
httpClient.DefaultRequestHeaders.Add("anthropic-beta", "files-api-2025-04-14");
|
|
|
|
|
var client = CreateClient(httpClient);
|
|
|
|
|
|
|
|
|
|
var fileBytes = await File.ReadAllBytesAsync(TestFileHelper.GetTestFilePath("story.txt"));
|
|
|
|
|
var createFileRequest = new CreateFileRequest(fileBytes, "story.txt", "text/plain");
|
|
|
|
|
var createdFile = await client.CreateFileAsync(createFileRequest);
|
|
|
|
|
|
|
|
|
|
var result = await client.DeleteFileAsync(createdFile.Value.Id);
|
|
|
|
|
|
|
|
|
|
result.IsSuccess.Should().BeTrue();
|
|
|
|
|
result.Value.Should().BeOfType<AnthropicFileDeleteResponse>();
|
|
|
|
|
result.Value.Id.Should().Be(createdFile.Value.Id);
|
|
|
|
|
}
|
2025-07-14 23:01:23 -05:00
|
|
|
|
|
|
|
|
public async Task DisposeAsync()
|
|
|
|
|
{
|
|
|
|
|
using var httpClient = new HttpClient();
|
|
|
|
|
httpClient.DefaultRequestHeaders.Add("anthropic-beta", "files-api-2025-04-14");
|
|
|
|
|
var client = CreateClient(httpClient);
|
|
|
|
|
|
|
|
|
|
foreach (var file in _filesToDelete)
|
|
|
|
|
{
|
|
|
|
|
var result = await client.DeleteFileAsync(file);
|
|
|
|
|
result.IsSuccess.Should().BeTrue();
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-06-27 23:26:53 -05:00
|
|
|
}
|