The loop now consults quota and host state before every dispatch and
DEFERS gated work with a logged reason instead of letting turns die at
the provider (the 2026-08-28 19:00 quota-wall failure mode, replayed as
a test). Adds internal/quota: 5h/weekly credit buckets (provider poll
when z.ai ships an endpoint - fake-server tested - else locally
estimated from the documented credit formula), TZ-aware peak window
(default 01:00-05:00 America/Chicago weekdays, matching the documented
z.ai peak Mon-Fri 14:00-18:00 Singapore), block/defer thresholds, a
read-only load/mem/disk/IO-PSI monitor, an optional redis shared-state
hop (stdlib RESP2 mini-client) so all instances of an account
coordinate, per-class token+credit accounting in loop.jsonl, and
`harness quota status|probe|gate`. Config: [quota] + [resources]
sections; README runbook covers the redis container and deploy-time
cgroup enforcement.
💘 Generated with Crush
Assisted-by: Crush:glm-5.2
78 lines
2.5 KiB
Go
78 lines
2.5 KiB
Go
package quota
|
|
|
|
import (
|
|
_ "time/tzdata" // embedded zone database: TZ-aware windows work in scratch containers
|
|
|
|
"time"
|
|
|
|
"ukrrs.com/mopac/harness/internal/config"
|
|
)
|
|
|
|
// Schedule is the TZ-aware peak window. z.ai peak hours are documented as
|
|
// Monday-Friday 14:00-18:00 Singapore (UTC+8), which is 01:00-05:00
|
|
// America/Chicago in winter (CST) / 00:00-04:00 during US DST — the default
|
|
// matches Charles's "0100 to 0500 CST" and is configurable to the minute.
|
|
// The window may wrap midnight (start > end): then it covers evenings of
|
|
// the start day plus early mornings of the following day.
|
|
type Schedule struct {
|
|
Start, End time.Duration // minutes-since-midnight in Loc
|
|
Loc *time.Location
|
|
WeekdaysOnly bool
|
|
}
|
|
|
|
// NewSchedule parses the window out of the [quota] config fields.
|
|
func NewSchedule(peakStart, peakEnd, tzName string, weekdaysOnly bool) (Schedule, error) {
|
|
start, err := config.ParseHHMM(peakStart)
|
|
if err != nil {
|
|
return Schedule{}, err
|
|
}
|
|
end, err := config.ParseHHMM(peakEnd)
|
|
if err != nil {
|
|
return Schedule{}, err
|
|
}
|
|
loc, err := time.LoadLocation(tzName)
|
|
if err != nil {
|
|
return Schedule{}, err
|
|
}
|
|
return Schedule{Start: start, End: end, Loc: loc, WeekdaysOnly: weekdaysOnly}, nil
|
|
}
|
|
|
|
// SameDay reports whether t is inside the window without crossing midnight
|
|
// (start <= t < end, single-day window).
|
|
func (s Schedule) inWindow(mins time.Duration) bool {
|
|
if s.Start <= s.End {
|
|
return mins >= s.Start && mins < s.End
|
|
}
|
|
// Wrapped window: [start, 24h) of the start day, [0, end) of the next.
|
|
return mins >= s.Start || mins < s.End
|
|
}
|
|
|
|
// InPeak reports whether t falls inside the peak window, evaluated in the
|
|
// schedule's timezone. For wrapped windows the weekday check applies to the
|
|
// day the window STARTED on (a Sunday 22:00-02:00 window is off all week
|
|
// when weekdays-only, because it starts on Sunday).
|
|
func (s Schedule) InPeak(t time.Time) bool {
|
|
local := t.In(s.Loc)
|
|
mins := time.Duration(local.Hour())*time.Hour + time.Duration(local.Minute())*time.Minute
|
|
if s.Start <= s.End {
|
|
if !s.inWindow(mins) {
|
|
return false
|
|
}
|
|
return !s.WeekdaysOnly || isWeekday(local)
|
|
}
|
|
// Evening half: today carries the window.
|
|
if mins >= s.Start {
|
|
return !s.WeekdaysOnly || isWeekday(local)
|
|
}
|
|
// Morning half: the window started yesterday.
|
|
if mins < s.End {
|
|
return !s.WeekdaysOnly || isWeekday(local.AddDate(0, 0, -1))
|
|
}
|
|
return false
|
|
}
|
|
|
|
func isWeekday(t time.Time) bool {
|
|
wd := t.Weekday()
|
|
return wd >= time.Monday && wd <= time.Friday
|
|
}
|