fix(ratelimit): clear window token counters on reset from response headers

UpdateFromHeaders was silently updating ResetsAt without clearing token
counters. When a window rolled over, the poll method would see ResetsAt
already updated and skip the reset. Extract setResetTime helper used by
both code paths.
This commit is contained in:
Alexander
2026-04-14 13:37:06 +02:00
parent e8af26d626
commit 744abc1d24
+12 -11
View File
@@ -71,7 +71,7 @@ func (t *Tracker) UpdateFromHeaders(h http.Header) {
}
if v := h.Get("Anthropic-Ratelimit-Unified-5h-Reset"); v != "" {
if ts, err := strconv.ParseInt(v, 10, 64); err == nil {
t.fiveHour.ResetsAt = time.Unix(ts, 0).UTC().Truncate(time.Minute)
t.setResetTime(&t.fiveHour, time.Unix(ts, 0).UTC().Truncate(time.Minute), "5h")
}
}
if v := h.Get("Anthropic-Ratelimit-Unified-7d-Utilization"); v != "" {
@@ -81,7 +81,7 @@ func (t *Tracker) UpdateFromHeaders(h http.Header) {
}
if v := h.Get("Anthropic-Ratelimit-Unified-7d-Reset"); v != "" {
if ts, err := strconv.ParseInt(v, 10, 64); err == nil {
t.sevenDay.ResetsAt = time.Unix(ts, 0).UTC().Truncate(time.Minute)
t.setResetTime(&t.sevenDay, time.Unix(ts, 0).UTC().Truncate(time.Minute), "7d")
}
}
}
@@ -190,24 +190,25 @@ func (t *Tracker) updateWindow(w *Window, rl *RateLimit, name string) {
if rl.ResetsAt != nil {
parsed, err := time.Parse(time.RFC3339Nano, *rl.ResetsAt)
if err != nil {
// Fallback to RFC3339 without fractional seconds
parsed, err = time.Parse(time.RFC3339, *rl.ResetsAt)
}
parsed = parsed.UTC().Truncate(time.Minute)
if err == nil && parsed != w.ResetsAt && !w.ResetsAt.IsZero() {
// Window reset detected — zero token counters
if err == nil {
t.setResetTime(w, parsed.UTC().Truncate(time.Minute), name)
}
}
}
func (t *Tracker) setResetTime(w *Window, newReset time.Time, name string) {
if !w.ResetsAt.IsZero() && newReset != w.ResetsAt {
log.Info().
Str("window", name).
Int64("prev_tokens_in", w.TokensIn.Load()).
Int64("prev_tokens_out", w.TokensOut.Load()).
Time("old_reset", w.ResetsAt).
Time("new_reset", parsed).
Time("new_reset", newReset).
Msg("window reset detected")
w.TokensIn.Store(0)
w.TokensOut.Store(0)
}
if err == nil {
w.ResetsAt = parsed
}
}
w.ResetsAt = newReset
}