From bd1edbcb4929c6d1391d46962115cc799d9a0f57 Mon Sep 17 00:00:00 2001 From: tghamm Date: Fri, 9 May 2025 18:22:55 -0400 Subject: [PATCH] Fixes a critical bug that causes the library to make double requests. --- Anthropic.SDK/Anthropic.SDK.csproj | 8 +++--- Anthropic.SDK/BaseEndpoint.cs | 30 +++++++++++++++++++++- Anthropic.SDK/EndpointBase.cs | 41 ++---------------------------- 3 files changed, 35 insertions(+), 44 deletions(-) diff --git a/Anthropic.SDK/Anthropic.SDK.csproj b/Anthropic.SDK/Anthropic.SDK.csproj index 2a87a04..fce3ba6 100644 --- a/Anthropic.SDK/Anthropic.SDK.csproj +++ b/Anthropic.SDK/Anthropic.SDK.csproj @@ -14,12 +14,12 @@ Claude, AI, ML, API, Anthropic Claude API - Update to latest version of Microsoft.Extensions.AI.Abstractions + Fixes a critical bug that causes the library to make double requests, increasing latency and cost. Anthropic.SDK - 5.2.1 - 5.2.1.0 - 5.2.1.0 + 5.2.2 + 5.2.2.0 + 5.2.2.0 True README.md icon.png diff --git a/Anthropic.SDK/BaseEndpoint.cs b/Anthropic.SDK/BaseEndpoint.cs index ec8e38a..90f5877 100644 --- a/Anthropic.SDK/BaseEndpoint.cs +++ b/Anthropic.SDK/BaseEndpoint.cs @@ -11,6 +11,7 @@ using System.Threading; using System.Threading.Tasks; using Anthropic.SDK.Messaging; +using System.Linq; namespace Anthropic.SDK { @@ -57,10 +58,37 @@ protected async Task HttpRequestMessages(string url = null using var ms = new MemoryStream(Encoding.UTF8.GetBytes(resultAsString)); var res = await JsonSerializer.DeserializeAsync(ms, options, cancellationToken: ctx).ConfigureAwait(false); - + if (res is MessageResponse messageResponse) + { + messageResponse.RateLimits = GetRateLimits(response); + } return res; } + protected RateLimits GetRateLimits(HttpResponseMessage message) + { + var rateLimits = new RateLimits(); + + TryParseHeaderValue(message, "anthropic-ratelimit-requests-limit", long.Parse, value => rateLimits.RequestsLimit = value); + TryParseHeaderValue(message, "anthropic-ratelimit-requests-remaining", long.Parse, value => rateLimits.RequestsRemaining = value); + TryParseHeaderValue(message, "anthropic-ratelimit-requests-reset", DateTime.Parse, value => rateLimits.RequestsReset = value); + TryParseHeaderValue(message, "anthropic-ratelimit-tokens-limit", long.Parse, value => rateLimits.TokensLimit = value); + TryParseHeaderValue(message, "anthropic-ratelimit-tokens-remaining", long.Parse, value => rateLimits.TokensRemaining = value); + TryParseHeaderValue(message, "anthropic-ratelimit-tokens-reset", DateTime.Parse, value => rateLimits.TokensReset = value); + + return rateLimits; + } + + private static void TryParseHeaderValue(HttpResponseMessage message, string headerName, Func parser, Action setter) + { + if (message.Headers.TryGetValues(headerName, out var values) && + values.FirstOrDefault() is string value && + parser(value) is T parsedValue) + { + setter(parsedValue); + } + } + /// /// Makes an HTTP request and deserializes the response to the specified type without custom converters. /// diff --git a/Anthropic.SDK/EndpointBase.cs b/Anthropic.SDK/EndpointBase.cs index c58c157..152fa78 100644 --- a/Anthropic.SDK/EndpointBase.cs +++ b/Anthropic.SDK/EndpointBase.cs @@ -102,22 +102,7 @@ private string GetErrorMessage(string resultAsString, HttpResponseMessage respon return $"{resultAsString ?? ""}"; } - /// - /// Override the base HttpRequestMessages to add rate limits - /// - protected new async Task HttpRequestMessages(string url = null, HttpMethod verb = null, - object postData = null, CancellationToken ctx = default) - { - var response = await base.HttpRequestMessages(url, verb, postData, ctx).ConfigureAwait(false); - - if (response is MessageResponse messageResponse) - { - messageResponse.RateLimits = GetRateLimits(await HttpRequestRaw(url, verb, postData, false, ctx)); - } - - return response; - } - + protected async IAsyncEnumerable HttpStreamingRequestBatches(string url = null, HttpMethod verb = null, object postData = null, [EnumeratorCancellation] CancellationToken ctx = default) @@ -168,29 +153,7 @@ protected async IAsyncEnumerable HttpStreamingRequestBatchesJsonl(string } } - private static RateLimits GetRateLimits(HttpResponseMessage message) - { - var rateLimits = new RateLimits(); - - TryParseHeaderValue(message, "anthropic-ratelimit-requests-limit", long.Parse, value => rateLimits.RequestsLimit = value); - TryParseHeaderValue(message, "anthropic-ratelimit-requests-remaining", long.Parse, value => rateLimits.RequestsRemaining = value); - TryParseHeaderValue(message, "anthropic-ratelimit-requests-reset", DateTime.Parse, value => rateLimits.RequestsReset = value); - TryParseHeaderValue(message, "anthropic-ratelimit-tokens-limit", long.Parse, value => rateLimits.TokensLimit = value); - TryParseHeaderValue(message, "anthropic-ratelimit-tokens-remaining", long.Parse, value => rateLimits.TokensRemaining = value); - TryParseHeaderValue(message, "anthropic-ratelimit-tokens-reset", DateTime.Parse, value => rateLimits.TokensReset = value); - - return rateLimits; - } - - private static void TryParseHeaderValue(HttpResponseMessage message, string headerName, Func parser, Action setter) - { - if (message.Headers.TryGetValues(headerName, out var values) && - values.FirstOrDefault() is string value && - parser(value) is T parsedValue) - { - setter(parsedValue); - } - } + /// /// Handle error responses from the API