Add retry support for OpenAI-compatible LLM providers (#37891)
Automatically retry the agent's LLM completion requests when the provider returns 429 Too Many Requests. Uses the Retry-After header to determine the retry delay if it is available. Many providers are frequently overloaded or have low rate limits. These providers are essentially unusable without automatic retries. Tested with Cerebras configured via openai_compatible. Related: #31531 Release Notes: - Added automatic retries for OpenAI-compatible LLM providers --------- Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
This commit is contained in:
co-authored by
Bennet Bo Fenner
parent
92e64f9cf0
commit
fb90b12073
@@ -226,12 +226,17 @@ impl OpenAiLanguageModel {
|
||||
};
|
||||
|
||||
let future = self.request_limiter.stream(async move {
|
||||
let provider = PROVIDER_NAME;
|
||||
let Some(api_key) = api_key else {
|
||||
return Err(LanguageModelCompletionError::NoApiKey {
|
||||
provider: PROVIDER_NAME,
|
||||
});
|
||||
return Err(LanguageModelCompletionError::NoApiKey { provider });
|
||||
};
|
||||
let request = stream_completion(http_client.as_ref(), &api_url, &api_key, request);
|
||||
let request = stream_completion(
|
||||
http_client.as_ref(),
|
||||
provider.0.as_str(),
|
||||
&api_url,
|
||||
&api_key,
|
||||
request,
|
||||
);
|
||||
let response = request.await?;
|
||||
Ok(response)
|
||||
});
|
||||
|
||||
@@ -205,8 +205,13 @@ impl OpenAiCompatibleLanguageModel {
|
||||
&self,
|
||||
request: open_ai::Request,
|
||||
cx: &AsyncApp,
|
||||
) -> BoxFuture<'static, Result<futures::stream::BoxStream<'static, Result<ResponseStreamEvent>>>>
|
||||
{
|
||||
) -> BoxFuture<
|
||||
'static,
|
||||
Result<
|
||||
futures::stream::BoxStream<'static, Result<ResponseStreamEvent>>,
|
||||
LanguageModelCompletionError,
|
||||
>,
|
||||
> {
|
||||
let http_client = self.http_client.clone();
|
||||
|
||||
let Ok((api_key, api_url)) = self.state.read_with(cx, |state, _cx| {
|
||||
@@ -216,7 +221,7 @@ impl OpenAiCompatibleLanguageModel {
|
||||
state.settings.api_url.clone(),
|
||||
)
|
||||
}) else {
|
||||
return future::ready(Err(anyhow!("App state dropped"))).boxed();
|
||||
return future::ready(Err(anyhow!("App state dropped").into())).boxed();
|
||||
};
|
||||
|
||||
let provider = self.provider_name.clone();
|
||||
@@ -224,7 +229,13 @@ impl OpenAiCompatibleLanguageModel {
|
||||
let Some(api_key) = api_key else {
|
||||
return Err(LanguageModelCompletionError::NoApiKey { provider });
|
||||
};
|
||||
let request = stream_completion(http_client.as_ref(), &api_url, &api_key, request);
|
||||
let request = stream_completion(
|
||||
http_client.as_ref(),
|
||||
provider.0.as_str(),
|
||||
&api_url,
|
||||
&api_key,
|
||||
request,
|
||||
);
|
||||
let response = request.await?;
|
||||
Ok(response)
|
||||
});
|
||||
|
||||
@@ -220,13 +220,17 @@ impl VercelLanguageModel {
|
||||
};
|
||||
|
||||
let future = self.request_limiter.stream(async move {
|
||||
let provider = PROVIDER_NAME;
|
||||
let Some(api_key) = api_key else {
|
||||
return Err(LanguageModelCompletionError::NoApiKey {
|
||||
provider: PROVIDER_NAME,
|
||||
});
|
||||
return Err(LanguageModelCompletionError::NoApiKey { provider });
|
||||
};
|
||||
let request =
|
||||
open_ai::stream_completion(http_client.as_ref(), &api_url, &api_key, request);
|
||||
let request = open_ai::stream_completion(
|
||||
http_client.as_ref(),
|
||||
provider.0.as_str(),
|
||||
&api_url,
|
||||
&api_key,
|
||||
request,
|
||||
);
|
||||
let response = request.await?;
|
||||
Ok(response)
|
||||
});
|
||||
|
||||
@@ -211,25 +211,34 @@ impl XAiLanguageModel {
|
||||
&self,
|
||||
request: open_ai::Request,
|
||||
cx: &AsyncApp,
|
||||
) -> BoxFuture<'static, Result<futures::stream::BoxStream<'static, Result<ResponseStreamEvent>>>>
|
||||
{
|
||||
) -> BoxFuture<
|
||||
'static,
|
||||
Result<
|
||||
futures::stream::BoxStream<'static, Result<ResponseStreamEvent>>,
|
||||
LanguageModelCompletionError,
|
||||
>,
|
||||
> {
|
||||
let http_client = self.http_client.clone();
|
||||
|
||||
let Ok((api_key, api_url)) = self.state.read_with(cx, |state, cx| {
|
||||
let api_url = XAiLanguageModelProvider::api_url(cx);
|
||||
(state.api_key_state.key(&api_url), api_url)
|
||||
}) else {
|
||||
return future::ready(Err(anyhow!("App state dropped"))).boxed();
|
||||
return future::ready(Err(anyhow!("App state dropped").into())).boxed();
|
||||
};
|
||||
|
||||
let future = self.request_limiter.stream(async move {
|
||||
let provider = PROVIDER_NAME;
|
||||
let Some(api_key) = api_key else {
|
||||
return Err(LanguageModelCompletionError::NoApiKey {
|
||||
provider: PROVIDER_NAME,
|
||||
});
|
||||
return Err(LanguageModelCompletionError::NoApiKey { provider });
|
||||
};
|
||||
let request =
|
||||
open_ai::stream_completion(http_client.as_ref(), &api_url, &api_key, request);
|
||||
let request = open_ai::stream_completion(
|
||||
http_client.as_ref(),
|
||||
provider.0.as_str(),
|
||||
&api_url,
|
||||
&api_key,
|
||||
request,
|
||||
);
|
||||
let response = request.await?;
|
||||
Ok(response)
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user