Files
magpie/internal/usage
Ethereal49 3c629133c2 usage: refresh caches after same-tick log rewrites (#1328)
* usage: same-tick rewrites refresh cached totals and request pages

Trust change stamps only when they were settled at the snapshot read's
start. Fingerprint recent snapshots and append prefixes, and discard
fingerprint trust when the content changes during a read.

Fixes https://github.com/yetone/magpie/issues/1305

Verified: deterministic old-predicate FAIL/new PASS; usage tests with race;
nogui vet; Linux nogui and Windows builds; usage test cross-compilation for
Windows, Darwin and the unsupported-stamp fallback on FreeBSD.
Full nogui suite exposed the independent sessions TestList empty-session
limit failure. Native Linux GUI vet/build lack pkg-config and GTK/WebKit;
native Windows/NTFS execution was not run.

* usage: keep FILETIME stamp ages valid beyond UnixNano range

Convert whole seconds and the 100ns fraction separately. Checked the epoch, current dates with fractional ticks and year 2500; Windows build and usage test cross-compilation pass. Native Windows execution was not run.

* usage: keep history cached after an append settles

An unchanged log now validates a recent snapshot's nonempty fingerprint
before publishing a settled clone. Preserve its version and sealed blocks
so the summary and page caches stay warm without mutating readers' snapshots.

Follow-up to the review on https://github.com/yetone/magpie/pull/1328,
for https://github.com/yetone/magpie/issues/1305.

On e28e686, the real-clock append/read/settle/read tests fail with a
version bump and 32,428 additional allocations over 4,000 versus 10 rows.
With this change the targeted race run passes, with 165 extra allocations.
The existing performance test now measures this path on the real clock.

Verification uses Go 1.26.8, a temporary HOME and pinned Go caches.
Native Linux GUI checks require missing pkg-config/GTK/WebKit libraries;
native Windows/NTFS execution is not available. No GUI assets change.
Implementation and verification used AI assistance.

Final checks: usage race PASS (421 test results); nogui suite PASS (7,273
test results, 44 packages; 21 skipped tests, 3 packages without tests).
Nogui vet, Linux nogui build, Windows build, and usage test cross-compilation
for Windows amd64, Darwin arm64 and FreeBSD amd64 PASS. Native Linux GUI
vet/build were attempted and failed at the missing pkg-config executable.
2026-10-09 02:48:29 +08:00
..
usage: each subscription account's tokens and cost, by the account that answered (#557: 希望用量账本能按订阅账号统计,每个 Codex / Claude 账号各用了多少 token、多少钱) — a call's record named the upstream key it went out on (ProviderKeyID/Name, #500) but never the subscription account, so with two Codex accounts signed in, or a Claude account that took over after a failover, the ledger could say what Codex cost and not what each account did. A record now carries providerAccount, the account that answered as the Routing trace names it (Account.User: its email or login, never a token), set by the gateway at the try that went out (the candidate loop's last try, so a failover's answer is the account that took over and the refused try stays the first account's; an X-Magpie-Account pin is the pinned one), and on the decide, draw and video paths too. Record.Account() gives it, or for a record written before the field the account its host named (Where() has long written "chatgpt.com as dee@example.com" for an account), and otherwise "" — shown as "account not recorded", never inferred from today's sign-in. The summary groups calls by provider and account (Summary.Accounts, the same email on Codex and on Claude being two accounts), priced as every other group; the ledger's Filter has Account, the search finds a call by its account, the CSV has a provider_account column beside provider_key_name (the provider's identity columns together, the caller's still last), and the Requests page's packed rows keep it and offer the period's accounts. magpie usage prints an accounts table after the provider keys, and magpie usage --csv --account <name> keeps one account's calls. The app's Usage page lists Accounts on the Overview (tokens and cost per account, the provider under its name, an unrecorded one in the faint colour, no stripe down a row's side) and has an Account filter on Requests, each account noting the providers it answered for, which asks for and exports that account's calls without moving the page. OTLP export is an allowlist and does not carry the account. TestUsageNamesPinnedAccount (both pinned accounts on magpie's endpoint and on Codex's own, the trace's Who and the record agreeing, no token in usage.jsonl) and TestUsageNamesAccountThatTookOver (Ordered routing, the first account refusing: the answer recorded as the spare, the refusal as the first) fail without the gateway change; TestSummaryByAccount, TestLedgerByAccount and TestRequestPageByAccount cover the grouping, the host fallback, "not recorded", keyed providers kept out, the filter, the search, the CSV column and the packed rows; TestLedger and TestLedgerWithSessionLogCalls take the new column. usage-accounts.test.cjs (WebKit and Chromium, en and zh: the Overview's accounts and costs, the Account picker's notes, its request and export, All accounts clearing it, scrollTop unchanged) fails without the asset change; api-key-usage, click-scroll, panel-usage, request-archive-usage, tray-usages, claude-usage-asked and every usage-* test pass in WebKit. go vet ./internal/..., go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui -count=1 ./... under a temp HOME pass (TestFXInState failed once on its TempDir cleanup, passes alone).
2026-10-02 20:15:33 +08:00
usage, gateway: a model the vendor spells with other separators isn't marked as another one (vincentzhang on Discord: 这个说明有点奇怪。看不明白。) — Volcengine Ark answers deepseek-v4.1-flash as deepseek-v4-1-flash, the dots written as hyphens, and the Usage page's Requests and the Routing page marked every such row amber 「实际 deepseek-v4-1-flash」, saying another model had answered, not the one asked for under a dated name. Swapped now compares the bare names without their separators (. - _ and spaces) and case, so deepseek-v4-1-flash, deepseek_v4_1_flash, GLM-4-6 for glm-4.6 and gpt-4-1-2025-04-14 for gpt-4.1 are the model asked for, while deepseek-v4-flash or deepseek-v4.1-pro for deepseek-v4.1-flash still are another. Rows and routes kept marked swapped before are read back as not swapped when the names differ only so (usage.SameSpelled, in rowChunk.row and Route.routedAgain). The tag's explanation says it in plainer words: asked for {sent}, the reply says {served} answered, likely another model; the same model under a dated name or spelled otherwise isn't marked (zh, ja, de). TestSwappedIgnoresSeparators fails on the old code; TestKeptRowSameSpelledNotSwapped, TestRouteKeptSpelledOtherwiseNotSwapped. usage-ledger (its expected title updated), remote-group-served, panel-routing, gui-ja and gui-de pass on WebKit. go vet, builds (darwin, GOOS=linux nogui, GOOS=windows) and go test -tags nogui -count=1 ./... under a temp HOME pass (TestCodexVersion timed out once under load and passes alone).
2026-10-05 12:49:59 +08:00
…