-
-
Notifications
You must be signed in to change notification settings - Fork 689
Background Quota Refresh & Automated Token Management #766
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from 7 commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
3374292
feat(quota): implement quota management and fetching for multiple pro…
dacsang97 38c721a
feat(quota): enhance quota data structure with rate limit windows for…
dacsang97 8f9c99c
feat(quota): add error handling for unknown providers in quota fetching
dacsang97 623274f
feat(quota): update comments and improve handling for unsupported pro…
dacsang97 a66c580
feat(quota): implement background quota refresh mechanism and related…
dacsang97 11b08ec
feat(quota): implement access token management with refresh functiona…
dacsang97 369f52d
feat(quota): enhance refresh interval handling and update LastUpdated…
dacsang97 173a01f
feat(quota): improve quota refresh handling and update LastUpdated lo…
dacsang97 d3fe406
fix(logging): improve request/response capture
hkfires cc2725c
fix(handlers): match raw error text before JSON body for duplicate de…
hkfires 5bca7af
fix(handlers): preserve upstream response logs before duplicate detec…
hkfires 9a2e319
chore: add codex, agents, and opencode dirs to ignore files
hkfires 8efb911
fix(translators): correct key path for `system_instruction.parts` in …
luispater 74bf4a6
feat(amp): add per-client upstream API key mapping support
hkfires 6b2550d
feat(api): add id token claims extraction for codex auth entries
hkfires 42f5731
refactor(api): simplify codex id token claims extraction
hkfires 6c9b019
fix(antigravity): inject required placeholder when properties exist w…
LTbinglingfeng File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -52,6 +52,12 @@ logs-max-total-size-mb: 0 | |
| # When false, disable in-memory usage statistics aggregation | ||
| usage-statistics-enabled: false | ||
|
|
||
| # Quota refresh interval in seconds. When set to a positive value, the server | ||
| # will periodically fetch quota data for all configured providers in the background | ||
| # and cache it in memory. This eliminates the need to manually refresh quota data. | ||
| # Set to 0 to disable background refresh (fetch on-demand only). Default: 0 | ||
| # quota-refresh-interval: 300 # 5 minutes | ||
|
|
||
| # Proxy URL. Supports socks5/http/https protocols. Example: socks5://user:[email protected]:1080/ | ||
| proxy-url: "" | ||
|
|
||
|
|
@@ -75,7 +81,6 @@ routing: | |
|
|
||
| # When true, enable authentication for the WebSocket API (/v1/ws). | ||
| ws-auth: false | ||
|
|
||
| # Streaming behavior (SSE keep-alives + safe bootstrap retries). | ||
| # streaming: | ||
| # keepalive-seconds: 15 # Default: 0 (disabled). <= 0 disables keep-alives. | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,132 @@ | ||
| package management | ||
|
|
||
| import ( | ||
| "errors" | ||
| "net/http" | ||
|
|
||
| "github.com/gin-gonic/gin" | ||
| "github.com/router-for-me/CLIProxyAPI/v6/internal/quota" | ||
| ) | ||
|
|
||
| // GetAllQuotas returns quota for all connected accounts. | ||
| // GET /v0/management/quotas | ||
| func (h *Handler) GetAllQuotas(c *gin.Context) { | ||
| if h.quotaManager == nil { | ||
| c.JSON(http.StatusServiceUnavailable, gin.H{"error": "quota manager not initialized"}) | ||
| return | ||
| } | ||
|
|
||
| ctx := c.Request.Context() | ||
| quotas, err := h.quotaManager.FetchAllQuotas(ctx) | ||
| if err != nil { | ||
| c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | ||
| return | ||
| } | ||
|
|
||
| c.JSON(http.StatusOK, quotas) | ||
| } | ||
|
|
||
| // GetProviderQuotas returns quota for a specific provider. | ||
| // GET /v0/management/quotas/:provider | ||
| func (h *Handler) GetProviderQuotas(c *gin.Context) { | ||
| if h.quotaManager == nil { | ||
| c.JSON(http.StatusServiceUnavailable, gin.H{"error": "quota manager not initialized"}) | ||
| return | ||
| } | ||
|
|
||
| provider := c.Param("provider") | ||
| if provider == "" { | ||
| c.JSON(http.StatusBadRequest, gin.H{"error": "provider is required"}) | ||
| return | ||
| } | ||
|
|
||
| ctx := c.Request.Context() | ||
| quotas, err := h.quotaManager.FetchProviderQuotas(ctx, provider) | ||
| if err != nil { | ||
| if errors.Is(err, quota.ErrUnknownProvider) { | ||
| c.JSON(http.StatusNotFound, gin.H{ | ||
| "error": err.Error(), | ||
| "known_providers": h.quotaManager.GetKnownProviders(), | ||
| }) | ||
| return | ||
| } | ||
| c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | ||
| return | ||
| } | ||
|
|
||
| c.JSON(http.StatusOK, quotas) | ||
| } | ||
|
|
||
| // GetAccountQuota returns quota for a specific account. | ||
| // GET /v0/management/quotas/:provider/:account | ||
| func (h *Handler) GetAccountQuota(c *gin.Context) { | ||
| if h.quotaManager == nil { | ||
| c.JSON(http.StatusServiceUnavailable, gin.H{"error": "quota manager not initialized"}) | ||
| return | ||
| } | ||
|
|
||
| provider := c.Param("provider") | ||
| account := c.Param("account") | ||
| if provider == "" || account == "" { | ||
| c.JSON(http.StatusBadRequest, gin.H{"error": "provider and account are required"}) | ||
| return | ||
| } | ||
|
|
||
| ctx := c.Request.Context() | ||
| quotaResp, err := h.quotaManager.FetchAccountQuota(ctx, provider, account) | ||
| if err != nil { | ||
| if errors.Is(err, quota.ErrUnknownProvider) { | ||
| c.JSON(http.StatusNotFound, gin.H{ | ||
| "error": err.Error(), | ||
| "known_providers": h.quotaManager.GetKnownProviders(), | ||
| }) | ||
| return | ||
| } | ||
| c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | ||
| return | ||
| } | ||
|
|
||
| c.JSON(http.StatusOK, quotaResp) | ||
| } | ||
|
|
||
| // RefreshQuotas forces a quota refresh for all or specific providers. | ||
| // POST /v0/management/quotas/refresh | ||
| func (h *Handler) RefreshQuotas(c *gin.Context) { | ||
| if h.quotaManager == nil { | ||
| c.JSON(http.StatusServiceUnavailable, gin.H{"error": "quota manager not initialized"}) | ||
| return | ||
| } | ||
|
|
||
| var req quota.RefreshRequest | ||
| if err := c.ShouldBindJSON(&req); err != nil { | ||
| // Allow empty body - refresh all | ||
| req.Providers = nil | ||
| } | ||
|
|
||
| ctx := c.Request.Context() | ||
| quotas, err := h.quotaManager.RefreshQuotas(ctx, req.Providers) | ||
| if err != nil { | ||
| c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | ||
| return | ||
| } | ||
|
|
||
| c.JSON(http.StatusOK, quotas) | ||
| } | ||
|
|
||
| // GetSubscriptionInfo returns subscription/tier info for Antigravity accounts. | ||
| // GET /v0/management/subscription-info | ||
| func (h *Handler) GetSubscriptionInfo(c *gin.Context) { | ||
| if h.quotaManager == nil { | ||
| c.JSON(http.StatusServiceUnavailable, gin.H{"error": "quota manager not initialized"}) | ||
| return | ||
| } | ||
|
|
||
| ctx := c.Request.Context() | ||
| info, err := h.quotaManager.GetSubscriptionInfo(ctx) | ||
| if err != nil { | ||
| c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | ||
| return | ||
| } | ||
|
|
||
| c.JSON(http.StatusOK, info) | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.