codetether_agent/tui/app/smart_switch/
error_detection.rs1pub fn normalize_provider_alias(provider_name: &str) -> &str {
5 if provider_name.eq_ignore_ascii_case("zhipuai") || provider_name.eq_ignore_ascii_case("z-ai") {
6 "zai"
7 } else {
8 provider_name
9 }
10}
11
12pub fn smart_switch_model_key(model_ref: &str) -> String {
14 model_ref.trim().to_ascii_lowercase()
15}
16
17pub fn is_retryable_provider_error(err: &str) -> bool {
19 let normalized = err.to_ascii_lowercase();
20 let transient_status_codes = [
21 " 429 ", " 500 ", " 502 ", " 503 ", " 504 ", " 520 ", " 521 ", " 522 ", " 523 ", " 524 ",
22 " 525 ", " 526 ", " 529 ", " 598 ", " 599 ", " 798 ",
23 ];
24 let non_retryable_status_codes = [" 400 ", " 401 ", " 403 ", " 404 ", " 422 "];
25 let markers = [
26 "429",
27 "rate limit",
28 "too many requests",
29 "quota exceeded",
30 "service unavailable",
31 "temporarily unavailable",
32 "bad gateway",
33 "gateway timeout",
34 "timed out",
35 "timeout",
36 "connection reset",
37 "connection refused",
38 "network error",
39 "unknown api error",
40 "api_error",
41 "unknown error",
42 "protocol error code 469",
43 "no text payload",
44 "context length exceeded",
45 "maximum context length",
46 ];
47
48 if non_retryable_status_codes
49 .iter()
50 .any(|c| normalized.contains(c))
51 {
52 return false;
53 }
54
55 markers.iter().any(|m| normalized.contains(m))
56 || transient_status_codes
57 .iter()
58 .any(|c| normalized.contains(c))
59}