feat: add ListAllMessageBatchesAsync method and corresponding tests
This commit is contained in:
@@ -48,6 +48,13 @@ public interface IAnthropicApiClient
|
||||
/// <returns>A task that represents the asynchronous operation. The task result contains the response as an <see cref="AnthropicResult{T}"/> where T is <see cref="Page{T}"/> where T is <see cref="MessageBatchResponse"/>.</returns>
|
||||
Task<AnthropicResult<Page<MessageBatchResponse>>> ListMessageBatchesAsync(PagingRequest? request = null);
|
||||
|
||||
/// <summary>
|
||||
/// Lists all message batches asynchronously.
|
||||
/// </summary>
|
||||
/// <param name="limit">The maximum number of message batches to return in each page.</param>
|
||||
/// <returns>An asynchronous enumerable that yields the response as an <see cref="AnthropicResult{T}"/> where T is <see cref="Page{T}"/> where T is <see cref="MessageBatchResponse"/>.</returns>
|
||||
IAsyncEnumerable<AnthropicResult<Page<MessageBatchResponse>>> ListAllMessageBatchesAsync(int limit = 20);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the results of a message batch asynchronously.
|
||||
/// </summary>
|
||||
@@ -368,6 +375,15 @@ public class AnthropicApiClient : IAnthropicApiClient
|
||||
return AnthropicResult<Page<MessageBatchResponse>>.Success(page, anthropicHeaders);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async IAsyncEnumerable<AnthropicResult<Page<MessageBatchResponse>>> ListAllMessageBatchesAsync(int limit = 20)
|
||||
{
|
||||
await foreach (var result in GetAllPagesAsync<MessageBatchResponse>(MessageBatchesEndpoint, limit))
|
||||
{
|
||||
yield return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<AnthropicResult<IAsyncEnumerable<MessageBatchResultItem>>> GetMessageBatchResultsAsync(string batchId)
|
||||
{
|
||||
@@ -439,37 +455,10 @@ public class AnthropicApiClient : IAnthropicApiClient
|
||||
/// <inheritdoc/>
|
||||
public async IAsyncEnumerable<AnthropicResult<Page<AnthropicModel>>> ListAllModelsAsync(int limit = 20)
|
||||
{
|
||||
var pagingRequest = new PagingRequest(limit: limit);
|
||||
string Endpoint() => $"{ModelsEndpoint}?{pagingRequest.ToQueryParameters()}";
|
||||
bool hasMore;
|
||||
|
||||
do
|
||||
await foreach (var result in GetAllPagesAsync<AnthropicModel>(ModelsEndpoint, limit))
|
||||
{
|
||||
var response = await SendRequestAsync(Endpoint());
|
||||
var anthropicHeaders = new AnthropicHeaders(response.Headers);
|
||||
var responseContent = await response.Content.ReadAsStringAsync();
|
||||
|
||||
if (response.IsSuccessStatusCode is false)
|
||||
{
|
||||
var error = Deserialize<AnthropicError>(responseContent) ?? new AnthropicError();
|
||||
yield return AnthropicResult<Page<AnthropicModel>>.Failure(error, anthropicHeaders);
|
||||
yield break;
|
||||
yield return result;
|
||||
}
|
||||
|
||||
var page = Deserialize<Page<AnthropicModel>>(responseContent) ?? new Page<AnthropicModel>();
|
||||
|
||||
if (page.HasMore && page.LastId is not null)
|
||||
{
|
||||
hasMore = true;
|
||||
pagingRequest = new PagingRequest(limit: limit, afterId: page.LastId);
|
||||
}
|
||||
else
|
||||
{
|
||||
hasMore = false;
|
||||
}
|
||||
|
||||
yield return AnthropicResult<Page<AnthropicModel>>.Success(page, anthropicHeaders);
|
||||
} while (hasMore);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -490,6 +479,41 @@ public class AnthropicApiClient : IAnthropicApiClient
|
||||
return AnthropicResult<AnthropicModel>.Success(model, anthropicHeaders);
|
||||
}
|
||||
|
||||
private async IAsyncEnumerable<AnthropicResult<Page<T>>> GetAllPagesAsync<T>(string endpoint, int limit = 20)
|
||||
{
|
||||
var pagingRequest = new PagingRequest(limit: limit);
|
||||
string Endpoint() => $"{endpoint}?{pagingRequest.ToQueryParameters()}";
|
||||
bool hasMore;
|
||||
|
||||
do
|
||||
{
|
||||
var response = await SendRequestAsync(Endpoint());
|
||||
var anthropicHeaders = new AnthropicHeaders(response.Headers);
|
||||
var responseContent = await response.Content.ReadAsStringAsync();
|
||||
|
||||
if (response.IsSuccessStatusCode is false)
|
||||
{
|
||||
var error = Deserialize<AnthropicError>(responseContent) ?? new AnthropicError();
|
||||
yield return AnthropicResult<Page<T>>.Failure(error, anthropicHeaders);
|
||||
yield break;
|
||||
}
|
||||
|
||||
var page = Deserialize<Page<T>>(responseContent) ?? new Page<T>();
|
||||
|
||||
if (page.HasMore && page.LastId is not null)
|
||||
{
|
||||
hasMore = true;
|
||||
pagingRequest = new PagingRequest(limit: limit, afterId: page.LastId);
|
||||
}
|
||||
else
|
||||
{
|
||||
hasMore = false;
|
||||
}
|
||||
|
||||
yield return AnthropicResult<Page<T>>.Success(page, anthropicHeaders);
|
||||
} while (hasMore);
|
||||
}
|
||||
|
||||
private ToolCall? GetToolCall(MessageResponse response, List<Tool> tools)
|
||||
{
|
||||
var toolUse = response.Content.OfType<ToolUseContent>().FirstOrDefault();
|
||||
|
||||
@@ -403,4 +403,36 @@ public class AnthropicApiClientTests(ConfigurationFixture configFixture) : EndTo
|
||||
result.Value.Data.Should().HaveCountGreaterThan(0);
|
||||
result.Value.Data.Should().ContainSingle(b => b.Id == createResult.Value.Id);
|
||||
}
|
||||
|
||||
[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);
|
||||
}
|
||||
}
|
||||
@@ -1696,4 +1696,131 @@ public class AnthropicApiClientTests : IntegrationTest
|
||||
result.Value.LastId.Should().BeEmpty();
|
||||
result.Value.Data.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ListAllMessageBatchesAsync_WhenCalled_ItShouldReturnAllBatches()
|
||||
{
|
||||
_mockHttpMessageHandler
|
||||
.WhenListMessageBatchesRequest()
|
||||
.WithExactQueryString(new Dictionary<string, string>()
|
||||
{
|
||||
{ "limit", "20" },
|
||||
})
|
||||
.Respond(
|
||||
HttpStatusCode.OK,
|
||||
"application/json",
|
||||
@"{
|
||||
""data"": [
|
||||
{
|
||||
""id"": ""msgbatch_013Zva2CMHLNnXjNJJKqJ2EF"",
|
||||
""type"": ""message_batch"",
|
||||
""processing_status"": ""in_progress"",
|
||||
""request_counts"": {
|
||||
""processing"": 100,
|
||||
""succeeded"": 50,
|
||||
""errored"": 30,
|
||||
""canceled"": 10,
|
||||
""expired"": 10
|
||||
},
|
||||
""ended_at"": ""2024-08-20T18:37:24.100435Z"",
|
||||
""created_at"": ""2024-08-20T18:37:24.100435Z"",
|
||||
""expires_at"": ""2024-08-20T18:37:24.100435Z"",
|
||||
""archived_at"": ""2024-08-20T18:37:24.100435Z"",
|
||||
""cancel_initiated_at"": ""2024-08-20T18:37:24.100435Z"",
|
||||
""results_url"": ""https://api.anthropic.com/v1/messages/batches/msgbatch_013Zva2CMHLNnXjNJJKqJ2EF/results""
|
||||
}
|
||||
],
|
||||
""has_more"": true,
|
||||
""first_id"": ""1"",
|
||||
""last_id"": ""1""
|
||||
}"
|
||||
);
|
||||
|
||||
_mockHttpMessageHandler
|
||||
.WhenListMessageBatchesRequest()
|
||||
.WithExactQueryString(new Dictionary<string, string>()
|
||||
{
|
||||
{ "after_id", "1" },
|
||||
{ "limit", "20" },
|
||||
})
|
||||
.Respond(
|
||||
HttpStatusCode.OK,
|
||||
"application/json",
|
||||
@"{
|
||||
""data"": [
|
||||
{
|
||||
""id"": ""msgbatch_013Zva2CMHLNnXjNJJKqJ2EF"",
|
||||
""type"": ""message_batch"",
|
||||
""processing_status"": ""in_progress"",
|
||||
""request_counts"": {
|
||||
""processing"": 100,
|
||||
""succeeded"": 50,
|
||||
""errored"": 30,
|
||||
""canceled"": 10,
|
||||
""expired"": 10
|
||||
},
|
||||
""ended_at"": ""2024-08-20T18:37:24.100435Z"",
|
||||
""created_at"": ""2024-08-20T18:37:24.100435Z"",
|
||||
""expires_at"": ""2024-08-20T18:37:24.100435Z"",
|
||||
""archived_at"": ""2024-08-20T18:37:24.100435Z"",
|
||||
""cancel_initiated_at"": ""2024-08-20T18:37:24.100435Z"",
|
||||
""results_url"": ""https://api.anthropic.com/v1/messages/batches/msgbatch_013Zva2CMHLNnXjNJJKqJ2EF/results""
|
||||
}
|
||||
],
|
||||
""has_more"": false,
|
||||
""first_id"": ""2"",
|
||||
""last_id"": ""2""
|
||||
}"
|
||||
);
|
||||
|
||||
var pageResponses = Client.ListAllMessageBatchesAsync();
|
||||
var collectedPages = new List<Page<MessageBatchResponse>>();
|
||||
|
||||
await foreach (var response in pageResponses)
|
||||
{
|
||||
response.IsSuccess.Should().BeTrue();
|
||||
response.Value.Should().BeOfType<Page<MessageBatchResponse>>();
|
||||
collectedPages.Add(response.Value);
|
||||
}
|
||||
|
||||
var expectedMessageBatchResponse = new MessageBatchResponse()
|
||||
{
|
||||
Id = "msgbatch_013Zva2CMHLNnXjNJJKqJ2EF",
|
||||
Type = "message_batch",
|
||||
ProcessingStatus = "in_progress",
|
||||
RequestCounts = new MessageBatchRequestCounts
|
||||
{
|
||||
Processing = 100,
|
||||
Succeeded = 50,
|
||||
Errored = 30,
|
||||
Canceled = 10,
|
||||
Expired = 10
|
||||
},
|
||||
EndedAt = DateTimeOffset.Parse("2024-08-20T18:37:24.100435Z"),
|
||||
CreatedAt = DateTimeOffset.Parse("2024-08-20T18:37:24.100435Z"),
|
||||
ExpiresAt = DateTimeOffset.Parse("2024-08-20T18:37:24.100435Z"),
|
||||
ArchivedAt = DateTimeOffset.Parse("2024-08-20T18:37:24.100435Z"),
|
||||
CancelInitiatedAt = DateTimeOffset.Parse("2024-08-20T18:37:24.100435Z"),
|
||||
ResultsUrl = "https://api.anthropic.com/v1/messages/batches/msgbatch_013Zva2CMHLNnXjNJJKqJ2EF/results"
|
||||
};
|
||||
|
||||
collectedPages.Should().HaveCount(2);
|
||||
collectedPages.Should().BeEquivalentTo(new List<Page<MessageBatchResponse>>()
|
||||
{
|
||||
new()
|
||||
{
|
||||
Data = [expectedMessageBatchResponse],
|
||||
FirstId = "1",
|
||||
LastId = "1",
|
||||
HasMore = true
|
||||
},
|
||||
new()
|
||||
{
|
||||
Data = [expectedMessageBatchResponse],
|
||||
FirstId = "2",
|
||||
LastId = "2",
|
||||
HasMore = false
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user