pub const CAPACITY_RESUME_MS: i64 = 60_000;
pub const QUOTA_RESUME_MS: i64 = 3_600_000;
pub fn limit_of(error: &str) -> Option<i64> {
let lower = error.to_lowercase();
let capacity = lower.contains("model_capacity_exhausted") || lower.contains("capacity");
let quota = [
"exhausted your quota",
"quota reached",
"quota exceeded",
"resource_exhausted",
"rate limit",
"too many requests",
" 429",
]
.iter()
.any(|w| lower.contains(w))
|| lower.starts_with("429");
if !capacity && !quota {
return None;
}
Some(
told_wait(&lower).unwrap_or(if quota && !lower.contains("capacity") {
QUOTA_RESUME_MS
} else {
CAPACITY_RESUME_MS
}),
)
}
fn told_wait(lower: &str) -> Option<i64> {
[
"refreshes in",
"resets in",
"reset in",
"retry in",
"retry after",
"try again in",
"after",
]
.iter()
.flat_map(|w| lower.match_indices(w).map(move |(i, _)| i + w.len()))
.find_map(|at| wait_at(&lower[at..]))
}
fn wait_at(text: &str) -> Option<i64> {
let mut total = 0i64;
let mut num = String::new();
for c in text.chars() {
match c {
'0'..='9' => num.push(c),
' ' | ',' if num.is_empty() => {}
_ if num.is_empty() => break,
_ => {
let unit = match c {
'd' => 86_400_000,
'h' => 3_600_000,
'm' => 60_000,
's' => 1_000,
_ => break,
};
total += num.parse::<i64>().ok()? * unit;
num.clear();
}
}
}
(total > 0).then_some(total)
}