mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-27 08:05:33 +00:00
fix(grok): serve current free quota policy
This commit is contained in:
@@ -7,6 +7,18 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const GrokFreeRolling24hTokenLimit int64 = 1_000_000
|
||||
|
||||
var grokFreeRolling24hTokenLimits = map[int64]struct{}{
|
||||
GrokFreeRolling24hTokenLimit: {},
|
||||
2_000_000: {}, // Legacy Free limit observed before July 2026.
|
||||
}
|
||||
|
||||
func IsGrokFreeRolling24hTokenLimit(limit int64) bool {
|
||||
_, ok := grokFreeRolling24hTokenLimits[limit]
|
||||
return ok
|
||||
}
|
||||
|
||||
type QuotaWindow struct {
|
||||
Limit *int64 `json:"limit,omitempty"`
|
||||
Remaining *int64 `json:"remaining,omitempty"`
|
||||
|
||||
@@ -61,3 +61,11 @@ func TestObserveQuotaHeadersRecordsNoHeaderProbe(t *testing.T) {
|
||||
require.Nil(t, snapshot.Requests)
|
||||
require.Nil(t, snapshot.Tokens)
|
||||
}
|
||||
|
||||
func TestIsGrokFreeRolling24hTokenLimit(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
require.True(t, IsGrokFreeRolling24hTokenLimit(GrokFreeRolling24hTokenLimit))
|
||||
require.True(t, IsGrokFreeRolling24hTokenLimit(2_000_000), "legacy snapshots remain classifiable")
|
||||
require.False(t, IsGrokFreeRolling24hTokenLimit(3_000_000))
|
||||
}
|
||||
|
||||
@@ -207,6 +207,7 @@ type UsageInfo struct {
|
||||
GrokLastQuotaProbeAt string `json:"grok_last_quota_probe_at,omitempty"`
|
||||
GrokLastHeadersSeenAt string `json:"grok_last_headers_seen_at,omitempty"`
|
||||
GrokLastStatusCode int `json:"grok_last_status_code,omitempty"`
|
||||
GrokFreeTokenLimit int64 `json:"grok_free_token_limit,omitempty"`
|
||||
GrokLocalUsage *WindowStats `json:"grok_local_usage,omitempty"`
|
||||
GrokLocalUsage24h *WindowStats `json:"grok_local_usage_24h,omitempty"`
|
||||
GrokLocalUsage7d *WindowStats `json:"grok_local_usage_7d,omitempty"`
|
||||
|
||||
@@ -21,8 +21,9 @@ func NewGrokQuotaFetcher() *GrokQuotaFetcher {
|
||||
func (f *GrokQuotaFetcher) BuildUsageInfo(account *Account) *UsageInfo {
|
||||
now := time.Now()
|
||||
usage := &UsageInfo{
|
||||
Source: "passive",
|
||||
UpdatedAt: &now,
|
||||
Source: "passive",
|
||||
UpdatedAt: &now,
|
||||
GrokFreeTokenLimit: xai.GrokFreeRolling24hTokenLimit,
|
||||
}
|
||||
if account == nil {
|
||||
usage.ErrorCode = "quota_unknown"
|
||||
|
||||
@@ -19,6 +19,7 @@ func TestGrokQuotaFetcherBuildUsageInfoUnknownUntilFirstSnapshot(t *testing.T) {
|
||||
|
||||
usage := NewGrokQuotaFetcher().BuildUsageInfo(&Account{Platform: PlatformGrok, Type: AccountTypeOAuth})
|
||||
require.Equal(t, "passive", usage.Source)
|
||||
require.Equal(t, xai.GrokFreeRolling24hTokenLimit, usage.GrokFreeTokenLimit)
|
||||
require.Equal(t, "quota_unknown", usage.ErrorCode)
|
||||
require.Contains(t, usage.Error, "unknown until billing is probed")
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/Wei-Shaw/sub2api/internal/pkg/xai"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/tidwall/gjson"
|
||||
"github.com/tidwall/sjson"
|
||||
@@ -15,7 +16,6 @@ const (
|
||||
grokConversationIDHeader = "X-Grok-Conv-Id"
|
||||
grokFreeCacheNativeToolsJSON = `[{"type":"web_search"},{"type":"x_search"}]`
|
||||
grokFreeCacheDisabledToolChoice = "none"
|
||||
grokFreeRolling24hTokenLimit = int64(2_000_000)
|
||||
)
|
||||
|
||||
// resolveGrokCacheIdentity derives one stable, tenant-isolated routing identity
|
||||
@@ -187,7 +187,7 @@ func isKnownGrokFreeAccount(account *Account) bool {
|
||||
}
|
||||
}
|
||||
if snapshot.Tokens != nil && snapshot.Tokens.Limit != nil &&
|
||||
*snapshot.Tokens.Limit == grokFreeRolling24hTokenLimit {
|
||||
xai.IsGrokFreeRolling24hTokenLimit(*snapshot.Tokens.Limit) {
|
||||
inferredFreeSignal = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/Wei-Shaw/sub2api/internal/pkg/xai"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/tidwall/gjson"
|
||||
@@ -325,7 +326,19 @@ func TestGrokFreeMessagesFunctionToolCacheRouteRequiresKnownFreeTier(t *testing.
|
||||
a := healthyGrokOAuthGatewayTestAccount(9112, "access-token")
|
||||
a.Extra = map[string]any{grokQuotaSnapshotExtraKey: map[string]any{
|
||||
"headers_observed": true,
|
||||
"tokens": map[string]any{"limit": grokFreeRolling24hTokenLimit},
|
||||
"tokens": map[string]any{"limit": xai.GrokFreeRolling24hTokenLimit},
|
||||
}}
|
||||
return a
|
||||
}(),
|
||||
wantMix: true,
|
||||
},
|
||||
{
|
||||
name: "legacy free rolling token quota",
|
||||
account: func() *Account {
|
||||
a := healthyGrokOAuthGatewayTestAccount(9113, "access-token")
|
||||
a.Extra = map[string]any{grokQuotaSnapshotExtraKey: map[string]any{
|
||||
"headers_observed": true,
|
||||
"tokens": map[string]any{"limit": int64(2_000_000)},
|
||||
}}
|
||||
return a
|
||||
}(),
|
||||
@@ -347,7 +360,7 @@ func TestGrokFreeMessagesFunctionToolCacheRouteRequiresKnownFreeTier(t *testing.
|
||||
grokBillingExtraKey: map[string]any{"plan": "SuperGrok", "status_code": http.StatusOK},
|
||||
grokQuotaSnapshotExtraKey: map[string]any{
|
||||
"headers_observed": true,
|
||||
"tokens": map[string]any{"limit": grokFreeRolling24hTokenLimit},
|
||||
"tokens": map[string]any{"limit": int64(2_000_000)},
|
||||
},
|
||||
}
|
||||
return a
|
||||
|
||||
Reference in New Issue
Block a user