Skip to content

feat(quota): collect weekly quota via retrieveUserQuotaSummary#3185

Open
FlintyLemming wants to merge 3 commits into
lbjlaq:mainfrom
FlintyLemming:feat/quota-summary-weekly
Open

feat(quota): collect weekly quota via retrieveUserQuotaSummary#3185
FlintyLemming wants to merge 3 commits into
lbjlaq:mainfrom
FlintyLemming:feat/quota-summary-weekly

Conversation

@FlintyLemming

Copy link
Copy Markdown

Note: Backend-only. This PR implements weekly quota collection, persistence, and API exposure only. The frontend display is not implemented in this PR — I couldn't settle on the UI design (the weekly quota is grouped by model family rather than per-model, which doesn't map cleanly onto the existing per-model progress bars), so I'm leaving the UI for a follow-up. The data is fully available via the HTTP API for anyone who wants to consume it now.

Summary

Antigravity now exposes a weekly quota alongside the existing 5-hour quota per model group (e.g. "Gemini Models", "Claude and GPT models"). This PR collects that data from the upstream retrieveUserQuotaSummary endpoint, persists it to account files, and exposes it through the HTTP API.

Background

The fetchAvailableModels endpoint only returns a single per-model 5h quota window. The weekly quota is served by a separate endpoint discovered via network inspection of the official client:

POST /v1internal:retrieveUserQuotaSummary
Body: { "project": "<project_id>" }

It returns grouped buckets, each carrying both a weekly and a 5h window. The 5h values returned here are consistent with fetchAvailableModels (e.g. Gemini 0.9545 → 95%, Claude/GPT 0.7015 → 70%), confirming they are complementary rather than conflicting.

Changes

File Change
src-tauri/src/models/quota.rs Add QuotaBucket, QuotaGroup structs; add optional quota_groups field to QuotaData (#[serde(default)] for backward compat)
src-tauri/src/models/mod.rs Re-export the new types
src-tauri/src/modules/quota.rs Add fetch_quota_summary() calling retrieveUserQuotaSummary (3-endpoint fallback, best-effort — returns None on failure so the primary 5h quota is never blocked); embed it in fetch_quota_with_cache so all 10+ callers benefit automatically
src-tauri/src/proxy/server.rs Expose quota_groups through GET /accounts and GET /accounts/:id via QuotaGroupDto/QuotaBucketDto
src/types/account.ts Sync TypeScript types (QuotaBucket, QuotaGroup)
docker/Dockerfile.backend.localdist Fix: add missing BoringSSL build deps (cmake, golang-go, clang, ninja-build, etc.) and COPY dist to the backend-builder stage

Design decisions

  • Best-effort: if the weekly endpoint fails, the 5h quota is still returned normally. quota_groups is simply None and omitted from the JSON via #[serde(skip_serializing_if = "Option::is_none")].
  • Zero-intrusion: no changes to fetch_quota_with_retry, persistence logic, or any UI component. No new Tauri command.
  • Backward compatible: old account files without quota_groups deserialize fine (serde(default)).

Proof it works

GET /api/accounts/:id/quota on a Docker build from this branch:

Response (truncated, click to expand)
{
  "models": [
    { "name": "gemini-2.5-pro", "percentage": 95, "reset_time": "2026-06-16T07:43:38Z", "display_name": "Gemini 2.5 Pro", "..." : "..." },
    { "name": "claude-sonnet-4-6", "percentage": 70, "reset_time": "2026-06-16T07:26:50Z", "display_name": "Claude Sonnet 4.6 (Thinking)", "..." : "..." },
    { "name": "gpt-oss-120b-medium", "percentage": 70, "reset_time": "2026-06-16T07:26:50Z", "display_name": "GPT-OSS 120B (Medium)", "..." : "..." }
  ],
  "last_updated": 1781592425,
  "subscription_tier": "Google AI Pro",
  "model_forwarding_rules": { "gemini-3.1-pro-high": "gemini-pro-agent" },
  "quota_groups": [
    {
      "display_name": "Gemini Models",
      "description": "Models within this group: Gemini Flash, Gemini Pro",
      "buckets": [
        {
          "bucket_id": "gemini-weekly",
          "window": "weekly",
          "remaining_fraction": 0.99242574,
          "reset_time": "2026-06-23T02:43:38Z",
          "display_name": "Weekly Limit",
          "description": "You have used some of your weekly limit, it will fully refresh in 6 days, 19 hours."
        },
        {
          "bucket_id": "gemini-5h",
          "window": "5h",
          "remaining_fraction": 0.9545545,
          "reset_time": "2026-06-16T07:43:38Z",
          "display_name": "Five Hour Limit",
          "description": "You have used some of your 5-hour limit, it will fully refresh in 56 minutes."
        }
      ]
    },
    {
      "display_name": "Claude and GPT models",
      "description": "Models within this group: Claude Opus, Claude Sonnet, GPT-OSS",
      "buckets": [
        {
          "bucket_id": "3p-weekly",
          "window": "weekly",
          "remaining_fraction": 0.6558833,
          "reset_time": "2026-06-18T03:29:41Z",
          "display_name": "Weekly Limit",
          "description": "You have used some of your weekly limit, it will fully refresh in 1 day, 20 hours."
        },
        {
          "bucket_id": "3p-5h",
          "window": "5h",
          "remaining_fraction": 0.7015412,
          "reset_time": "2026-06-16T07:26:50Z",
          "display_name": "Five Hour Limit",
          "description": "You have used some of your 5-hour limit, it will fully refresh in 39 minutes."
        }
      ]
    }
  ]
}

Data consistency check — the 5h values from quota_groups match the per-model percentages in models:

Group quota_groups 5h remaining_fraction models percentage
Gemini 0.9545545 95%
Claude & GPT 0.7015412 70%

The weekly reset times (6d19h, 1d20h) are sliding windows, matching the behavior shown in the official Antigravity client.

图片

Commits

  • feat(quota): add weekly quota collection via retrieveUserQuotaSummary
  • fix(docker): add missing build deps for BoringSSL in localdist Dockerfile
  • fix: resolve compile errors and missing dist in localdist Dockerfile

Antigravity now exposes grouped quota (weekly + 5h windows) in addition
to the per-model 5h quota from fetchAvailableModels.

- models/quota.rs: add QuotaBucket, QuotaGroup structs; add optional
  quota_groups field to QuotaData (serde default for backward compat)
- modules/quota.rs: add fetch_quota_summary() calling the
  retrieveUserQuotaSummary endpoint (best-effort, never blocks the
  primary 5h quota fetch); embed it in fetch_quota_with_cache so all
  callers benefit automatically
- proxy/server.rs: expose quota_groups through the GET /accounts and
  GET /accounts/:id API responses via QuotaGroupDto/QuotaBucketDto
- types/account.ts: sync TypeScript types for QuotaBucket/QuotaGroup

No UI changes; data is collected, persisted to account files, and
exposed via the HTTP API only.
…file

Dockerfile.backend.localdist was missing cmake, golang-go, clang,
libclang-dev, perl, git, and ninja-build — all required by boring-sys2
to compile BoringSSL. This caused the build to panic with
"No such file or directory". Align deps with the main Dockerfile.
- quota.rs: add explicit type annotation for groups collect() (E0282)
- account.rs: add quota_groups field to QuotaData literal in
  mark_account_forbidden (E0063)
- Dockerfile.backend.localdist: COPY dist to backend-builder stage so
  tauri::generate_context!() can find frontendDist at build time
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant