fix(openai): skip stale and reset Codex snapshots in scheduling threshold evaluator

This commit is contained in:
wucm667
2026-08-10 14:27:04 +08:00
parent 10a4c6e3ad
commit 3d3aee2e72
2 changed files with 91 additions and 6 deletions
@@ -53,7 +53,7 @@ func EvaluateAccountSchedulingThreshold(account *Account, thresholds map[string]
var winner *accountSchedulingThresholdCandidate
switch decision.Platform {
case PlatformOpenAI:
winner = pickLatestResetSchedulingCandidate(openAIThresholdCandidates(account), threshold, now)
winner = pickLatestResetSchedulingCandidate(openAIThresholdCandidates(account, now), threshold, now)
case PlatformAnthropic:
winner = pickLatestResetSchedulingCandidate(anthropicThresholdCandidates(account), threshold, now)
case PlatformGrok:
@@ -149,7 +149,7 @@ func lookupAccountSchedulingThreshold(thresholds map[string]int, platform string
return value, ok
}
func openAIThresholdCandidates(account *Account) []*accountSchedulingThresholdCandidate {
func openAIThresholdCandidates(account *Account, now time.Time) []*accountSchedulingThresholdCandidate {
if account == nil {
return nil
}
@@ -157,8 +157,8 @@ func openAIThresholdCandidates(account *Account) []*accountSchedulingThresholdCa
return nil
}
return []*accountSchedulingThresholdCandidate{
openAIThresholdCandidate(account.Extra, "5h"),
openAIThresholdCandidate(account.Extra, "7d"),
openAIThresholdCandidate(account.Extra, "5h", now),
openAIThresholdCandidate(account.Extra, "7d", now),
}
}
@@ -219,7 +219,7 @@ func firstStringValue(values map[string]any, keys ...string) string {
return ""
}
func openAIThresholdCandidate(extra map[string]any, window string) *accountSchedulingThresholdCandidate {
func openAIThresholdCandidate(extra map[string]any, window string, now time.Time) *accountSchedulingThresholdCandidate {
if len(extra) == 0 {
return nil
}
@@ -243,6 +243,9 @@ func openAIThresholdCandidate(extra map[string]any, window string) *accountSched
if !ok {
return nil
}
if openAIQuotaWindowReset(extra, window, now) || openAICodexSnapshotStaleForPause(extra, now) {
return nil
}
return &accountSchedulingThresholdCandidate{
window: window,
usedPercent: schedulingPercentValue(usedPercent),
@@ -109,7 +109,7 @@ func TestEvaluateAccountSchedulingThreshold_OpenAIPreservesPercentageSemantics(t
},
}
candidate := openAIThresholdCandidate(openAIAccount.Extra, "5h")
candidate := openAIThresholdCandidate(openAIAccount.Extra, "5h", now)
require.NotNil(t, candidate)
require.Equal(t, 1.0, candidate.usedPercent)
@@ -126,6 +126,88 @@ func TestEvaluateAccountSchedulingThreshold_OpenAIPreservesPercentageSemantics(t
require.Equal(t, 91.0, openAIDecision.UsedPercent)
}
func TestEvaluateAccountSchedulingThreshold_OpenAISkipsStaleSnapshot(t *testing.T) {
t.Parallel()
now := time.Date(2026, 6, 3, 12, 0, 0, 0, time.UTC)
account := &Account{
Platform: PlatformOpenAI,
Extra: map[string]any{
"codex_usage_updated_at": now.Add(-2 * time.Hour).Format(time.RFC3339),
"codex_5h_used_percent": 100.0,
"codex_5h_reset_at": now.Add(3 * time.Hour).Format(time.RFC3339),
},
}
decision := EvaluateAccountSchedulingThreshold(account, map[string]int{PlatformOpenAI: 90}, now)
require.False(t, decision.ShouldPause)
}
func TestEvaluateAccountSchedulingThreshold_OpenAISkipsResetWindow(t *testing.T) {
t.Parallel()
now := time.Date(2026, 6, 3, 12, 0, 0, 0, time.UTC)
account := &Account{
Platform: PlatformOpenAI,
Extra: map[string]any{
"codex_usage_updated_at": now.Add(-time.Minute).Format(time.RFC3339),
"codex_5h_used_percent": 100.0,
"codex_5h_reset_at": now.Add(-time.Second).Format(time.RFC3339),
},
}
decision := EvaluateAccountSchedulingThreshold(account, map[string]int{PlatformOpenAI: 90}, now)
require.False(t, decision.ShouldPause)
}
func TestEvaluateAccountSchedulingThreshold_OpenAIPausesFreshExhaustedSnapshot(t *testing.T) {
t.Parallel()
now := time.Date(2026, 6, 3, 12, 0, 0, 0, time.UTC)
resetAt := now.Add(3 * time.Hour)
account := &Account{
Platform: PlatformOpenAI,
Extra: map[string]any{
"codex_usage_updated_at": now.Add(-time.Minute).Format(time.RFC3339),
"codex_5h_used_percent": 100.0,
"codex_5h_reset_at": resetAt.Format(time.RFC3339),
},
}
decision := EvaluateAccountSchedulingThreshold(account, map[string]int{PlatformOpenAI: 90}, now)
require.True(t, decision.ShouldPause)
require.Equal(t, "5h", decision.Window)
require.Equal(t, 100.0, decision.UsedPercent)
require.NotNil(t, decision.Until)
require.True(t, resetAt.Equal(*decision.Until))
}
func TestEvaluateAccountSchedulingThreshold_OpenAIPausesFreshExhaustedSevenDayWindow(t *testing.T) {
t.Parallel()
now := time.Date(2026, 6, 3, 12, 0, 0, 0, time.UTC)
resetAt := now.Add(5 * 24 * time.Hour)
account := &Account{
Platform: PlatformOpenAI,
Extra: map[string]any{
"codex_usage_updated_at": now.Add(-time.Minute).Format(time.RFC3339),
"codex_7d_used_percent": 95.0,
"codex_7d_reset_at": resetAt.Format(time.RFC3339),
},
}
decision := EvaluateAccountSchedulingThreshold(account, map[string]int{PlatformOpenAI: 90}, now)
require.True(t, decision.ShouldPause)
require.Equal(t, "7d", decision.Window)
require.Equal(t, 95.0, decision.UsedPercent)
require.NotNil(t, decision.Until)
require.True(t, resetAt.Equal(*decision.Until))
}
func TestEvaluateAccountSchedulingThreshold_AnthropicPreservesFractionalUtilizationSemantics(t *testing.T) {
t.Parallel()