| 60 | } |
| 61 | |
| 62 | pub fn retryable(error: &crate::MessageError) -> Option<String> { |
| 63 | match error { |
| 64 | crate::MessageError::ContextOverflowError { .. } => None, |
| 65 | crate::MessageError::ApiError { |
| 66 | message, |
| 67 | is_retryable, |
| 68 | response_body, |
| 69 | .. |
| 70 | } => { |
| 71 | if !is_retryable { |
| 72 | return None; |
| 73 | } |
| 74 | |
| 75 | if let Some(body) = response_body { |
| 76 | if body.contains("FreeUsageLimitError") { |
| 77 | return Some( |
| 78 | "Free usage exceeded, add credits https://opencode.ai/zen".to_string(), |
| 79 | ); |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | if message.contains("Overloaded") { |
| 84 | Some("Provider is overloaded".to_string()) |
| 85 | } else { |
| 86 | Some(message.clone()) |
| 87 | } |
| 88 | } |
| 89 | crate::MessageError::Unknown { message } => { |
| 90 | if let Ok(json) = serde_json::from_str::<serde_json::Value>(message) { |
| 91 | if let Some(obj) = json.as_object() { |
| 92 | let code = obj.get("code").and_then(|c| c.as_str()).unwrap_or(""); |
| 93 | |
| 94 | if obj.get("type").and_then(|t| t.as_str()) == Some("error") { |
| 95 | if let Some(error_obj) = obj.get("error").and_then(|e| e.as_object()) { |
| 96 | if error_obj.get("type").and_then(|t| t.as_str()) |
| 97 | == Some("too_many_requests") |
| 98 | { |
| 99 | return Some("Too Many Requests".to_string()); |
| 100 | } |
| 101 | if error_obj |
| 102 | .get("code") |
| 103 | .and_then(|c| c.as_str()) |
| 104 | .map(|c| c.contains("rate_limit")) |
| 105 | .unwrap_or(false) |
| 106 | { |
| 107 | return Some("Rate Limited".to_string()); |
| 108 | } |
| 109 | } |
| 110 | } |
| 111 | |
| 112 | if code.contains("exhausted") || code.contains("unavailable") { |
| 113 | return Some("Provider is overloaded".to_string()); |
| 114 | } |
| 115 | |
| 116 | return Some(message.clone()); |
| 117 | } |
| 118 | } |
| 119 | None |