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:
@@ -71,7 +71,7 @@ func (t *Tracker) UpdateFromHeaders(h http.Header) {
|
|||||||
}
|
}
|
||||||
if v := h.Get("Anthropic-Ratelimit-Unified-5h-Reset"); v != "" {
|
if v := h.Get("Anthropic-Ratelimit-Unified-5h-Reset"); v != "" {
|
||||||
if ts, err := strconv.ParseInt(v, 10, 64); err == nil {
|
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 != "" {
|
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 v := h.Get("Anthropic-Ratelimit-Unified-7d-Reset"); v != "" {
|
||||||
if ts, err := strconv.ParseInt(v, 10, 64); err == nil {
|
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 {
|
if rl.ResetsAt != nil {
|
||||||
parsed, err := time.Parse(time.RFC3339Nano, *rl.ResetsAt)
|
parsed, err := time.Parse(time.RFC3339Nano, *rl.ResetsAt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Fallback to RFC3339 without fractional seconds
|
|
||||||
parsed, err = time.Parse(time.RFC3339, *rl.ResetsAt)
|
parsed, err = time.Parse(time.RFC3339, *rl.ResetsAt)
|
||||||
}
|
}
|
||||||
parsed = parsed.UTC().Truncate(time.Minute)
|
if err == nil {
|
||||||
if err == nil && parsed != w.ResetsAt && !w.ResetsAt.IsZero() {
|
t.setResetTime(w, parsed.UTC().Truncate(time.Minute), name)
|
||||||
// Window reset detected — zero token counters
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Tracker) setResetTime(w *Window, newReset time.Time, name string) {
|
||||||
|
if !w.ResetsAt.IsZero() && newReset != w.ResetsAt {
|
||||||
log.Info().
|
log.Info().
|
||||||
Str("window", name).
|
Str("window", name).
|
||||||
Int64("prev_tokens_in", w.TokensIn.Load()).
|
Int64("prev_tokens_in", w.TokensIn.Load()).
|
||||||
Int64("prev_tokens_out", w.TokensOut.Load()).
|
Int64("prev_tokens_out", w.TokensOut.Load()).
|
||||||
Time("old_reset", w.ResetsAt).
|
Time("old_reset", w.ResetsAt).
|
||||||
Time("new_reset", parsed).
|
Time("new_reset", newReset).
|
||||||
Msg("window reset detected")
|
Msg("window reset detected")
|
||||||
w.TokensIn.Store(0)
|
w.TokensIn.Store(0)
|
||||||
w.TokensOut.Store(0)
|
w.TokensOut.Store(0)
|
||||||
}
|
}
|
||||||
if err == nil {
|
w.ResetsAt = newReset
|
||||||
w.ResetsAt = parsed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user