From 57abb20778b19423debe35dde26a3447d5db09cb Mon Sep 17 00:00:00 2001 From: ZheFox <77232781+zhefox@users.noreply.github.com> Date: Tue, 1 Sep 2026 11:29:48 +0800 Subject: [PATCH] fix(pool): show Antigravity quota reset times --- .../src/handlers/shared/catalog.rs | 31 +++++++++++++++++-- crates/aether-admin/src/provider/quota.rs | 4 +++ .../__tests__/PoolKeyDisplayPanels.spec.ts | 10 ++++-- .../__tests__/antigravityQuotaSummary.spec.ts | 5 +-- .../providers/utils/antigravityQuota.ts | 3 +- frontend/src/views/admin/PoolManagement.vue | 6 ---- 6 files changed, 44 insertions(+), 15 deletions(-) diff --git a/apps/aether-gateway/src/handlers/shared/catalog.rs b/apps/aether-gateway/src/handlers/shared/catalog.rs index 394c04fff..04dbe1a9c 100644 --- a/apps/aether-gateway/src/handlers/shared/catalog.rs +++ b/apps/aether-gateway/src/handlers/shared/catalog.rs @@ -443,7 +443,18 @@ fn provider_quota_metadata_bucket<'a>( fn provider_quota_timestamp_unix_secs(value: Option<&Value>) -> Option { let mut parsed = match value { Some(Value::Number(number)) => number.as_f64(), - Some(Value::String(text)) => text.trim().parse::().ok(), + Some(Value::String(text)) => { + let text = text.trim(); + if let Ok(timestamp) = text.parse::() { + Some(timestamp) + } else { + return chrono::DateTime::parse_from_rfc3339(text) + .ok()? + .timestamp() + .try_into() + .ok(); + } + } _ => None, }?; if !parsed.is_finite() || parsed <= 0.0 { @@ -560,7 +571,10 @@ fn model_quota_window_snapshot( .map(|value| value.clamp(0.0, 1.0)) .or_else(|| used_ratio.map(|value| (1.0 - value).max(0.0))); let reset_at = provider_quota_timestamp_unix_secs( - item.get("reset_at").or_else(|| item.get("next_reset_at")), + item.get("reset_at") + .or_else(|| item.get("next_reset_at")) + .or_else(|| item.get("reset_time")) + .or_else(|| item.get("next_reset_time")), ); let reset_seconds = quota_window_reset_seconds(observed_at_unix_secs, reset_at); let is_exhausted = item @@ -4083,7 +4097,8 @@ mod tests { }, "claude-sonnet-4-6": { "display_name": "Claude Sonnet 4.6 (Thinking)", - "remaining_fraction": 0.3 + "remaining_fraction": 0.3, + "reset_time": "2026-04-07T12:34:56Z" } } } @@ -4128,6 +4143,16 @@ mod tests { label_for_model("claude-sonnet-4-6"), Some(json!("Claude Sonnet 4.6 (Thinking)")) ); + let claude_window = windows + .iter() + .filter_map(Value::as_object) + .find(|window| window.get("model") == Some(&json!("claude-sonnet-4-6"))) + .expect("Claude quota window should exist"); + assert_eq!( + claude_window.get("reset_at"), + Some(&json!(1_775_565_296u64)) + ); + assert_eq!(claude_window.get("reset_seconds"), Some(&json!(12_011u64))); } #[test] diff --git a/crates/aether-admin/src/provider/quota.rs b/crates/aether-admin/src/provider/quota.rs index 19e61466b..48e75ebef 100644 --- a/crates/aether-admin/src/provider/quota.rs +++ b/crates/aether-admin/src/provider/quota.rs @@ -6753,6 +6753,10 @@ mod tests { ["display_name"], json!("Key-1") ); + assert_eq!( + parsed["models"]["RateLimitResetCredit_05cbb6eeeb9c81918e011d8300f9ebfb"]["reset_time"], + json!("2030-01-01T00:00:00Z") + ); assert_eq!( parsed["models"]["gemini-3-pro-preview"]["display_name"], json!("Gemini 3 Pro Preview") diff --git a/frontend/src/features/pool/components/__tests__/PoolKeyDisplayPanels.spec.ts b/frontend/src/features/pool/components/__tests__/PoolKeyDisplayPanels.spec.ts index 063729921..8ac3b3dcb 100644 --- a/frontend/src/features/pool/components/__tests__/PoolKeyDisplayPanels.spec.ts +++ b/frontend/src/features/pool/components/__tests__/PoolKeyDisplayPanels.spec.ts @@ -99,7 +99,7 @@ describe('pool key display panels', () => { label: 'Gemini额度', remainingPercent: 90.6, resetText: '1h 后重置', - meterText: '90.6–100', + meterText: '90.6%–100%', barClass: 'bg-emerald-500', meterClass: 'text-emerald-600', }, @@ -107,7 +107,7 @@ describe('pool key display panels', () => { label: 'Claude额度', remainingPercent: 100, resetText: '1h 后重置', - meterText: '100', + meterText: '100%', barClass: 'bg-emerald-500', meterClass: 'text-emerald-600', }, @@ -121,8 +121,12 @@ describe('pool key display panels', () => { 'Gemini额度', 'Claude额度', ]) - expect(Array.from(root.querySelectorAll('[data-testid="pool-quota-meter-text"]')).map(node => node.textContent)).toEqual(['90.6–100', '100']) + expect(Array.from(root.querySelectorAll('[data-testid="pool-quota-meter-text"]')).map(node => node.textContent)).toEqual(['90.6%–100%', '100%']) expect(root.querySelectorAll('[data-testid="pool-quota-progress-track"]')).toHaveLength(2) + expect(Array.from(root.querySelectorAll('[data-testid="pool-quota-reset-text"]')).map(node => node.textContent)).toEqual([ + '1h 后重置', + '1h 后重置', + ]) app.unmount() root.remove() diff --git a/frontend/src/features/providers/utils/__tests__/antigravityQuotaSummary.spec.ts b/frontend/src/features/providers/utils/__tests__/antigravityQuotaSummary.spec.ts index ee0db2b03..877978c76 100644 --- a/frontend/src/features/providers/utils/__tests__/antigravityQuotaSummary.spec.ts +++ b/frontend/src/features/providers/utils/__tests__/antigravityQuotaSummary.spec.ts @@ -15,9 +15,10 @@ describe('summarizeAntigravityQuotaItems', () => { ]) expect(items.map(item => [item.label, item.remainingPercent, item.detail])).toEqual([ - ['Gemini额度', 90.6, '90.6–95'], - ['Claude额度', 82, '82–100'], + ['Gemini额度', 90.6, '90.6%–95%'], + ['Claude额度', 82, '82%–100%'], ]) + expect(items.map(item => item.resetSeconds)).toEqual([180, 120]) expect(items[1]?.model).toBe('claude-sonnet-4-6') }) }) \ No newline at end of file diff --git a/frontend/src/features/providers/utils/antigravityQuota.ts b/frontend/src/features/providers/utils/antigravityQuota.ts index d27071200..f884d685e 100644 --- a/frontend/src/features/providers/utils/antigravityQuota.ts +++ b/frontend/src/features/providers/utils/antigravityQuota.ts @@ -158,5 +158,6 @@ export function summarizeAntigravityQuotaItems item != null))) - .map(item => ({ - ...item, - resetAtSeconds: null, - resetSeconds: null, - allowDynamicReset: false, - })) } if (providerType === 'gemini_cli') {