diff --git a/sandbox/BlazorApp1/Components/Pages/Home.razor.cs b/sandbox/BlazorApp1/Components/Pages/Home.razor.cs index 423fe34..1c2043c 100644 --- a/sandbox/BlazorApp1/Components/Pages/Home.razor.cs +++ b/sandbox/BlazorApp1/Components/Pages/Home.razor.cs @@ -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, diff --git a/src/Claudia/Anthropic.cs b/src/Claudia/Anthropic.cs index c943b0a..cfebbd3 100644 --- a/src/Claudia/Anthropic.cs +++ b/src/Claudia/Anthropic.cs @@ -1,5 +1,6 @@ using System.Net.Http.Json; using System.Runtime.CompilerServices; +using System.Text; using System.Text.Json; namespace Claudia; @@ -68,7 +69,7 @@ async Task 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(AnthropicJsonSerialzierContext.Default.Options, ct), null).ConfigureAwait(ConfigureAwait); + var result = await RequestWithAsync(msg, cancellationToken, overrideOptions, static (x, ct, _) => x.Content.ReadFromJsonAsync(AnthropicJsonSerialzierContext.Default.Options, ct), null).ConfigureAwait(ConfigureAwait); return result!; } @@ -77,7 +78,11 @@ async IAsyncEnumerable 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); @@ -98,7 +103,7 @@ async Task 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; @@ -199,21 +204,40 @@ async Task SendRequestAsync(MessageRequest request, Request case 200: return msg!; default: - var shape = await RequestWithAsync(msg, cancellationToken, overrideOptions, static (x, ct) => x.Content.ReadFromJsonAsync(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(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 RequestWithAsync(TState state, CancellationToken cancellationToken, RequestOptions? overrideOptions, Func> func, Func? shouldRetry) + async Task RequestWithAsync(TState state, CancellationToken cancellationToken, RequestOptions? overrideOptions, Func> func, Func? shouldRetry) { var retriesRemaining = (shouldRetry == null) ? 0 : (overrideOptions?.MaxRetries ?? MaxRetries); var timeout = overrideOptions?.Timeout ?? Timeout; @@ -224,7 +248,7 @@ async Task RequestWithAsync(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);