feat(seats): an API seat's cycle starts on its own grant day
The ledger reset every API seat's spend on one REVIEW_GATES_API_CYCLE_DAY and cleared every credit-out, an OpenRouter key's too, at that reset. Each API seat's cycle starts on its ANTHROPIC_API_KEY<n>_GRANT_DAY now (1..31, a day past a month's end its last), else the cycle day, also 1..31. A credit-out carries across the rollover, and the run's probe tries a key held out on credit again, as `seats` does an API key: an ok lifts the mark - an API key's is new credits, its cycle restarting from the grant day, or from today when that day is over a week back; an OpenRouter key keeps its cycle's spend, and a key that answers with its cycle's budget spent stays out on that budget. A probe that finds a key out of credit records it, unless the ledger already has it out. Both are decided in one locked update on the file's entry, a mark another driver made since the probe started standing, and the ledger's stamps carry their UTC offset so marks are ordered by instant. `seats` shows the probe's reading beside the driver's verdict (the `probe` and `driver` columns replace `state` and `ledger: out`), each seat's grant day and when its next credits are due, and notes on what the probe changed in the ledger or why the driver's verdict stands; the run logs the days.
This commit is contained in:
parent
c6b4d78245
commit
ceb87f70ef
12 changed files with 774 additions and 83 deletions
|
|
@ -767,7 +767,7 @@ namespace Crafter::Review::Run {
|
|||
std::ranges::sort(sorted);
|
||||
Die(std::format("OpenRouter seats need OpenRouter model ids (vendor/model): --models opus=...,fable=... (now {})", Dyn::Join(sorted, ", ")));
|
||||
}
|
||||
if (!a.dryRun && !a.noProbe) SeatPool::Probe(seats, runner->Bin(), 30, modelIds, log);
|
||||
if (!a.dryRun && !a.noProbe) SeatPool::Probe(seats, runner->Bin(), 30, modelIds, log, {}, ledger.get());
|
||||
if (board) {
|
||||
// Our readings onto the board (a probed-ok seat clears a verdict
|
||||
// another driver left), theirs into our seats.
|
||||
|
|
@ -793,7 +793,12 @@ namespace Crafter::Review::Run {
|
|||
for (const Json& s : seats) names.push_back(std::format("{} ({})", Dyn::Str(s.at("id")), Dyn::Str(s.at("kind"))));
|
||||
std::string line = std::format("seats: {} from {}", Dyn::Join(names, ", "), envFile && !envFile->empty() ? *envFile : std::string("this machine"));
|
||||
if (std::ranges::any_of(seats, [](const Json& s) { return s.at("kind") == "api"; })) {
|
||||
line += std::format("; {}, budget ${:g} per api seat per cycle (ledger {})", Dyn::Str(apiCfg.at("order")), Number(apiCfg.at("budgetUsd")), ledger->Path().string());
|
||||
std::vector<std::string> days;
|
||||
for (const Json& s : seats) {
|
||||
if (s.at("kind") == "api") days.push_back(std::format("{} {}", Dyn::Str(s.at("id")), Dyn::Str(Dyn::Get(s, "grantDay"))));
|
||||
}
|
||||
line += std::format("; {}, budget ${:g} per api seat per cycle from its grant day ({}; ledger {})", Dyn::Str(apiCfg.at("order")), Number(apiCfg.at("budgetUsd")), Dyn::Join(days, ", "),
|
||||
ledger->Path().string());
|
||||
}
|
||||
if (openrouter) {
|
||||
line += std::format("; budget ${:g} per OpenRouter seat per cycle, {} concurrent (ledger {})", Number(apiCfg.at("orBudgetUsd")), Dyn::Str(apiCfg.at("orConcurrency")), ledger->Path().string());
|
||||
|
|
|
|||
Loading…
Reference in a new issue