mirror of
https://github.com/ultraworkers/claw-code.git
synced 2026-06-07 02:12:29 +08:00
feat: API timeout config, Retry-After header, configurable retry, and 400 transient retry
Cherry-picked from PR #2816 onto current upstream/main, resolving conflicts from PR #3015's merge (which added retry_after to ApiError but some construction sites were missing it). Commits preserved: -ade85398: API timeout config, Retry-After header, configurable retry - TimeoutConfig in HTTP client builder (connect 30s, request 5min) - CLAW_API_CONNECT_TIMEOUT and CLAW_API_REQUEST_TIMEOUT env vars - Retry-After header parsing on 429 responses - ApiTimeoutConfig in runtime config (settings.json) -8a883430: retry 400 responses with transient gateway error bodies - Detects known gateway phrases in 400 response bodies - Marks them as retryable instead of hard-failing -ed91a61e: add 'no parseable body' to CONTEXT_WINDOW_ERROR_MARKERS - Some providers return 400 with 'no parseable body' for oversized requests instead of a proper context_length_exceeded error Commits skipped (already in upstream via PR #3015): -453ab642: optional id field (already merged) -baa8d1ba: HTML detection in streaming (already merged) -33d2f789: JSON error detection in streaming (already merged) 8 files changed, 299 insertions, 80 deletions
This commit is contained in:
@ -582,6 +582,7 @@ mod tests {
|
||||
body: String::new(),
|
||||
retryable: false,
|
||||
suggested_action: None,
|
||||
retry_after: None,
|
||||
};
|
||||
|
||||
assert!(error.is_context_window_failure());
|
||||
|
||||
@ -12,9 +12,8 @@ pub use client::{
|
||||
};
|
||||
pub use error::ApiError;
|
||||
pub use http_client::{
|
||||
TimeoutConfig,
|
||||
build_http_client, build_http_client_or_default, build_http_client_with,
|
||||
build_http_client_with_opts, ProxyConfig,
|
||||
build_http_client_with_opts, ProxyConfig, TimeoutConfig,
|
||||
};
|
||||
pub use prompt_cache::{
|
||||
CacheBreakEvent, PromptCache, PromptCacheConfig, PromptCachePaths, PromptCacheRecord,
|
||||
|
||||
@ -467,7 +467,8 @@ impl AnthropicClient {
|
||||
break;
|
||||
}
|
||||
|
||||
let delay = if let Some(retry_after) = last_error.as_ref().and_then(|e| e.retry_after()) {
|
||||
let delay = if let Some(retry_after) = last_error.as_ref().and_then(|e| e.retry_after())
|
||||
{
|
||||
retry_after
|
||||
} else {
|
||||
self.jittered_backoff_for_attempt(attempts)?
|
||||
@ -909,7 +910,10 @@ async fn expect_success(response: reqwest::Response) -> Result<reqwest::Response
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_retry_after(headers: &reqwest::header::HeaderMap, status: reqwest::StatusCode) -> Option<std::time::Duration> {
|
||||
fn parse_retry_after(
|
||||
headers: &reqwest::header::HeaderMap,
|
||||
status: reqwest::StatusCode,
|
||||
) -> Option<std::time::Duration> {
|
||||
if status != reqwest::StatusCode::TOO_MANY_REQUESTS {
|
||||
return None;
|
||||
}
|
||||
|
||||
@ -1667,7 +1667,10 @@ async fn expect_success(response: reqwest::Response) -> Result<reqwest::Response
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_retry_after(headers: &reqwest::header::HeaderMap, status: reqwest::StatusCode) -> Option<std::time::Duration> {
|
||||
fn parse_retry_after(
|
||||
headers: &reqwest::header::HeaderMap,
|
||||
status: reqwest::StatusCode,
|
||||
) -> Option<std::time::Duration> {
|
||||
if status != reqwest::StatusCode::TOO_MANY_REQUESTS {
|
||||
return None;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user