use super::super::error::ChatError;
use rand::Rng;
pub(super) struct RetryPolicy {
pub(super) max_attempts: u32,
pub(super) base_ms: u64,
pub(super) cap_ms: u64,
}
pub(super) fn retry_policy_for(error: &ChatError) -> Option<RetryPolicy> {
match error {
ChatError::NetworkTimeout(_) | ChatError::StreamInterrupted(_) => Some(RetryPolicy {
max_attempts: 5,
base_ms: 1_000,
cap_ms: 30_000,
}),
ChatError::NetworkError(_) => Some(RetryPolicy {
max_attempts: 5,
base_ms: 2_000,
cap_ms: 30_000,
}),
ChatError::ApiServerError { status, .. } => match status {
503 | 504 | 529 => Some(RetryPolicy {
max_attempts: 4,
base_ms: 2_000,
cap_ms: 30_000,
}),
500 | 502 => Some(RetryPolicy {
max_attempts: 3,
base_ms: 3_000,
cap_ms: 30_000,
}),
_ => None,
},
ChatError::ApiRateLimit {
retry_after_secs: Some(secs),
..
} => {
let wait = (*secs).min(120);
Some(RetryPolicy {
max_attempts: 1,
base_ms: wait * 1_000,
cap_ms: 120_000,
})
}
ChatError::ApiRateLimit {
retry_after_secs: None,
..
} => Some(RetryPolicy {
max_attempts: 3,
base_ms: 5_000,
cap_ms: 60_000,
}),
ChatError::AbnormalFinish(reason)
if matches!(reason.as_str(), "network_error" | "timeout" | "overloaded") =>
{
Some(RetryPolicy {
max_attempts: 3,
base_ms: 2_000,
cap_ms: 20_000,
})
}
ChatError::Other(msg)
if msg.contains("访问量过大")
|| msg.contains("过载")
|| msg.contains("overloaded")
|| msg.contains("too busy")
|| msg.contains("1305") =>
{
Some(RetryPolicy {
max_attempts: 3,
base_ms: 3_000,
cap_ms: 30_000,
})
}
_ => None,
}
}
pub(super) fn backoff_delay_ms(attempt: u32, base_ms: u64, cap_ms: u64) -> u64 {
let shift = (attempt - 1).min(10) as u64;
let exp = base_ms.saturating_mul(1u64 << shift).min(cap_ms);
let jitter = rand::thread_rng().gen_range(0..=(exp / 5));
exp + jitter
}