Revert "fix(usage): 修正 OpenAI 5h 用量百分比语义"
This reverts commit b65dde634bf7a5338f22b46ad0f9d98a197526fc.
This commit is contained in:
parent
aa69e3947d
commit
a8ffb052ca
@ -132,7 +132,7 @@ func TestAccountTestService_OpenAISuccessPersistsSnapshotFromHeaders(t *testing.
|
|||||||
require.Len(t, upstream.requests, 1)
|
require.Len(t, upstream.requests, 1)
|
||||||
require.Equal(t, HTTPUpstreamProfileOpenAI, HTTPUpstreamProfileFromContext(upstream.requests[0].Context()))
|
require.Equal(t, HTTPUpstreamProfileOpenAI, HTTPUpstreamProfileFromContext(upstream.requests[0].Context()))
|
||||||
require.NotEmpty(t, repo.updatedExtra)
|
require.NotEmpty(t, repo.updatedExtra)
|
||||||
require.Equal(t, 58.0, repo.updatedExtra["codex_5h_used_percent"])
|
require.Equal(t, 42.0, repo.updatedExtra["codex_5h_used_percent"])
|
||||||
require.Equal(t, 88.0, repo.updatedExtra["codex_7d_used_percent"])
|
require.Equal(t, 88.0, repo.updatedExtra["codex_7d_used_percent"])
|
||||||
require.Contains(t, recorder.Body.String(), "test_complete")
|
require.Contains(t, recorder.Body.String(), "test_complete")
|
||||||
}
|
}
|
||||||
@ -170,7 +170,7 @@ func TestAccountTestService_OpenAI429PersistsSnapshotAndRateLimitState(t *testin
|
|||||||
resp.Header.Set("x-codex-primary-used-percent", "100")
|
resp.Header.Set("x-codex-primary-used-percent", "100")
|
||||||
resp.Header.Set("x-codex-primary-reset-after-seconds", "604800")
|
resp.Header.Set("x-codex-primary-reset-after-seconds", "604800")
|
||||||
resp.Header.Set("x-codex-primary-window-minutes", "10080")
|
resp.Header.Set("x-codex-primary-window-minutes", "10080")
|
||||||
resp.Header.Set("x-codex-secondary-used-percent", "0")
|
resp.Header.Set("x-codex-secondary-used-percent", "100")
|
||||||
resp.Header.Set("x-codex-secondary-reset-after-seconds", "18000")
|
resp.Header.Set("x-codex-secondary-reset-after-seconds", "18000")
|
||||||
resp.Header.Set("x-codex-secondary-window-minutes", "300")
|
resp.Header.Set("x-codex-secondary-window-minutes", "300")
|
||||||
|
|
||||||
|
|||||||
@ -73,7 +73,7 @@ func TestExtractOpenAICodexProbeUpdatesAccepts429WithCodexHeaders(t *testing.T)
|
|||||||
headers.Set("x-codex-primary-used-percent", "100")
|
headers.Set("x-codex-primary-used-percent", "100")
|
||||||
headers.Set("x-codex-primary-reset-after-seconds", "604800")
|
headers.Set("x-codex-primary-reset-after-seconds", "604800")
|
||||||
headers.Set("x-codex-primary-window-minutes", "10080")
|
headers.Set("x-codex-primary-window-minutes", "10080")
|
||||||
headers.Set("x-codex-secondary-used-percent", "0")
|
headers.Set("x-codex-secondary-used-percent", "100")
|
||||||
headers.Set("x-codex-secondary-reset-after-seconds", "18000")
|
headers.Set("x-codex-secondary-reset-after-seconds", "18000")
|
||||||
headers.Set("x-codex-secondary-window-minutes", "300")
|
headers.Set("x-codex-secondary-window-minutes", "300")
|
||||||
|
|
||||||
@ -92,33 +92,6 @@ func TestExtractOpenAICodexProbeUpdatesAccepts429WithCodexHeaders(t *testing.T)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBuildCodexUsageProgressFromExtra_UsesCanonicalUsedPercent(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
now := time.Date(2026, 5, 30, 7, 4, 9, 0, time.UTC)
|
|
||||||
extra := map[string]any{
|
|
||||||
"codex_5h_used_percent": 94.0,
|
|
||||||
"codex_5h_reset_at": now.Add(2 * time.Hour).Format(time.RFC3339),
|
|
||||||
"codex_7d_used_percent": 93.0,
|
|
||||||
"codex_7d_reset_at": now.Add(5 * 24 * time.Hour).Format(time.RFC3339),
|
|
||||||
}
|
|
||||||
|
|
||||||
fiveHour := buildCodexUsageProgressFromExtra(extra, "5h", now)
|
|
||||||
if fiveHour == nil {
|
|
||||||
t.Fatal("expected non-nil 5h progress")
|
|
||||||
}
|
|
||||||
if fiveHour.Utilization != 94.0 {
|
|
||||||
t.Fatalf("5h Utilization = %v, want 94", fiveHour.Utilization)
|
|
||||||
}
|
|
||||||
|
|
||||||
sevenDay := buildCodexUsageProgressFromExtra(extra, "7d", now)
|
|
||||||
if sevenDay == nil {
|
|
||||||
t.Fatal("expected non-nil 7d progress")
|
|
||||||
}
|
|
||||||
if sevenDay.Utilization != 93.0 {
|
|
||||||
t.Fatalf("7d Utilization = %v, want 93", sevenDay.Utilization)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAccountUsageService_PersistOpenAICodexProbeSnapshotOnlyUpdatesExtra(t *testing.T) {
|
func TestAccountUsageService_PersistOpenAICodexProbeSnapshotOnlyUpdatesExtra(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|||||||
@ -126,19 +126,6 @@ type NormalizedCodexLimits struct {
|
|||||||
Window7dMinutes *int
|
Window7dMinutes *int
|
||||||
}
|
}
|
||||||
|
|
||||||
func normalizeCodexFiveHourUsedPercent(raw *float64) *float64 {
|
|
||||||
if raw == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// OpenAI's 5h Codex quota header is remaining%, despite the upstream header
|
|
||||||
// name saying "used"; the canonical codex_5h_used_percent field stores used%.
|
|
||||||
used := 100 - *raw
|
|
||||||
if used < 0 {
|
|
||||||
used = 0
|
|
||||||
}
|
|
||||||
return &used
|
|
||||||
}
|
|
||||||
|
|
||||||
// Normalize converts primary/secondary fields to canonical 5h/7d fields.
|
// Normalize converts primary/secondary fields to canonical 5h/7d fields.
|
||||||
// Strategy: Compare window_minutes to determine which is 5h vs 7d.
|
// Strategy: Compare window_minutes to determine which is 5h vs 7d.
|
||||||
// Returns nil if snapshot is nil or has no useful data.
|
// Returns nil if snapshot is nil or has no useful data.
|
||||||
@ -197,7 +184,7 @@ func (s *OpenAICodexUsageSnapshot) Normalize() *NormalizedCodexLimits {
|
|||||||
|
|
||||||
// Assign values
|
// Assign values
|
||||||
if use5hFromPrimary {
|
if use5hFromPrimary {
|
||||||
result.Used5hPercent = normalizeCodexFiveHourUsedPercent(s.PrimaryUsedPercent)
|
result.Used5hPercent = s.PrimaryUsedPercent
|
||||||
result.Reset5hSeconds = s.PrimaryResetAfterSeconds
|
result.Reset5hSeconds = s.PrimaryResetAfterSeconds
|
||||||
result.Window5hMinutes = s.PrimaryWindowMinutes
|
result.Window5hMinutes = s.PrimaryWindowMinutes
|
||||||
result.Used7dPercent = s.SecondaryUsedPercent
|
result.Used7dPercent = s.SecondaryUsedPercent
|
||||||
@ -207,7 +194,7 @@ func (s *OpenAICodexUsageSnapshot) Normalize() *NormalizedCodexLimits {
|
|||||||
result.Used7dPercent = s.PrimaryUsedPercent
|
result.Used7dPercent = s.PrimaryUsedPercent
|
||||||
result.Reset7dSeconds = s.PrimaryResetAfterSeconds
|
result.Reset7dSeconds = s.PrimaryResetAfterSeconds
|
||||||
result.Window7dMinutes = s.PrimaryWindowMinutes
|
result.Window7dMinutes = s.PrimaryWindowMinutes
|
||||||
result.Used5hPercent = normalizeCodexFiveHourUsedPercent(s.SecondaryUsedPercent)
|
result.Used5hPercent = s.SecondaryUsedPercent
|
||||||
result.Reset5hSeconds = s.SecondaryResetAfterSeconds
|
result.Reset5hSeconds = s.SecondaryResetAfterSeconds
|
||||||
result.Window5hMinutes = s.SecondaryWindowMinutes
|
result.Window5hMinutes = s.SecondaryWindowMinutes
|
||||||
}
|
}
|
||||||
|
|||||||
@ -104,40 +104,6 @@ func TestBuildCodexUsageExtraUpdates_UsesSnapshotUpdatedAt(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBuildCodexUsageExtraUpdates_NormalizesFiveHourRemainingToUsedPercent(t *testing.T) {
|
|
||||||
primaryUsed := 93.0
|
|
||||||
primaryReset := 86400
|
|
||||||
primaryWindow := 10080
|
|
||||||
secondaryRemaining := 6.0
|
|
||||||
secondaryReset := 3600
|
|
||||||
secondaryWindow := 300
|
|
||||||
|
|
||||||
snapshot := &OpenAICodexUsageSnapshot{
|
|
||||||
PrimaryUsedPercent: &primaryUsed,
|
|
||||||
PrimaryResetAfterSeconds: &primaryReset,
|
|
||||||
PrimaryWindowMinutes: &primaryWindow,
|
|
||||||
SecondaryUsedPercent: &secondaryRemaining,
|
|
||||||
SecondaryResetAfterSeconds: &secondaryReset,
|
|
||||||
SecondaryWindowMinutes: &secondaryWindow,
|
|
||||||
UpdatedAt: "2026-05-30T07:04:09Z",
|
|
||||||
}
|
|
||||||
|
|
||||||
updates := buildCodexUsageExtraUpdates(snapshot, time.Time{})
|
|
||||||
if updates == nil {
|
|
||||||
t.Fatal("expected non-nil updates")
|
|
||||||
}
|
|
||||||
|
|
||||||
if got := updates["codex_secondary_used_percent"]; got != 6.0 {
|
|
||||||
t.Fatalf("codex_secondary_used_percent = %v, want raw upstream value 6", got)
|
|
||||||
}
|
|
||||||
if got := updates["codex_5h_used_percent"]; got != 94.0 {
|
|
||||||
t.Fatalf("codex_5h_used_percent = %v, want 94", got)
|
|
||||||
}
|
|
||||||
if got := updates["codex_7d_used_percent"]; got != 93.0 {
|
|
||||||
t.Fatalf("codex_7d_used_percent = %v, want 93", got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuildCodexUsageExtraUpdates_FallbackToNowWhenUpdatedAtInvalid(t *testing.T) {
|
func TestBuildCodexUsageExtraUpdates_FallbackToNowWhenUpdatedAtInvalid(t *testing.T) {
|
||||||
primaryUsed := 15.0
|
primaryUsed := 15.0
|
||||||
primaryReset := 30
|
primaryReset := 30
|
||||||
|
|||||||
@ -1774,7 +1774,7 @@ func TestOpenAIUpdateCodexUsageSnapshotFromHeaders(t *testing.T) {
|
|||||||
|
|
||||||
select {
|
select {
|
||||||
case updates := <-repo.updateExtraCalls:
|
case updates := <-repo.updateExtraCalls:
|
||||||
require.Equal(t, 88.0, updates["codex_5h_used_percent"])
|
require.Equal(t, 12.0, updates["codex_5h_used_percent"])
|
||||||
require.Equal(t, 34.0, updates["codex_7d_used_percent"])
|
require.Equal(t, 34.0, updates["codex_7d_used_percent"])
|
||||||
require.Equal(t, 600, updates["codex_5h_reset_after_seconds"])
|
require.Equal(t, 600, updates["codex_5h_reset_after_seconds"])
|
||||||
require.Equal(t, 86400, updates["codex_7d_reset_after_seconds"])
|
require.Equal(t, 86400, updates["codex_7d_reset_after_seconds"])
|
||||||
|
|||||||
@ -51,7 +51,7 @@ func TestCalculateOpenAI429ResetTime_5hExhausted(t *testing.T) {
|
|||||||
headers.Set("x-codex-primary-used-percent", "50")
|
headers.Set("x-codex-primary-used-percent", "50")
|
||||||
headers.Set("x-codex-primary-reset-after-seconds", "500000")
|
headers.Set("x-codex-primary-reset-after-seconds", "500000")
|
||||||
headers.Set("x-codex-primary-window-minutes", "10080") // 7 days
|
headers.Set("x-codex-primary-window-minutes", "10080") // 7 days
|
||||||
headers.Set("x-codex-secondary-used-percent", "0")
|
headers.Set("x-codex-secondary-used-percent", "100")
|
||||||
headers.Set("x-codex-secondary-reset-after-seconds", "3600") // 1 hour
|
headers.Set("x-codex-secondary-reset-after-seconds", "3600") // 1 hour
|
||||||
headers.Set("x-codex-secondary-window-minutes", "300") // 5 hours
|
headers.Set("x-codex-secondary-window-minutes", "300") // 5 hours
|
||||||
|
|
||||||
@ -122,7 +122,7 @@ func TestCalculateOpenAI429ResetTime_ReversedWindowOrder(t *testing.T) {
|
|||||||
|
|
||||||
// Test when OpenAI sends primary as 5h and secondary as 7d (reversed)
|
// Test when OpenAI sends primary as 5h and secondary as 7d (reversed)
|
||||||
headers := http.Header{}
|
headers := http.Header{}
|
||||||
headers.Set("x-codex-primary-used-percent", "0") // This is 5h remaining%
|
headers.Set("x-codex-primary-used-percent", "100") // This is 5h
|
||||||
headers.Set("x-codex-primary-reset-after-seconds", "3600") // 1 hour
|
headers.Set("x-codex-primary-reset-after-seconds", "3600") // 1 hour
|
||||||
headers.Set("x-codex-primary-window-minutes", "300") // 5 hours - smaller!
|
headers.Set("x-codex-primary-window-minutes", "300") // 5 hours - smaller!
|
||||||
headers.Set("x-codex-secondary-used-percent", "50")
|
headers.Set("x-codex-secondary-used-percent", "50")
|
||||||
@ -180,7 +180,7 @@ func TestHandle429_OpenAIPersistsCodexSnapshotImmediately(t *testing.T) {
|
|||||||
headers.Set("x-codex-primary-used-percent", "100")
|
headers.Set("x-codex-primary-used-percent", "100")
|
||||||
headers.Set("x-codex-primary-reset-after-seconds", "604800")
|
headers.Set("x-codex-primary-reset-after-seconds", "604800")
|
||||||
headers.Set("x-codex-primary-window-minutes", "10080")
|
headers.Set("x-codex-primary-window-minutes", "10080")
|
||||||
headers.Set("x-codex-secondary-used-percent", "0")
|
headers.Set("x-codex-secondary-used-percent", "100")
|
||||||
headers.Set("x-codex-secondary-reset-after-seconds", "18000")
|
headers.Set("x-codex-secondary-reset-after-seconds", "18000")
|
||||||
headers.Set("x-codex-secondary-window-minutes", "300")
|
headers.Set("x-codex-secondary-window-minutes", "300")
|
||||||
|
|
||||||
@ -224,7 +224,7 @@ func TestNormalizedCodexLimits(t *testing.T) {
|
|||||||
pUsed := 100.0
|
pUsed := 100.0
|
||||||
pReset := 384607
|
pReset := 384607
|
||||||
pWindow := 10080
|
pWindow := 10080
|
||||||
sRemaining := 3.0
|
sUsed := 3.0
|
||||||
sReset := 17369
|
sReset := 17369
|
||||||
sWindow := 300
|
sWindow := 300
|
||||||
|
|
||||||
@ -232,7 +232,7 @@ func TestNormalizedCodexLimits(t *testing.T) {
|
|||||||
PrimaryUsedPercent: &pUsed,
|
PrimaryUsedPercent: &pUsed,
|
||||||
PrimaryResetAfterSeconds: &pReset,
|
PrimaryResetAfterSeconds: &pReset,
|
||||||
PrimaryWindowMinutes: &pWindow,
|
PrimaryWindowMinutes: &pWindow,
|
||||||
SecondaryUsedPercent: &sRemaining,
|
SecondaryUsedPercent: &sUsed,
|
||||||
SecondaryResetAfterSeconds: &sReset,
|
SecondaryResetAfterSeconds: &sReset,
|
||||||
SecondaryWindowMinutes: &sWindow,
|
SecondaryWindowMinutes: &sWindow,
|
||||||
}
|
}
|
||||||
@ -249,8 +249,8 @@ func TestNormalizedCodexLimits(t *testing.T) {
|
|||||||
if normalized.Reset7dSeconds == nil || *normalized.Reset7dSeconds != 384607 {
|
if normalized.Reset7dSeconds == nil || *normalized.Reset7dSeconds != 384607 {
|
||||||
t.Errorf("expected Reset7dSeconds=384607, got %v", normalized.Reset7dSeconds)
|
t.Errorf("expected Reset7dSeconds=384607, got %v", normalized.Reset7dSeconds)
|
||||||
}
|
}
|
||||||
if normalized.Used5hPercent == nil || *normalized.Used5hPercent != 97.0 {
|
if normalized.Used5hPercent == nil || *normalized.Used5hPercent != 3.0 {
|
||||||
t.Errorf("expected Used5hPercent=97, got %v", normalized.Used5hPercent)
|
t.Errorf("expected Used5hPercent=3, got %v", normalized.Used5hPercent)
|
||||||
}
|
}
|
||||||
if normalized.Reset5hSeconds == nil || *normalized.Reset5hSeconds != 17369 {
|
if normalized.Reset5hSeconds == nil || *normalized.Reset5hSeconds != 17369 {
|
||||||
t.Errorf("expected Reset5hSeconds=17369, got %v", normalized.Reset5hSeconds)
|
t.Errorf("expected Reset5hSeconds=17369, got %v", normalized.Reset5hSeconds)
|
||||||
@ -338,11 +338,11 @@ func TestRateLimitService_HandleUpstreamError_403FallsBackToRawBody(t *testing.T
|
|||||||
|
|
||||||
func TestNormalizedCodexLimits_OnlySecondaryData(t *testing.T) {
|
func TestNormalizedCodexLimits_OnlySecondaryData(t *testing.T) {
|
||||||
// Test when only secondary has data, no window_minutes
|
// Test when only secondary has data, no window_minutes
|
||||||
sRemaining := 60.0
|
sUsed := 60.0
|
||||||
sReset := 3000
|
sReset := 3000
|
||||||
|
|
||||||
snapshot := &OpenAICodexUsageSnapshot{
|
snapshot := &OpenAICodexUsageSnapshot{
|
||||||
SecondaryUsedPercent: &sRemaining,
|
SecondaryUsedPercent: &sUsed,
|
||||||
SecondaryResetAfterSeconds: &sReset,
|
SecondaryResetAfterSeconds: &sReset,
|
||||||
// No window_minutes, no primary data
|
// No window_minutes, no primary data
|
||||||
}
|
}
|
||||||
@ -354,8 +354,8 @@ func TestNormalizedCodexLimits_OnlySecondaryData(t *testing.T) {
|
|||||||
|
|
||||||
// Legacy assumption: primary=7d, secondary=5h
|
// Legacy assumption: primary=7d, secondary=5h
|
||||||
// So secondary goes to 5h
|
// So secondary goes to 5h
|
||||||
if normalized.Used5hPercent == nil || *normalized.Used5hPercent != 40.0 {
|
if normalized.Used5hPercent == nil || *normalized.Used5hPercent != 60.0 {
|
||||||
t.Errorf("expected Used5hPercent=40, got %v", normalized.Used5hPercent)
|
t.Errorf("expected Used5hPercent=60, got %v", normalized.Used5hPercent)
|
||||||
}
|
}
|
||||||
if normalized.Reset5hSeconds == nil || *normalized.Reset5hSeconds != 3000 {
|
if normalized.Reset5hSeconds == nil || *normalized.Reset5hSeconds != 3000 {
|
||||||
t.Errorf("expected Reset5hSeconds=3000, got %v", normalized.Reset5hSeconds)
|
t.Errorf("expected Reset5hSeconds=3000, got %v", normalized.Reset5hSeconds)
|
||||||
@ -370,13 +370,13 @@ func TestNormalizedCodexLimits_BothDataNoWindowMinutes(t *testing.T) {
|
|||||||
// Test when both have data but no window_minutes
|
// Test when both have data but no window_minutes
|
||||||
pUsed := 100.0
|
pUsed := 100.0
|
||||||
pReset := 400000
|
pReset := 400000
|
||||||
sRemaining := 30.0
|
sUsed := 50.0
|
||||||
sReset := 10000
|
sReset := 10000
|
||||||
|
|
||||||
snapshot := &OpenAICodexUsageSnapshot{
|
snapshot := &OpenAICodexUsageSnapshot{
|
||||||
PrimaryUsedPercent: &pUsed,
|
PrimaryUsedPercent: &pUsed,
|
||||||
PrimaryResetAfterSeconds: &pReset,
|
PrimaryResetAfterSeconds: &pReset,
|
||||||
SecondaryUsedPercent: &sRemaining,
|
SecondaryUsedPercent: &sUsed,
|
||||||
SecondaryResetAfterSeconds: &sReset,
|
SecondaryResetAfterSeconds: &sReset,
|
||||||
// No window_minutes
|
// No window_minutes
|
||||||
}
|
}
|
||||||
@ -393,8 +393,8 @@ func TestNormalizedCodexLimits_BothDataNoWindowMinutes(t *testing.T) {
|
|||||||
if normalized.Reset7dSeconds == nil || *normalized.Reset7dSeconds != 400000 {
|
if normalized.Reset7dSeconds == nil || *normalized.Reset7dSeconds != 400000 {
|
||||||
t.Errorf("expected Reset7dSeconds=400000, got %v", normalized.Reset7dSeconds)
|
t.Errorf("expected Reset7dSeconds=400000, got %v", normalized.Reset7dSeconds)
|
||||||
}
|
}
|
||||||
if normalized.Used5hPercent == nil || *normalized.Used5hPercent != 70.0 {
|
if normalized.Used5hPercent == nil || *normalized.Used5hPercent != 50.0 {
|
||||||
t.Errorf("expected Used5hPercent=70, got %v", normalized.Used5hPercent)
|
t.Errorf("expected Used5hPercent=50, got %v", normalized.Used5hPercent)
|
||||||
}
|
}
|
||||||
if normalized.Reset5hSeconds == nil || *normalized.Reset5hSeconds != 10000 {
|
if normalized.Reset5hSeconds == nil || *normalized.Reset5hSeconds != 10000 {
|
||||||
t.Errorf("expected Reset5hSeconds=10000, got %v", normalized.Reset5hSeconds)
|
t.Errorf("expected Reset5hSeconds=10000, got %v", normalized.Reset5hSeconds)
|
||||||
@ -425,7 +425,7 @@ func TestCalculateOpenAI429ResetTime_UserProvidedScenario(t *testing.T) {
|
|||||||
// This is the exact scenario from the user:
|
// This is the exact scenario from the user:
|
||||||
// codex_7d_used_percent: 100
|
// codex_7d_used_percent: 100
|
||||||
// codex_7d_reset_after_seconds: 384607 (约4.5天后重置)
|
// codex_7d_reset_after_seconds: 384607 (约4.5天后重置)
|
||||||
// codex_5h_used_percent: 97 (from upstream 3% remaining)
|
// codex_5h_used_percent: 3
|
||||||
// codex_5h_reset_after_seconds: 17369 (约4.8小时后重置)
|
// codex_5h_reset_after_seconds: 17369 (约4.8小时后重置)
|
||||||
|
|
||||||
svc := &RateLimitService{}
|
svc := &RateLimitService{}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user