Classify provider budget errors as quota limits
This commit is contained in:
@@ -155,10 +155,13 @@ pub async fn generate_multi_agent_output(
|
|||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!("[openai] Translator error: {e}");
|
log::error!("[openai] Translator error: {e}");
|
||||||
let err = Arc::new(crate::server::server_api::AIApiError::Stream {
|
let err = Arc::new(
|
||||||
stream_type: "openai_chat_completions",
|
crate::server::server_api::AIApiError::Stream {
|
||||||
source: anyhow::anyhow!("{e}"),
|
stream_type: "openai_chat_completions",
|
||||||
});
|
source: anyhow::anyhow!("{e}"),
|
||||||
|
}
|
||||||
|
.into_quota_limit_if_provider_budget_exhausted(),
|
||||||
|
);
|
||||||
let (tx, rx) = async_channel::unbounded();
|
let (tx, rx) = async_channel::unbounded();
|
||||||
let _ = tx.send(Err(err)).await;
|
let _ = tx.send(Err(err)).await;
|
||||||
Ok(Box::pin(rx))
|
Ok(Box::pin(rx))
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ fn request_params_with_ask_user_question_enabled(ask_user_question_enabled: bool
|
|||||||
bedrock_progressive_summary: None,
|
bedrock_progressive_summary: None,
|
||||||
bedrock_tool_result_archive: Vec::new(),
|
bedrock_tool_result_archive: Vec::new(),
|
||||||
bedrock_messages_sent: std::sync::Arc::new(std::sync::Mutex::new(Vec::new())),
|
bedrock_messages_sent: std::sync::Arc::new(std::sync::Mutex::new(Vec::new())),
|
||||||
|
global_rules: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -205,6 +205,29 @@ impl From<serde_json::Error> for AIApiError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AIApiError {
|
impl AIApiError {
|
||||||
|
/// Turns an upstream provider quota/budget response into a user-facing
|
||||||
|
/// quota error instead of a generic stream failure.
|
||||||
|
pub fn into_quota_limit_if_provider_budget_exhausted(self) -> Self {
|
||||||
|
let AIApiError::Stream { source, .. } = self else {
|
||||||
|
return self;
|
||||||
|
};
|
||||||
|
let message = source.to_string();
|
||||||
|
let lower = message.to_ascii_lowercase();
|
||||||
|
if lower.contains("budget has been exceeded")
|
||||||
|
|| lower.contains("rate limit") && lower.contains("429")
|
||||||
|
{
|
||||||
|
return Self::QuotaLimit {
|
||||||
|
user_display_message: Some(
|
||||||
|
"The AI provider has reached its rate limit or monthly budget. Please try again later or choose another configured model.".to_string(),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Self::Stream {
|
||||||
|
stream_type: "provider",
|
||||||
|
source,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Converts a reqwest error to an AIApiError, using response headers to distinguish
|
/// Converts a reqwest error to an AIApiError, using response headers to distinguish
|
||||||
/// between different types of 429 errors.
|
/// between different types of 429 errors.
|
||||||
fn from_response_error(
|
fn from_response_error(
|
||||||
|
|||||||
Reference in New Issue
Block a user