mirror of
https://github.com/Twigpine/zero.git
synced 2026-11-09 18:08:31 -10:00
* token efficiency: lossless cost accounting + minified file reads Two complementary improvements to how Zero handles tokens. ACCOUNTING — make cost measurement lossless and correct: - Carry all five token dimensions end-to-end (input, cache-read, cache-write, output, reasoning) in one Usage; add CacheWriteTokens (cache creation), which the Anthropic adapter previously parsed then discarded. - Price cache-writes at their premium rate (Anthropic 1.25x input, derived per model). Regression-safe: a model without the rate bills cache-write at the input rate exactly as before. - Persist cache-read/cache-write/reasoning in EventUsage so `zero usage report` reconstructs the SAME cost as the live tracker instead of over-pricing cache-heavy sessions. A single EventUsagePayload writer is paired with the reader so the JSON keys can't drift. - Add a live context-window gauge to the status bar (used/window + percent, graded green/amber/red) using the last request's input tokens. REDUCTION — read_minified_file, a dense token-cheap file view: - New internal/minify package + read_minified_file tool returning a comment-free, line-number-free view: Go via the real go/parser AST; C/Java/CSS/SCSS/LESS/ Python via a string-aware comment lexer (handles strings, char literals, triple-quotes, Java text blocks, Python f-strings incl. 3.12 same-quote nesting). Every other language falls back to whitespace-only, so the stripper can never corrupt content. - ~32% fewer tokens on a realistic multi-file read; +~102 fixed tokens for the tool schema, recovered many times over by a single subsystem read. Tests: cache-write pricing + fallback, lossless persistence round-trip, the context gauge, comment-stripping golden cases per language (comment-chars in strings, Java text blocks, Python f-string nesting, CSS has no //), and the unsupported-language safe fallback. gofmt / vet / build / -race all green. * specialist: register read_minified_file in the tool registry + read-only sets Smoke caught TestKnownToolNamesMatchCoreRegistry drifting once read_minified_file joined the core read-only tools: the specialist tool allowlist (knownToolNames), the read-only/edit/execute categories, and the read-only auto-approve set did not list it. It is a read-only companion to read_file, so it belongs alongside read_file in all of them; updated the default-allowlist test accordingly.
63 lines
2.4 KiB
Go
63 lines
2.4 KiB
Go
package modelregistry
|
|
|
|
import (
|
|
"math"
|
|
"testing"
|
|
|
|
"github.com/Gitlawb/zero/internal/zeroruntime"
|
|
)
|
|
|
|
// A premium cache-write rate prices cache-creation tokens separately from the
|
|
// uncached input and the discounted cache-read.
|
|
func TestCalculateCostCacheWritePremium(t *testing.T) {
|
|
model := ModelEntry{
|
|
ID: "test-cw", Provider: ProviderAnthropic,
|
|
Cost: ModelCost{Currency: "USD", InputPerMillion: 10, OutputPerMillion: 30, CachedInputPerMillion: 1, CacheWritePerMillion: 12.5},
|
|
}
|
|
// 1M total input = 600k uncached + 300k cache-read + 100k cache-write.
|
|
usage := zeroruntime.Usage{InputTokens: 1_000_000, CachedInputTokens: 300_000, CacheWriteTokens: 100_000, OutputTokens: 200_000}
|
|
cost, err := CalculateCost(model, usage)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
want := 6.00 /*uncached 600k@10*/ + 0.30 /*read 300k@1*/ + 1.25 /*write [email protected]*/ + 6.00 /*out 200k@30*/
|
|
if math.Abs(cost.TotalCost-want) > 1e-9 {
|
|
t.Fatalf("total = %v, want %v", cost.TotalCost, want)
|
|
}
|
|
if cost.CacheWriteTokens != 100_000 || math.Abs(cost.CacheWriteCost-1.25) > 1e-9 {
|
|
t.Fatalf("cacheWrite = %d tok / $%v", cost.CacheWriteTokens, cost.CacheWriteCost)
|
|
}
|
|
}
|
|
|
|
// Without a configured cache-write rate, cache-write tokens fall back to the full
|
|
// input rate (the prior behavior) — no regression for models lacking the rate.
|
|
func TestCalculateCostCacheWriteFallsBackToInputRate(t *testing.T) {
|
|
model := ModelEntry{ID: "test", Provider: ProviderOpenAI, Cost: ModelCost{Currency: "USD", InputPerMillion: 10, OutputPerMillion: 30}}
|
|
usage := zeroruntime.Usage{InputTokens: 1_000_000, CacheWriteTokens: 100_000, OutputTokens: 0}
|
|
cost, err := CalculateCost(model, usage)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if math.Abs(cost.TotalCost-10.0) > 1e-9 { // all 1M input at 10/1e6
|
|
t.Fatalf("total = %v, want 10.0 (cache-write folds into input when unpriced)", cost.TotalCost)
|
|
}
|
|
if cost.CacheWriteTokens != 0 {
|
|
t.Fatalf("cache-write should not split out when unpriced, got %d", cost.CacheWriteTokens)
|
|
}
|
|
}
|
|
|
|
// Anthropic models derive cache-write = 1.25x input from the helper.
|
|
func TestAnthropicModelsDeriveCacheWriteRate(t *testing.T) {
|
|
reg, err := DefaultRegistry()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
m, err := reg.Require("claude-sonnet-4.5")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got, want := m.Cost.CacheWritePerMillion, 3.0*1.25; math.Abs(got-want) > 1e-9 {
|
|
t.Fatalf("sonnet cache-write rate = %v, want %v", got, want)
|
|
}
|
|
}
|