Skip to content

Commit

Permalink
throw raw string data when error data can't deserialize as json
Browse files Browse the repository at this point in the history
  • Loading branch information
neuecc committed Mar 26, 2024
1 parent 2154898 commit 2f4e7b0
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 13 deletions.
2 changes: 1 addition & 1 deletion sandbox/BlazorApp1/Components/Pages/Home.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ async Task SendClick()

var stream = Anthropic.Messages.CreateStreamAsync(new()
{
Model = Models.Claude3Haiku,
Model = Models.Claude3Opus,
MaxTokens = 1024,
Temperature = temperature,
System = string.IsNullOrWhiteSpace(systemInput) ? null : systemInput,
Expand Down
48 changes: 36 additions & 12 deletions src/Claudia/Anthropic.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Net.Http.Json;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.Json;

namespace Claudia;
Expand Down Expand Up @@ -68,7 +69,7 @@ async Task<MessageResponse> IMessages.CreateAsync(MessageRequest request, Reques
request.Stream = null;
using var msg = await SendRequestAsync(request, overrideOptions, cancellationToken).ConfigureAwait(ConfigureAwait);

var result = await RequestWithAsync(msg, cancellationToken, overrideOptions, static (x, ct) => x.Content.ReadFromJsonAsync<MessageResponse>(AnthropicJsonSerialzierContext.Default.Options, ct), null).ConfigureAwait(ConfigureAwait);
var result = await RequestWithAsync(msg, cancellationToken, overrideOptions, static (x, ct, _) => x.Content.ReadFromJsonAsync<MessageResponse>(AnthropicJsonSerialzierContext.Default.Options, ct), null).ConfigureAwait(ConfigureAwait);
return result!;
}

Expand All @@ -77,7 +78,11 @@ async IAsyncEnumerable<IMessageStreamEvent> IMessages.CreateStreamAsync(MessageR
request.Stream = true;
using var msg = await SendRequestAsync(request, overrideOptions, cancellationToken).ConfigureAwait(ConfigureAwait);

#if NETSTANDARD
using var stream = await msg.Content.ReadAsStreamAsync().ConfigureAwait(ConfigureAwait);
#else
using var stream = await msg.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(ConfigureAwait);
#endif

var reader = new StreamMessageReader(stream, ConfigureAwait);

Expand All @@ -98,7 +103,7 @@ async Task<HttpResponseMessage> SendRequestAsync(MessageRequest request, Request
}

// use ResponseHeadersRead to ignore buffering response.
var msg = await RequestWithAsync((httpClient, (bytes, requestUri, overrideOptions, ApiKey)), cancellationToken, overrideOptions, static (x, ct) =>
var msg = await RequestWithAsync((httpClient, (bytes, requestUri, overrideOptions, ApiKey)), cancellationToken, overrideOptions, static (x, ct, _) =>
{
// for retry, create new HttpRequestMessage per request.
var state = x.Item2;
Expand Down Expand Up @@ -199,21 +204,40 @@ async Task<HttpResponseMessage> SendRequestAsync(MessageRequest request, Request
case 200:
return msg!;
default:
var shape = await RequestWithAsync(msg, cancellationToken, overrideOptions, static (x, ct) => x.Content.ReadFromJsonAsync<ErrorResponseShape>(AnthropicJsonSerialzierContext.Default.Options, ct), null).ConfigureAwait(ConfigureAwait);

var error = shape!.ErrorResponse;
var errorMsg = error.Message;
var code = (ErrorCode)statusCode;
if (code == ErrorCode.InvalidRequestError && IncludeRequestJsonOnInvalidRequestError)
using (msg)
{
errorMsg += ". Request: " + JsonSerializer.Serialize(request, AnthropicJsonSerialzierContext.Default.Options);
var shape = await RequestWithAsync(msg, cancellationToken, overrideOptions, static async (x, ct, configureAwait) =>
{
// for debuggability when fails deserialize(server returns invalid error data), alloc data first.
#if NETSTANDARD
var responseData = await x.Content.ReadAsByteArrayAsync().ConfigureAwait(configureAwait);
#else
var responseData = await x.Content.ReadAsByteArrayAsync(ct).ConfigureAwait(configureAwait);
#endif
try
{
return JsonSerializer.Deserialize<ErrorResponseShape>(responseData, AnthropicJsonSerialzierContext.Default.Options);
}
catch
{
throw new InvalidOperationException($"Response data is invalid error json. Data: + {Encoding.UTF8.GetString(responseData)}");
}
}, null).ConfigureAwait(ConfigureAwait);

var error = shape!.ErrorResponse;
var errorMsg = error.Message;
var code = (ErrorCode)statusCode;
if (code == ErrorCode.InvalidRequestError && IncludeRequestJsonOnInvalidRequestError)
{
errorMsg += ". Request: " + JsonSerializer.Serialize(request, AnthropicJsonSerialzierContext.Default.Options);
}
throw new ClaudiaException((ErrorCode)statusCode, error.Type, errorMsg);
}
throw new ClaudiaException((ErrorCode)statusCode, error.Type, errorMsg);
}
}

// with Cancel, Timeout, Retry.
async Task<TResult> RequestWithAsync<TResult, TState>(TState state, CancellationToken cancellationToken, RequestOptions? overrideOptions, Func<TState, CancellationToken, Task<TResult>> func, Func<TResult, TimeSpan?>? shouldRetry)
async Task<TResult> RequestWithAsync<TResult, TState>(TState state, CancellationToken cancellationToken, RequestOptions? overrideOptions, Func<TState, CancellationToken, bool, Task<TResult>> func, Func<TResult, TimeSpan?>? shouldRetry)
{
var retriesRemaining = (shouldRetry == null) ? 0 : (overrideOptions?.MaxRetries ?? MaxRetries);
var timeout = overrideOptions?.Timeout ?? Timeout;
Expand All @@ -224,7 +248,7 @@ async Task<TResult> RequestWithAsync<TResult, TState>(TState state, Cancellation

try
{
var result = await func(state, cts.Token).ConfigureAwait(ConfigureAwait);
var result = await func(state, cts.Token, ConfigureAwait).ConfigureAwait(ConfigureAwait);
if (shouldRetry != null)
{
var retryTime = shouldRetry(result);
Expand Down

0 comments on commit 2f4e7b0

Please sign in to comment.