backend/usage/ polls each account's rate-limits.json (read-only mount,
USAGE_STATE_DIRS) and keeps every change (+ a 30 min heartbeat) in
usage_limits. A daily fit over 14 days of hourly rises vs the runs'
meterBins gives plan units per 1 % of the 5h / week window; only trusted
fits (n>=24, R2>=0.5, within x3 of the seed) replace the rate, and a >20 %
shift pushes a notification. Cards carry planPct, --final notifications
get ' · $X · ≈Y% wk', and /api/usage/{limits,history,calibration,
calibrate,import} serve the Plan usage page (owner only).
scripts/usage-backfill-ha.py imports the HA hourly statistics since July.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
112 lines
2.5 KiB
Python
112 lines
2.5 KiB
Python
"""Plan usage: the official /usage percentages (rate-limits.json snapshots, HA
|
||
backfill) and their calibration against the plan units conversations spend."""
|
||
from __future__ import annotations
|
||
|
||
from typing import Literal, Optional
|
||
|
||
from .base import Schema
|
||
|
||
__all__ = [
|
||
"LimitWindow",
|
||
"WindowFit",
|
||
"Calibration",
|
||
"AccountLimits",
|
||
"UsageLimitsResponse",
|
||
"LimitPoint",
|
||
"HourMeter",
|
||
"DayMeter",
|
||
"UsageHistory",
|
||
"CalibrationHistory",
|
||
"CalibrateResult",
|
||
"UsageImportResult",
|
||
]
|
||
|
||
|
||
class LimitWindow(Schema):
|
||
pct: float
|
||
resetsAt: Optional[float] = None
|
||
|
||
|
||
class WindowFit(Schema):
|
||
"""One window's exchange rate: plan units (and list $) per 1 %."""
|
||
unitsPerPct: float
|
||
usdPerPct: Optional[float] = None
|
||
r2: Optional[float] = None
|
||
n: int = 0
|
||
interceptPct: Optional[float] = None
|
||
|
||
|
||
class Calibration(Schema):
|
||
account: str
|
||
fittedAt: float
|
||
days: int
|
||
source: Literal["fit", "default"]
|
||
# Passed the bar to become the rate in force (enough hours, R², and within
|
||
# ×3 of the seed rate); untrusted fits are kept for the history chart.
|
||
trusted: bool = False
|
||
week: WindowFit
|
||
h5: WindowFit
|
||
|
||
|
||
class AccountLimits(Schema):
|
||
account: str
|
||
label: Optional[str] = None
|
||
# "five_hour", "seven_day" (and any model-specific "seven_day_<model>").
|
||
windows: dict[str, LimitWindow] = {}
|
||
updatedAt: Optional[float] = None
|
||
stale: bool = True
|
||
calibration: Optional[Calibration] = None
|
||
|
||
|
||
class UsageLimitsResponse(Schema):
|
||
accounts: dict[str, AccountLimits]
|
||
|
||
|
||
class LimitPoint(Schema):
|
||
t: float
|
||
pct: float
|
||
resetsAt: Optional[float] = None
|
||
|
||
|
||
class HourMeter(Schema):
|
||
t: float
|
||
cost: float
|
||
units: float
|
||
dWeek: Optional[float] = None
|
||
d5h: Optional[float] = None
|
||
|
||
|
||
class DayMeter(Schema):
|
||
day: str
|
||
cost: float
|
||
units: float
|
||
observedWeek: Optional[float] = None
|
||
predictedWeek: Optional[float] = None
|
||
untrackedWeek: Optional[float] = None
|
||
observed5h: Optional[float] = None
|
||
predicted5h: Optional[float] = None
|
||
|
||
|
||
class UsageHistory(Schema):
|
||
account: str
|
||
days: int
|
||
series: dict[str, list[LimitPoint]]
|
||
hourly: list[HourMeter]
|
||
daily: list[DayMeter]
|
||
calibration: Optional[Calibration] = None
|
||
|
||
|
||
class CalibrationHistory(Schema):
|
||
account: str
|
||
current: Optional[Calibration] = None
|
||
history: list[Calibration] = []
|
||
|
||
|
||
class CalibrateResult(Schema):
|
||
calibrations: dict[str, Calibration]
|
||
shifted: list[str] = []
|
||
|
||
|
||
class UsageImportResult(Schema):
|
||
imported: int
|