fix(usage): improve usage-query resilience and error surfacing

- useUsageQuery: retry once + keep-last-good — show the last successful
  result for up to 10min when a query fails transiently (network/timeout/
  HTTP 5xx), so a single blip no longer flips the card to red. Deterministic
  failures (auth, empty key, unknown provider, 4xx) surface immediately and
  clear the snapshot so a stale quota can't resurface after credentials change.
- bump native balance/coding-plan/subscription request timeouts 10s -> 15s
  for slow cross-border endpoints.
- coding_plan: return explicit errors ("API key is empty" / "Unknown coding
  plan provider") instead of a blank failure, mirroring balance.
- add unit tests for keep-last-good and transient/deterministic classification.
This commit is contained in:
Jason
2026-06-14 19:25:58 +08:00
parent 9a3d6a4e84
commit 89ff2d58d1
5 changed files with 321 additions and 19 deletions
+5 -5
View File
@@ -328,7 +328,7 @@ async fn query_claude_quota(access_token: &str) -> SubscriptionQuota {
.header("Authorization", format!("Bearer {access_token}"))
.header("anthropic-beta", "oauth-2025-04-20")
.header("Accept", "application/json")
.timeout(std::time::Duration::from_secs(10))
.timeout(std::time::Duration::from_secs(15))
.send()
.await;
@@ -668,7 +668,7 @@ pub(crate) async fn query_codex_quota(
req = req.header("ChatGPT-Account-Id", id);
}
let resp = match req.timeout(std::time::Duration::from_secs(10)).send().await {
let resp = match req.timeout(std::time::Duration::from_secs(15)).send().await {
Ok(r) => r,
Err(e) => {
return SubscriptionQuota::error(
@@ -964,7 +964,7 @@ async fn refresh_gemini_token(refresh_token: &str) -> Option<String> {
("refresh_token", refresh_token),
("grant_type", "refresh_token"),
])
.timeout(std::time::Duration::from_secs(10))
.timeout(std::time::Duration::from_secs(15))
.send()
.await
.ok()?;
@@ -1048,7 +1048,7 @@ async fn query_gemini_quota(access_token: &str) -> SubscriptionQuota {
"pluginType": "GEMINI"
}
}))
.timeout(std::time::Duration::from_secs(10))
.timeout(std::time::Duration::from_secs(15))
.send()
.await;
@@ -1109,7 +1109,7 @@ async fn query_gemini_quota(access_token: &str) -> SubscriptionQuota {
.header("Authorization", format!("Bearer {access_token}"))
.header("Content-Type", "application/json")
.json(&quota_body)
.timeout(std::time::Duration::from_secs(10))
.timeout(std::time::Duration::from_secs(15))
.send()
.await;