Expose anthropic API errors to the client (#16129)

Now, when an anthropic request is invalid or anthropic's API is down,
we'll expose that to the user instead of just returning a generic 500.

Release Notes:

- N/A

Co-authored-by: Marshall <marshall@zed.dev>
This commit is contained in:
Max Brunsfeld 2024-08-12 13:11:48 -07:00 committed by GitHub
parent f3ec8d425f
commit 1674e12ccb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 20 additions and 12 deletions

View file

@ -207,16 +207,22 @@ async fn perform_completion(
) )
.await .await
.map_err(|err| match err { .map_err(|err| match err {
anthropic::AnthropicError::ApiError(ref api_error) => { anthropic::AnthropicError::ApiError(ref api_error) => match api_error.code() {
if api_error.code() == Some(anthropic::ApiErrorCode::RateLimitError) { Some(anthropic::ApiErrorCode::RateLimitError) => Error::http(
return Error::http( StatusCode::TOO_MANY_REQUESTS,
StatusCode::TOO_MANY_REQUESTS, "Upstream Anthropic rate limit exceeded.".to_string(),
"Upstream Anthropic rate limit exceeded.".to_string(), ),
); Some(anthropic::ApiErrorCode::InvalidRequestError) => {
Error::http(StatusCode::BAD_REQUEST, api_error.message.clone())
} }
Some(anthropic::ApiErrorCode::OverloadedError) => {
Error::Internal(anyhow!(err)) Error::http(StatusCode::SERVICE_UNAVAILABLE, api_error.message.clone())
} }
Some(_) => {
Error::http(StatusCode::INTERNAL_SERVER_ERROR, api_error.message.clone())
}
None => Error::Internal(anyhow!(err)),
},
anthropic::AnthropicError::Other(err) => Error::Internal(err), anthropic::AnthropicError::Other(err) => Error::Internal(err),
})?; })?;

View file

@ -20,7 +20,7 @@ use serde::{Deserialize, Serialize};
use serde_json::value::RawValue; use serde_json::value::RawValue;
use settings::{Settings, SettingsStore}; use settings::{Settings, SettingsStore};
use smol::{ use smol::{
io::BufReader, io::{AsyncReadExt, BufReader},
lock::{RwLock, RwLockUpgradableReadGuard, RwLockWriteGuard}, lock::{RwLock, RwLockUpgradableReadGuard, RwLockWriteGuard},
}; };
use std::{future, sync::Arc}; use std::{future, sync::Arc};
@ -334,7 +334,7 @@ impl CloudLanguageModel {
.header("Content-Type", "application/json") .header("Content-Type", "application/json")
.header("Authorization", format!("Bearer {token}")) .header("Authorization", format!("Bearer {token}"))
.body(serde_json::to_string(&body)?.into())?; .body(serde_json::to_string(&body)?.into())?;
let response = http_client.send(request).await?; let mut response = http_client.send(request).await?;
if response.status().is_success() { if response.status().is_success() {
break response; break response;
} else if !did_retry } else if !did_retry
@ -346,8 +346,10 @@ impl CloudLanguageModel {
did_retry = true; did_retry = true;
token = llm_api_token.refresh(&client).await?; token = llm_api_token.refresh(&client).await?;
} else { } else {
let mut body = String::new();
response.body_mut().read_to_string(&mut body).await?;
break Err(anyhow!( break Err(anyhow!(
"cloud language model completion failed with status {}", "cloud language model completion failed with status {}: {body}",
response.status() response.status()
))?; ))?;
} }