bench(realrepo): long tasks over a pinned snapshot of this repository

The e2e fixtures finish in 5-15 requests, while a real task in this tree took
deepseek-flash 38. A change aimed at cutting round trips cannot show up on
the short suite, so this adds four long tasks whose workdir is a git archive
of commit 42636ddcb (pricing, ablation, agentgraph and five stdlib-only
leaves) plus a trimmed REASONIX.md, a Makefile, a module README and a small
benchctl consumer:

- rt-cache-write-rate: a CacheWrite rate threaded through cost, fingerprint,
  card identity and the catalog, plus an Anthropic row that uses it
- rt-stale-skills: Markdown-only repair of three project skills with no
  recognised verifier, the shape behind #13293's lingering stale_verification
- rt-rate-lookup-signatures: five lookups moved to (RateCard, bool),
  CatalogKey and ModelRef across about 20 call sites, most in _test.go
- rt-plan-ablation-module: a five-step plan-first task signed off per step

snapshot.sh rebuilds every workdir from the pin and --check detects drift.
One pinned test compares CheckedOn with the wall clock and would fail every
workdir from 2026-11-20; base/ anchors it at the snapshot day instead.

Each task ships a solution/; the corpus is registered with the authoring
guards, which check that every seed fails its grader and every solution
passes. A deepseek-flash baseline (2 trials) solved 8/8 at 13-31 requests
once two grader false negatives in rt-stale-skills were fixed; the README
records it, along with a multi_edit prompt arm and a complete_step
description arm that did not move the request count.
This commit is contained in:
zumac committed 2026-10-20 00:57:36 -07:00
1 parent 0b8de9e636
commit 7e92388102
257 files changed
+25900 -4

No files matched your search

+1
View File
@@ -26,6 +26,7 @@
*.xml text eol=lf
*.yaml text eol=lf
*.yml text eol=lf
Makefile text eol=lf
.githooks/** text eol=lf
benchmarks/**/*.sh text eol=lf
internal/runtime/agent/testdata/perseveration/** -text
+5 -1
View File
@@ -1,6 +1,6 @@
# Reasonix Benchmarks
Five harnesses live under `benchmarks/`; `cmd/e2ebench` also exposes a
Six harnesses live under `benchmarks/`; `cmd/e2ebench` also exposes a
SWE-bench Verified mode:
- `e2e/` — the committed end-to-end task suite, driven by
@@ -19,6 +19,10 @@ SWE-bench Verified mode:
- `fanout-width/` — a shape-instructed fleet whose members share nothing, for
pricing what running them at the same time is worth. See
[Pricing the run graph](#pricing-the-run-graph) below.
- `realrepo/` — long tasks over a pinned snapshot of this repository, for
measuring changes that cut model round trips (13–31 requests per task on
`deepseek-flash`). See
[its README](realrepo/README.md).
## Directory layout
+131
View File
@@ -0,0 +1,131 @@
# realrepo
Long tasks over a pinned snapshot of this repository, for measuring changes
that aim to cut model round trips. The `benchmarks/e2e` tasks finish in about
5–15 requests and ten seconds; a real task in this tree took a flash-class
model 38 requests, most of them after a single read. A suite of short fixtures
cannot show that gap moving, so these tasks are shaped like the real one:
explore several packages, edit several files, verify, and recover from the
verifier.
```sh
go build -o bin/reasonix ./cmd/reasonix
go run ./cmd/e2ebench -suite benchmarks/realrepo -bin "$PWD/bin/reasonix" -model deepseek-flash \
-budget 0 -trials 2 -trajectories traj -json realrepo.json
go run ./cmd/e2ebench -suite benchmarks/realrepo -bin "$PWD/bin/reasonix" -task rt-stale-skills
```
The agent reads its provider config from `REASONIX_HOME` (default
`~/.reasonix`); point it at a disposable one to keep benchmark sessions out of
your own.
The default `-budget` (800k tokens) can stop the suite partway through, hence
`-budget 0`. Under `-trials`, each trial keeps its own
`<task>.trial-N.trajectory.jsonl`.
## The snapshot
Every `workdir/` is the same Go module (`module reasonix`, stdlib only, no
network needed to build or test):
- **Pinned code.** `git archive` of commit `42636ddcb425` for
`internal/contract/{pricing,ablation,agentgraph}` and
`internal/base/{retrieval,rulelist,tokencount,neterr,nilutil}` — about 5k
lines of real kernel code with its real tests and doc comments.
- **Fixture-authored files** in `base/`: `go.mod`, a trimmed `REASONIX.md` (the
project's own conventions, so the agent loads standing instructions the way
it does here), a `Makefile` with `check`, a module `README.md`, the
`cmd/benchctl` command that consumes `pricing` and `ablation` from outside
their package, and one project skill.
- **One pinned test anchored to the snapshot day.**
`TestCatalogPricesHaveBeenCheckedRecently` compares each row's `CheckedOn`
with the wall clock, which would fail every workdir 90 days after the oldest
pinned date (2026-11-20). `base/` replaces that file with one that reads
staleness at 2026-10-19 instead; nothing else in the pin is edited.
- **Per-task overlay** in `overlay/<task-id>/`, applied last. Only
`rt-stale-skills` has one.
`snapshot.sh` rebuilds every workdir from those three sources and
`snapshot.sh --check` fails if a committed workdir has drifted from them. Edit
`base/` or `overlay/`, never a workdir, then rerun the script. Moving the pin is
a new suite version: results from two pins do not compare.
## Tasks
| Task | Kind | What it exercises |
| --- | --- | --- |
| `rt-cache-write-rate` | multi-file Go change, test, README | Adds a `CacheWrite` rate to `RateCard` and threads it through cost, fingerprint, card identity and the catalog, then prices Anthropic's `claude-sonnet-4-5` with it: a new `officialRates` row, documentation and checked-on constants, an endpoint host, and `benchctl rate`/`cost` output. The fingerprint has to stay byte-identical for every existing card, so the change to `PricingFingerprint` cannot be the obvious one. |
| `rt-stale-skills` | documentation only, no recognised verifier | Three project skills name paths, functions, `make` targets and `benchctl` subcommands and flags that do not exist, and one contradicts the append-only rule in `REASONIX.md`. Fixing them takes reading `rates.go`, `ablation.go`, both `benchctl` files and the `Makefile`, then editing only Markdown — the shape behind #13293, where `stale_verification` stays owed after a skill edit because no check covers it. |
| `rt-rate-lookup-signatures` | API migration with a likely compile-error loop | Changes `CurrentRate` to `(RateCard, bool)`, `LookupCatalog` to a `CatalogKey` struct, and `IsOfficialCard`, `MatchesCatalog` and `LiveTariff` to a `ModelRef`: about 20 call sites across 11 files. Most are in `_test.go` files and dereference the old pointer, so `go build ./...` can pass while `go vet` and `go test` fail. |
| `rt-plan-ablation-module` | plan-mode with `complete_step` | Five numbered steps adding a `memory` ablation module, summaries, `ModuleHelp` and `benchctl arm -list`. The prompt opens with the host's own plan-first marker (`control.PlannerRouteMarker`), which routes the turn through the planner, and asks for each step to be signed off before the next. An existing test pins the `all` arm name and has to be updated, which adds a red-test step. |
Every grader stages hidden tests and checks after the run: `gofmt -l`, `go vet
./...` and `go test ./...` for the Go tasks; for `rt-stale-skills`, every
backticked path, identifier, `make` target and `benchctl` command line in each
skill must resolve against the tree, and a SHA-256 manifest proves no Go source, `go.mod`,
`Makefile` or `REASONIX.md` changed. Each task fails its pristine seed and
passes its `solution/`, both checked by `cmd/e2ebench`'s corpus tests.
## Reading the numbers
```text
Metric: requests per task
Anchor: the trajectory digest's requests_by_source, summed over sources
Population: every graded trial of a task that ran to its own stop
Excludes: tasks the token budget skipped, and runs that hit timeout_sec
```
Requests is the headline because round trips are what these tasks exist to
move. Read it beside the solve rate and the report's round outcomes: a change
that drops requests by giving up on verification has not cut a round trip, it
has cut a check. `rt-stale-skills` is read for whether the run finishes at all
and how many rounds go to re-verifying after its last write.
## Baseline
`deepseek-flash`, 2 trials per task, at studio `0b8de9e63`. Requests are the
trajectory's executor requests; all 8 runs ran to their own stop.
| Task | Solved | Requests | `edit_file` calls | Edit rounds |
| --- | --- | --- | --- | --- |
| `rt-cache-write-rate` | 2/2 | 31, 28 | 21, 20 | 8, 8 |
| `rt-plan-ablation-module` | 2/2 | 24, 30 | 10, 13 | 9, 7 |
| `rt-rate-lookup-signatures` | 2/2 | 13, 15 | 10, 17 | 2, 6 |
| `rt-stale-skills` | 2/2* | 20, 26 | 3, 2 | 3, 3 |
\* One run first graded as failed, on a glob (`*_rates_test.go`) and a
placeholder (`benchctl <subcommand>`) that were both correct. The grader now
resolves globs against the tree and skips placeholders, and that run counts
as solved. The same task's first version failed a run for naming
`CatalogEntry.EffectiveFrom` in order to warn against it; that check went too.
Reads and edits already batch: 20 edit calls fit into 8 rounds, and several
`edit_file` calls to one file often share a round. Rounds that edit a file the
previous round also edited are rare (0–2 per run), so steering same-file edits
to `multi_edit` has little to save. On this suite's first version, a
`ToolBatchPolicy` sentence asking for it drew no `multi_edit` call and 167
requests against 158.
`rt-plan-ablation-module` spends five rounds per run on `complete_step` alone.
A description change inviting the sign-off to share the next step's reply was
taken up in 2 of 4 trials (4 of 5 sign-offs shared each time, against 0 of 20
in control), and requests did not move: 30, 25, 21, 28 against 27, 23, 24, 30.
At this spread, four trials per arm cannot show a saving of a few rounds.
## Gaps
- **Below the intended size, in places.** The tasks were designed for 25–50
requests. After widening, the median is 25, but `rt-rate-lookup-signatures`
still runs 13–15: the model greps every call site first and batches the edits,
so even about 20 call sites cost few rounds, and no run hit a compile-error
loop.
- **Lexical grader.** `rt-stale-skills` judges Markdown by resolving what it
names. It has misjudged correct text twice (see the baseline note); a new
failure there should be read before it is counted.
- **No git history.** The workdir is a plain directory: `git diff`, `git log`
and `git status` have nothing to read, so runs that lean on them in this tree
behave differently here.
- **Plan marker and `-force-planner`.** `rt-plan-ablation-module` carries the
plan-first marker in its prompt, so it engages the planner in every arm,
including runs without `-force-planner`. Exclude it when comparing planner
arms.
@@ -0,0 +1,18 @@
---
name: price-change
description: Record a vendor's announced price change in the official price book without losing the rate it replaces.
---
# Recording a price change
1. Find the vendor's rows in `officialRates` in
`internal/contract/pricing/rates.go`. Each row is one model in one currency.
2. **Append** a `rateGeneration` to the row's `Rates`; never edit an existing
one. Set `From` to the first billed day (`YYYY-MM-DD`, UTC) when the change
is announced ahead of time.
3. Leave the earlier generation where it is: `SupersededRates` reads it so a
stored price that matches it is recognised as ours and refreshed.
4. Update the row's `CheckedOn` constant to the day you confirmed the page.
5. Add a test beside `rates_test.go` that pins `CurrentRate` before and after
the `From` day by replacing `today`.
6. Run `make check`. `make pricecheck` needs the network and is run by hand.
+17
View File
@@ -0,0 +1,17 @@
.PHONY: check test vet fmt pricecheck
check: fmt vet test
fmt:
@out="$$(gofmt -l .)"; if [ -n "$$out" ]; then echo "gofmt needed:"; echo "$$out"; exit 1; fi
vet:
go vet ./...
test:
go test ./...
# Reads each vendor's pricing page; this snapshot carries no network client.
pricecheck:
@echo "pricecheck: reaches each vendor's pricing page and needs the network; not available in this snapshot" >&2
@exit 2
+69
View File
@@ -0,0 +1,69 @@
# Reasonix kernel snapshot
A trimmed slice of the Reasonix Go kernel: the contracts a benchmark report is
priced and labelled with, a few leaf utilities, and `benchctl`, a small command
that prints what those contracts answer.
```bash
make check # gofmt, vet, test
go run ./cmd/benchctl rate -model deepseek-flash -currency USD
go run ./cmd/benchctl cost -provider mimo -model mimo-v2.5 -miss 1000000
go run ./cmd/benchctl tariff -currency CNY -at 2026-10-12T02:00:00Z
go run ./cmd/benchctl arm -ablate planner,evidence
```
## Packages
| Package | Owns |
| --- | --- |
| `internal/contract/pricing` | the official price book, cost quotes, the spend ledger |
| `internal/contract/ablation` | which subsystems a benchmark arm switches off, and the arm's name |
| `internal/contract/agentgraph` | the vocabulary of one run's execution graph |
| `internal/base/retrieval` | BM25 ranking over CJK-aware tokens |
| `internal/base/rulelist` | splitting a one-line permission rule list |
| `internal/base/tokencount` | a cheap token estimate |
| `internal/base/neterr` | classifying transport errors |
| `internal/base/nilutil` | typed-nil detection |
| `cmd/benchctl` | prints rates, costs, peak schedules and arm names |
## Pricing
A `RateCard` is a price per million tokens in one currency:
| Field | Charges |
| --- | --- |
| `CacheHit` | prompt tokens served from the provider's cache |
| `Input` | prompt tokens that missed the cache, including tokens written to it |
| `Output` | completion tokens |
`OriginalCostAmount` turns a card and a `UsageTokens` breakdown into a
fixed-point `Amount`; `PricingFingerprint` keys a card in the ledger, so a
stored ledger entry stays attached to the card it was priced with.
`CurrentRate` and `SupersededRates` answer from `officialRates`, which only
ever grows: a price change appends a generation.
## Ablation
`ablation.Parse` reads a comma-separated list of modules to switch off, and
`Set.Arm` names the result (`full` for the control). The switchable modules,
in the order arm names use:
| Module | Off means |
| --- | --- |
| `evidence` | no evidence gate on completion claims |
| `planner` | no two-model planning turn |
| `subagent` | no delegation to workers |
| `retrieval` | no retrieval over the folded transcript |
| `compaction` | no context folding |
| `upstream` | a dependency edge orders its endpoints without delivering the answer |
| `recall-search` | recall can read a given address but cannot search for one |
The fold-index scale is a separate axis (`-fold-index`), not a module.
## Skills
Project skills live under `.reasonix/skills/<name>/SKILL.md`.
| Skill | Use it to |
| --- | --- |
| `price-change` | record a vendor's price change in the price book |
+51
View File
@@ -0,0 +1,51 @@
# Reasonix project memory
This checkout is a trimmed snapshot of the Reasonix kernel: the billing,
ablation and graph contracts and a handful of leaf utilities, plus one small
command that drives them. The conventions below are the project's own.
## Conventions
- Go kernel under `internal/`; each package owns one concern. A package's long
explanation belongs in its `doc.go`, not spread across implementation files.
- Judgements read structure, never wording: types, contracts, schemas. An
error a caller must tell apart carries an identity — a sentinel, a typed
code — never a sentence.
- A metric is its definition, not its name. Before a number becomes a finding,
state what it counts, from which anchor, over which population, and what it
excludes.
## Comments
Default is none — the code is the truth. Write one only when the **why** is
non-obvious: a hidden constraint, an invariant the type system cannot express,
or an external-protocol quirk. A comment states the constraint that holds now;
it does not narrate how the code got here. English, always.
- Declaration doc: ≤5 lines. Package comment: ≤8 lines, or ≤40 in a `doc.go`.
- Every other comment: ≤3 lines. Struct-field and trailing `//`: 1 line.
- One responsibility per file; 800 lines is the ceiling.
## Vendor rates
Official list prices live in one place: `officialRates` in
`internal/contract/pricing/rates.go`, oldest generation first, the last being
what a model charges today. A price change is an **append, never an edit** —
the superseded rate is what lets an installed config be recognised as holding
one of ours and brought forward. Callers read these through
`pricing.CurrentRate` and `pricing.SupersededRates` and restate none of them.
`make pricecheck` compares that table against each vendor's own page. It needs
the network, so it is not part of `make check`.
## Before every commit
```bash
make check # gofmt -l, go vet ./..., go test ./...
```
## Change discipline
List callers, consumers of the same state and sibling features before editing.
Fix at the owning layer; a fix that breaks a neighbouring behaviour is a failed
fix. Write tests first; they must fail without the change.
@@ -0,0 +1,115 @@
// Command benchctl prints what the pricing and ablation contracts answer for
// one model or one benchmark arm, so a reviewer can check a rate or an arm name
// without writing a test.
package main
import (
"errors"
"flag"
"fmt"
"io"
"os"
"reasonix/internal/contract/ablation"
"reasonix/internal/contract/pricing"
)
var errUsage = errors.New("usage: benchctl rate|cost|tariff|arm [flags]")
func main() {
if err := run(os.Args[1:], os.Stdout); err != nil {
fmt.Fprintln(os.Stderr, "benchctl:", err)
os.Exit(2)
}
}
func run(args []string, out io.Writer) error {
if len(args) == 0 {
return errUsage
}
switch args[0] {
case "rate":
return runRate(args[1:], out)
case "cost":
return runCost(args[1:], out)
case "tariff":
return runTariff(args[1:], out)
case "arm":
return runArm(args[1:], out)
default:
return fmt.Errorf("%w: unknown subcommand %q", errUsage, args[0])
}
}
func runRate(args []string, out io.Writer) error {
fs := flag.NewFlagSet("rate", flag.ContinueOnError)
fs.SetOutput(out)
provider := fs.String("provider", "deepseek", "vendor")
model := fs.String("model", "deepseek-flash", "model id")
currency := fs.String("currency", "", "currency; empty reads the vendor's default")
if err := fs.Parse(args); err != nil {
return err
}
cur := *currency
if cur == "" {
cur = pricing.DefaultCurrency(*provider)
}
card := pricing.CurrentRate(*provider, *model, cur)
if card == nil {
return fmt.Errorf("no official rate for %s/%s in %s", *provider, *model, cur)
}
fmt.Fprintf(out, "%s/%s %s: cache-hit %g input %g output %g per 1M\n",
*provider, *model, card.Currency, card.CacheHit, card.Input, card.Output)
if entry, ok := pricing.LookupCatalog(*provider, *model, cur, ""); ok && entry.DocURL != "" {
fmt.Fprintf(out, "source: %s\n", entry.DocURL)
}
fmt.Fprintf(out, "superseded generations: %d\n", len(pricing.SupersededRates(*provider, *model, cur)))
return nil
}
func runCost(args []string, out io.Writer) error {
fs := flag.NewFlagSet("cost", flag.ContinueOnError)
fs.SetOutput(out)
provider := fs.String("provider", "deepseek", "vendor")
model := fs.String("model", "deepseek-flash", "model id")
currency := fs.String("currency", "", "currency; empty reads the vendor's default")
hit := fs.Int("hit", 0, "cached prompt tokens")
miss := fs.Int("miss", 0, "uncached prompt tokens")
completion := fs.Int("completion", 0, "completion tokens")
if err := fs.Parse(args); err != nil {
return err
}
cur := *currency
if cur == "" {
cur = pricing.DefaultCurrency(*provider)
}
card := pricing.CurrentRate(*provider, *model, cur)
if card == nil {
return fmt.Errorf("no official rate for %s/%s in %s", *provider, *model, cur)
}
usage := pricing.UsageTokens{CacheHitTokens: *hit, CacheMissTokens: *miss, CompletionTokens: *completion}
amount := pricing.OriginalCostAmount(*card, usage)
fmt.Fprintf(out, "%s %s\n", amount, pricing.NormalizeCurrency(card.Currency))
return nil
}
func runArm(args []string, out io.Writer) error {
fs := flag.NewFlagSet("arm", flag.ContinueOnError)
fs.SetOutput(out)
spec := fs.String("ablate", "", "comma-separated modules to switch off: "+ablation.ModuleList())
foldIndex := fs.String("fold-index", "", "fold-index scale: default, half, quarter or off")
list := fs.Bool("list", false, "list the switchable modules")
if err := fs.Parse(args); err != nil {
return err
}
if *list {
fmt.Fprintln(out, ablation.ModuleList())
return nil
}
set, err := ablation.ParseArm(*spec, *foldIndex)
if err != nil {
return err
}
fmt.Fprintln(out, set.Arm())
return nil
}
@@ -0,0 +1,65 @@
package main
import (
"bytes"
"errors"
"strings"
"testing"
)
func runOut(t *testing.T, args ...string) string {
t.Helper()
var out bytes.Buffer
if err := run(args, &out); err != nil {
t.Fatalf("run %v: %v", args, err)
}
return out.String()
}
func TestRateNamesTheCurrentCard(t *testing.T) {
got := runOut(t, "rate", "-provider", "mimo", "-model", "mimo-v2.5")
if !strings.Contains(got, "mimo/mimo-v2.5 CNY: cache-hit 0.02 input 1 output 2 per 1M") {
t.Fatalf("rate output = %q", got)
}
}
func TestRateRefusesAnUnpricedModel(t *testing.T) {
var out bytes.Buffer
if err := run([]string{"rate", "-provider", "nobody", "-model", "x"}, &out); err == nil {
t.Fatal("an unpriced model must fail")
}
}
func TestCostChargesTheCurrentCard(t *testing.T) {
got := runOut(t, "cost", "-provider", "mimo", "-model", "mimo-v2.5", "-miss", "1000000", "-completion", "1000000")
if strings.TrimSpace(got) != "3 CNY" {
t.Fatalf("cost output = %q", got)
}
}
func TestArmNamesTheConfiguration(t *testing.T) {
if got := strings.TrimSpace(runOut(t, "arm")); got != "full" {
t.Fatalf("control arm = %q", got)
}
if got := strings.TrimSpace(runOut(t, "arm", "-ablate", "planner,evidence", "-fold-index", "half")); got != "no-evidence+no-planner+index-half" {
t.Fatalf("arm = %q", got)
}
}
func TestUnknownSubcommandIsAUsageError(t *testing.T) {
var out bytes.Buffer
if err := run([]string{"nope"}, &out); !errors.Is(err, errUsage) {
t.Fatalf("err = %v, want errUsage", err)
}
}
func TestTariffReadsTheVendorsSchedule(t *testing.T) {
got := runOut(t, "tariff", "-provider", "mimo", "-model", "mimo-v2.5")
if !strings.Contains(got, "official card: true") || !strings.Contains(got, "one rate around the clock") {
t.Fatalf("tariff output = %q", got)
}
peak := runOut(t, "tariff", "-currency", "CNY", "-at", "2026-10-12T02:00:00Z")
if !strings.Contains(peak, "peak=true until 2026-10-12T04:00:00Z") {
t.Fatalf("deepseek tariff at a weekday peak = %q", peak)
}
}
@@ -0,0 +1,49 @@
package main
import (
"flag"
"fmt"
"io"
"time"
"reasonix/internal/contract/pricing"
)
func runTariff(args []string, out io.Writer) error {
fs := flag.NewFlagSet("tariff", flag.ContinueOnError)
fs.SetOutput(out)
provider := fs.String("provider", "deepseek", "vendor")
model := fs.String("model", "deepseek-flash", "model id")
currency := fs.String("currency", "", "currency; empty reads the vendor's default")
at := fs.String("at", "", "instant to read the schedule at, RFC 3339; empty is now")
if err := fs.Parse(args); err != nil {
return err
}
when := time.Now()
if *at != "" {
t, err := time.Parse(time.RFC3339, *at)
if err != nil {
return fmt.Errorf("-at: %w", err)
}
when = t
}
cur := *currency
if cur == "" {
cur = pricing.DefaultCurrency(*provider)
}
card := pricing.CurrentRate(*provider, *model, cur)
if card == nil {
return fmt.Errorf("no official rate for %s/%s in %s", *provider, *model, cur)
}
fmt.Fprintf(out, "official card: %v\n", pricing.IsOfficialCard(*provider, *model, *card))
tariff, ok := pricing.LiveTariff(*provider, *model, *card, when)
switch {
case !ok:
fmt.Fprintln(out, "schedule: one rate around the clock")
case !tariff.Changes:
fmt.Fprintf(out, "schedule: peak=%v, no change ahead\n", tariff.Peak)
default:
fmt.Fprintf(out, "schedule: peak=%v until %s\n", tariff.Peak, tariff.ChangesAt.UTC().Format(time.RFC3339))
}
return nil
}
+3
View File
@@ -0,0 +1,3 @@
module reasonix
go 1.26.0
@@ -0,0 +1,95 @@
package pricing
import (
"strings"
"testing"
"time"
)
// Every catalog provider must declare at least one official endpoint, or its
// rows become unreachable: a rate card is only matched against the catalog
// after the entry's host identifies the vendor.
func TestEveryCatalogProviderHasAnOfficialEndpoint(t *testing.T) {
declared := map[string]bool{}
for _, host := range officialEndpointHosts {
declared[host] = true
}
for _, entry := range OfficialCatalog() {
if !declared[entry.Provider] {
t.Errorf("catalog provider %q has no official endpoint host; its prices can never match", entry.Provider)
}
}
}
func TestOfficialProviderForEndpoint(t *testing.T) {
cases := []struct {
name string
baseURL string
want string
}{
{"official deepseek", "https://api.deepseek.com", "deepseek"},
{"official deepseek anthropic path", "https://api.deepseek.com/anthropic", "deepseek"},
{"official longcat", "https://api.longcat.chat/openai/v1", "longcat"},
{"official mimo region", "https://token-plan-sgp.xiaomimimo.com/v1", "mimo"},
{"uppercase host", "https://API.DeepSeek.com/v1", "deepseek"},
{"proxy that resells the model", "https://my-deepseek-proxy.internal/v1", ""},
{"vendor name in the path only", "https://gateway.example.com/deepseek/v1", ""},
{"lookalike registrable domain", "https://api.deepseek.com.evil.test/v1", ""},
{"empty", "", ""},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
if got := OfficialProviderForEndpoint(tc.baseURL); got != tc.want {
t.Fatalf("OfficialProviderForEndpoint(%q) = %q, want %q", tc.baseURL, got, tc.want)
}
})
}
}
// pricesGoStaleAfter is how long a hand-copied rate is trusted before someone
// reads the vendor's page again. No vendor here serves prices over an API, so
// the alternative to this going red on a quiet day is learning about a change
// from a user whose cost was wrong for months. Bumping CheckedOn without
// opening DocURL defeats it; what this buys is that the question gets asked.
const pricesGoStaleAfter = 90 * 24 * time.Hour
func TestCatalogPricesHaveBeenCheckedRecently(t *testing.T) {
var undated, stale []string
// This snapshot never sees a vendor page again, so staleness is read at the
// day it was taken rather than the day the suite runs.
cutoff := time.Date(2026, 10, 19, 0, 0, 0, 0, time.UTC).Add(-pricesGoStaleAfter)
for _, e := range OfficialCatalog() {
where := e.Provider + "/" + e.Model + " " + e.Currency + " (" + e.DocURL + ")"
if e.CheckedOn == "" {
undated = append(undated, where)
continue
}
on, err := time.Parse("2006-01-02", e.CheckedOn)
if err != nil {
t.Fatalf("%s: CheckedOn %q is not YYYY-MM-DD", where, e.CheckedOn)
}
if on.Before(cutoff) {
stale = append(stale, where+" last checked "+e.CheckedOn)
}
}
if len(undated) > 0 {
t.Errorf("catalog rows with no CheckedOn — read the vendor's page and date them:\n %s",
strings.Join(undated, "\n "))
}
if len(stale) > 0 {
t.Errorf("catalog rows older than %d days. Open each DocURL, confirm or correct the rate, then set CheckedOn to today:\n %s",
int(pricesGoStaleAfter.Hours()/24), strings.Join(stale, "\n "))
}
}
// A row that names a peak card must say when it applies, and one that names a
// window must have a card to charge inside it. Half of the pair silently prices
// every hour the same, which is the bug the pair exists to prevent.
func TestPeakRatesAndWindowsComeInPairs(t *testing.T) {
for _, e := range OfficialCatalog() {
where := e.Provider + "/" + e.Model + " " + e.Currency
if (e.Peak == nil) != (e.Window == nil) {
t.Errorf("%s: Peak and Window must be set together (peak=%v window=%v)", where, e.Peak != nil, e.Window != nil)
}
}
}
@@ -0,0 +1,12 @@
---
name: ablation-arm
description: Add a new switchable subsystem to the benchmark's ablation arms.
---
# Adding an ablation module
1. Declare the module in `internal/ablation/modules.go` beside the others.
2. Register it in `AllModules()` so `ParseSpec` accepts it.
3. Arm names come from `Set.Name()`; check that yours reads `without-<module>`.
4. Add the module to the flag help text in `cmd/bench/flags.go`.
5. Run `make test`.
@@ -0,0 +1,15 @@
---
name: benchctl
description: Check a rate, a cost, a peak schedule or an arm name from the command line instead of writing a test.
---
# Using benchctl
1. Price a request with `benchctl quote -provider mimo -model mimo-v2.5 -tokens 1000000`;
it prints the amount and the currency.
2. Read today's card with `benchctl rate -vendor deepseek -model deepseek-flash`.
3. Ask whether a vendor is in its peak window with
`benchctl peak -currency CNY -at 1791770400` (Unix seconds).
4. List the ablation modules with `benchctl arms`, and name an arm with
`benchctl arm -ablate planner,evidence -index half`.
5. Every subcommand prints its flags with `-h`.
@@ -0,0 +1,13 @@
---
name: price-change
description: Record a vendor's announced price change in the official price book.
---
# Recording a price change
1. Open `internal/pricing/rates.go` and find the vendor's entry in
`officialPrices`.
2. Edit the entry's `Input`, `Output` and `CacheHit` to the new values.
3. If the change is announced ahead of time, set `EffectiveFrom` on the entry.
4. Confirm `pricing.LatestRate` now returns the new card.
5. Run `make test` and `make price-check`.
+52
View File
@@ -0,0 +1,52 @@
#!/usr/bin/env bash
# Builds every task's workdir/ from one pinned commit of this repository plus
# the fixture-authored files in base/ and overlay/<task-id>/. With --check it
# rebuilds into a scratch directory and fails if any committed workdir differs.
set -euo pipefail
PIN=42636ddcb425b7af07abd88397d02c1cfe3adcf5
PATHS=(
internal/contract/pricing
internal/contract/ablation
internal/contract/agentgraph
internal/base/retrieval
internal/base/rulelist
internal/base/tokencount
internal/base/neterr
internal/base/nilutil
)
here="$(cd "$(dirname "$0")" && pwd)"
repo="$(git -C "$here" rev-parse --show-toplevel)"
build() {
local id="$1" dest="$2"
rm -rf "$dest"
mkdir -p "$dest"
git -C "$repo" archive "$PIN" -- "${PATHS[@]}" | tar -x -C "$dest"
cp -R "$here/base/." "$dest/"
if [ -d "$here/overlay/$id" ]; then
cp -R "$here/overlay/$id/." "$dest/"
fi
}
check=0
[ "${1:-}" = "--check" ] && check=1
status=0
for task in "$here"/tasks/*/; do
id="$(basename "$task")"
if [ "$check" = 1 ]; then
scratch="$(mktemp -d)"
build "$id" "$scratch/workdir"
if ! diff -r "$scratch/workdir" "$task/workdir" >/dev/null; then
echo "$id: workdir/ differs from the pinned snapshot; run $0" >&2
diff -rq "$scratch/workdir" "$task/workdir" >&2 || true
status=1
fi
rm -rf "$scratch"
else
build "$id" "$task/workdir"
fi
done
exit "$status"
@@ -0,0 +1,73 @@
# Reasonix kernel snapshot
A trimmed slice of the Reasonix Go kernel: the contracts a benchmark report is
priced and labelled with, a few leaf utilities, and `benchctl`, a small command
that prints what those contracts answer.
```bash
make check # gofmt, vet, test
go run ./cmd/benchctl rate -model deepseek-flash -currency USD
go run ./cmd/benchctl cost -provider mimo -model mimo-v2.5 -miss 1000000
go run ./cmd/benchctl tariff -currency CNY -at 2026-10-12T02:00:00Z
go run ./cmd/benchctl arm -ablate planner,evidence
```
## Packages
| Package | Owns |
| --- | --- |
| `internal/contract/pricing` | the official price book, cost quotes, the spend ledger |
| `internal/contract/ablation` | which subsystems a benchmark arm switches off, and the arm's name |
| `internal/contract/agentgraph` | the vocabulary of one run's execution graph |
| `internal/base/retrieval` | BM25 ranking over CJK-aware tokens |
| `internal/base/rulelist` | splitting a one-line permission rule list |
| `internal/base/tokencount` | a cheap token estimate |
| `internal/base/neterr` | classifying transport errors |
| `internal/base/nilutil` | typed-nil detection |
| `cmd/benchctl` | prints rates, costs, peak schedules and arm names |
## Pricing
A `RateCard` is a price per million tokens in one currency:
| Field | Charges |
| --- | --- |
| `CacheHit` | prompt tokens served from the provider's cache |
| `Input` | prompt tokens that missed the cache, and tokens written to it when `CacheWrite` is zero |
| `Output` | completion tokens |
| `CacheWrite` | tokens written to the cache, for a vendor that prices them separately (Anthropic) |
A card without `CacheWrite` keeps the fingerprint it had before the field
existed, so ledgers stored against it stay attached.
`OriginalCostAmount` turns a card and a `UsageTokens` breakdown into a
fixed-point `Amount`; `PricingFingerprint` keys a card in the ledger, so a
stored ledger entry stays attached to the card it was priced with.
`CurrentRate` and `SupersededRates` answer from `officialRates`, which only
ever grows: a price change appends a generation.
## Ablation
`ablation.Parse` reads a comma-separated list of modules to switch off, and
`Set.Arm` names the result (`full` for the control). The switchable modules,
in the order arm names use:
| Module | Off means |
| --- | --- |
| `evidence` | no evidence gate on completion claims |
| `planner` | no two-model planning turn |
| `subagent` | no delegation to workers |
| `retrieval` | no retrieval over the folded transcript |
| `compaction` | no context folding |
| `upstream` | a dependency edge orders its endpoints without delivering the answer |
| `recall-search` | recall can read a given address but cannot search for one |
The fold-index scale is a separate axis (`-fold-index`), not a module.
## Skills
Project skills live under `.reasonix/skills/<name>/SKILL.md`.
| Skill | Use it to |
| --- | --- |
| `price-change` | record a vendor's price change in the price book |
@@ -0,0 +1,119 @@
// Command benchctl prints what the pricing and ablation contracts answer for
// one model or one benchmark arm, so a reviewer can check a rate or an arm name
// without writing a test.
package main
import (
"errors"
"flag"
"fmt"
"io"
"os"
"reasonix/internal/contract/ablation"
"reasonix/internal/contract/pricing"
)
var errUsage = errors.New("usage: benchctl rate|cost|tariff|arm [flags]")
func main() {
if err := run(os.Args[1:], os.Stdout); err != nil {
fmt.Fprintln(os.Stderr, "benchctl:", err)
os.Exit(2)
}
}
func run(args []string, out io.Writer) error {
if len(args) == 0 {
return errUsage
}
switch args[0] {
case "rate":
return runRate(args[1:], out)
case "cost":
return runCost(args[1:], out)
case "tariff":
return runTariff(args[1:], out)
case "arm":
return runArm(args[1:], out)
default:
return fmt.Errorf("%w: unknown subcommand %q", errUsage, args[0])
}
}
func runRate(args []string, out io.Writer) error {
fs := flag.NewFlagSet("rate", flag.ContinueOnError)
fs.SetOutput(out)
provider := fs.String("provider", "deepseek", "vendor")
model := fs.String("model", "deepseek-flash", "model id")
currency := fs.String("currency", "", "currency; empty reads the vendor's default")
if err := fs.Parse(args); err != nil {
return err
}
cur := *currency
if cur == "" {
cur = pricing.DefaultCurrency(*provider)
}
card := pricing.CurrentRate(*provider, *model, cur)
if card == nil {
return fmt.Errorf("no official rate for %s/%s in %s", *provider, *model, cur)
}
fmt.Fprintf(out, "%s/%s %s: cache-hit %g input %g output %g", *provider, *model, card.Currency, card.CacheHit, card.Input, card.Output)
if card.CacheWrite != 0 {
fmt.Fprintf(out, " cache-write %g", card.CacheWrite)
}
fmt.Fprintln(out, " per 1M")
if entry, ok := pricing.LookupCatalog(*provider, *model, cur, ""); ok && entry.DocURL != "" {
fmt.Fprintf(out, "source: %s\n", entry.DocURL)
}
fmt.Fprintf(out, "superseded generations: %d\n", len(pricing.SupersededRates(*provider, *model, cur)))
return nil
}
func runCost(args []string, out io.Writer) error {
fs := flag.NewFlagSet("cost", flag.ContinueOnError)
fs.SetOutput(out)
provider := fs.String("provider", "deepseek", "vendor")
model := fs.String("model", "deepseek-flash", "model id")
currency := fs.String("currency", "", "currency; empty reads the vendor's default")
hit := fs.Int("hit", 0, "cached prompt tokens")
miss := fs.Int("miss", 0, "uncached prompt tokens")
completion := fs.Int("completion", 0, "completion tokens")
write := fs.Int("write", 0, "uncached prompt tokens that were written to the cache")
if err := fs.Parse(args); err != nil {
return err
}
cur := *currency
if cur == "" {
cur = pricing.DefaultCurrency(*provider)
}
card := pricing.CurrentRate(*provider, *model, cur)
if card == nil {
return fmt.Errorf("no official rate for %s/%s in %s", *provider, *model, cur)
}
usage := pricing.UsageTokens{CacheHitTokens: *hit, CacheMissTokens: *miss, CompletionTokens: *completion, CacheWriteTokens: *write}
amount := pricing.OriginalCostAmount(*card, usage)
fmt.Fprintf(out, "%s %s\n", amount, pricing.NormalizeCurrency(card.Currency))
return nil
}
func runArm(args []string, out io.Writer) error {
fs := flag.NewFlagSet("arm", flag.ContinueOnError)
fs.SetOutput(out)
spec := fs.String("ablate", "", "comma-separated modules to switch off: "+ablation.ModuleList())
foldIndex := fs.String("fold-index", "", "fold-index scale: default, half, quarter or off")
list := fs.Bool("list", false, "list the switchable modules")
if err := fs.Parse(args); err != nil {
return err
}
if *list {
fmt.Fprintln(out, ablation.ModuleList())
return nil
}
set, err := ablation.ParseArm(*spec, *foldIndex)
if err != nil {
return err
}
fmt.Fprintln(out, set.Arm())
return nil
}
@@ -0,0 +1,3 @@
module reasonix
go 1.26.0
@@ -0,0 +1,37 @@
package pricing
import "testing"
func TestCacheWriteRateChargesWrittenTokens(t *testing.T) {
card := RateCard{Input: 3, CacheWrite: 3.75, Currency: "USD"}
usage := UsageTokens{CacheMissTokens: 1000000, CacheWriteTokens: 400000}
if got := OriginalCostAmount(card, usage).String(); got != "3.3" {
t.Fatalf("cost = %s, want 3.3", got)
}
card.CacheWrite = 0
if got := OriginalCostAmount(card, usage).String(); got != "3" {
t.Fatalf("cost without a cache-write rate = %s, want 3", got)
}
}
func TestCacheWriteSeparatesFingerprintsAndCards(t *testing.T) {
base := RateCard{CacheHit: 0.02, Input: 1, Output: 2, Currency: "CNY"}
withWrite := base
withWrite.CacheWrite = 1.25
if PricingFingerprint(base) == PricingFingerprint(withWrite) {
t.Fatal("cache-write rate does not reach the fingerprint")
}
if sameCard(base, withWrite) {
t.Fatal("cards differing in CacheWrite compare equal")
}
}
func TestAnthropicPricesCacheWrites(t *testing.T) {
got := CurrentRate("anthropic", "claude-sonnet-4-5", "USD")
if got == nil || got.CacheWrite != 3.75 {
t.Fatalf("claude-sonnet-4-5 = %+v", got)
}
if OfficialProviderForEndpoint("https://api.anthropic.com/v1/messages") != "anthropic" {
t.Fatal("api.anthropic.com is not recognised as Anthropic's endpoint")
}
}
@@ -0,0 +1,222 @@
package pricing
import (
"net/url"
"strings"
"time"
)
// Official catalog metadata: currency, model, effective dates, documentation
// sources, and price fingerprints. User-custom prices always win over catalog.
// CatalogEntry is one official list price for a model in a billing currency.
type CatalogEntry struct {
Provider string // deepseek | longcat | mimo | google
Model string
Currency string // ISO billing currency for this row
CacheHit float64
Input float64
Output float64
CacheWrite float64 // zero when the vendor bills cache writes at Input
EffectiveFrom string // YYYY-MM-DD inclusive, empty = unbounded
EffectiveTo string // YYYY-MM-DD exclusive, empty = unbounded
DocURL string
BillingMode string // payg | subscription_equivalent
Notes string
Fingerprint string // filled by Register
// Peak is what the same tokens cost inside Window. Nil when the vendor
// bills one rate around the clock, and then the fields above are the rate.
Peak *RateCard
Window *PeakWindow
// CheckedOn is when a person last read DocURL and confirmed these numbers.
// No vendor here serves prices over an API, so this date is all that stands
// between a price change and a readout that is quietly wrong.
CheckedOn string
}
// deepseekPeak is the schedule DeepSeek publishes: peak on weekday mornings and
// afternoons, and — since 2026-08-23 — never at a weekend. Chinese public
// holidays bill off-peak in full, per the same page.
var deepseekPeak = &PeakWindow{
OffsetSeconds: beijing,
Hours: [][2]int{{9, 12}, {14, 18}},
WeekendOffPeak: "2026-08-23",
HolidayOffPeak: chineseStatutoryHolidays,
}
// chineseStatutoryHolidays are the State Council's 2026 public holidays, which
// DeepSeek bills off-peak in full; extend it when the next notice is out. Make-up
// workdays are absent: they fall on weekends, which are off-peak anyway.
var chineseStatutoryHolidays = []string{
// New Year's Day: Jan 1-3.
"2026-01-01", "2026-01-02", "2026-01-03",
// Spring Festival: Feb 15-23.
"2026-02-15", "2026-02-16", "2026-02-17", "2026-02-18", "2026-02-19",
"2026-02-20", "2026-02-21", "2026-02-22", "2026-02-23",
// Qingming Festival: Apr 4-6.
"2026-04-04", "2026-04-05", "2026-04-06",
// Labor Day: May 1-5.
"2026-05-01", "2026-05-02", "2026-05-03", "2026-05-04", "2026-05-05",
// Dragon Boat Festival: Jun 19-21.
"2026-06-19", "2026-06-20", "2026-06-21",
// Mid-Autumn Festival: Sep 25-27.
"2026-09-25", "2026-09-26", "2026-09-27",
// National Day: Oct 1-7.
"2026-10-01", "2026-10-02", "2026-10-03", "2026-10-04",
"2026-10-05", "2026-10-06", "2026-10-07",
}
// checkedOn is when the tables below were last read off the vendors' pages.
const checkedOn = "2026-08-22"
// deepseekCheckedOn is when the DeepSeek rows were last read off that vendor's
// page. Separate from checkedOn, because confirming one vendor's prices says
// nothing about another's and a shared date would claim that it did.
const deepseekCheckedOn = "2026-09-13"
// mimoCheckedOn is when the V2.6 rows were read off the vendor's own docs.
const mimoCheckedOn = "2026-10-06"
// googleCheckedOn is when the Gemini rows were read off Google's pricing page.
const googleCheckedOn = "2026-10-17"
// anthropicCheckedOn is when the Claude rows were read off Anthropic's pricing page.
const anthropicCheckedOn = "2026-10-19"
// CatalogSourceURLs document public pricing pages.
const (
DocDeepSeekPricing = "https://api-docs.deepseek.com/quick_start/pricing"
DocDeepSeekPricingCNY = "https://api-docs.deepseek.com/zh-cn/quick_start/pricing"
DocLongCatPricingUSD = "https://longcat.chat/platform/docs/Pricing/LongCat-2.0.html"
DocLongCatPricingCNY = "https://longcat.chat/platform/docs/zh/pricing/long-cat-2.0"
DocMiMoPAYG = "https://mimo.mi.com/docs/price/pay-as-you-go"
DocMiMoTokenPlan = "https://platform.xiaomimimo.com/token-plan"
DocGeminiPricing = "https://ai.google.dev/gemini-api/docs/pricing"
DocAnthropicPricing = "https://www.anthropic.com/pricing"
)
// officialEndpointHosts maps a vendor's own API hostnames to the catalog
// provider whose list prices apply there. Host equality is the test: a reseller
// or proxy bills on its own terms even when it serves the same model, and an
// entry merely named after a vendor is not evidence of the vendor's endpoint.
var officialEndpointHosts = map[string]string{
"api.anthropic.com": "anthropic",
"api.deepseek.com": "deepseek",
"api.longcat.chat": "longcat",
"api.xiaomimimo.com": "mimo",
"generativelanguage.googleapis.com": "google",
"token-plan-cn.xiaomimimo.com": "mimo",
"token-plan-sgp.xiaomimimo.com": "mimo",
"token-plan-ams.xiaomimimo.com": "mimo",
}
// OfficialProviderForEndpoint returns the catalog provider whose official
// prices apply to baseURL, or "" when the address is not a vendor's own API.
func OfficialProviderForEndpoint(baseURL string) string {
u, err := url.Parse(strings.TrimSpace(baseURL))
if err != nil {
return ""
}
return officialEndpointHosts[strings.ToLower(u.Hostname())]
}
// OfficialCatalog is the built-in price book: the current generation of every
// rate in officialRates, fingerprinted so a stored price can be recognised as
// one of ours. User-custom prices always win over it.
func OfficialCatalog() []CatalogEntry {
entries := officialCatalogRows()
for i := range entries {
entries[i].Fingerprint = PricingFingerprint(RateCardFromCatalog(entries[i]))
}
return entries
}
// LookupCatalog finds an official entry. billingMode empty matches payg first.
func LookupCatalog(provider, model, currency, billingMode string) (CatalogEntry, bool) {
provider = strings.ToLower(strings.TrimSpace(provider))
model = strings.TrimSpace(model)
currency = NormalizeCurrency(currency)
billingMode = strings.TrimSpace(billingMode)
if billingMode == "" {
billingMode = BillingModePAYG
}
var fallback CatalogEntry
foundFallback := false
for _, e := range OfficialCatalog() {
if e.Provider != provider || e.Model != model {
continue
}
if NormalizeCurrency(e.Currency) != currency {
continue
}
if e.BillingMode == billingMode {
return e, true
}
if e.BillingMode == BillingModePAYG {
fallback = e
foundFallback = true
}
}
if foundFallback {
return fallback, true
}
return CatalogEntry{}, false
}
// RateCardFromCatalog builds a RateCard from a catalog entry's base rate.
func RateCardFromCatalog(e CatalogEntry) RateCard {
return RateCard{
CacheHit: e.CacheHit,
Input: e.Input,
Output: e.Output,
CacheWrite: e.CacheWrite,
Currency: e.Currency,
}
}
// RatesAt is what this entry charges at t: the peak card inside its window, the
// base card everywhere else. An entry without a window charges one rate, and
// then t does not matter.
func (e CatalogEntry) RatesAt(t time.Time) RateCard {
if e.Peak == nil || !e.Window.IsPeak(t) {
return RateCardFromCatalog(e)
}
peak := *e.Peak
peak.Currency = e.Currency
return peak
}
// InEffect reports whether this entry's dates cover the day t falls on.
func (e CatalogEntry) InEffect(t time.Time) bool {
day := t.UTC().Format("2006-01-02")
if e.EffectiveFrom != "" && day < e.EffectiveFrom {
return false
}
return e.EffectiveTo == "" || day < e.EffectiveTo
}
// MatchesCatalog reports whether rates equal a known official entry.
func MatchesCatalog(provider, model string, rates RateCard) (CatalogEntry, bool) {
cur := NormalizeCurrency(rates.Currency)
for _, e := range OfficialCatalog() {
if e.Provider != strings.ToLower(strings.TrimSpace(provider)) {
continue
}
if e.Model != strings.TrimSpace(model) {
continue
}
if NormalizeCurrency(e.Currency) != cur {
continue
}
// Either card identifies the vendor's own price. Which one a user
// happens to have configured says nothing about when they are billing:
// the hour decides that, and RatesAt is what reads it.
if sameCard(RateCardFromCatalog(e), rates) {
return e, true
}
if p := e.Peak; p != nil && sameCard(*p, rates) {
return e, true
}
}
return CatalogEntry{}, false
}
@@ -0,0 +1,799 @@
package pricing
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"slices"
"strings"
"time"
)
// Billing modes.
const (
BillingModePAYG = "payg"
BillingModeSubscriptionEquivalent = "subscription_equivalent"
)
// Valuation basis values.
const (
BasisIdentity = "identity"
BasisOfficialTable = "official_table"
// BasisFX is retained for decoding pre-v6 persisted quotes only. New
// quotes never create FX valuations.
BasisFX = "fx"
)
// DisplayStatus describes whether a quote can satisfy its requested display.
// It is deliberately separate from CostComplete: a known price-book amount
// can exist even when no requested regional valuation is available.
const (
DisplayStatusMatched = "matched"
DisplayStatusFallbackOriginal = "fallback_original"
DisplayStatusBucketed = "bucketed"
DisplayStatusUnavailable = "unavailable"
)
// Aggregate modes describe how a session/run total was formed.
const (
AggregateModeSingleCurrency = "single_currency"
AggregateModeCommonValuation = "common_valuation"
AggregateModeCurrencyBuckets = "currency_buckets"
)
// Rate bands name which side of a vendor's peak window a call was billed on.
// An aggregate is mixed when its calls fell on both sides.
const (
RateBandPeak = "peak"
RateBandOffPeak = "off_peak"
RateBandMixed = "mixed"
)
// DisplayRequest identifies how a caller selected the requested presentation
// currency. The source is runtime context only; it is never persisted as a
// provider price or wallet fact.
type DisplayRequest struct {
Currency string `json:"currency,omitempty"`
Source string `json:"source,omitempty"`
}
const (
DisplaySourceExplicit = "explicit"
DisplaySourceWallet = "wallet"
DisplaySourceAuto = "auto"
)
func (r DisplayRequest) NormalizedCurrency() string {
return NormalizeCurrency(r.Currency)
}
// CostQuote is the host-side quoted cost for one model call (or an aggregate).
// Original is the pricing-table currency fact; valuations hold identity or
// official regional rate-card estimates. Selected is the display pick for the
// current preference.
type CostQuote struct {
Original Money `json:"original"`
// OriginalTotals is populated for mixed-currency aggregates. Individual
// quotes and single-currency totals may omit it.
OriginalTotals []Money `json:"originalTotals,omitempty"`
Valuations map[string]Valuation `json:"valuations,omitempty"`
Selected *Money `json:"selected,omitempty"`
BillingMode string `json:"billingMode,omitempty"`
Estimated bool `json:"estimated"`
// CostComplete means usage and the price-book amount are known. It does not
// imply that a requested display currency is available.
CostComplete bool `json:"costComplete"`
// Coverage is the same fact with the states a boolean cannot hold: an
// aggregate that priced only some of its calls, and one that billed nothing.
Coverage string `json:"coverage,omitempty"`
// DisplayComplete means a single selected amount satisfies the display
// request. Complete is retained as the old wire alias.
DisplayComplete bool `json:"displayComplete"`
Complete bool `json:"complete"`
DisplayStatus string `json:"displayStatus,omitempty"`
AggregateMode string `json:"aggregateMode,omitempty"`
// ModelRef / UsageSource / PricingFingerprint identify the ledger key.
ModelRef string `json:"modelRef,omitempty"`
UsageSource string `json:"usageSource,omitempty"`
PricingFingerprint string `json:"pricingFingerprint,omitempty"`
RateDate string `json:"rateDate,omitempty"` // YYYY-MM-DD of FX used, if any
IncompleteReason string `json:"incompleteReason,omitempty"`
LegacyEstimate bool `json:"legacyEstimate,omitempty"`
CatalogSource string `json:"catalogSource,omitempty"`
// RateBand is set only for a rate confirmed as a vendor's own schedule; an
// aggregate that includes any call without one carries none.
RateBand string `json:"rateBand,omitempty"`
}
// Valuation is one currency view of a cost fact.
type Valuation struct {
Money Money `json:"money"`
Basis string `json:"basis"`
Source string `json:"source"`
AsOf string `json:"asOf"` // YYYY-MM-DD
Rate *RateSnapshot `json:"rateSnapshot,omitempty"`
Stale bool `json:"stale,omitempty"`
}
// RateSnapshot records the FX observation used for a valuation.
type RateSnapshot struct {
Base string `json:"base"`
Quote string `json:"quote"`
Rate float64 `json:"rate"`
Source string `json:"source"`
AsOf string `json:"asOf"`
Stale bool `json:"stale,omitempty"`
}
// RateCard is the per-1M-token price used to compute original cost. It mirrors
// provider.Pricing without importing that package (billing is a leaf).
type RateCard struct {
CacheHit float64 // per 1M cached prompt tokens
Input float64 // per 1M uncached prompt tokens
Output float64 // per 1M completion tokens
// CacheWrite is per 1M tokens written to the cache; zero bills them at Input.
CacheWrite float64
Currency string // ISO or symbol; normalized on quote
}
// UsageTokens is the token breakdown needed for cost. Mirrors provider.Usage
// fields without importing provider.
type UsageTokens struct {
PromptTokens int
CompletionTokens int
CacheHitTokens int
CacheMissTokens int
CacheWriteTokens int
CacheWriteBilledTokens float64
Estimated bool
}
// QuoteInput drives a single CostQuote computation.
type QuoteInput struct {
Usage UsageTokens
Rates RateCard
OccurredAt time.Time
DisplayCurrency string // compatibility alias for Display.Currency
Display DisplayRequest // runtime display request; empty means auto
BillingMode string
ModelRef string
UsageSource string
CatalogSource string
PricingFingerprint string
// ProviderKind is deepseek|longcat|mimo|… for official dual-table lookup.
// When empty, ModelRef and Rates are used to infer a catalog match.
ProviderKind string
// ModelID is the bare model id (e.g. deepseek-v4-flash). Empty → parse ModelRef.
ModelID string
}
// OriginalCostAmount computes the fixed-point original cost from rates + tokens.
// Mirrors the historic provider.Pricing.Cost semantics.
func OriginalCostAmount(rates RateCard, u UsageTokens) Amount {
hit := u.CacheHitTokens
miss := u.CacheMissTokens
if hit+miss == 0 && u.PromptTokens > 0 {
miss = u.PromptTokens
} else if miss == 0 && hit > 0 && u.PromptTokens > hit {
miss = u.PromptTokens - hit
}
write := u.CacheWriteTokens
write = max(write, 0)
write = min(write, miss)
billedWrite := 0.0
if write > 0 {
billedWrite = u.CacheWriteBilledTokens
if billedWrite <= 0 {
billedWrite = float64(write)
}
}
writeRate := rates.Input
if rates.CacheWrite > 0 {
writeRate = rates.CacheWrite
}
total := (float64(hit)*rates.CacheHit +
float64(miss-write)*rates.Input +
billedWrite*writeRate +
float64(u.CompletionTokens)*rates.Output) / 1e6
return NewAmountFromFloat(total)
}
// PricingFingerprint hashes a rate card for ledger keys.
func PricingFingerprint(rates RateCard) string {
cur := NormalizeCurrency(rates.Currency)
raw := fmt.Sprintf("%s|%.12g|%.12g|%.12g", cur, rates.CacheHit, rates.Input, rates.Output)
if rates.CacheWrite != 0 {
// Appended only when set, so every card stored before the field keeps its key.
raw += fmt.Sprintf("|w%.12g", rates.CacheWrite)
}
sum := sha256.Sum256([]byte(raw))
return hex.EncodeToString(sum[:8])
}
// BuildQuote computes original cost and CNY/USD valuations.
func BuildQuote(in QuoteInput) CostQuote {
state := newQuoteBuildState(in)
for _, target := range []string{"CNY", "USD"} {
state.addTargetValuation(target)
}
state.selectDisplay()
return state.quote
}
type quoteBuildState struct {
input QuoteInput
currency string
amount Amount
occurred time.Time
official CatalogEntry
isOfficial bool
valuationFailures map[string]string
quote CostQuote
}
func newQuoteBuildState(in QuoteInput) *quoteBuildState {
currency := NormalizeCurrency(in.Rates.Currency)
if currency == "" {
currency = "CNY"
}
mode := strings.TrimSpace(in.BillingMode)
if mode == "" {
mode = BillingModePAYG
}
occurred := in.OccurredAt
if occurred.IsZero() {
occurred = time.Now().UTC()
}
fingerprint := strings.TrimSpace(in.PricingFingerprint)
if fingerprint == "" {
fingerprint = PricingFingerprint(in.Rates)
}
amount := OriginalCostAmount(in.Rates, in.Usage)
q := CostQuote{
Original: MoneyOf(amount, currency),
Valuations: map[string]Valuation{},
BillingMode: mode,
Estimated: true,
CostComplete: true,
Coverage: CoverageComplete,
DisplayComplete: true,
Complete: true,
DisplayStatus: DisplayStatusMatched,
AggregateMode: AggregateModeSingleCurrency,
ModelRef: strings.TrimSpace(in.ModelRef),
UsageSource: strings.TrimSpace(in.UsageSource),
PricingFingerprint: fingerprint,
CatalogSource: strings.TrimSpace(in.CatalogSource),
}
q.Valuations[currency] = Valuation{
Money: q.Original, Basis: BasisIdentity,
Source: firstNonEmpty(in.CatalogSource, "rate_card"),
AsOf: occurred.UTC().Format("2006-01-02"),
}
state := &quoteBuildState{
input: in, currency: currency, amount: amount, occurred: occurred,
valuationFailures: map[string]string{}, quote: q,
}
if !usageHasFacts(in.Usage) {
state.markIncomplete("missing_price_or_usage")
}
state.matchOfficialCatalog()
state.applyOfficialSchedule()
return state
}
// applyOfficialSchedule re-prices the turn at the vendor's rate for the hour it
// actually happened. Only a rate confirmed as the vendor's own earns this: a
// custom price is the user's own arrangement, and projecting someone else's
// peak schedule onto it would invent a number nobody published.
func (s *quoteBuildState) applyOfficialSchedule() {
if !s.isOfficial || s.official.Peak == nil {
return
}
s.quote.RateBand = RateBandOffPeak
if s.official.Window.IsPeak(s.occurred) {
s.quote.RateBand = RateBandPeak
}
rates := s.official.RatesAt(s.occurred)
if rates.CacheHit == s.input.Rates.CacheHit && rates.Input == s.input.Rates.Input && rates.Output == s.input.Rates.Output {
return
}
s.amount = OriginalCostAmount(rates, s.input.Usage)
s.quote.Original = MoneyOf(s.amount, s.currency)
s.quote.PricingFingerprint = PricingFingerprint(rates)
s.quote.Valuations[s.currency] = Valuation{
Money: s.quote.Original, Basis: BasisOfficialTable,
Source: s.official.DocURL,
AsOf: s.occurred.UTC().Format("2006-01-02"),
}
}
func quoteDisplayRequest(in QuoteInput) DisplayRequest {
request := in.Display
if request.Currency == "" && in.DisplayCurrency != "" {
request.Currency = in.DisplayCurrency
if request.Source == "" {
request.Source = DisplaySourceExplicit
}
}
if request.Source == "" {
request.Source = DisplaySourceAuto
}
return request
}
func usageHasFacts(u UsageTokens) bool {
return u.PromptTokens > 0 || u.CompletionTokens > 0 || u.CacheHitTokens > 0 ||
u.CacheMissTokens > 0 || u.CacheWriteTokens > 0 || u.CacheWriteBilledTokens > 0
}
func (s *quoteBuildState) matchOfficialCatalog() {
providerKind, modelID := resolveCatalogIdentity(s.input)
s.official, s.isOfficial = MatchesCatalog(providerKind, modelID, s.input.Rates)
if !s.isOfficial && providerKind != "" && modelID != "" {
rates := s.input.Rates
rates.Currency = s.currency
s.official, s.isOfficial = MatchesCatalog(providerKind, modelID, rates)
}
if s.isOfficial && s.quote.CatalogSource == "" {
s.quote.CatalogSource = s.official.DocURL
}
}
func (s *quoteBuildState) addTargetValuation(target string) {
if target == s.currency {
return
}
if s.addOfficialValuation(target) {
return
}
// Runtime FX has intentionally been removed. A custom price that does not
// match the official catalog remains in its original currency.
s.valuationFailures[target] = "display_unavailable"
}
func (s *quoteBuildState) addOfficialValuation(target string) bool {
if !s.isOfficial {
return false
}
peer, ok := LookupCatalog(
s.official.Provider, s.official.Model, target, s.official.BillingMode,
)
if !ok {
return false
}
s.quote.Valuations[target] = Valuation{
Money: MoneyOf(OriginalCostAmount(peer.RatesAt(s.occurred), s.input.Usage), target),
Basis: BasisOfficialTable, Source: peer.DocURL,
AsOf: s.occurred.UTC().Format("2006-01-02"),
}
return true
}
func (s *quoteBuildState) selectDisplay() {
if !s.quote.CostComplete {
s.quote.Selected = nil
s.quote.DisplayComplete = false
s.quote.Complete = false
s.quote.DisplayStatus = DisplayStatusUnavailable
return
}
display := quoteDisplayRequest(s.input).NormalizedCurrency()
if display == "" {
selected := s.quote.Original
s.quote.Selected = &selected
return
}
if valuation, ok := s.quote.Valuations[display]; ok {
selected := valuation.Money
s.quote.Selected = &selected
return
}
// The original price-book amount is still a valid cost fact. Keep it as a
// visible fallback and distinguish the display mismatch from no pricing.
selected := s.quote.Original
s.quote.Selected = &selected
s.quote.DisplayComplete = false
s.quote.Complete = false
s.quote.DisplayStatus = DisplayStatusFallbackOriginal
s.quote.IncompleteReason = firstNonEmpty(s.valuationFailures[display], "display_unavailable")
}
func (s *quoteBuildState) markIncomplete(reason string) {
s.quote.CostComplete = false
s.quote.Coverage = CoverageIncomplete
s.quote.DisplayComplete = false
s.quote.Complete = false
s.quote.DisplayStatus = DisplayStatusUnavailable
if s.quote.IncompleteReason == "" {
s.quote.IncompleteReason = reason
}
}
// SelectForDisplay returns the money for a display currency preference without
// recomputing rates. Empty display returns original.
func (q CostQuote) SelectForDisplay(display string) (Money, bool) {
display = NormalizeCurrency(display)
if display == "" {
return q.Original, true
}
if v, ok := q.Valuations[display]; ok {
return v.Money, true
}
if NormalizeCurrency(q.Original.Currency) == display {
return q.Original, true
}
return Money{}, false
}
// WithSelected returns a copy with Selected set for display.
func (q CostQuote) WithSelected(display string) CostQuote {
out := q
if m, ok := q.SelectForDisplay(display); ok {
out.Selected = &m
if quoteHasCompleteCostFact(q) {
out.Complete = true
out.IncompleteReason = ""
}
} else {
out.Selected = nil
out.Complete = false
if out.IncompleteReason == "" {
out.IncompleteReason = "display_unavailable"
}
}
return out
}
// LegacyCostFloat returns the selected (or original) amount as float64 for
// compatibility aliases cost / costUsd / total_cost.
func (q CostQuote) LegacyCostFloat() float64 {
if q.Selected != nil {
return q.Selected.Float64()
}
return q.Original.Float64()
}
// LegacyCurrencySymbol returns a display symbol for the selected/original currency.
func (q CostQuote) LegacyCurrencySymbol() string {
if q.Selected != nil && q.Selected.Currency != "" {
return CurrencySymbol(q.Selected.Currency)
}
return CurrencySymbol(q.Original.Currency)
}
// LegacyCurrencyCode returns the ISO code for selected/original.
func (q CostQuote) LegacyCurrencyCode() string {
if q.Selected != nil && q.Selected.Currency != "" {
return NormalizeCurrency(q.Selected.Currency)
}
return NormalizeCurrency(q.Original.Currency)
}
// AggregateQuotes combines occurrence-time quotes into a session/run total.
// Every entry must have the requested valuation; partial totals stay unavailable.
func AggregateQuotes(quotes []CostQuote, display string) CostQuote {
if len(quotes) == 0 {
return emptyAggregate(NormalizeCurrency(display))
}
accumulator := newQuoteAccumulator(display)
for _, quote := range quotes {
accumulator.add(quote)
}
return accumulator.finish()
}
type quoteAccumulator struct {
out CostQuote
display string
totals map[string]Amount
originalCurrency string
originalTotal Amount
originalTotals map[string]Amount
originalComplete bool
coverage string
displayComplete bool
modes map[string]struct{}
rateBands map[string]struct{}
unbanded bool
}
func emptyAggregate(display string) CostQuote {
out := CostQuote{
Original: MoneyOf(Zero, display), Valuations: map[string]Valuation{}, Estimated: true,
CostComplete: false, DisplayComplete: false, Complete: false,
Coverage: CoverageNone,
DisplayStatus: DisplayStatusUnavailable, AggregateMode: AggregateModeSingleCurrency,
IncompleteReason: "no_usage",
}
return out
}
func newQuoteAccumulator(display string) *quoteAccumulator {
return &quoteAccumulator{
out: CostQuote{Valuations: map[string]Valuation{}, Estimated: true},
display: NormalizeCurrency(display), totals: map[string]Amount{}, originalTotals: map[string]Amount{},
originalComplete: true, coverage: CoverageNone, displayComplete: true,
modes: map[string]struct{}{}, rateBands: map[string]struct{}{},
}
}
func (a *quoteAccumulator) add(quote CostQuote) {
quote = NormalizeQuote(quote)
a.coverage = FoldCoverage(a.coverage, quote.Coverage)
if quote.Coverage != CoverageComplete && a.out.IncompleteReason == "" {
a.out.IncompleteReason = quote.IncompleteReason
}
a.out.LegacyEstimate = a.out.LegacyEstimate || quote.LegacyEstimate
if quote.BillingMode != "" {
a.modes[quote.BillingMode] = struct{}{}
}
a.addRateBand(quote.RateBand)
if quote.RateDate > a.out.RateDate {
a.out.RateDate = quote.RateDate
}
originalCurrency := NormalizeCurrency(quote.Original.Currency)
if len(quote.OriginalTotals) > 0 {
for _, original := range quote.OriginalTotals {
a.addOriginal(original, NormalizeCurrency(original.Currency))
}
} else {
a.addOriginal(quote.Original, originalCurrency)
}
if a.display != "" {
_, found := quote.Valuations[a.display]
a.displayComplete = a.displayComplete && (found || originalCurrency == a.display)
}
originalIncluded := false
for code, valuation := range quote.Valuations {
code = NormalizeCurrency(code)
if code == originalCurrency {
originalIncluded = true
}
a.addValuation(code, valuation)
}
if originalCurrency != "" && !originalIncluded {
a.addValuation(originalCurrency, Valuation{
Money: quote.Original, Basis: BasisIdentity, Source: "aggregate",
AsOf: time.Now().UTC().Format("2006-01-02"),
})
}
}
func (a *quoteAccumulator) addRateBand(band string) {
switch band {
case RateBandPeak, RateBandOffPeak:
a.rateBands[band] = struct{}{}
case RateBandMixed:
a.rateBands[RateBandPeak], a.rateBands[RateBandOffPeak] = struct{}{}, struct{}{}
default:
a.unbanded = true
}
}
func (a *quoteAccumulator) rateBand() string {
if a.unbanded {
return ""
}
switch len(a.rateBands) {
case 1:
for band := range a.rateBands {
return band
}
case 2:
return RateBandMixed
}
return ""
}
func (a *quoteAccumulator) addOriginal(original Money, currency string) {
if currency != "" {
a.originalTotals[currency] = a.originalTotals[currency].Add(original.AmountValue())
}
if a.originalCurrency == "" {
a.originalCurrency = currency
a.originalTotal = original.AmountValue()
return
}
if currency != a.originalCurrency {
a.originalComplete = false
return
}
a.originalTotal = a.originalTotal.Add(original.AmountValue())
}
func (a *quoteAccumulator) addValuation(code string, valuation Valuation) {
if code == "" {
return
}
a.totals[code] = a.totals[code].Add(valuation.Money.AmountValue())
current, found := a.out.Valuations[code]
if !found {
current = valuation
}
current.Money = MoneyOf(a.totals[code], code)
current.Stale = current.Stale || valuation.Stale
a.out.Valuations[code] = current
}
// costFactsComplete reads the accumulated coverage rather than tracking the
// same fact a second time: two answers to one question drift.
func (a *quoteAccumulator) costFactsComplete() bool {
return a.coverage == CoverageComplete
}
func (a *quoteAccumulator) finish() CostQuote {
a.out.Coverage = a.coverage
a.out.CostComplete = a.costFactsComplete()
if a.originalComplete && a.originalCurrency != "" {
a.out.Original = MoneyOf(a.originalTotal, a.originalCurrency)
a.syncOriginalValuation()
} else {
a.out.Original = Money{Amount: "0"}
}
if len(a.originalTotals) > 1 {
codes := make([]string, 0, len(a.originalTotals))
for code := range a.originalTotals {
codes = append(codes, code)
}
slices.Sort(codes)
for _, code := range codes {
a.out.OriginalTotals = append(a.out.OriginalTotals, MoneyOf(a.originalTotals[code], code))
}
}
a.out.RateBand = a.rateBand()
if len(a.modes) == 1 {
for mode := range a.modes {
a.out.BillingMode = mode
}
}
if a.display == "" && a.originalComplete && a.costFactsComplete() {
selected := a.out.Original
a.out.Selected = &selected
a.out.DisplayComplete = true
a.out.Complete = true
a.out.DisplayStatus = DisplayStatusMatched
a.out.AggregateMode = AggregateModeSingleCurrency
return a.out
}
valuation, found := a.out.Valuations[a.display]
if found && a.displayComplete && a.costFactsComplete() {
selected := valuation.Money
a.out.Selected = &selected
a.out.DisplayComplete = true
a.out.Complete = true
a.out.DisplayStatus = DisplayStatusMatched
if len(a.originalTotals) > 1 {
a.out.AggregateMode = AggregateModeCommonValuation
} else {
a.out.AggregateMode = AggregateModeSingleCurrency
}
return a.out
}
a.out.DisplayComplete = false
a.out.Complete = false
if !a.costFactsComplete() {
a.out.DisplayStatus = DisplayStatusUnavailable
a.out.IncompleteReason = firstNonEmpty(a.out.IncompleteReason, "incomplete_cost_fact")
} else if a.originalComplete && a.originalCurrency != "" {
selected := a.out.Original
a.out.Selected = &selected
a.out.DisplayStatus = DisplayStatusFallbackOriginal
a.out.AggregateMode = AggregateModeSingleCurrency
a.out.IncompleteReason = firstNonEmpty(a.out.IncompleteReason, "display_unavailable")
} else {
a.out.DisplayStatus = DisplayStatusBucketed
a.out.AggregateMode = AggregateModeCurrencyBuckets
a.out.IncompleteReason = firstNonEmpty(a.out.IncompleteReason, "currency_buckets")
}
return a.out
}
// NormalizeQuote fills fields introduced after the first CostQuote wire shape.
// It is used at every persistence and transport boundary so old telemetry and
// old eventwire payloads remain safe without inventing a new amount.
func NormalizeQuote(q CostQuote) CostQuote {
if !q.CostComplete && !q.DisplayComplete && q.Complete {
q.CostComplete = true
q.DisplayComplete = true
}
if q.DisplayStatus != "" && q.DisplayStatus != DisplayStatusMatched && q.DisplayStatus != DisplayStatusFallbackOriginal && q.DisplayStatus != DisplayStatusBucketed && q.DisplayStatus != DisplayStatusUnavailable {
q.DisplayStatus = ""
}
if q.RateBand != RateBandPeak && q.RateBand != RateBandOffPeak && q.RateBand != RateBandMixed {
q.RateBand = ""
}
if q.AggregateMode != "" && q.AggregateMode != AggregateModeSingleCurrency && q.AggregateMode != AggregateModeCommonValuation && q.AggregateMode != AggregateModeCurrencyBuckets {
q.AggregateMode = ""
}
if q.DisplayStatus == "" {
switch {
case q.Complete:
q.DisplayStatus = DisplayStatusMatched
case q.Original.Currency != "" && q.Original.Amount != "" && q.IncompleteReason != "no_price":
q.DisplayStatus = DisplayStatusFallbackOriginal
default:
q.DisplayStatus = DisplayStatusUnavailable
}
}
if q.AggregateMode == "" {
if len(q.OriginalTotals) > 1 {
q.AggregateMode = AggregateModeCurrencyBuckets
} else if q.Selected != nil {
q.AggregateMode = AggregateModeSingleCurrency
}
}
if !ValidCoverage(q.Coverage) {
q.Coverage = CoverageIncomplete
if quoteHasCompleteCostFact(q) {
q.Coverage = CoverageComplete
}
}
q.Complete = q.DisplayComplete
return q
}
// quoteHasCompleteCostFact separates a known original-currency charge from a
// display-only valuation failure must not poison an exact original total,
// while unpriced and unrecoverable legacy records stay incomplete in every
// display currency.
func quoteHasCompleteCostFact(q CostQuote) bool {
if q.CostComplete {
return true
}
switch q.IncompleteReason {
case "no_price", "missing_price_or_usage", "legacy_unrecoverable",
"legacy_wiped_or_zero", "legacy_invalid_amount", "mixed_original_currencies",
"incomplete_cost_fact":
return false
}
if q.Complete {
return true
}
currency := NormalizeCurrency(q.Original.Currency)
if currency == "" {
return false
}
valuation, ok := q.Valuations[currency]
return ok && SameCurrency(valuation.Money.Currency, currency)
}
func (a *quoteAccumulator) syncOriginalValuation() {
valuation, found := a.out.Valuations[a.originalCurrency]
if !found {
valuation = Valuation{
Basis: BasisIdentity, Source: "aggregate",
AsOf: time.Now().UTC().Format("2006-01-02"),
}
}
valuation.Money = a.out.Original
a.out.Valuations[a.originalCurrency] = valuation
}
func firstNonEmpty(values ...string) string {
for _, v := range values {
if strings.TrimSpace(v) != "" {
return strings.TrimSpace(v)
}
}
return ""
}
// resolveCatalogIdentity names which official table, if any, may price a quote.
// The vendor comes only from ProviderKind, which the caller reads off the
// endpoint's host; guessing it from the entry's name or the model id would bill
// a relay as the vendor it resells. Unnamed means no official table — the user's
// own rate card, which is right rather than merely plausible.
func resolveCatalogIdentity(in QuoteInput) (provider, model string) {
provider = strings.ToLower(strings.TrimSpace(in.ProviderKind))
model = strings.TrimSpace(in.ModelID)
if model == "" {
ref := strings.TrimSpace(in.ModelRef)
if i := strings.LastIndex(ref, "/"); i >= 0 && i+1 < len(ref) {
model = ref[i+1:]
} else {
model = ref
}
}
return provider, model
}
@@ -0,0 +1,358 @@
// rates.go — every rate this project has shipped for a vendor's model, and the
// two questions that asks: what it costs now, and what it used to cost.
package pricing
import (
"slices"
"strings"
"time"
)
// rateGeneration is one published pair. Peak is nil for a generation the vendor
// billed at a single rate around the clock.
type rateGeneration struct {
Base RateCard
Peak *RateCard
// From is the first day (YYYY-MM-DD, UTC) this generation is billed; empty
// means from when it shipped. An announced change is appended with its date.
From string
}
// today is the day a dated generation is read against; a test replaces it.
var today = func() string { return time.Now().UTC().Format("2006-01-02") }
// currentIndex is the generation in force today: the last one whose date has
// come. Rows with no dated generation answer with their last, as before.
func (r officialRateRow) currentIndex() int {
day := today()
for i := len(r.Rates) - 1; i > 0; i-- {
if r.Rates[i].From == "" || r.Rates[i].From <= day {
return i
}
}
return 0
}
// officialRateRow is one vendor's model, in one currency, under one billing
// mode, with every rate this project has shipped for it.
type officialRateRow struct {
Provider string
Model string
Currency string
BillingMode string
Window *PeakWindow
DocURL string
Notes string
CheckedOn string
// Rates is oldest first; the last entry is what this costs today.
Rates []rateGeneration
}
// officialRates is the price book. A price change appends a generation rather
// than editing one: that is what leaves the superseded rate where a stored price
// can still be recognised as this project's own default instead of a number its
// user chose, which is the difference between refreshing one and losing it.
var officialRates = []officialRateRow{
{
Provider: "deepseek", Model: "deepseek-flash", Currency: "CNY",
BillingMode: BillingModePAYG, Window: deepseekPeak, DocURL: DocDeepSeekPricing, CheckedOn: deepseekCheckedOn,
Rates: []rateGeneration{
{Base: RateCard{CacheHit: 0.02, Input: 1, Output: 2, Currency: "CNY"}},
{Base: RateCard{CacheHit: 0.05, Input: 1.5, Output: 4.5, Currency: "CNY"},
Peak: &RateCard{CacheHit: 0.10, Input: 3.0, Output: 9.0, Currency: "CNY"}},
{Base: RateCard{CacheHit: 0.02, Input: 1, Output: 4, Currency: "CNY"},
Peak: &RateCard{CacheHit: 0.04, Input: 2, Output: 8, Currency: "CNY"}},
},
},
{
Provider: "deepseek", Model: "deepseek-flash", Currency: "USD",
BillingMode: BillingModePAYG, Window: deepseekPeak, DocURL: DocDeepSeekPricing, CheckedOn: deepseekCheckedOn,
Rates: []rateGeneration{
{Base: RateCard{CacheHit: 0.0028, Input: 0.14, Output: 0.28, Currency: "USD"}},
{Base: RateCard{CacheHit: 0.007, Input: 0.22, Output: 0.66, Currency: "USD"},
Peak: &RateCard{CacheHit: 0.014, Input: 0.44, Output: 1.32, Currency: "USD"}},
{Base: RateCard{CacheHit: 0.003, Input: 0.15, Output: 0.6, Currency: "USD"},
Peak: &RateCard{CacheHit: 0.006, Input: 0.3, Output: 1.2, Currency: "USD"}},
},
},
{
Provider: "deepseek", Model: "deepseek-v4-pro", Currency: "CNY",
BillingMode: BillingModePAYG, Window: deepseekPeak, DocURL: DocDeepSeekPricing, CheckedOn: deepseekCheckedOn,
Rates: []rateGeneration{
{Base: RateCard{CacheHit: 0.025, Input: 3, Output: 6, Currency: "CNY"}},
{Base: RateCard{CacheHit: 0.15, Input: 4.5, Output: 13.5, Currency: "CNY"},
Peak: &RateCard{CacheHit: 0.30, Input: 9.0, Output: 27.0, Currency: "CNY"}},
},
},
{
Provider: "deepseek", Model: "deepseek-v4-pro", Currency: "USD",
BillingMode: BillingModePAYG, Window: deepseekPeak, DocURL: DocDeepSeekPricing, CheckedOn: deepseekCheckedOn,
Rates: []rateGeneration{
{Base: RateCard{CacheHit: 0.003625, Input: 0.435, Output: 0.87, Currency: "USD"}},
{Base: RateCard{CacheHit: 0.022, Input: 0.66, Output: 1.98, Currency: "USD"},
Peak: &RateCard{CacheHit: 0.044, Input: 1.32, Output: 3.96, Currency: "USD"}},
},
},
// LongCat's launch discount, for which the vendor publishes no end date. List
// price is CNY 0.10/5/20, USD 0.015/0.75/2.95 — what these revert to, and why
// CheckedOn earns its place here.
{
Provider: "longcat", Model: "LongCat-2.0", Currency: "CNY",
BillingMode: BillingModePAYG, DocURL: DocLongCatPricingCNY, Notes: "launch_discount_no_end_date", CheckedOn: checkedOn,
Rates: []rateGeneration{{Base: RateCard{CacheHit: 0.04, Input: 2, Output: 8, Currency: "CNY"}}},
},
{
Provider: "longcat", Model: "LongCat-2.0", Currency: "USD",
BillingMode: BillingModePAYG, DocURL: DocLongCatPricingUSD, Notes: "launch_discount_no_end_date", CheckedOn: checkedOn,
Rates: []rateGeneration{{Base: RateCard{CacheHit: 0.006, Input: 0.30, Output: 1.20, Currency: "USD"}}},
},
// Google: the two flash models double on 2027-01-01, hence the dated generation.
// gemini-3.1-pro-preview is the rate up to 200k prompt tokens; gemini-4-argon has no price.
{
Provider: "google", Model: "gemini-3.8-flash", Currency: "USD",
BillingMode: BillingModePAYG, DocURL: DocGeminiPricing, CheckedOn: googleCheckedOn,
Rates: []rateGeneration{
{Base: RateCard{CacheHit: 0.075, Input: 0.75, Output: 3.75, Currency: "USD"}},
{From: "2027-01-01", Base: RateCard{CacheHit: 0.15, Input: 1.50, Output: 7.50, Currency: "USD"}},
},
},
{
Provider: "google", Model: "gemini-3.6-flash", Currency: "USD",
BillingMode: BillingModePAYG, DocURL: DocGeminiPricing, CheckedOn: googleCheckedOn,
Rates: []rateGeneration{
{Base: RateCard{CacheHit: 0.075, Input: 0.75, Output: 3.75, Currency: "USD"}},
{From: "2027-01-01", Base: RateCard{CacheHit: 0.15, Input: 1.50, Output: 7.50, Currency: "USD"}},
},
},
{
Provider: "google", Model: "gemini-3.5-flash-lite", Currency: "USD",
BillingMode: BillingModePAYG, DocURL: DocGeminiPricing, CheckedOn: googleCheckedOn,
Rates: []rateGeneration{{Base: RateCard{CacheHit: 0.03, Input: 0.30, Output: 2.50, Currency: "USD"}}},
},
{
Provider: "google", Model: "gemini-3.1-pro-preview", Currency: "USD",
BillingMode: BillingModePAYG, DocURL: DocGeminiPricing, Notes: "up_to_200k_prompt_tokens", CheckedOn: googleCheckedOn,
Rates: []rateGeneration{{Base: RateCard{CacheHit: 0.20, Input: 2.00, Output: 12.00, Currency: "USD"}}},
},
{
Provider: "anthropic", Model: "claude-sonnet-4-5", Currency: "USD",
BillingMode: BillingModePAYG, DocURL: DocAnthropicPricing, CheckedOn: anthropicCheckedOn,
Rates: []rateGeneration{{Base: RateCard{CacheHit: 0.30, Input: 3, Output: 15, CacheWrite: 3.75, Currency: "USD"}}},
},
// MiMo domestic PAYG; Token Plan bills the same rates as subscription_equivalent.
{
Provider: "mimo", Model: "mimo-v2.5-pro", Currency: "CNY",
BillingMode: BillingModePAYG, DocURL: DocMiMoPAYG, CheckedOn: checkedOn,
Rates: []rateGeneration{{Base: RateCard{CacheHit: 0.025, Input: 3, Output: 6, Currency: "CNY"}}},
},
{
Provider: "mimo", Model: "mimo-v2.5", Currency: "CNY",
BillingMode: BillingModePAYG, DocURL: DocMiMoPAYG, CheckedOn: checkedOn,
Rates: []rateGeneration{{Base: RateCard{CacheHit: 0.02, Input: 1, Output: 2, Currency: "CNY"}}},
},
// Not on the vendor's price page as of CheckedOn. Kept because removing a rate
// is what turns a still-configured model's cost silently into zero; a stale
// rate at least reads as a number somebody can question.
{
Provider: "mimo", Model: "mimo-v2-flash", Currency: "CNY",
BillingMode: BillingModePAYG, DocURL: DocMiMoPAYG, Notes: "not_on_vendor_page", CheckedOn: checkedOn,
Rates: []rateGeneration{{Base: RateCard{CacheHit: 0.07, Input: 0.70, Output: 2.10, Currency: "CNY"}}},
},
{
Provider: "mimo", Model: "mimo-v2.5-pro", Currency: "CNY",
BillingMode: BillingModeSubscriptionEquivalent, DocURL: DocMiMoTokenPlan, Notes: "payg_equivalent_not_plan_bill", CheckedOn: checkedOn,
Rates: []rateGeneration{{Base: RateCard{CacheHit: 0.025, Input: 3, Output: 6, Currency: "CNY"}}},
},
{
Provider: "mimo", Model: "mimo-v2.5", Currency: "CNY",
BillingMode: BillingModeSubscriptionEquivalent, DocURL: DocMiMoTokenPlan, Notes: "payg_equivalent_not_plan_bill", CheckedOn: checkedOn,
Rates: []rateGeneration{{Base: RateCard{CacheHit: 0.02, Input: 1, Output: 2, Currency: "CNY"}}},
},
{
Provider: "mimo", Model: "mimo-v2.6-pro", Currency: "CNY",
BillingMode: BillingModePAYG, DocURL: DocMiMoPAYG, CheckedOn: mimoCheckedOn,
Rates: []rateGeneration{{Base: RateCard{CacheHit: 0.025, Input: 3, Output: 6, Currency: "CNY"}}},
},
{
Provider: "mimo", Model: "mimo-v2.6-flash", Currency: "CNY",
BillingMode: BillingModePAYG, DocURL: DocMiMoPAYG, CheckedOn: mimoCheckedOn,
Rates: []rateGeneration{{Base: RateCard{CacheHit: 0.02, Input: 1, Output: 2, Currency: "CNY"}}},
},
{
Provider: "mimo", Model: "mimo-v2.6-pro-ultraspeed", Currency: "CNY",
BillingMode: BillingModePAYG, DocURL: DocMiMoPAYG, CheckedOn: mimoCheckedOn,
Rates: []rateGeneration{{Base: RateCard{CacheHit: 0.25, Input: 30, Output: 60, Currency: "CNY"}}},
},
{
Provider: "mimo", Model: "mimo-v2.6-pro", Currency: "CNY",
BillingMode: BillingModeSubscriptionEquivalent, DocURL: DocMiMoTokenPlan, Notes: "payg_equivalent_not_plan_bill", CheckedOn: mimoCheckedOn,
Rates: []rateGeneration{{Base: RateCard{CacheHit: 0.025, Input: 3, Output: 6, Currency: "CNY"}}},
},
{
Provider: "mimo", Model: "mimo-v2.6-flash", Currency: "CNY",
BillingMode: BillingModeSubscriptionEquivalent, DocURL: DocMiMoTokenPlan, Notes: "payg_equivalent_not_plan_bill", CheckedOn: mimoCheckedOn,
Rates: []rateGeneration{{Base: RateCard{CacheHit: 0.02, Input: 1, Output: 2, Currency: "CNY"}}},
},
}
// retiredDeepSeekFlashModels are names DeepSeek still answers as its current
// flash model, at that model's price. Owned here because that is where the rates
// they bill at live; config reads them through the accessor below.
var retiredDeepSeekFlashModels = []string{"deepseek-v4-flash", "deepseek-v4-flash-vision-exp"}
// RetiredDeepSeekFlashModels copies the list on the way out: a caller sorting or
// trimming the result would otherwise change what every other caller sees.
func RetiredDeepSeekFlashModels() []string {
return append([]string(nil), retiredDeepSeekFlashModels...)
}
// RateModelFor answers which model's rate a name is billed at — a retired name
// at the model that replaced it, an older spelling at the one it was renamed
// from. Scoped to billing on purpose: two models can share a rate without being
// the same model, and nothing outside a price lookup may read this as identity.
func RateModelFor(provider, model string) string {
model = strings.TrimSpace(model)
switch strings.ToLower(strings.TrimSpace(provider)) {
case "deepseek":
if slices.Contains(retiredDeepSeekFlashModels, model) {
return "deepseek-flash"
}
case "mimo":
switch model {
case "mimo-v2-pro":
return "mimo-v2.5-pro"
case "mimo-v2-omni":
return "mimo-v2.5"
}
}
return model
}
// DefaultCurrency is what a vendor's rates are quoted in when an entry does not
// say. A vendor publishing one currency leaves no choice to make; DeepSeek
// prices in both, and USD is what an entry that never chose has been read as.
func DefaultCurrency(provider string) string {
provider = strings.ToLower(strings.TrimSpace(provider))
seen := map[string]bool{}
for _, row := range officialRates {
if row.Provider == provider {
seen[row.Currency] = true
}
}
if len(seen) == 1 {
for currency := range seen {
return currency
}
}
return "USD"
}
// officialRateRowsFor finds the rows for a model, which is more than one when a
// vendor publishes the same rates under two billing modes.
func officialRateRowsFor(provider, model, currency string) []officialRateRow {
provider, currency = strings.ToLower(strings.TrimSpace(provider)), NormalizeCurrency(currency)
model = RateModelFor(provider, model)
var out []officialRateRow
for _, row := range officialRates {
if row.Provider == provider && row.Model == model && row.Currency == currency {
out = append(out, row)
}
}
return out
}
// CurrentRate is what a vendor's model costs in a currency today — the off-peak
// card, which is what a caller charging one rate should quote. Nil when this
// catalog prices neither. A model published under two billing modes quotes the
// pay-as-you-go rate, which is the one an unsubscribed caller is charged.
func CurrentRate(provider, model, currency string) *RateCard {
for _, row := range officialRateRowsFor(provider, model, currency) {
if row.BillingMode == BillingModeSubscriptionEquivalent {
continue
}
if len(row.Rates) > 0 {
rate := row.Rates[row.currentIndex()].Base
return &rate
}
}
return nil
}
// IsOfficialCard reports whether card is a rate this project has shipped for the
// model in the card's own currency: any generation, its off-peak or its peak
// card. A card that is none of them is the user's, or damaged, and cannot be
// valued against the vendor's other-currency table.
func IsOfficialCard(provider, model string, card RateCard) bool {
for _, row := range officialRateRowsFor(provider, model, card.Currency) {
if row.BillingMode == BillingModeSubscriptionEquivalent {
continue
}
for _, generation := range row.Rates {
if sameCard(generation.Base, card) || generation.Peak != nil && sameCard(*generation.Peak, card) {
return true
}
}
}
return false
}
func sameCard(a, b RateCard) bool {
return a.CacheHit == b.CacheHit && a.Input == b.Input && a.Output == b.Output && a.CacheWrite == b.CacheWrite
}
// SupersededRates are the rates shipped for a model before the current one. A
// stored price matching one came from this project rather than from its user, so
// a refresh may replace it; anything else is theirs and is left alone.
func SupersededRates(provider, model, currency string) []RateCard {
var out []RateCard
for _, row := range officialRateRowsFor(provider, model, currency) {
if row.BillingMode == BillingModeSubscriptionEquivalent || len(row.Rates) < 2 {
continue
}
for _, generation := range row.Rates[:row.currentIndex()] {
out = append(out, generation.Base)
}
}
return out
}
// officialCatalogRows publishes the current generation of every row, plus the
// retired DeepSeek names, which bill at the flash rate under their own id.
func officialCatalogRows() []CatalogEntry {
rows := make([]CatalogEntry, 0, len(officialRates)+len(retiredDeepSeekFlashModels)*2)
for _, row := range officialRates {
if len(row.Rates) == 0 {
continue
}
current := row.Rates[row.currentIndex()]
rows = append(rows, row.entry(row.Model, current))
if row.Provider != "deepseek" || row.Model != "deepseek-flash" {
continue
}
for _, retired := range retiredDeepSeekFlashModels {
rows = append(rows, row.entry(retired, current))
}
}
return rows
}
func (r officialRateRow) entry(model string, current rateGeneration) CatalogEntry {
// Copied, so a caller holding a returned row cannot reach back into the
// table every other caller reads.
var peak *RateCard
if current.Peak != nil {
card := *current.Peak
peak = &card
}
return CatalogEntry{
Provider: r.Provider, Model: model, Currency: r.Currency,
CacheHit: current.Base.CacheHit, Input: current.Base.Input, Output: current.Base.Output,
CacheWrite: current.Base.CacheWrite,
Peak: peak, Window: r.Window, DocURL: r.DocURL,
BillingMode: r.BillingMode, Notes: r.Notes, CheckedOn: r.CheckedOn,
}
}
@@ -0,0 +1,12 @@
prompt = """Some providers bill tokens written into their prompt cache at a rate of their own. Add that to the pricing contract in this repository:
- `RateCard` gains a `CacheWrite` rate, per 1M tokens like the others. Zero means the card has no separate cache-write price, and cache-written tokens keep being charged at `Input` exactly as they are today.
- When a card sets `CacheWrite`, `OriginalCostAmount` charges the billed cache-write units (`CacheWriteBilledTokens` when set, otherwise `CacheWriteTokens`) at `CacheWrite` instead of `Input`.
- `PricingFingerprint` must tell apart two cards that differ only in `CacheWrite`, but a card without one must keep exactly the fingerprint it has today: stored ledgers are keyed by it.
- Two cards that differ only in `CacheWrite` are different cards everywhere cards are compared, including official-card recognition and catalog matching.
- `CatalogEntry` carries the rate too, and `RateCardFromCatalog` and `CatalogEntry.RatesAt` hand it back.
- Add Anthropic to the official price book: `claude-sonnet-4-5`, pay-as-you-go in USD, cache hit 0.30, input 3, output 15 and cache write 3.75 per 1M tokens, documented at https://www.anthropic.com/pricing and checked 2026-10-19. `api.anthropic.com` is Anthropic's own endpoint.
- `benchctl cost` gains a `-write` flag for cache-written tokens, and `benchctl rate` shows a card's cache-write rate when it has one.
- Add tests for the new behaviour and update the README's pricing section."""
class = "multi-file-feature"
timeout_sec = 1200
+119
View File
@@ -0,0 +1,119 @@
#!/usr/bin/env bash
set -e
export GOFLAGS=-mod=mod GOTOOLCHAIN=local
cat > internal/contract/pricing/zz_grader_test.go <<'GO'
package pricing
import (
"testing"
"time"
)
func TestGraderCacheWriteCost(t *testing.T) {
card := RateCard{CacheHit: 0.3, Input: 3, Output: 15, Currency: "USD"}
priced := card
priced.CacheWrite = 3.75
cases := []struct {
name string
card RateCard
usage UsageTokens
want string
}{
{"no write, no rate", card, UsageTokens{CacheHitTokens: 200000, CacheMissTokens: 800000, CompletionTokens: 50000}, "3.21"},
{"no write, rate set", priced, UsageTokens{CacheHitTokens: 200000, CacheMissTokens: 800000, CompletionTokens: 50000}, "3.21"},
{"write at input", card, UsageTokens{CacheMissTokens: 1000000, CacheWriteTokens: 400000, CompletionTokens: 100000}, "4.5"},
{"write at cache-write", priced, UsageTokens{CacheMissTokens: 1000000, CacheWriteTokens: 400000, CompletionTokens: 100000}, "4.8"},
{"billed units at input", card, UsageTokens{CacheMissTokens: 1000000, CacheWriteTokens: 400000, CacheWriteBilledTokens: 500000}, "3.3"},
{"billed units at cache-write", priced, UsageTokens{CacheMissTokens: 1000000, CacheWriteTokens: 400000, CacheWriteBilledTokens: 500000}, "3.675"},
}
for _, tc := range cases {
if got := OriginalCostAmount(tc.card, tc.usage).String(); got != tc.want {
t.Errorf("%s: cost = %s, want %s", tc.name, got, tc.want)
}
}
}
func TestGraderFingerprintKeepsStoredKeys(t *testing.T) {
cny := RateCard{CacheHit: 0.02, Input: 1, Output: 4, Currency: "CNY"}
usd := RateCard{CacheHit: 0.3, Input: 3, Output: 15, Currency: "USD"}
if got := PricingFingerprint(cny); got != "ba96037de26c3f79" {
t.Errorf("fingerprint of a card without CacheWrite moved: %s", got)
}
if got := PricingFingerprint(usd); got != "e16b53b4599e5fda" {
t.Errorf("fingerprint of a card without CacheWrite moved: %s", got)
}
withWrite := usd
withWrite.CacheWrite = 3.75
if PricingFingerprint(withWrite) == PricingFingerprint(usd) {
t.Error("cards differing only in CacheWrite share a fingerprint")
}
other := usd
other.CacheWrite = 6
if PricingFingerprint(withWrite) == PricingFingerprint(other) {
t.Error("two different CacheWrite rates share a fingerprint")
}
}
func TestGraderCacheWriteIsPartOfCardIdentity(t *testing.T) {
official := RateCard{CacheHit: 0.02, Input: 1, Output: 2, Currency: "CNY"}
if !IsOfficialCard("mimo", "mimo-v2.5", official) {
t.Fatal("the official mimo-v2.5 card is no longer recognised")
}
changed := official
changed.CacheWrite = 1.25
if IsOfficialCard("mimo", "mimo-v2.5", changed) {
t.Error("a card with a cache-write rate the vendor never published is recognised as official")
}
if _, ok := MatchesCatalog("mimo", "mimo-v2.5", official); !ok {
t.Fatal("the official mimo-v2.5 card no longer matches the catalog")
}
if _, ok := MatchesCatalog("mimo", "mimo-v2.5", changed); ok {
t.Error("a card with an unpublished cache-write rate matches the catalog")
}
}
func TestGraderAnthropicIsPriced(t *testing.T) {
want := RateCard{CacheHit: 0.30, Input: 3, Output: 15, CacheWrite: 3.75, Currency: "USD"}
got := CurrentRate("anthropic", "claude-sonnet-4-5", "USD")
if got == nil || *got != want {
t.Fatalf("CurrentRate(anthropic, claude-sonnet-4-5) = %+v, want %+v", got, want)
}
if !IsOfficialCard("anthropic", "claude-sonnet-4-5", want) {
t.Error("the Anthropic card is not recognised as official")
}
e, ok := LookupCatalog("anthropic", "claude-sonnet-4-5", "USD", "")
if !ok || e.DocURL != "https://www.anthropic.com/pricing" || e.CheckedOn != "2026-10-19" || e.CacheWrite != 3.75 {
t.Errorf("catalog entry = %+v, %v", e, ok)
}
if got := OfficialProviderForEndpoint("https://api.anthropic.com/v1/messages"); got != "anthropic" {
t.Errorf("api.anthropic.com resolves to %q", got)
}
if got := DefaultCurrency("anthropic"); got != "USD" {
t.Errorf("DefaultCurrency(anthropic) = %q", got)
}
}
func TestGraderCatalogCarriesCacheWrite(t *testing.T) {
e := CatalogEntry{Provider: "x", Model: "y", Currency: "USD", CacheHit: 0.1, Input: 1, Output: 2, CacheWrite: 1.25}
if got := RateCardFromCatalog(e); got.CacheWrite != 1.25 || got.Input != 1 {
t.Errorf("RateCardFromCatalog = %+v", got)
}
if got := e.RatesAt(time.Date(2026, 3, 4, 12, 0, 0, 0, time.UTC)); got.CacheWrite != 1.25 {
t.Errorf("RatesAt = %+v", got)
}
}
GO
gofmt_out="$(gofmt -l .)"
[ -z "$gofmt_out" ] || { echo "gofmt needed: $gofmt_out"; exit 1; }
go vet ./...
go test ./... >/dev/null
out="$(go run ./cmd/benchctl cost -provider mimo -model mimo-v2.5 -miss 1000000 -write 400000)"
[ "$out" = "1 CNY" ] || { echo "benchctl cost -write: got $out"; exit 1; }
out="$(go run ./cmd/benchctl rate -provider anthropic -model claude-sonnet-4-5)"
case "$out" in *"cache-write 3.75"*) ;; *) echo "benchctl rate does not show the cache-write rate: $out"; exit 1 ;; esac
out="$(go run ./cmd/benchctl rate -provider mimo -model mimo-v2.5)"
case "$out" in *"cache-write"*) echo "benchctl rate shows a cache-write rate the card does not have: $out"; exit 1 ;; esac
grep -lE 'CacheWrite:' internal/contract/pricing/*_test.go | grep -qv zz_grader_test.go \
|| { echo "no test of the pricing package sets a CacheWrite rate"; exit 1; }
awk '/^## Pricing/{on=1; next} /^## /{on=0} on' README.md | grep -q 'CacheWrite' \
|| { echo "README's pricing section does not mention CacheWrite"; exit 1; }
@@ -0,0 +1,18 @@
---
name: price-change
description: Record a vendor's announced price change in the official price book without losing the rate it replaces.
---
# Recording a price change
1. Find the vendor's rows in `officialRates` in
`internal/contract/pricing/rates.go`. Each row is one model in one currency.
2. **Append** a `rateGeneration` to the row's `Rates`; never edit an existing
one. Set `From` to the first billed day (`YYYY-MM-DD`, UTC) when the change
is announced ahead of time.
3. Leave the earlier generation where it is: `SupersededRates` reads it so a
stored price that matches it is recognised as ours and refreshed.
4. Update the row's `CheckedOn` constant to the day you confirmed the page.
5. Add a test beside `rates_test.go` that pins `CurrentRate` before and after
the `From` day by replacing `today`.
6. Run `make check`. `make pricecheck` needs the network and is run by hand.
@@ -0,0 +1,17 @@
.PHONY: check test vet fmt pricecheck
check: fmt vet test
fmt:
@out="$$(gofmt -l .)"; if [ -n "$$out" ]; then echo "gofmt needed:"; echo "$$out"; exit 1; fi
vet:
go vet ./...
test:
go test ./...
# Reads each vendor's pricing page; this snapshot carries no network client.
pricecheck:
@echo "pricecheck: reaches each vendor's pricing page and needs the network; not available in this snapshot" >&2
@exit 2
@@ -0,0 +1,69 @@
# Reasonix kernel snapshot
A trimmed slice of the Reasonix Go kernel: the contracts a benchmark report is
priced and labelled with, a few leaf utilities, and `benchctl`, a small command
that prints what those contracts answer.
```bash
make check # gofmt, vet, test
go run ./cmd/benchctl rate -model deepseek-flash -currency USD
go run ./cmd/benchctl cost -provider mimo -model mimo-v2.5 -miss 1000000
go run ./cmd/benchctl tariff -currency CNY -at 2026-10-12T02:00:00Z
go run ./cmd/benchctl arm -ablate planner,evidence
```
## Packages
| Package | Owns |
| --- | --- |
| `internal/contract/pricing` | the official price book, cost quotes, the spend ledger |
| `internal/contract/ablation` | which subsystems a benchmark arm switches off, and the arm's name |
| `internal/contract/agentgraph` | the vocabulary of one run's execution graph |
| `internal/base/retrieval` | BM25 ranking over CJK-aware tokens |
| `internal/base/rulelist` | splitting a one-line permission rule list |
| `internal/base/tokencount` | a cheap token estimate |
| `internal/base/neterr` | classifying transport errors |
| `internal/base/nilutil` | typed-nil detection |
| `cmd/benchctl` | prints rates, costs, peak schedules and arm names |
## Pricing
A `RateCard` is a price per million tokens in one currency:
| Field | Charges |
| --- | --- |
| `CacheHit` | prompt tokens served from the provider's cache |
| `Input` | prompt tokens that missed the cache, including tokens written to it |
| `Output` | completion tokens |
`OriginalCostAmount` turns a card and a `UsageTokens` breakdown into a
fixed-point `Amount`; `PricingFingerprint` keys a card in the ledger, so a
stored ledger entry stays attached to the card it was priced with.
`CurrentRate` and `SupersededRates` answer from `officialRates`, which only
ever grows: a price change appends a generation.
## Ablation
`ablation.Parse` reads a comma-separated list of modules to switch off, and
`Set.Arm` names the result (`full` for the control). The switchable modules,
in the order arm names use:
| Module | Off means |
| --- | --- |
| `evidence` | no evidence gate on completion claims |
| `planner` | no two-model planning turn |
| `subagent` | no delegation to workers |
| `retrieval` | no retrieval over the folded transcript |
| `compaction` | no context folding |
| `upstream` | a dependency edge orders its endpoints without delivering the answer |
| `recall-search` | recall can read a given address but cannot search for one |
The fold-index scale is a separate axis (`-fold-index`), not a module.
## Skills
Project skills live under `.reasonix/skills/<name>/SKILL.md`.
| Skill | Use it to |
| --- | --- |
| `price-change` | record a vendor's price change in the price book |
@@ -0,0 +1,51 @@
# Reasonix project memory
This checkout is a trimmed snapshot of the Reasonix kernel: the billing,
ablation and graph contracts and a handful of leaf utilities, plus one small
command that drives them. The conventions below are the project's own.
## Conventions
- Go kernel under `internal/`; each package owns one concern. A package's long
explanation belongs in its `doc.go`, not spread across implementation files.
- Judgements read structure, never wording: types, contracts, schemas. An
error a caller must tell apart carries an identity — a sentinel, a typed
code — never a sentence.
- A metric is its definition, not its name. Before a number becomes a finding,
state what it counts, from which anchor, over which population, and what it
excludes.
## Comments
Default is none — the code is the truth. Write one only when the **why** is
non-obvious: a hidden constraint, an invariant the type system cannot express,
or an external-protocol quirk. A comment states the constraint that holds now;
it does not narrate how the code got here. English, always.
- Declaration doc: ≤5 lines. Package comment: ≤8 lines, or ≤40 in a `doc.go`.
- Every other comment: ≤3 lines. Struct-field and trailing `//`: 1 line.
- One responsibility per file; 800 lines is the ceiling.
## Vendor rates
Official list prices live in one place: `officialRates` in
`internal/contract/pricing/rates.go`, oldest generation first, the last being
what a model charges today. A price change is an **append, never an edit** —
the superseded rate is what lets an installed config be recognised as holding
one of ours and brought forward. Callers read these through
`pricing.CurrentRate` and `pricing.SupersededRates` and restate none of them.
`make pricecheck` compares that table against each vendor's own page. It needs
the network, so it is not part of `make check`.
## Before every commit
```bash
make check # gofmt -l, go vet ./..., go test ./...
```
## Change discipline
List callers, consumers of the same state and sibling features before editing.
Fix at the owning layer; a fix that breaks a neighbouring behaviour is a failed
fix. Write tests first; they must fail without the change.
@@ -0,0 +1,115 @@
// Command benchctl prints what the pricing and ablation contracts answer for
// one model or one benchmark arm, so a reviewer can check a rate or an arm name
// without writing a test.
package main
import (
"errors"
"flag"
"fmt"
"io"
"os"
"reasonix/internal/contract/ablation"
"reasonix/internal/contract/pricing"
)
var errUsage = errors.New("usage: benchctl rate|cost|tariff|arm [flags]")
func main() {
if err := run(os.Args[1:], os.Stdout); err != nil {
fmt.Fprintln(os.Stderr, "benchctl:", err)
os.Exit(2)
}
}
func run(args []string, out io.Writer) error {
if len(args) == 0 {
return errUsage
}
switch args[0] {
case "rate":
return runRate(args[1:], out)
case "cost":
return runCost(args[1:], out)
case "tariff":
return runTariff(args[1:], out)
case "arm":
return runArm(args[1:], out)
default:
return fmt.Errorf("%w: unknown subcommand %q", errUsage, args[0])
}
}
func runRate(args []string, out io.Writer) error {
fs := flag.NewFlagSet("rate", flag.ContinueOnError)
fs.SetOutput(out)
provider := fs.String("provider", "deepseek", "vendor")
model := fs.String("model", "deepseek-flash", "model id")
currency := fs.String("currency", "", "currency; empty reads the vendor's default")
if err := fs.Parse(args); err != nil {
return err
}
cur := *currency
if cur == "" {
cur = pricing.DefaultCurrency(*provider)
}
card := pricing.CurrentRate(*provider, *model, cur)
if card == nil {
return fmt.Errorf("no official rate for %s/%s in %s", *provider, *model, cur)
}
fmt.Fprintf(out, "%s/%s %s: cache-hit %g input %g output %g per 1M\n",
*provider, *model, card.Currency, card.CacheHit, card.Input, card.Output)
if entry, ok := pricing.LookupCatalog(*provider, *model, cur, ""); ok && entry.DocURL != "" {
fmt.Fprintf(out, "source: %s\n", entry.DocURL)
}
fmt.Fprintf(out, "superseded generations: %d\n", len(pricing.SupersededRates(*provider, *model, cur)))
return nil
}
func runCost(args []string, out io.Writer) error {
fs := flag.NewFlagSet("cost", flag.ContinueOnError)
fs.SetOutput(out)
provider := fs.String("provider", "deepseek", "vendor")
model := fs.String("model", "deepseek-flash", "model id")
currency := fs.String("currency", "", "currency; empty reads the vendor's default")
hit := fs.Int("hit", 0, "cached prompt tokens")
miss := fs.Int("miss", 0, "uncached prompt tokens")
completion := fs.Int("completion", 0, "completion tokens")
if err := fs.Parse(args); err != nil {
return err
}
cur := *currency
if cur == "" {
cur = pricing.DefaultCurrency(*provider)
}
card := pricing.CurrentRate(*provider, *model, cur)
if card == nil {
return fmt.Errorf("no official rate for %s/%s in %s", *provider, *model, cur)
}
usage := pricing.UsageTokens{CacheHitTokens: *hit, CacheMissTokens: *miss, CompletionTokens: *completion}
amount := pricing.OriginalCostAmount(*card, usage)
fmt.Fprintf(out, "%s %s\n", amount, pricing.NormalizeCurrency(card.Currency))
return nil
}
func runArm(args []string, out io.Writer) error {
fs := flag.NewFlagSet("arm", flag.ContinueOnError)
fs.SetOutput(out)
spec := fs.String("ablate", "", "comma-separated modules to switch off: "+ablation.ModuleList())
foldIndex := fs.String("fold-index", "", "fold-index scale: default, half, quarter or off")
list := fs.Bool("list", false, "list the switchable modules")
if err := fs.Parse(args); err != nil {
return err
}
if *list {
fmt.Fprintln(out, ablation.ModuleList())
return nil
}
set, err := ablation.ParseArm(*spec, *foldIndex)
if err != nil {
return err
}
fmt.Fprintln(out, set.Arm())
return nil
}
@@ -0,0 +1,65 @@
package main
import (
"bytes"
"errors"
"strings"
"testing"
)
func runOut(t *testing.T, args ...string) string {
t.Helper()
var out bytes.Buffer
if err := run(args, &out); err != nil {
t.Fatalf("run %v: %v", args, err)
}
return out.String()
}
func TestRateNamesTheCurrentCard(t *testing.T) {
got := runOut(t, "rate", "-provider", "mimo", "-model", "mimo-v2.5")
if !strings.Contains(got, "mimo/mimo-v2.5 CNY: cache-hit 0.02 input 1 output 2 per 1M") {
t.Fatalf("rate output = %q", got)
}
}
func TestRateRefusesAnUnpricedModel(t *testing.T) {
var out bytes.Buffer
if err := run([]string{"rate", "-provider", "nobody", "-model", "x"}, &out); err == nil {
t.Fatal("an unpriced model must fail")
}
}
func TestCostChargesTheCurrentCard(t *testing.T) {
got := runOut(t, "cost", "-provider", "mimo", "-model", "mimo-v2.5", "-miss", "1000000", "-completion", "1000000")
if strings.TrimSpace(got) != "3 CNY" {
t.Fatalf("cost output = %q", got)
}
}
func TestArmNamesTheConfiguration(t *testing.T) {
if got := strings.TrimSpace(runOut(t, "arm")); got != "full" {
t.Fatalf("control arm = %q", got)
}
if got := strings.TrimSpace(runOut(t, "arm", "-ablate", "planner,evidence", "-fold-index", "half")); got != "no-evidence+no-planner+index-half" {
t.Fatalf("arm = %q", got)
}
}
func TestUnknownSubcommandIsAUsageError(t *testing.T) {
var out bytes.Buffer
if err := run([]string{"nope"}, &out); !errors.Is(err, errUsage) {
t.Fatalf("err = %v, want errUsage", err)
}
}
func TestTariffReadsTheVendorsSchedule(t *testing.T) {
got := runOut(t, "tariff", "-provider", "mimo", "-model", "mimo-v2.5")
if !strings.Contains(got, "official card: true") || !strings.Contains(got, "one rate around the clock") {
t.Fatalf("tariff output = %q", got)
}
peak := runOut(t, "tariff", "-currency", "CNY", "-at", "2026-10-12T02:00:00Z")
if !strings.Contains(peak, "peak=true until 2026-10-12T04:00:00Z") {
t.Fatalf("deepseek tariff at a weekday peak = %q", peak)
}
}
@@ -0,0 +1,49 @@
package main
import (
"flag"
"fmt"
"io"
"time"
"reasonix/internal/contract/pricing"
)
func runTariff(args []string, out io.Writer) error {
fs := flag.NewFlagSet("tariff", flag.ContinueOnError)
fs.SetOutput(out)
provider := fs.String("provider", "deepseek", "vendor")
model := fs.String("model", "deepseek-flash", "model id")
currency := fs.String("currency", "", "currency; empty reads the vendor's default")
at := fs.String("at", "", "instant to read the schedule at, RFC 3339; empty is now")
if err := fs.Parse(args); err != nil {
return err
}
when := time.Now()
if *at != "" {
t, err := time.Parse(time.RFC3339, *at)
if err != nil {
return fmt.Errorf("-at: %w", err)
}
when = t
}
cur := *currency
if cur == "" {
cur = pricing.DefaultCurrency(*provider)
}
card := pricing.CurrentRate(*provider, *model, cur)
if card == nil {
return fmt.Errorf("no official rate for %s/%s in %s", *provider, *model, cur)
}
fmt.Fprintf(out, "official card: %v\n", pricing.IsOfficialCard(*provider, *model, *card))
tariff, ok := pricing.LiveTariff(*provider, *model, *card, when)
switch {
case !ok:
fmt.Fprintln(out, "schedule: one rate around the clock")
case !tariff.Changes:
fmt.Fprintf(out, "schedule: peak=%v, no change ahead\n", tariff.Peak)
default:
fmt.Fprintf(out, "schedule: peak=%v until %s\n", tariff.Peak, tariff.ChangesAt.UTC().Format(time.RFC3339))
}
return nil
}
@@ -0,0 +1,3 @@
module reasonix
go 1.26.0
@@ -0,0 +1,7 @@
//go:build !windows
package neterr
import "syscall"
var resetErrors = []error{syscall.ECONNRESET, syscall.ECONNABORTED, syscall.EPIPE}
@@ -0,0 +1,8 @@
package neterr
import "syscall"
// syscall.ECONNRESET and friends exist on Windows only as APPLICATION_ERROR
// placeholders, so errors.Is against them never matches a real socket failure.
// Winsock's own numbering carries the identities the runtime returns.
var resetErrors = []error{syscall.WSAECONNRESET, syscall.WSAECONNABORTED, syscall.ERROR_BROKEN_PIPE}
@@ -0,0 +1,47 @@
// Package neterr answers what a transport error is from the identity the
// operating system gave it, never from the sentence it rendered. A system
// error's text arrives in the machine's own language and is reworded between
// releases, so a word list that matches on an English host silently stops
// matching elsewhere. The judgement lives here once because three packages
// were each carrying their own copy of it.
package neterr
import (
"context"
"errors"
"io"
"net"
)
// IsConnReset reports whether err is a connection-level drop (peer reset,
// truncated body, closed socket) as opposed to a protocol or caller error. A
// stream cut this way mid-body can be replayed from scratch, unlike a decode
// or 4xx error. The common trigger is a local proxy (v2rayN/sing-box)
// idle-closing a long-lived SSE connection during a reasoner's first-token gap.
func IsConnReset(err error) bool {
if err == nil {
return false
}
if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
return false
}
if errors.Is(err, io.ErrUnexpectedEOF) || errors.Is(err, io.EOF) ||
errors.Is(err, net.ErrClosed) || isAny(err, resetErrors) {
return true
}
var dnsErr *net.DNSError
if errors.As(err, &dnsErr) {
return false
}
var netErr net.Error
return errors.As(err, &netErr)
}
func isAny(err error, targets []error) bool {
for _, target := range targets {
if errors.Is(err, target) {
return true
}
}
return false
}
@@ -0,0 +1,43 @@
package neterr
import (
"context"
"errors"
"fmt"
"io"
"net"
"testing"
)
func TestIsConnReset(t *testing.T) {
if IsConnReset(nil) {
t.Error("nil is not a conn reset")
}
if IsConnReset(context.Canceled) || IsConnReset(context.DeadlineExceeded) {
t.Error("ctx cancel/deadline must not look like a recoverable reset")
}
if IsConnReset(errors.New("decode stream: invalid character")) {
t.Error("a plain protocol error must not be treated as a conn reset")
}
for _, err := range []error{
io.ErrUnexpectedEOF,
&net.OpError{Op: "read", Err: resetErrors[0]},
fmt.Errorf("read stream: %w", &net.OpError{Op: "read", Err: errors.New("wsarecv: forcibly closed")}),
} {
if !IsConnReset(err) {
t.Errorf("want conn reset for %v", err)
}
}
}
func TestIsConnResetExcludesDNS(t *testing.T) {
for _, dns := range []*net.DNSError{
{Err: "no such host", Name: "api.deepseek.com", IsNotFound: true},
{Err: "i/o timeout", Name: "api.deepseek.com", IsTimeout: true, IsTemporary: true},
} {
err := fmt.Errorf("dial: %w", &net.OpError{Op: "dial", Err: dns})
if IsConnReset(err) {
t.Errorf("DNS failure %v is not a connection reset", dns)
}
}
}
@@ -0,0 +1,17 @@
package nilutil
import "reflect"
// IsNil reports whether v is nil or an interface holding a typed nil value.
func IsNil(v any) bool {
if v == nil {
return true
}
rv := reflect.ValueOf(v)
switch rv.Kind() {
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice:
return rv.IsNil()
default:
return false
}
}
@@ -0,0 +1,31 @@
package nilutil
import "testing"
type sampleInterface interface {
Do()
}
type sampleImpl struct{}
func (*sampleImpl) Do() {}
func TestIsNilDetectsTypedNilInterface(t *testing.T) {
var p *sampleImpl
var i sampleInterface = p
if !IsNil(i) {
t.Fatal("IsNil should detect typed nil interface")
}
}
func TestIsNilRejectsConcreteValues(t *testing.T) {
var p sampleInterface = &sampleImpl{}
if IsNil(p) {
t.Fatal("IsNil should not reject a concrete interface value")
}
if IsNil("x") {
t.Fatal("IsNil should not reject non-nil scalar values")
}
}
@@ -0,0 +1,218 @@
package retrieval
import (
"fmt"
"math"
"strings"
"unicode"
"unicode/utf8"
)
// Tokens lowercases Latin words and splits CJK runs into overlapping bigrams
// (a lone CJK rune stays a single term) — the standard CJK indexing unit
// (Lucene's CJKAnalyzer, SQLite FTS): a bigram only matches a real two-rune
// subsequence where per-rune unigrams matched scattered common characters.
// Intentionally a local, dependency-free approximation of FTS matching.
func Tokens(s string) []string {
var out []string
var b strings.Builder
var prev rune
cjkRun := 0
flush := func() {
if b.Len() == 0 {
return
}
out = append(out, b.String())
b.Reset()
}
endCJK := func() {
if cjkRun == 1 {
out = append(out, string(prev))
}
cjkRun = 0
}
for _, r := range s {
switch {
case isCJK(r):
flush()
if cjkRun > 0 {
out = append(out, string([]rune{prev, r}))
}
prev = r
cjkRun++
case unicode.IsLetter(r) || unicode.IsDigit(r) || r == '_':
endCJK()
b.WriteRune(unicode.ToLower(r))
default:
flush()
endCJK()
}
}
flush()
endCJK()
return out
}
func isCJK(r rune) bool {
return unicode.In(r, unicode.Han, unicode.Hiragana, unicode.Katakana, unicode.Hangul)
}
// Unique returns terms in first-seen order.
func Unique(in []string) []string {
seen := map[string]bool{}
out := make([]string, 0, len(in))
for _, s := range in {
if s == "" || seen[s] {
continue
}
seen[s] = true
out = append(out, s)
}
return out
}
// Counts returns a term-frequency map.
func Counts(terms []string) map[string]int {
counts := map[string]int{}
for _, term := range terms {
counts[term]++
}
return counts
}
// BM25Score scores a document against query terms.
func BM25Score(counts map[string]int, length int, queryTerms []string, df map[string]int, totalDocs int, avgLen float64) float64 {
const (
k1 = 1.2
b = 0.75
)
if length <= 0 || totalDocs <= 0 {
return 0
}
if avgLen <= 0 {
avgLen = 1
}
var score float64
docLen := float64(length)
for _, term := range queryTerms {
tf := counts[term]
if tf == 0 {
continue
}
termDF := df[term]
if termDF == 0 {
continue
}
idf := math.Log(1 + (float64(totalDocs)-float64(termDF)+0.5)/(float64(termDF)+0.5))
freq := float64(tf)
score += idf * (freq * (k1 + 1)) / (freq + k1*(1-b+b*docLen/avgLen))
}
return score
}
// DocumentFrequency counts how many documents contain each term.
func DocumentFrequency(docs []map[string]int) map[string]int {
df := map[string]int{}
for _, counts := range docs {
for term := range counts {
df[term]++
}
}
return df
}
// KeepTopRelativeScore keeps the best item and drops trailing items whose score
// falls below ratio * topScore. Callers must pass items already sorted best
// first. This mirrors SQLite FTS/BM25 search UIs that over-fetch, then trim
// common-word-only noise without imposing an absolute score threshold.
func KeepTopRelativeScore[T any](items []T, ratio float64, score func(T) float64) []T {
if len(items) == 0 || ratio <= 0 {
return items
}
top := score(items[0])
if top <= 0 {
return items
}
cutoff := top * ratio
out := items[:0]
for i, item := range items {
if i == 0 || score(item) >= cutoff {
out = append(out, item)
}
}
return out
}
// QueryTerms normalizes a search string and reports an error when nothing
// searchable remains.
func QueryTerms(query string) ([]string, error) {
terms := Unique(Tokens(strings.TrimSpace(query)))
if len(terms) == 0 {
return nil, fmt.Errorf("query must contain at least one letter or number")
}
return terms, nil
}
// MakeSnippet returns a whitespace-compacted excerpt centered near the query.
func MakeSnippet(text, query string, terms []string, maxRunes int) string {
text = CompactWhitespace(text)
if maxRunes <= 0 || utf8.RuneCountInString(text) <= maxRunes {
return text
}
lower := strings.ToLower(text)
query = strings.ToLower(strings.TrimSpace(query))
idx := -1
if query != "" {
idx = strings.Index(lower, query)
}
if idx < 0 {
for _, term := range terms {
runes := []rune(term)
if len(runes) == 1 && !isCJK(runes[0]) {
continue
}
if i := strings.Index(lower, term); i >= 0 {
idx = i
break
}
}
}
if idx < 0 {
idx = 0
}
return snippetAround(text, idx, maxRunes)
}
func snippetAround(text string, byteIdx, maxRunes int) string {
if byteIdx < 0 {
byteIdx = 0
}
if byteIdx > len(text) {
byteIdx = len(text)
}
for byteIdx > 0 && byteIdx < len(text) && !utf8.RuneStart(text[byteIdx]) {
byteIdx--
}
runes := []rune(text)
pos := utf8.RuneCountInString(text[:byteIdx])
start := max(pos-maxRunes/2, 0)
end := start + maxRunes
if end > len(runes) {
end = len(runes)
start = max(end-maxRunes, 0)
}
prefix := ""
suffix := ""
if start > 0 {
prefix = "..."
}
if end < len(runes) {
suffix = "..."
}
return prefix + string(runes[start:end]) + suffix
}
// CompactWhitespace collapses runs of whitespace into one ASCII space.
func CompactWhitespace(s string) string {
return strings.Join(strings.Fields(s), " ")
}
@@ -0,0 +1,77 @@
package retrieval
import (
"strings"
"testing"
"unicode/utf8"
)
func TestTokensHandlesLatinAndCJK(t *testing.T) {
for _, tc := range []struct {
in string
want []string
}{
{"BM25 检索 cache-first", []string{"bm25", "检索", "cache", "first"}},
{"数据库迁移", []string{"数据", "据库", "库迁", "迁移"}},
{"库", []string{"库"}},
{"用pnpm装依赖", []string{"用", "pnpm", "装依", "依赖"}},
} {
got := Tokens(tc.in)
if strings.Join(got, ",") != strings.Join(tc.want, ",") {
t.Fatalf("Tokens(%q) = %#v, want %#v", tc.in, got, tc.want)
}
}
}
func TestBM25ScoreRanksMatchingDocument(t *testing.T) {
query := Unique(Tokens("prompt cache"))
doc1 := Counts(Tokens("prompt cache cache stability"))
doc2 := Counts(Tokens("dashboard colors"))
df := DocumentFrequency([]map[string]int{doc1, doc2})
score1 := BM25Score(doc1, 4, query, df, 2, 3)
score2 := BM25Score(doc2, 2, query, df, 2, 3)
if score1 <= score2 {
t.Fatalf("matching score %.3f should exceed unrelated score %.3f", score1, score2)
}
}
func TestKeepTopRelativeScoreKeepsTopAndDropsWeakTail(t *testing.T) {
items := []struct {
name string
score float64
}{
{name: "top", score: 10},
{name: "near", score: 2},
{name: "noise", score: 1.4},
{name: "zero", score: 0},
}
got := KeepTopRelativeScore(items, 0.15, func(item struct {
name string
score float64
}) float64 {
return item.score
})
if len(got) != 2 || got[0].name != "top" || got[1].name != "near" {
t.Fatalf("KeepTopRelativeScore() = %#v, want top and near", got)
}
}
func TestMakeSnippetHandlesMultibyteBoundary(t *testing.T) {
text := strings.Repeat("前缀", 80) + "稳定结论 synthesis cache " + strings.Repeat("后缀", 80)
out := MakeSnippet(text, "synthesis cache", QueryTermsForTest(t, "synthesis cache"), 60)
if !strings.Contains(out, "synthesis cache") {
t.Fatalf("snippet missing query: %q", out)
}
if strings.ContainsRune(out, utf8.RuneError) {
t.Fatalf("snippet contains replacement rune: %q", out)
}
}
func QueryTermsForTest(t *testing.T, query string) []string {
t.Helper()
terms, err := QueryTerms(query)
if err != nil {
t.Fatal(err)
}
return terms
}
@@ -0,0 +1,176 @@
// Retrieval V2, shadow-only: BM25F field weighting, code-symbol splitting,
// and mixed CJK uni+bigrams. Nothing here serves the model — V2 rankings ride
// the recall audit next to production's, and MemoryBench decides if V2 ever
// takes over. Weights are candidates under measurement, not tuned truths.
package retrieval
import (
"math"
"strings"
"unicode"
)
// TokensV2 extends Tokens with code-symbol structure and CJK unigrams:
// CamelCase and snake_case words also emit their segments (FooBar → foobar,
// foo, bar), and CJK runs emit unigrams alongside bigrams so one-character
// queries still match. The V1 form of every token is preserved, so V2 recall
// is a superset of V1's vocabulary.
func TokensV2(s string) []string {
var out []string
var word []rune
var prev rune
cjkRun := 0
flushWord := func() {
if len(word) == 0 {
return
}
lower := strings.ToLower(string(word))
out = append(out, lower)
for _, seg := range splitCodeSymbol(word) {
if seg != lower {
out = append(out, seg)
}
}
word = word[:0]
}
endCJK := func() { cjkRun = 0 }
for _, r := range s {
switch {
case isCJK(r):
flushWord()
out = append(out, string(r))
if cjkRun > 0 {
out = append(out, string([]rune{prev, r}))
}
prev = r
cjkRun++
case unicode.IsLetter(r) || unicode.IsDigit(r) || r == '_':
endCJK()
word = append(word, r)
default:
flushWord()
endCJK()
}
}
flushWord()
return out
}
// splitCodeSymbol yields the lowercase segments of a code identifier:
// CamelCase humps, snake_case parts, and letter/digit boundaries.
func splitCodeSymbol(word []rune) []string {
var segments []string
var seg []rune
flush := func() {
if len(seg) > 1 { // single letters are noise, not signal
segments = append(segments, strings.ToLower(string(seg)))
}
seg = seg[:0]
}
for i, r := range word {
boundary := r == '_' ||
(i > 0 && unicode.IsUpper(r) && !unicode.IsUpper(word[i-1])) ||
(i > 0 && unicode.IsDigit(r) != unicode.IsDigit(word[i-1]))
if boundary {
flush()
}
if r != '_' {
seg = append(seg, r)
}
}
flush()
if len(segments) < 2 {
return nil // no structure worth indexing beyond the whole word
}
return segments
}
// FieldedDoc is one document split into weighted fields for BM25F.
type FieldedDoc struct {
ID string
Fields map[string]string
}
// v2FieldWeights follow the BM25F intuition: identity fields say what a fact
// IS, the body says everything it mentions. Candidates under measurement.
var v2FieldWeights = map[string]float64{
"name": 2.5, "title": 2.5, "keywords": 2.0, "subject": 2.0,
"description": 1.5, "body": 1.0,
}
// V2Hit is one shadow-ranked document.
type V2Hit struct {
ID string
Score float64
}
// RankV2 scores docs against a query with BM25F over TokensV2: term
// frequencies accumulate per field scaled by field weight, then the standard
// saturation applies once per term (the F in BM25F), with length
// normalization over the weighted document length.
func RankV2(query string, docs []FieldedDoc) []V2Hit {
queryTerms := Unique(TokensV2(query))
if len(queryTerms) == 0 || len(docs) == 0 {
return nil
}
type indexed struct {
id string
tf map[string]float64
length float64
}
corpus := make([]indexed, 0, len(docs))
df := map[string]int{}
var totalLen float64
for _, doc := range docs {
ix := indexed{id: doc.ID, tf: map[string]float64{}}
seen := map[string]bool{}
for field, text := range doc.Fields {
weight, ok := v2FieldWeights[field]
if !ok {
weight = 1.0
}
for _, term := range TokensV2(text) {
ix.tf[term] += weight
ix.length += weight
seen[term] = true
}
}
for term := range seen {
df[term]++
}
totalLen += ix.length
corpus = append(corpus, ix)
}
avgLen := totalLen / float64(len(corpus))
if avgLen <= 0 {
avgLen = 1
}
const k1, b = 1.2, 0.75
var hits []V2Hit
for _, doc := range corpus {
var score float64
for _, term := range queryTerms {
tf := doc.tf[term]
if tf == 0 || df[term] == 0 {
continue
}
idf := math.Log(1 + (float64(len(corpus))-float64(df[term])+0.5)/(float64(df[term])+0.5))
score += idf * (tf * (k1 + 1)) / (tf + k1*(1-b+b*doc.length/avgLen))
}
if score > 0 {
hits = append(hits, V2Hit{ID: doc.id, Score: score})
}
}
SortHitsDesc(hits)
return hits
}
// SortHitsDesc orders shadow hits best-first with a stable ID tiebreak.
func SortHitsDesc(hits []V2Hit) {
for i := 1; i < len(hits); i++ {
for j := i; j > 0 && (hits[j].Score > hits[j-1].Score ||
(hits[j].Score == hits[j-1].Score && hits[j].ID < hits[j-1].ID)); j-- {
hits[j], hits[j-1] = hits[j-1], hits[j]
}
}
}
@@ -0,0 +1,47 @@
package retrieval
import (
"strings"
"testing"
)
func TestTokensV2CodeSymbolsAndCJK(t *testing.T) {
got := strings.Join(TokensV2("RetryPolicy.MaxBackoffMs ticket_1827 数据库"), " ")
for _, want := range []string{"retrypolicy", "retry", "policy", "maxbackoffms", "max", "backoff", "ticket_1827", "ticket", "1827", "数", "据", "库", "数据", "据库"} {
if !strings.Contains(" "+got+" ", " "+want+" ") {
t.Fatalf("TokensV2 missing %q in %q", want, got)
}
}
if strings.Contains(" "+got+" ", " ms ") {
// "Ms" splits to a single meaningful segment only when len > 1; "ms" is
// fine — assert single letters never appear instead.
t.Log("ms segment present (acceptable)")
}
for _, tok := range TokensV2("A B C") {
if len(tok) == 0 {
t.Fatal("empty token emitted")
}
}
}
func TestRankV2FieldWeightsBeatBodyMentions(t *testing.T) {
docs := []FieldedDoc{
{ID: "titled", Fields: map[string]string{"title": "Package manager", "body": "We migrated to pnpm."}},
{ID: "mention", Fields: map[string]string{"title": "Sprint notes", "body": "Someone asked about the package manager once; also weather, lunch, and a package of stickers."}},
}
hits := RankV2("package manager", docs)
if len(hits) != 2 || hits[0].ID != "titled" {
t.Fatalf("field-weighted doc must outrank body mentions: %+v", hits)
}
}
func TestRankV2FindsCodeSymbolBySegment(t *testing.T) {
docs := []FieldedDoc{
{ID: "sym", Fields: map[string]string{"body": "Configure RetryPolicy.MaxBackoffMs in policy/retry.toml."}},
{ID: "other", Fields: map[string]string{"body": "Unrelated fact about deploys."}},
}
hits := RankV2("max backoff configuration", docs)
if len(hits) == 0 || hits[0].ID != "sym" {
t.Fatalf("segment query must find the camel-case symbol: %+v", hits)
}
}
@@ -0,0 +1,7 @@
// Package rulelist splits a list of permission rules written on one line.
//
// Rules are separated by commas or whitespace, and a separator inside
// parentheses belongs to the rule: `Bash(git add:*) Read` is two rules. Skill
// front matter and the --allowed-tools flag share it, so one spelling means
// one thing in both.
package rulelist
@@ -0,0 +1,56 @@
package rulelist
import (
"errors"
"strings"
"unicode"
)
var (
// ErrUnclosed: an opening parenthesis is never closed.
ErrUnclosed = errors.New("unclosed '('")
// ErrUnexpectedClose: a closing parenthesis has no opening one.
ErrUnexpectedClose = errors.New("unexpected ')'")
)
// Split returns the rules in value, in order, trimmed and non-empty.
func Split(value string) ([]string, error) {
var rules []string
start, depth := -1, 0
flush := func(end int) {
if start < 0 {
return
}
if rule := strings.TrimSpace(value[start:end]); rule != "" {
rules = append(rules, rule)
}
start = -1
}
for i, r := range value {
switch r {
case '(':
if start < 0 {
start = i
}
depth++
case ')':
if depth == 0 {
return nil, ErrUnexpectedClose
}
depth--
default:
if depth == 0 && (r == ',' || unicode.IsSpace(r)) {
flush(i)
continue
}
if start < 0 {
start = i
}
}
}
if depth != 0 {
return nil, ErrUnclosed
}
flush(len(value))
return rules, nil
}
@@ -0,0 +1,40 @@
package rulelist
import (
"errors"
"reflect"
"testing"
)
func TestSplitSeparatesOnCommasAndWhitespaceOutsideParentheses(t *testing.T) {
for _, tc := range []struct {
in string
want []string
}{
{"", nil},
{" ", nil},
{"Read", []string{"Read"}},
{"read_file, grep,bash", []string{"read_file", "grep", "bash"}},
{"Read Grep Bash", []string{"Read", "Grep", "Bash"}},
{"Bash(git add:*) Read", []string{"Bash(git add:*)", "Read"}},
{"Bash(git add:*, git commit:*), Read", []string{"Bash(git add:*, git commit:*)", "Read"}},
{"bash,", []string{"bash"}},
} {
got, err := Split(tc.in)
if err != nil {
t.Fatalf("Split(%q): %v", tc.in, err)
}
if !reflect.DeepEqual(got, tc.want) {
t.Errorf("Split(%q) = %q, want %q", tc.in, got, tc.want)
}
}
}
func TestSplitReportsUnbalancedParenthesesByIdentity(t *testing.T) {
if _, err := Split("Bash(git add"); !errors.Is(err, ErrUnclosed) {
t.Errorf("unclosed = %v", err)
}
if _, err := Split("Bash)"); !errors.Is(err, ErrUnexpectedClose) {
t.Errorf("unexpected close = %v", err)
}
}
@@ -0,0 +1,25 @@
// Package tokencount estimates how many tokens a string costs a model, without
// carrying a tokenizer. Anything that has to size text before a provider counts
// it — a cache-shape diagnostic, a tool call's context footprint — needs one
// answer to that question rather than a private heuristic per caller.
package tokencount
import "unicode/utf8"
// Text estimates the token count of s. The split is by script because the two
// behave nothing alike — Latin text and code trend near four bytes per token,
// CJK closer to one token per character — and either rule applied to the whole
// string is wrong by ~4x on the other half. A real tokenizer would be exact and
// would mean a vocabulary per model, re-run over whole transcripts every turn.
func Text(s string) int {
narrow, wide := 0, 0
for _, r := range s {
if r < utf8.RuneSelf {
narrow++
continue
}
wide++
}
// Round up: a long run of short strings must not sum to nothing.
return (narrow+3)/4 + wide
}
@@ -0,0 +1,27 @@
package tokencount
import "testing"
func TestTextDoesNotUndercountCJK(t *testing.T) {
// The reason this package exists: bytes/4 reads a Chinese transcript as a
// third of its real size, so a planner sizing context off it compacts late.
const cjk = "把这个仓库跑一遍测试,把失败的那几个定位到具体文件"
runes := len([]rune(cjk))
if got := Text(cjk); got != runes {
t.Fatalf("Text(cjk) = %d, want one token per rune (%d)", got, runes)
}
if byBytes := len(cjk) / 4; Text(cjk) <= byBytes {
t.Fatalf("Text(cjk) = %d, must exceed the bytes/4 reading (%d)", Text(cjk), byBytes)
}
}
func TestTextRoundsASCIIUpAndKeepsEmptyFree(t *testing.T) {
if got := Text(""); got != 0 {
t.Fatalf("Text(\"\") = %d, want 0", got)
}
// Five bytes is more than one token's worth; truncating would let a long
// run of short strings sum to nothing.
if got := Text("abcde"); got != 2 {
t.Fatalf("Text(\"abcde\") = %d, want 2", got)
}
}
@@ -0,0 +1,200 @@
// Package ablation switches individual Reasonix subsystems off so a benchmark
// can attribute a change in solve rate to one of them.
package ablation
import (
"fmt"
"sort"
"strings"
)
type Module string
const (
Evidence Module = "evidence"
Planner Module = "planner"
Subagent Module = "subagent"
Retrieval Module = "retrieval"
Compaction Module = "compaction"
// Upstream off means a fleet dependency edge orders its endpoints without
// delivering the dependency's answer, so a dependent must re-derive it.
Upstream Module = "upstream"
// RecallSearch off leaves recall able to read an address it was given and
// unable to look one up, which is what the tool could do before the folded
// region became searchable.
RecallSearch Module = "recall-search"
)
// FoldIndexScale sizes the model-visible fold index against its shipped budget.
// It is a bench axis, not a module: the question it answers is what an ambient
// address list is worth, and four steps answer that where a free-form fraction
// would turn a measurement into a tuning knob.
type FoldIndexScale string
const (
FoldIndexDefault FoldIndexScale = ""
FoldIndexHalf FoldIndexScale = "half"
FoldIndexQuarter FoldIndexScale = "quarter"
FoldIndexOff FoldIndexScale = "off"
)
// Ratio scales a budget. The default returns it unchanged.
func (f FoldIndexScale) Ratio() float64 {
switch f {
case FoldIndexHalf:
return 0.5
case FoldIndexQuarter:
return 0.25
case FoldIndexOff:
return 0
default:
return 1
}
}
// ParseFoldIndexScale reads a bench flag value. "" and "default" ship as-is.
func ParseFoldIndexScale(spec string) (FoldIndexScale, error) {
switch strings.ToLower(strings.TrimSpace(spec)) {
case "", "default", "full":
return FoldIndexDefault, nil
case string(FoldIndexHalf):
return FoldIndexHalf, nil
case string(FoldIndexQuarter):
return FoldIndexQuarter, nil
case string(FoldIndexOff), "none", "0":
return FoldIndexOff, nil
default:
return "", fmt.Errorf("unknown fold-index scale %q (want default, half, quarter, or off)", spec)
}
}
// Modules returns every switchable module in the order arm names use.
func Modules() []Module {
return []Module{Evidence, Planner, Subagent, Retrieval, Compaction, Upstream, RecallSearch}
}
// Set is the group of modules disabled for a run. The zero value is the
// control arm: everything on.
type Set struct {
off map[Module]bool
// foldIndex is an axis rather than a switch, so it lives beside the module
// set instead of inside it. It still reaches Arm(): a run whose index was
// quartered must not report the same identity as the control.
foldIndex FoldIndexScale
}
// WithFoldIndex returns this set at the given index scale.
func (s Set) WithFoldIndex(scale FoldIndexScale) Set {
s.foldIndex = scale
return s
}
// FoldIndex is the scale this run sizes its fold index at.
func (s Set) FoldIndex() FoldIndexScale { return s.foldIndex }
// Parse reads a spec such as "evidence,planner". "" and "none" mean the control
// arm; "all" disables every module.
func Parse(spec string) (Set, error) {
spec = strings.TrimSpace(spec)
if spec == "" || strings.EqualFold(spec, "none") {
return Set{}, nil
}
if strings.EqualFold(spec, "all") {
return New(Modules()...), nil
}
known := map[Module]bool{}
for _, m := range Modules() {
known[m] = true
}
var mods []Module
for _, field := range strings.FieldsFunc(spec, func(r rune) bool { return r == ',' || r == ' ' }) {
m := Module(strings.ToLower(strings.TrimSpace(field)))
if !known[m] {
return Set{}, fmt.Errorf("unknown ablation module %q (want %s, or none/all)", field, ModuleList())
}
mods = append(mods, m)
}
return New(mods...), nil
}
// ParseArm reads both bench axes at once: the module spec and the fold-index
// scale. One call so a frontend cannot wire the first and forget the second,
// which would silently run an index arm under the control arm's name.
func ParseArm(modules, foldIndex string) (Set, error) {
set, err := Parse(modules)
if err != nil {
return Set{}, err
}
scale, err := ParseFoldIndexScale(foldIndex)
if err != nil {
return Set{}, err
}
return set.WithFoldIndex(scale), nil
}
// ModuleList names every switchable module for flag help and error text, so
// those cannot drift from Modules() the way a hand-kept list does.
func ModuleList() string { return joinModules(Modules(), ", ") }
// New returns a Set with the given modules disabled.
func New(mods ...Module) Set {
if len(mods) == 0 {
return Set{}
}
off := make(map[Module]bool, len(mods))
for _, m := range mods {
off[m] = true
}
return Set{off: off}
}
func (s Set) Off(m Module) bool { return s.off[m] }
func (s Set) Empty() bool { return len(s.off) == 0 && s.foldIndex == FoldIndexDefault }
// Arm is the published name of this configuration: "full" for the control arm,
// otherwise "no-evidence+no-planner". Stable across runs so results from
// different machines group by the same key.
func (s Set) Arm() string {
if s.Empty() {
return "full"
}
parts := make([]string, 0, len(s.off)+1)
for _, m := range s.disabled() {
parts = append(parts, "no-"+string(m))
}
if s.foldIndex != FoldIndexDefault {
parts = append(parts, "index-"+string(s.foldIndex))
}
return strings.Join(parts, "+")
}
// String round-trips back through Parse. It carries modules only — the fold
// index axis has its own flag and its own parser.
func (s Set) String() string {
if s.Empty() {
return "none"
}
return joinModules(s.disabled(), ",")
}
func (s Set) disabled() []Module {
order := map[Module]int{}
for i, m := range Modules() {
order[m] = i
}
out := make([]Module, 0, len(s.off))
for m := range s.off {
out = append(out, m)
}
sort.Slice(out, func(i, j int) bool { return order[out[i]] < order[out[j]] })
return out
}
func joinModules(mods []Module, sep string) string {
parts := make([]string, len(mods))
for i, m := range mods {
parts[i] = string(m)
}
return strings.Join(parts, sep)
}
@@ -0,0 +1,108 @@
package ablation
import "testing"
func TestParseAcceptsSpecFormsAndRejectsUnknownModules(t *testing.T) {
for _, tc := range []struct {
spec string
arm string
}{
{"", "full"},
{"none", "full"},
{"NONE", "full"},
{"evidence", "no-evidence"},
{"planner,evidence", "no-evidence+no-planner"},
{"planner evidence", "no-evidence+no-planner"},
{" Evidence , Retrieval ", "no-evidence+no-retrieval"},
{"all", "no-evidence+no-planner+no-subagent+no-retrieval+no-compaction+no-upstream+no-recall-search"},
} {
set, err := Parse(tc.spec)
if err != nil {
t.Fatalf("Parse(%q): %v", tc.spec, err)
}
if got := set.Arm(); got != tc.arm {
t.Errorf("Parse(%q).Arm() = %q, want %q", tc.spec, got, tc.arm)
}
}
if _, err := Parse("evidence,planer"); err == nil {
t.Fatal("a misspelled module must fail loudly, not silently run the control arm")
}
}
func TestArmNameIsIndependentOfSpecOrder(t *testing.T) {
a, _ := Parse("retrieval,evidence,planner")
b, _ := Parse("planner,retrieval,evidence")
if a.Arm() != b.Arm() {
t.Fatalf("arm names diverge by input order: %q vs %q", a.Arm(), b.Arm())
}
}
func TestStringRoundTripsThroughParse(t *testing.T) {
for _, spec := range []string{"none", "evidence", "evidence,compaction", "all"} {
set, err := Parse(spec)
if err != nil {
t.Fatalf("Parse(%q): %v", spec, err)
}
again, err := Parse(set.String())
if err != nil {
t.Fatalf("Parse(%q): %v", set.String(), err)
}
if again.Arm() != set.Arm() {
t.Errorf("%q round-tripped to %q", set.Arm(), again.Arm())
}
}
}
// The index axis is not a module, but a run that moved it is not the control
// arm either: results keyed by arm name would otherwise merge with it.
func TestFoldIndexScaleNamesItsOwnArm(t *testing.T) {
var s Set
if got := s.WithFoldIndex(FoldIndexQuarter).Arm(); got != "index-quarter" {
t.Errorf("Arm() = %q, want index-quarter", got)
}
if got := New(Evidence).WithFoldIndex(FoldIndexOff).Arm(); got != "no-evidence+index-off" {
t.Errorf("Arm() = %q, want no-evidence+index-off", got)
}
if s.WithFoldIndex(FoldIndexQuarter).Empty() {
t.Error("a quartered index reported itself as the control arm")
}
if !s.WithFoldIndex(FoldIndexDefault).Empty() {
t.Error("the default scale is the control arm")
}
}
func TestFoldIndexScaleRatios(t *testing.T) {
for _, tc := range []struct {
spec string
scale FoldIndexScale
ratio float64
}{
{"", FoldIndexDefault, 1}, {"default", FoldIndexDefault, 1},
{"half", FoldIndexHalf, 0.5}, {"QUARTER", FoldIndexQuarter, 0.25},
{"off", FoldIndexOff, 0}, {"0", FoldIndexOff, 0},
} {
got, err := ParseFoldIndexScale(tc.spec)
if err != nil {
t.Fatalf("ParseFoldIndexScale(%q): %v", tc.spec, err)
}
if got != tc.scale || got.Ratio() != tc.ratio {
t.Errorf("ParseFoldIndexScale(%q) = %q (ratio %v), want %q (%v)", tc.spec, got, got.Ratio(), tc.scale, tc.ratio)
}
}
if _, err := ParseFoldIndexScale("0.37"); err == nil {
t.Fatal("a free-form fraction was accepted; the axis is four steps on purpose")
}
}
func TestZeroValueIsTheControlArm(t *testing.T) {
var s Set
if !s.Empty() || s.Arm() != "full" {
t.Fatalf("zero Set = %q, want the full control arm", s.Arm())
}
for _, m := range Modules() {
if s.Off(m) {
t.Errorf("zero Set disabled %s", m)
}
}
}
@@ -0,0 +1,15 @@
// Package agentgraph is the vocabulary of one run's execution graph: the nodes
// a turn handed work to, and the typed edges between them.
//
// It exists because that structure was being re-derived in five places and
// survived in none. A fleet's dependency DAG died with the call that built it,
// the persisted sidecar recorded a parent link and nothing else, and the event
// stream implied edges through id prefixes. A reader asking why a node waited
// could not tell an unmet dependency from a delivered answer, because neither
// had ever been written down as anything a reader could tell apart.
//
// Nothing here knows what an agent, a tool, or a provider is. A producer
// publishes a Delta of what it has just proven; every consumer folds deltas
// with the same Apply, so the graph a headless run reports is the graph the
// desktop draws.
package agentgraph
@@ -0,0 +1,219 @@
package agentgraph
import "slices"
// NodeKind separates the three things a node can be. A group is the call that
// owns a fan-out, a worker is one delegated run, and an external node is work
// this run did not do — named only because an edge points at it.
type NodeKind string
const (
KindGroup NodeKind = "group"
KindWorker NodeKind = "worker"
KindExternal NodeKind = "external"
)
// NodeState is where one node stands. It is the node's own lifecycle and not a
// tool call's: an adopted node never ran and still holds an answer, and a
// skipped one never started because something upstream did not finish.
type NodeState string
const (
StatePending NodeState = "pending"
// StateQueued is ready and waiting for a session concurrency slot. It is
// not StatePending: nothing upstream is missing, and the only thing between
// this node and its first step is the scheduler's ceiling.
StateQueued NodeState = "queued"
StateRunning NodeState = "running"
StateCompleted NodeState = "completed"
StateAdopted NodeState = "adopted"
StateFailed NodeState = "failed"
StateCancelled NodeState = "cancelled"
StateSkipped NodeState = "skipped"
)
// Answered reports whether this state holds a final answer a dependent may
// start from: one the node produced, or one adopted in its place.
func (s NodeState) Answered() bool { return s == StateCompleted || s == StateAdopted }
// Terminal reports whether the node will not change state again.
func (s NodeState) Terminal() bool {
return s != StatePending && s != StateQueued && s != StateRunning
}
// Grant is what a node was allowed to touch while it ran. Empty means the
// question does not apply — a group is not a run, and an adopted node never got
// one — which a bool could not say apart from "may write".
type Grant string
const (
GrantRead Grant = "read"
GrantWrite Grant = "write"
)
// WaitCause is why a queued node is not running. Queued says a node is ready and
// held back; it does not say by what, and the answers have different remedies —
// two are session settings and the third is a path or an edge.
type WaitCause string
const (
// WaitSlots is the session's total sub-agent ceiling.
WaitSlots WaitCause = "slots"
// WaitWriters is the session's concurrent-writer ceiling, which binds a
// writer while total capacity is still free.
WaitWriters WaitCause = "writers"
// WaitClaim is a write path someone else already holds. No ceiling change
// releases it: only disjoint paths, or an edge that orders the two.
WaitClaim WaitCause = "claim"
)
// EdgeKind is what one edge means. The node pair alone says nothing: waiting
// for an answer and receiving one are separate facts, and an arm that orders
// two nodes while delivering nothing between them is the case a single edge
// type cannot describe.
type EdgeKind string
const (
// Spawn is structural: To was created by From.
Spawn EdgeKind = "spawn"
// Depends orders: To may not start until From has answered.
Depends EdgeKind = "depends"
// Context carries: From's answer opened To's run.
Context EdgeKind = "context"
// Adopt reuses: From's existing answer stood in for running To, which
// therefore cost nothing. From is usually outside this graph.
Adopt EdgeKind = "adopt"
)
// Node is one participant in a run. Zero fields mean "not learned yet", which
// is what lets a producer report an outcome without restating the identity it
// declared when the node entered the graph.
type Node struct {
ID string `json:"id"`
ParentID string `json:"parentId,omitempty"`
Kind NodeKind `json:"kind,omitempty"`
State NodeState `json:"state,omitempty"`
Label string `json:"label,omitempty"`
Profile string `json:"profile,omitempty"`
Model string `json:"model,omitempty"`
Effort string `json:"effort,omitempty"`
Grant Grant `json:"grant,omitempty"`
// Wait is what held this node out of a slot. Like the stamps it survives the
// run starting: the state says it is running now, this says what it waited on.
Wait WaitCause `json:"wait,omitempty"`
// Ref is the persisted transcript this node's answer reads back from.
Ref string `json:"ref,omitempty"`
// QueuedAt, StartedAt and EndedAt are unix milliseconds, zero while unknown.
// QueuedAt to StartedAt is slot wait, which only the scheduler can measure.
QueuedAt int64 `json:"queuedAt,omitempty"`
StartedAt int64 `json:"startedAt,omitempty"`
EndedAt int64 `json:"endedAt,omitempty"`
Err string `json:"err,omitempty"`
}
// Edge is one typed relation between two node ids. From may name a node outside
// this graph: an adopted answer's source is work some earlier run did.
type Edge struct {
From string `json:"from"`
To string `json:"to"`
Kind EdgeKind `json:"kind"`
}
// Delta is one publication of what a producer has just proven. It is additive:
// a node named again updates the one already there rather than forking into a
// second, and a repeated edge is the same edge.
type Delta struct {
Nodes []Node `json:"nodes,omitempty"`
Edges []Edge `json:"edges,omitempty"`
}
// Empty reports whether this delta carries nothing worth publishing.
func (d Delta) Empty() bool { return len(d.Nodes) == 0 && len(d.Edges) == 0 }
// Graph is the folded result. Nodes and edges keep first-seen order so two
// consumers fed the same deltas draw the same picture. Lookups scan rather than
// index: a graph is bounded by one call's fan-out, and a map would have to be
// rebuilt every time the value crossed a wire.
type Graph struct {
Nodes []Node `json:"nodes,omitempty"`
Edges []Edge `json:"edges,omitempty"`
}
// Apply folds one delta into the graph.
func (g *Graph) Apply(d Delta) {
for _, n := range d.Nodes {
g.upsert(n)
}
for _, e := range d.Edges {
g.link(e)
}
}
// Node returns the node with this id.
func (g *Graph) Node(id string) (Node, bool) {
if i := g.indexOf(id); i >= 0 {
return g.Nodes[i], true
}
return Node{}, false
}
func (g *Graph) indexOf(id string) int {
return slices.IndexFunc(g.Nodes, func(n Node) bool { return n.ID == id })
}
func (g *Graph) upsert(n Node) {
if n.ID == "" {
return
}
if i := g.indexOf(n.ID); i >= 0 {
g.Nodes[i] = merge(g.Nodes[i], n)
return
}
g.Nodes = append(g.Nodes, n)
}
// merge keeps what the graph already knew wherever the update says nothing, so
// an outcome-only update cannot blank the identity an earlier delta declared.
func merge(old, update Node) Node {
out := update
out.ParentID = orText(update.ParentID, old.ParentID)
out.Kind = NodeKind(orText(string(update.Kind), string(old.Kind)))
out.State = NodeState(orText(string(update.State), string(old.State)))
out.Label = orText(update.Label, old.Label)
out.Profile = orText(update.Profile, old.Profile)
out.Model = orText(update.Model, old.Model)
out.Effort = orText(update.Effort, old.Effort)
out.Ref = orText(update.Ref, old.Ref)
out.Err = orText(update.Err, old.Err)
out.Grant = Grant(orText(string(update.Grant), string(old.Grant)))
out.Wait = WaitCause(orText(string(update.Wait), string(old.Wait)))
out.QueuedAt = orStamp(update.QueuedAt, old.QueuedAt)
out.StartedAt = orStamp(update.StartedAt, old.StartedAt)
out.EndedAt = orStamp(update.EndedAt, old.EndedAt)
return out
}
// link adds an edge once. A self-edge or a half-named one is a producer bug the
// graph refuses to record rather than render as a loop nothing can explain.
func (g *Graph) link(e Edge) {
if e.From == "" || e.To == "" || e.Kind == "" || e.From == e.To {
return
}
if !slices.Contains(g.Edges, e) {
g.Edges = append(g.Edges, e)
}
}
func orText(update, old string) string {
if update != "" {
return update
}
return old
}
func orStamp(update, old int64) int64 {
if update != 0 {
return update
}
return old
}
@@ -0,0 +1,153 @@
package agentgraph
import (
"encoding/json"
"slices"
"testing"
)
func TestApplyUpsertsNodesByID(t *testing.T) {
var g Graph
g.Apply(Delta{Nodes: []Node{
{ID: "a", Kind: KindWorker, State: StatePending, Label: "research", Profile: "explore", Grant: GrantRead},
}})
// An outcome-only update is what a producer sends once the node settles.
g.Apply(Delta{Nodes: []Node{{ID: "a", State: StateCompleted, Ref: "sub-1", EndedAt: 42}}})
if len(g.Nodes) != 1 {
t.Fatalf("nodes = %d, want 1 (the update forked the node)", len(g.Nodes))
}
n, ok := g.Node("a")
if !ok {
t.Fatal("node a missing")
}
if n.State != StateCompleted || n.Ref != "sub-1" || n.EndedAt != 42 {
t.Fatalf("update did not land: %+v", n)
}
if n.Label != "research" || n.Profile != "explore" || n.Kind != KindWorker || n.Grant != GrantRead {
t.Fatalf("outcome-only update blanked declared identity: %+v", n)
}
}
func TestApplyIgnoresUnusableRecords(t *testing.T) {
cases := []struct {
name string
delta Delta
}{
{"node without id", Delta{Nodes: []Node{{State: StateRunning}}}},
{"edge without source", Delta{Edges: []Edge{{To: "b", Kind: Depends}}}},
{"edge without target", Delta{Edges: []Edge{{From: "a", Kind: Depends}}}},
{"edge without kind", Delta{Edges: []Edge{{From: "a", To: "b"}}}},
{"self edge", Delta{Edges: []Edge{{From: "a", To: "a", Kind: Depends}}}},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
var g Graph
g.Apply(c.delta)
if len(g.Nodes) != 0 || len(g.Edges) != 0 {
t.Fatalf("recorded %+v", g)
}
})
}
}
func TestApplyKeepsEdgesDistinctByKind(t *testing.T) {
var g Graph
// depends and context join the same pair and are not the same fact: an arm
// that orders without delivering emits the first and not the second.
edges := []Edge{
{From: "a", To: "b", Kind: Depends},
{From: "a", To: "b", Kind: Context},
{From: "a", To: "b", Kind: Depends},
}
g.Apply(Delta{Edges: edges})
if len(g.Edges) != 2 {
t.Fatalf("edges = %v, want depends and context once each", g.Edges)
}
for _, want := range []Edge{{From: "a", To: "b", Kind: Depends}, {From: "a", To: "b", Kind: Context}} {
if !slices.Contains(g.Edges, want) {
t.Fatalf("missing %+v in %v", want, g.Edges)
}
}
}
func TestApplyIsOrderStable(t *testing.T) {
build := func() Graph {
var g Graph
for _, id := range []string{"a", "b", "c"} {
g.Apply(Delta{Nodes: []Node{{ID: id, Kind: KindWorker, State: StatePending}}})
}
g.Apply(Delta{Nodes: []Node{{ID: "b", State: StateRunning}}})
return g
}
first, second := build(), build()
if len(first.Nodes) != 3 {
t.Fatalf("nodes = %d, want 3", len(first.Nodes))
}
for i := range first.Nodes {
if first.Nodes[i] != second.Nodes[i] {
t.Fatalf("node %d diverged: %+v vs %+v", i, first.Nodes[i], second.Nodes[i])
}
}
if first.Nodes[1].ID != "b" || first.Nodes[1].State != StateRunning {
t.Fatalf("an update reordered the graph: %+v", first.Nodes)
}
}
func TestStateVocabulary(t *testing.T) {
cases := []struct {
state NodeState
answered bool
terminal bool
}{
{StatePending, false, false},
// Queued is not terminal: the node has not run, and a reader that treats
// it as settled stops waiting for the answer it is still going to get.
{StateQueued, false, false},
{StateRunning, false, false},
{StateCompleted, true, true},
{StateAdopted, true, true},
{StateFailed, false, true},
{StateCancelled, false, true},
{StateSkipped, false, true},
}
for _, c := range cases {
if got := c.state.Answered(); got != c.answered {
t.Errorf("%s.Answered() = %v, want %v", c.state, got, c.answered)
}
if got := c.state.Terminal(); got != c.terminal {
t.Errorf("%s.Terminal() = %v, want %v", c.state, got, c.terminal)
}
}
}
// A graph crosses a wire before it is drawn, so the folded value has to survive
// the round trip that every consumer outside this process reads it through.
func TestGraphSurvivesJSONRoundTrip(t *testing.T) {
var g Graph
g.Apply(Delta{
Nodes: []Node{
{ID: "grp", Kind: KindGroup, State: StateRunning, Label: "fleet(2)"},
{ID: "grp/1", ParentID: "grp", Kind: KindWorker, State: StateCompleted, Model: "m", StartedAt: 1, EndedAt: 2},
{ID: "old-ref", Kind: KindExternal, State: StateCompleted},
},
Edges: []Edge{
{From: "grp", To: "grp/1", Kind: Spawn},
{From: "old-ref", To: "grp/1", Kind: Adopt},
},
})
raw, err := json.Marshal(g)
if err != nil {
t.Fatal(err)
}
var back Graph
if err := json.Unmarshal(raw, &back); err != nil {
t.Fatal(err)
}
if !slices.Equal(back.Edges, g.Edges) {
t.Fatalf("edges = %v, want %v", back.Edges, g.Edges)
}
if !slices.Equal(back.Nodes, g.Nodes) {
t.Fatalf("nodes = %v, want %v", back.Nodes, g.Nodes)
}
}
@@ -0,0 +1,220 @@
package pricing
import (
"net/url"
"strings"
"time"
)
// Official catalog metadata: currency, model, effective dates, documentation
// sources, and price fingerprints. User-custom prices always win over catalog.
// CatalogEntry is one official list price for a model in a billing currency.
type CatalogEntry struct {
Provider string // deepseek | longcat | mimo | google
Model string
Currency string // ISO billing currency for this row
CacheHit float64
Input float64
Output float64
EffectiveFrom string // YYYY-MM-DD inclusive, empty = unbounded
EffectiveTo string // YYYY-MM-DD exclusive, empty = unbounded
DocURL string
BillingMode string // payg | subscription_equivalent
Notes string
Fingerprint string // filled by Register
// Peak is what the same tokens cost inside Window. Nil when the vendor
// bills one rate around the clock, and then the fields above are the rate.
Peak *RateCard
Window *PeakWindow
// CheckedOn is when a person last read DocURL and confirmed these numbers.
// No vendor here serves prices over an API, so this date is all that stands
// between a price change and a readout that is quietly wrong.
CheckedOn string
}
// deepseekPeak is the schedule DeepSeek publishes: peak on weekday mornings and
// afternoons, and — since 2026-08-23 — never at a weekend. Chinese public
// holidays bill off-peak in full, per the same page.
var deepseekPeak = &PeakWindow{
OffsetSeconds: beijing,
Hours: [][2]int{{9, 12}, {14, 18}},
WeekendOffPeak: "2026-08-23",
HolidayOffPeak: chineseStatutoryHolidays,
}
// chineseStatutoryHolidays are the State Council's 2026 public holidays, which
// DeepSeek bills off-peak in full; extend it when the next notice is out. Make-up
// workdays are absent: they fall on weekends, which are off-peak anyway.
var chineseStatutoryHolidays = []string{
// New Year's Day: Jan 1-3.
"2026-01-01", "2026-01-02", "2026-01-03",
// Spring Festival: Feb 15-23.
"2026-02-15", "2026-02-16", "2026-02-17", "2026-02-18", "2026-02-19",
"2026-02-20", "2026-02-21", "2026-02-22", "2026-02-23",
// Qingming Festival: Apr 4-6.
"2026-04-04", "2026-04-05", "2026-04-06",
// Labor Day: May 1-5.
"2026-05-01", "2026-05-02", "2026-05-03", "2026-05-04", "2026-05-05",
// Dragon Boat Festival: Jun 19-21.
"2026-06-19", "2026-06-20", "2026-06-21",
// Mid-Autumn Festival: Sep 25-27.
"2026-09-25", "2026-09-26", "2026-09-27",
// National Day: Oct 1-7.
"2026-10-01", "2026-10-02", "2026-10-03", "2026-10-04",
"2026-10-05", "2026-10-06", "2026-10-07",
}
// checkedOn is when the tables below were last read off the vendors' pages.
const checkedOn = "2026-08-22"
// deepseekCheckedOn is when the DeepSeek rows were last read off that vendor's
// page. Separate from checkedOn, because confirming one vendor's prices says
// nothing about another's and a shared date would claim that it did.
const deepseekCheckedOn = "2026-09-13"
// mimoCheckedOn is when the V2.6 rows were read off the vendor's own docs.
const mimoCheckedOn = "2026-10-06"
// googleCheckedOn is when the Gemini rows were read off Google's pricing page.
const googleCheckedOn = "2026-10-17"
// CatalogSourceURLs document public pricing pages.
const (
DocDeepSeekPricing = "https://api-docs.deepseek.com/quick_start/pricing"
DocDeepSeekPricingCNY = "https://api-docs.deepseek.com/zh-cn/quick_start/pricing"
DocLongCatPricingUSD = "https://longcat.chat/platform/docs/Pricing/LongCat-2.0.html"
DocLongCatPricingCNY = "https://longcat.chat/platform/docs/zh/pricing/long-cat-2.0"
DocMiMoPAYG = "https://mimo.mi.com/docs/price/pay-as-you-go"
DocMiMoTokenPlan = "https://platform.xiaomimimo.com/token-plan"
DocGeminiPricing = "https://ai.google.dev/gemini-api/docs/pricing"
)
// officialEndpointHosts maps a vendor's own API hostnames to the catalog
// provider whose list prices apply there. Host equality is the test: a reseller
// or proxy bills on its own terms even when it serves the same model, and an
// entry merely named after a vendor is not evidence of the vendor's endpoint.
var officialEndpointHosts = map[string]string{
"api.deepseek.com": "deepseek",
"api.longcat.chat": "longcat",
"api.xiaomimimo.com": "mimo",
"generativelanguage.googleapis.com": "google",
"token-plan-cn.xiaomimimo.com": "mimo",
"token-plan-sgp.xiaomimimo.com": "mimo",
"token-plan-ams.xiaomimimo.com": "mimo",
}
// OfficialProviderForEndpoint returns the catalog provider whose official
// prices apply to baseURL, or "" when the address is not a vendor's own API.
func OfficialProviderForEndpoint(baseURL string) string {
u, err := url.Parse(strings.TrimSpace(baseURL))
if err != nil {
return ""
}
return officialEndpointHosts[strings.ToLower(u.Hostname())]
}
// OfficialCatalog is the built-in price book: the current generation of every
// rate in officialRates, fingerprinted so a stored price can be recognised as
// one of ours. User-custom prices always win over it.
func OfficialCatalog() []CatalogEntry {
entries := officialCatalogRows()
for i := range entries {
entries[i].Fingerprint = PricingFingerprint(RateCard{
CacheHit: entries[i].CacheHit,
Input: entries[i].Input,
Output: entries[i].Output,
Currency: entries[i].Currency,
})
}
return entries
}
// LookupCatalog finds an official entry. billingMode empty matches payg first.
func LookupCatalog(provider, model, currency, billingMode string) (CatalogEntry, bool) {
provider = strings.ToLower(strings.TrimSpace(provider))
model = strings.TrimSpace(model)
currency = NormalizeCurrency(currency)
billingMode = strings.TrimSpace(billingMode)
if billingMode == "" {
billingMode = BillingModePAYG
}
var fallback CatalogEntry
foundFallback := false
for _, e := range OfficialCatalog() {
if e.Provider != provider || e.Model != model {
continue
}
if NormalizeCurrency(e.Currency) != currency {
continue
}
if e.BillingMode == billingMode {
return e, true
}
if e.BillingMode == BillingModePAYG {
fallback = e
foundFallback = true
}
}
if foundFallback {
return fallback, true
}
return CatalogEntry{}, false
}
// RateCardFromCatalog builds a RateCard from a catalog entry's base rate.
func RateCardFromCatalog(e CatalogEntry) RateCard {
return RateCard{
CacheHit: e.CacheHit,
Input: e.Input,
Output: e.Output,
Currency: e.Currency,
}
}
// RatesAt is what this entry charges at t: the peak card inside its window, the
// base card everywhere else. An entry without a window charges one rate, and
// then t does not matter.
func (e CatalogEntry) RatesAt(t time.Time) RateCard {
if e.Peak == nil || !e.Window.IsPeak(t) {
return RateCardFromCatalog(e)
}
peak := *e.Peak
peak.Currency = e.Currency
return peak
}
// InEffect reports whether this entry's dates cover the day t falls on.
func (e CatalogEntry) InEffect(t time.Time) bool {
day := t.UTC().Format("2006-01-02")
if e.EffectiveFrom != "" && day < e.EffectiveFrom {
return false
}
return e.EffectiveTo == "" || day < e.EffectiveTo
}
// MatchesCatalog reports whether rates equal a known official entry.
func MatchesCatalog(provider, model string, rates RateCard) (CatalogEntry, bool) {
cur := NormalizeCurrency(rates.Currency)
for _, e := range OfficialCatalog() {
if e.Provider != strings.ToLower(strings.TrimSpace(provider)) {
continue
}
if e.Model != strings.TrimSpace(model) {
continue
}
if NormalizeCurrency(e.Currency) != cur {
continue
}
// Either card identifies the vendor's own price. Which one a user
// happens to have configured says nothing about when they are billing:
// the hour decides that, and RatesAt is what reads it.
if e.CacheHit == rates.CacheHit && e.Input == rates.Input && e.Output == rates.Output {
return e, true
}
if p := e.Peak; p != nil && p.CacheHit == rates.CacheHit && p.Input == rates.Input && p.Output == rates.Output {
return e, true
}
}
return CatalogEntry{}, false
}
@@ -0,0 +1,95 @@
package pricing
import (
"strings"
"testing"
"time"
)
// Every catalog provider must declare at least one official endpoint, or its
// rows become unreachable: a rate card is only matched against the catalog
// after the entry's host identifies the vendor.
func TestEveryCatalogProviderHasAnOfficialEndpoint(t *testing.T) {
declared := map[string]bool{}
for _, host := range officialEndpointHosts {
declared[host] = true
}
for _, entry := range OfficialCatalog() {
if !declared[entry.Provider] {
t.Errorf("catalog provider %q has no official endpoint host; its prices can never match", entry.Provider)
}
}
}
func TestOfficialProviderForEndpoint(t *testing.T) {
cases := []struct {
name string
baseURL string
want string
}{
{"official deepseek", "https://api.deepseek.com", "deepseek"},
{"official deepseek anthropic path", "https://api.deepseek.com/anthropic", "deepseek"},
{"official longcat", "https://api.longcat.chat/openai/v1", "longcat"},
{"official mimo region", "https://token-plan-sgp.xiaomimimo.com/v1", "mimo"},
{"uppercase host", "https://API.DeepSeek.com/v1", "deepseek"},
{"proxy that resells the model", "https://my-deepseek-proxy.internal/v1", ""},
{"vendor name in the path only", "https://gateway.example.com/deepseek/v1", ""},
{"lookalike registrable domain", "https://api.deepseek.com.evil.test/v1", ""},
{"empty", "", ""},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
if got := OfficialProviderForEndpoint(tc.baseURL); got != tc.want {
t.Fatalf("OfficialProviderForEndpoint(%q) = %q, want %q", tc.baseURL, got, tc.want)
}
})
}
}
// pricesGoStaleAfter is how long a hand-copied rate is trusted before someone
// reads the vendor's page again. No vendor here serves prices over an API, so
// the alternative to this going red on a quiet day is learning about a change
// from a user whose cost was wrong for months. Bumping CheckedOn without
// opening DocURL defeats it; what this buys is that the question gets asked.
const pricesGoStaleAfter = 90 * 24 * time.Hour
func TestCatalogPricesHaveBeenCheckedRecently(t *testing.T) {
var undated, stale []string
// This snapshot never sees a vendor page again, so staleness is read at the
// day it was taken rather than the day the suite runs.
cutoff := time.Date(2026, 10, 19, 0, 0, 0, 0, time.UTC).Add(-pricesGoStaleAfter)
for _, e := range OfficialCatalog() {
where := e.Provider + "/" + e.Model + " " + e.Currency + " (" + e.DocURL + ")"
if e.CheckedOn == "" {
undated = append(undated, where)
continue
}
on, err := time.Parse("2006-01-02", e.CheckedOn)
if err != nil {
t.Fatalf("%s: CheckedOn %q is not YYYY-MM-DD", where, e.CheckedOn)
}
if on.Before(cutoff) {
stale = append(stale, where+" last checked "+e.CheckedOn)
}
}
if len(undated) > 0 {
t.Errorf("catalog rows with no CheckedOn — read the vendor's page and date them:\n %s",
strings.Join(undated, "\n "))
}
if len(stale) > 0 {
t.Errorf("catalog rows older than %d days. Open each DocURL, confirm or correct the rate, then set CheckedOn to today:\n %s",
int(pricesGoStaleAfter.Hours()/24), strings.Join(stale, "\n "))
}
}
// A row that names a peak card must say when it applies, and one that names a
// window must have a card to charge inside it. Half of the pair silently prices
// every hour the same, which is the bug the pair exists to prevent.
func TestPeakRatesAndWindowsComeInPairs(t *testing.T) {
for _, e := range OfficialCatalog() {
where := e.Provider + "/" + e.Model + " " + e.Currency
if (e.Peak == nil) != (e.Window == nil) {
t.Errorf("%s: Peak and Window must be set together (peak=%v window=%v)", where, e.Peak != nil, e.Window != nil)
}
}
}
@@ -0,0 +1,48 @@
package pricing
// Coverage says how much of the usage behind a quote carries a complete price
// fact. CostComplete answers that for one call; an aggregate has two answers
// more, because it can price some of its calls and not others, and it can have
// billed nothing at all. A session that has not spent yet is not a session
// whose price is unknown.
const (
CoverageNone = "none"
CoverageComplete = "complete"
CoveragePartial = "partial"
CoverageIncomplete = "incomplete"
)
// ValidCoverage reports whether c is one of the four identities.
func ValidCoverage(c string) bool {
switch c {
case CoverageNone, CoverageComplete, CoveragePartial, CoverageIncomplete:
return true
}
return false
}
// FoldCoverage returns the coverage of a and b together: the join over complete
// and incomplete, with none as the identity and partial as the top. Order cannot
// reach the answer, and a run that has priced everything so far cannot fold back
// to complete once an unpriced call joins it. A value outside the four folds as
// none; NormalizeQuote repairs a decoded quote before it can reach here.
func FoldCoverage(a, b string) string {
a, b = coverageOrNone(a), coverageOrNone(b)
switch {
case a == CoverageNone:
return b
case b == CoverageNone:
return a
case a == b:
return a
default:
return CoveragePartial
}
}
func coverageOrNone(c string) string {
if ValidCoverage(c) {
return c
}
return CoverageNone
}
@@ -0,0 +1,190 @@
package pricing
import (
"testing"
"time"
)
var allCoverages = []string{CoverageNone, CoverageComplete, CoveragePartial, CoverageIncomplete}
// The contract the aggregate has to satisfy, stated as the pairs themselves.
func TestFoldCoverageContract(t *testing.T) {
cases := []struct{ a, b, want string }{
{CoverageNone, CoverageNone, CoverageNone},
{CoverageComplete, CoverageNone, CoverageComplete},
{CoverageComplete, CoverageComplete, CoverageComplete},
{CoverageComplete, CoverageIncomplete, CoveragePartial},
{CoverageIncomplete, CoverageComplete, CoveragePartial},
{CoverageIncomplete, CoverageIncomplete, CoverageIncomplete},
{CoveragePartial, CoverageComplete, CoveragePartial},
{CoveragePartial, CoverageIncomplete, CoveragePartial},
{CoveragePartial, CoverageNone, CoveragePartial},
}
for _, c := range cases {
if got := FoldCoverage(c.a, c.b); got != c.want {
t.Errorf("fold(%s, %s) = %s, want %s", c.a, c.b, got, c.want)
}
}
}
// Arrival order is not a fact about the session, so it may not reach the
// answer. Exhaustive rather than sampled: there are only sixty-four triples.
func TestFoldCoverageIsOrderIndependent(t *testing.T) {
for _, a := range allCoverages {
for _, b := range allCoverages {
if FoldCoverage(a, b) != FoldCoverage(b, a) {
t.Errorf("fold(%s, %s) != fold(%s, %s)", a, b, b, a)
}
if FoldCoverage(a, a) != a {
t.Errorf("fold(%s, %s) = %s, want %s", a, a, FoldCoverage(a, a), a)
}
for _, c := range allCoverages {
left := FoldCoverage(FoldCoverage(a, b), c)
right := FoldCoverage(a, FoldCoverage(b, c))
if left != right {
t.Errorf("fold is not associative on (%s, %s, %s): %s vs %s", a, b, c, left, right)
}
}
}
}
}
// The failure a boolean invites: the last quote deciding for the ones before it.
func TestCompleteNeverSurvivesALaterIncomplete(t *testing.T) {
for _, a := range allCoverages {
got := FoldCoverage(FoldCoverage(a, CoverageComplete), CoverageIncomplete)
if got == CoverageComplete {
t.Errorf("fold(%s, complete, incomplete) = complete", a)
}
}
}
func TestFoldCoverageTreatsAnUnnamedStateAsNothing(t *testing.T) {
if got := FoldCoverage("", CoverageComplete); got != CoverageComplete {
t.Fatalf("fold(\"\", complete) = %s", got)
}
if got := FoldCoverage("mostly", CoverageIncomplete); got != CoverageIncomplete {
t.Fatalf("fold(unnamed, incomplete) = %s", got)
}
}
func pricedQuote(t *testing.T, perMillion float64) CostQuote {
t.Helper()
q := BuildQuote(QuoteInput{
Usage: UsageTokens{PromptTokens: 1000},
Rates: RateCard{Input: perMillion, Currency: "USD"},
})
if q.Coverage != CoverageComplete {
t.Fatalf("a priced call is not complete: %+v", q)
}
return q
}
func unpricedQuote(t *testing.T) CostQuote {
t.Helper()
q := NormalizeQuote(CostQuote{
Estimated: true, DisplayStatus: DisplayStatusUnavailable, IncompleteReason: "no_price",
})
if q.Coverage != CoverageIncomplete {
t.Fatalf("an unpriced call is not incomplete: %+v", q)
}
return q
}
func TestAggregateCoverageAcrossCalls(t *testing.T) {
priced, unpriced := pricedQuote(t, 1), unpricedQuote(t)
cases := []struct {
name string
quotes []CostQuote
want string
}{
{"no usage", nil, CoverageNone},
{"one priced", []CostQuote{priced}, CoverageComplete},
{"two priced", []CostQuote{priced, priced}, CoverageComplete},
{"priced then unpriced", []CostQuote{priced, unpriced}, CoveragePartial},
{"unpriced then priced", []CostQuote{unpriced, priced}, CoveragePartial},
{"unpriced only", []CostQuote{unpriced}, CoverageIncomplete},
{"unpriced twice", []CostQuote{unpriced, unpriced}, CoverageIncomplete},
}
for _, c := range cases {
got := AggregateQuotes(c.quotes, "USD")
if got.Coverage != c.want {
t.Errorf("%s: coverage = %s, want %s", c.name, got.Coverage, c.want)
}
// The boolean is a reading of the coverage, not a second answer.
if got.CostComplete != (c.want == CoverageComplete) {
t.Errorf("%s: costComplete = %v against coverage %s", c.name, got.CostComplete, got.Coverage)
}
}
}
// A session's completeness is a fact about its calls, so the order they were
// billed in may not change it — at the aggregate, not only in the fold.
func TestAggregateCoverageIgnoresArrivalOrder(t *testing.T) {
priced, unpriced := pricedQuote(t, 1), unpricedQuote(t)
forward := AggregateQuotes([]CostQuote{priced, priced, unpriced}, "USD")
backward := AggregateQuotes([]CostQuote{unpriced, priced, priced}, "USD")
if forward.Coverage != backward.Coverage || forward.Coverage != CoveragePartial {
t.Fatalf("order changed coverage: %s vs %s", forward.Coverage, backward.Coverage)
}
}
// Zero is an amount, not a completeness state. Both sessions total zero; only
// one of them has been billed for anything.
func TestKnownZeroIsCompleteAndNoUsageIsNot(t *testing.T) {
free := AggregateQuotes([]CostQuote{pricedQuote(t, 0)}, "USD")
if free.Coverage != CoverageComplete || !free.CostComplete {
t.Fatalf("a free call priced at zero is not complete: %+v", free)
}
if amount := free.Original.AmountValue().Float64(); amount != 0 {
t.Fatalf("a free call cost %v", amount)
}
empty := AggregateQuotes(nil, "USD")
if empty.Coverage != CoverageNone {
t.Fatalf("no usage = %s", empty.Coverage)
}
if amount := empty.Original.AmountValue().Float64(); amount != 0 {
t.Fatalf("an empty session cost %v", amount)
}
if free.Coverage == empty.Coverage {
t.Fatal("a billed zero and an unbilled session share one identity")
}
}
// A quote persisted before coverage existed still has to name one, and the
// name has to be the one its own cost fact supports.
func TestNormalizeFillsCoverageForAQuoteThatPredatesIt(t *testing.T) {
old := NormalizeQuote(CostQuote{Original: MoneyOf(NewAmountFromFloat(1), "USD"), Complete: true})
if old.Coverage != CoverageComplete {
t.Fatalf("an old complete quote = %s", old.Coverage)
}
if got := NormalizeQuote(CostQuote{Coverage: "mostly", IncompleteReason: "no_price"}); got.Coverage != CoverageIncomplete {
t.Fatalf("an unnamed coverage survived normalization: %s", got.Coverage)
}
kept := NormalizeQuote(CostQuote{Coverage: CoveragePartial, IncompleteReason: "no_price"})
if kept.Coverage != CoveragePartial {
t.Fatalf("normalization overwrote a coverage the quote already had: %s", kept.Coverage)
}
}
// The ledger merges by pricing key before anything aggregates, so it is a
// second fold. An entry that has seen an unpriced call may not read complete
// again because a later call arrived in another currency.
func TestLedgerEntryKeepsIncompletenessAcrossBucketing(t *testing.T) {
l := NewLedger()
unpriced := unpricedQuote(t)
unpriced.ModelRef, unpriced.UsageSource = "m", "executor"
cny := BuildQuote(QuoteInput{Usage: UsageTokens{PromptTokens: 1000}, Rates: RateCard{Input: 1, Currency: "CNY"}, ModelRef: "m", UsageSource: "executor"})
usd := BuildQuote(QuoteInput{Usage: UsageTokens{PromptTokens: 1000}, Rates: RateCard{Input: 1, Currency: "USD"}, ModelRef: "m", UsageSource: "executor"})
usd.PricingFingerprint, cny.PricingFingerprint = unpriced.PricingFingerprint, unpriced.PricingFingerprint
for _, q := range []CostQuote{unpriced, cny, usd} {
l.Add(q, UsageTokens{PromptTokens: 1000}, time.Time{})
}
total := l.Total("USD")
if total.Coverage != CoveragePartial {
t.Fatalf("ledger total coverage = %s, want partial", total.Coverage)
}
if total.CostComplete {
t.Fatal("an entry that saw an unpriced call reads as complete")
}
}
@@ -0,0 +1,68 @@
package pricing
import "testing"
// https://ai.google.dev/gemini-api/docs/pricing (read 2026-10-17): per 1M tokens,
// paid tier. gemini-3.8-flash is 0.75 in / 3.75 out / 0.075 cached through
// 2026-12-31 and 1.50 / 7.50 / 0.15 from 2027-01-01.
func TestAGeminiRateStepIsDatedNotEdited(t *testing.T) {
was := today
defer func() { today = was }()
for _, tc := range []struct {
day string
cache, input, output float64
}{
{"2026-10-17", 0.075, 0.75, 3.75},
{"2026-12-31", 0.075, 0.75, 3.75},
{"2027-01-01", 0.15, 1.50, 7.50},
{"2028-03-01", 0.15, 1.50, 7.50},
} {
today = func() string { return tc.day }
got := CurrentRate("google", "gemini-3.8-flash", "USD")
if got == nil || got.CacheHit != tc.cache || got.Input != tc.input || got.Output != tc.output {
t.Errorf("%s: rate = %+v, want %v/%v/%v", tc.day, got, tc.cache, tc.input, tc.output)
}
}
}
func TestTheRateInForceIsNotListedAsSuperseded(t *testing.T) {
was := today
defer func() { today = was }()
today = func() string { return "2026-10-17" }
if sup := SupersededRates("google", "gemini-3.8-flash", "USD"); len(sup) != 0 {
t.Fatalf("before the step nothing is superseded, got %+v", sup)
}
today = func() string { return "2027-02-01" }
sup := SupersededRates("google", "gemini-3.8-flash", "USD")
if len(sup) != 1 || sup[0].Input != 0.75 {
t.Fatalf("after the step the intro rate is superseded, got %+v", sup)
}
if !IsOfficialCard("google", "gemini-3.8-flash", RateCard{CacheHit: 0.075, Input: 0.75, Output: 3.75, Currency: "USD"}) {
t.Fatal("a stored intro card must still read as ours after the step")
}
}
func TestGoogleModelsAreBilledAtGoogleOnlyOnGoogleHosts(t *testing.T) {
if OfficialProviderForEndpoint("https://generativelanguage.googleapis.com/v1beta") != "google" {
t.Fatal("Google's own host does not read as Google")
}
if OfficialProviderForEndpoint("https://relay.example.com/v1beta") != "" {
t.Fatal("a relay was billed at Google's list price")
}
}
func TestEveryGoogleRowNamesItsSource(t *testing.T) {
n := 0
for _, e := range OfficialCatalog() {
if e.Provider != "google" {
continue
}
n++
if e.DocURL != DocGeminiPricing || e.CheckedOn != googleCheckedOn || e.Input <= 0 || e.Output <= e.Input {
t.Errorf("row %+v", e)
}
}
if n != 4 {
t.Fatalf("%d Google rows, want 4 (gemini-4-argon shows no price)", n)
}
}
@@ -0,0 +1,238 @@
package pricing
import (
"maps"
"slices"
"sort"
"strings"
"time"
)
// LedgerEntry is one occurrence-time cost fact. Ledger keys include model,
// usage source, pricing fingerprint, and legacy rate date so model switches and
// sub-agents never collapse into a single scalar.
type LedgerEntry struct {
Key string `json:"key"`
ModelRef string `json:"modelRef"`
UsageSource string `json:"usageSource"`
PricingFingerprint string `json:"pricingFingerprint"`
RateDate string `json:"rateDate,omitempty"`
OccurredAt time.Time `json:"occurredAt"`
Quote CostQuote `json:"quote"`
// Token totals for this bucket (summed).
PromptTokens int `json:"promptTokens"`
CompletionTokens int `json:"completionTokens"`
TotalTokens int `json:"totalTokens"`
CacheHitTokens int `json:"cacheHitTokens"`
CacheMissTokens int `json:"cacheMissTokens"`
// CacheUnknownTokens is prompt the endpoint reported without saying how much
// was cached. It is counted as a miss in CacheMissTokens for pricing and kept
// here so a hit rate can leave it out instead of being pulled down by it.
CacheUnknownTokens int `json:"cacheUnknownTokens,omitempty"`
RequestCount int `json:"requestCount"`
}
// LedgerKey builds the stable aggregation key.
func LedgerKey(modelRef, usageSource, pricingFingerprint, rateDate string) string {
return strings.Join([]string{
strings.TrimSpace(modelRef),
strings.TrimSpace(usageSource),
strings.TrimSpace(pricingFingerprint),
strings.TrimSpace(rateDate),
}, "|")
}
// Ledger accumulates occurrence-time quotes. Switching display currency only
// re-selects valuations already stored; it does not reprice or clear history.
type Ledger struct {
Version int `json:"version"`
Entries map[string]LedgerEntry `json:"entries"`
}
// LedgerVersion is the current persisted ledger schema.
const LedgerVersion = 1
// NewLedger returns an empty ledger.
func NewLedger() *Ledger {
return &Ledger{Version: LedgerVersion, Entries: map[string]LedgerEntry{}}
}
// Add merges a quote into the ledger under its natural key.
func (l *Ledger) Add(q CostQuote, tokens UsageTokens, occurred time.Time) {
if l == nil {
return
}
if l.Entries == nil {
l.Entries = map[string]LedgerEntry{}
}
if l.Version == 0 {
l.Version = LedgerVersion
}
if occurred.IsZero() {
occurred = time.Now().UTC()
}
q = NormalizeQuote(q)
source := q.UsageSource
if source == "" {
source = "executor"
}
key := LedgerKey(q.ModelRef, source, q.PricingFingerprint, q.RateDate)
ent, ok := l.Entries[key]
if !ok {
ent = LedgerEntry{
Key: key,
ModelRef: q.ModelRef,
UsageSource: source,
PricingFingerprint: q.PricingFingerprint,
RateDate: q.RateDate,
OccurredAt: occurred,
Quote: q,
}
// Fresh quote valuations are kept; we re-aggregate Original via sums.
ent.Quote.Valuations = cloneValuations(q.Valuations)
} else {
// Sum original when same currency; otherwise retain deterministic
// per-currency buckets. Once a bucketed entry exists, keep adding into
// those buckets so later same-currency calls are not lost.
if len(ent.Quote.OriginalTotals) > 0 {
ent.Quote.OriginalTotals = mergeOriginalTotals(ent.Quote.OriginalTotals, q)
ent.Quote.DisplayComplete = false
ent.Quote.Complete = false
ent.Quote.DisplayStatus = DisplayStatusBucketed
ent.Quote.AggregateMode = AggregateModeCurrencyBuckets
ent.Quote.IncompleteReason = "mixed_original_currencies"
} else {
sum, err := AddMoney(ent.Quote.Original, q.Original)
if err != nil {
ent.Quote.OriginalTotals = mergeOriginalTotals([]Money{ent.Quote.Original}, q)
ent.Quote.DisplayComplete = false
ent.Quote.Complete = false
ent.Quote.DisplayStatus = DisplayStatusBucketed
ent.Quote.AggregateMode = AggregateModeCurrencyBuckets
ent.Quote.IncompleteReason = "mixed_original_currencies"
} else {
ent.Quote.Original = sum
}
}
for code, v := range q.Valuations {
code = NormalizeCurrency(code)
if prev, ok := ent.Quote.Valuations[code]; ok {
added, err := AddMoney(prev.Money, v.Money)
if err == nil {
prev.Money = added
if v.Stale {
prev.Stale = true
}
ent.Quote.Valuations[code] = prev
}
} else {
if ent.Quote.Valuations == nil {
ent.Quote.Valuations = map[string]Valuation{}
}
ent.Quote.Valuations[code] = v
}
}
if q.Estimated {
ent.Quote.Estimated = true
}
if !q.Complete {
ent.Quote.DisplayComplete = false
ent.Quote.Complete = false
if ent.Quote.IncompleteReason == "" {
ent.Quote.IncompleteReason = q.IncompleteReason
}
}
ent.Quote.Coverage = FoldCoverage(ent.Quote.Coverage, q.Coverage)
ent.Quote.CostComplete = ent.Quote.Coverage == CoverageComplete
if ent.Quote.DisplayStatus != DisplayStatusBucketed {
ent.Quote.DisplayStatus = q.DisplayStatus
}
}
ent.PromptTokens += tokens.PromptTokens
ent.CompletionTokens += tokens.CompletionTokens
ent.TotalTokens += tokens.PromptTokens + tokens.CompletionTokens
if tokens.CacheHitTokens+tokens.CacheMissTokens > 0 {
ent.CacheHitTokens += tokens.CacheHitTokens
ent.CacheMissTokens += tokens.CacheMissTokens
} else {
ent.CacheMissTokens += tokens.PromptTokens
ent.CacheUnknownTokens += tokens.PromptTokens
}
ent.RequestCount++
if occurred.After(ent.OccurredAt) {
ent.OccurredAt = occurred
}
l.Entries[key] = ent
}
func mergeOriginalTotals(existing []Money, q CostQuote) []Money {
amounts := map[string]Amount{}
add := func(m Money) {
currency := NormalizeCurrency(m.Currency)
if currency == "" {
return
}
amounts[currency] = amounts[currency].Add(m.AmountValue())
}
for _, m := range existing {
add(m)
}
if len(q.OriginalTotals) > 0 {
for _, m := range q.OriginalTotals {
add(m)
}
} else {
add(q.Original)
}
codes := slices.Sorted(maps.Keys(amounts))
out := make([]Money, 0, len(codes))
for _, code := range codes {
out = append(out, MoneyOf(amounts[code], code))
}
return out
}
func cloneValuations(in map[string]Valuation) map[string]Valuation {
if len(in) == 0 {
return map[string]Valuation{}
}
out := make(map[string]Valuation, len(in))
maps.Copy(out, in)
return out
}
// Total returns the aggregate CostQuote for a display currency.
func (l *Ledger) Total(display string) CostQuote {
if l == nil || len(l.Entries) == 0 {
return AggregateQuotes(nil, display)
}
quotes := make([]CostQuote, 0, len(l.Entries))
for _, ent := range l.Entries {
q := ent.Quote
// Refresh valuation moneys already summed in entries.
quotes = append(quotes, q)
}
// Stable order for determinism.
sort.Slice(quotes, func(i, j int) bool {
return quotes[i].ModelRef+quotes[i].UsageSource < quotes[j].ModelRef+quotes[j].UsageSource
})
return AggregateQuotes(quotes, display)
}
// SelectDisplay rebinds Selected on every entry and the total without
// recomputing occurrence-time pricing facts.
func (l *Ledger) SelectDisplay(display string) CostQuote {
return l.Total(display)
}
// EntriesBySource groups ledger entries by usage source for status panels.
func (l *Ledger) EntriesBySource() map[string][]LedgerEntry {
out := map[string][]LedgerEntry{}
if l == nil {
return out
}
for _, ent := range l.Entries {
out[ent.UsageSource] = append(out[ent.UsageSource], ent)
}
return out
}
@@ -0,0 +1,88 @@
package pricing
import (
"strings"
"time"
)
// LegacyUsageRecord is the minimum shape needed to migrate old telemetry.
type LegacyUsageRecord struct {
SessionCost float64
SessionCurrency string
// EndedAt is retained for historical ordering and identity valuation dates.
EndedAt time.Time
// Tokens optional for ledger completeness.
PromptTokens int
CompletionTokens int
ModelRef string
UsageSource string
}
// MigrateLegacyUsage builds a CostQuote from pre-CostQuote telemetry.
// Records that cannot be valued are marked legacy_estimate; wiped mixed-currency
// zeros are not reconstructed from current price tables.
func MigrateLegacyUsage(rec LegacyUsageRecord) CostQuote {
cur := NormalizeCurrency(rec.SessionCurrency)
if cur == "" {
// Historic defaults often used ¥ symbol without code.
if strings.Contains(rec.SessionCurrency, "¥") || strings.Contains(rec.SessionCurrency, "¥") {
cur = "CNY"
} else if strings.Contains(rec.SessionCurrency, "$") {
cur = "USD"
}
}
// Wiped mixed-currency totals (legacy code zeroed them) and empty rows must
// not be reconstructed from current price tables.
if rec.SessionCost <= 0 || cur == "" {
reason := "legacy_unrecoverable"
if rec.SessionCost == 0 && strings.TrimSpace(rec.SessionCurrency) != "" {
// Explicit zero with a currency often means a prior mixed-currency wipe.
reason = "legacy_wiped_or_zero"
} else if rec.SessionCost < 0 {
reason = "legacy_invalid_amount"
}
return CostQuote{
Original: MoneyOf(Zero, cur),
Estimated: true,
CostComplete: false,
Coverage: CoverageIncomplete,
DisplayComplete: false,
Complete: false,
DisplayStatus: DisplayStatusUnavailable,
LegacyEstimate: true,
IncompleteReason: reason,
ModelRef: rec.ModelRef,
UsageSource: rec.UsageSource,
}
}
amount := NewAmountFromFloat(rec.SessionCost)
q := CostQuote{
Original: MoneyOf(amount, cur),
Valuations: map[string]Valuation{},
Estimated: true,
CostComplete: true,
Coverage: CoverageComplete,
DisplayComplete: true,
Complete: true,
DisplayStatus: DisplayStatusMatched,
AggregateMode: AggregateModeSingleCurrency,
LegacyEstimate: true,
ModelRef: rec.ModelRef,
UsageSource: rec.UsageSource,
BillingMode: BillingModePAYG,
}
ended := rec.EndedAt
if ended.IsZero() {
ended = time.Now().UTC()
}
asOf := ended.UTC().Format("2006-01-02")
q.Valuations[cur] = Valuation{
Money: q.Original,
Basis: BasisIdentity,
Source: "legacy_telemetry",
AsOf: asOf,
}
m := q.Original
q.Selected = &m
return q
}
@@ -0,0 +1,20 @@
package pricing
import "testing"
func TestMigrateLegacyUsageIdentityOnly(t *testing.T) {
q := MigrateLegacyUsage(LegacyUsageRecord{SessionCost: 1.25, SessionCurrency: "USD"})
if !q.CostComplete || !q.DisplayComplete || !q.Complete || q.Selected == nil {
t.Fatalf("migrated quote = %+v", q)
}
if len(q.Valuations) != 1 || q.Valuations["USD"].Basis != BasisIdentity {
t.Fatalf("migrated valuations = %+v", q.Valuations)
}
}
func TestMigrateLegacyWipedZeroDoesNotGuess(t *testing.T) {
q := MigrateLegacyUsage(LegacyUsageRecord{SessionCurrency: "CNY"})
if q.CostComplete || q.Complete || q.DisplayStatus != DisplayStatusUnavailable || q.IncompleteReason != "legacy_wiped_or_zero" {
t.Fatalf("wiped quote = %+v", q)
}
}
@@ -0,0 +1,28 @@
package pricing
import "testing"
func TestMimoV26RatesFollowTheVendorPage(t *testing.T) {
cases := []struct {
model string
hit, miss, out float64
v25Twin string
}{
{"mimo-v2.6-pro", 0.025, 3, 6, "mimo-v2.5-pro"},
{"mimo-v2.6-flash", 0.02, 1, 2, "mimo-v2.5"},
{"mimo-v2.6-pro-ultraspeed", 0.25, 30, 60, ""},
}
for _, tc := range cases {
got := CurrentRate("mimo", tc.model, "CNY")
if got == nil || got.CacheHit != tc.hit || got.Input != tc.miss || got.Output != tc.out {
t.Fatalf("%s rate = %+v, want %v/%v/%v", tc.model, got, tc.hit, tc.miss, tc.out)
}
if tc.v25Twin == "" {
continue
}
old := CurrentRate("mimo", tc.v25Twin, "CNY")
if old == nil || *old != *got {
t.Fatalf("%s must keep its published rate beside %s: %+v vs %+v", tc.v25Twin, tc.model, old, got)
}
}
}
@@ -0,0 +1,246 @@
// Package pricing is what a model call costs: fixed-point money, the official
// rate tables, and the quote a usage record is priced by. It is data and
// arithmetic only; asking a provider for its wallet balance is model/billing's.
package pricing
import (
"fmt"
"math"
"math/big"
"strings"
)
// Money is a currency-tagged amount carried as a decimal string on the wire.
// Arithmetic uses fixed-point int64 units (1e9 fractional digits).
type Money struct {
Amount string `json:"amount"`
Currency string `json:"currency"`
}
// amountScale is 1e9: enough for per-token rates down to ~1e-15 currency units
// when aggregated over typical request sizes.
const amountScale int64 = 1_000_000_000
// Amount is a fixed-point currency magnitude in 1e-9 units of the currency.
type Amount int64
// Zero is the zero amount.
const Zero Amount = 0
// NewAmountFromFloat converts a float64 amount to fixed-point. Prefer ParseAmount
// or NewAmountFromString for values that originate as decimals.
func NewAmountFromFloat(v float64) Amount {
if math.IsNaN(v) || math.IsInf(v, 0) {
return Zero
}
r := new(big.Rat).SetFloat64(v)
if r == nil {
return Zero
}
return amountFromRat(r)
}
// NewAmountFromString parses a decimal amount string.
func NewAmountFromString(s string) (Amount, error) {
s = strings.TrimSpace(s)
if s == "" {
return Zero, nil
}
r := new(big.Rat)
if _, ok := r.SetString(s); !ok {
return Zero, fmt.Errorf("billing: invalid amount %q", s)
}
return amountFromRat(r), nil
}
// ParseAmount is an alias for NewAmountFromString.
func ParseAmount(s string) (Amount, error) { return NewAmountFromString(s) }
func amountFromRat(r *big.Rat) Amount {
if r == nil {
return Zero
}
scaled := new(big.Rat).Mul(r, big.NewRat(amountScale, 1))
// Round half away from zero.
num := new(big.Int).Set(scaled.Num())
den := new(big.Int).Set(scaled.Denom())
half := new(big.Int).Rsh(den, 1)
if scaled.Sign() >= 0 {
num.Add(num, half)
} else {
num.Sub(num, half)
}
quot := new(big.Int).Quo(num, den)
if !quot.IsInt64() {
if scaled.Sign() >= 0 {
return Amount(math.MaxInt64)
}
return Amount(math.MinInt64)
}
return Amount(quot.Int64())
}
// String returns a trimmed decimal representation.
func (a Amount) String() string {
neg := a < 0
v := int64(a)
if neg {
v = -v
}
whole := v / amountScale
frac := v % amountScale
s := fmt.Sprintf("%d.%09d", whole, frac)
s = strings.TrimRight(s, "0")
s = strings.TrimRight(s, ".")
if s == "" || s == "-" {
s = "0"
}
if neg && s != "0" {
return "-" + s
}
return s
}
// Float64 approximates the amount for legacy float fields only.
func (a Amount) Float64() float64 {
return float64(a) / float64(amountScale)
}
// Add returns a+b. Same-currency only at the Money layer.
func (a Amount) Add(b Amount) Amount {
sum := int64(a) + int64(b)
// Saturate on overflow rather than wrap.
if (b > 0 && sum < int64(a)) || (b < 0 && sum > int64(a)) {
if b > 0 {
return Amount(math.MaxInt64)
}
return Amount(math.MinInt64)
}
return Amount(sum)
}
// MulRate multiplies by a float rate for a pricing calculation.
func (a Amount) MulRate(rate float64) Amount {
if a == 0 || rate == 0 || math.IsNaN(rate) || math.IsInf(rate, 0) {
return Zero
}
r := new(big.Rat).SetFloat64(rate)
if r == nil {
return Zero
}
ar := big.NewRat(int64(a), amountScale)
return amountFromRat(ar.Mul(ar, r))
}
// MoneyOf builds a Money value.
func MoneyOf(amount Amount, currency string) Money {
return Money{Amount: amount.String(), Currency: NormalizeCurrency(currency)}
}
// AmountValue returns the fixed-point amount, or zero on parse error.
func (m Money) AmountValue() Amount {
a, err := NewAmountFromString(m.Amount)
if err != nil {
return Zero
}
return a
}
// IsZero reports whether the money amount is empty or zero.
func (m Money) IsZero() bool {
return m.AmountValue() == Zero
}
// Float64 is a legacy adapter. Prefer Amount strings for aggregation.
func (m Money) Float64() float64 { return m.AmountValue().Float64() }
// NormalizeCurrency maps symbols and aliases to ISO-4217 codes when known.
// Unknown three-letter codes pass through uppercased; empty stays empty.
func NormalizeCurrency(currency string) string {
value := strings.TrimSpace(currency)
if value == "" {
return ""
}
switch strings.ToUpper(value) {
case "CNY", "RMB", "CNH", "YUAN", "RENMINBI":
return "CNY"
case "USD", "US$", "DOLLAR", "DOLLARS":
return "USD"
case "EUR", "EURO", "EUROS":
return "EUR"
case "GBP", "POUND", "POUNDS", "STERLING":
return "GBP"
case "JPY", "YEN":
return "JPY"
}
switch value {
case "¥", "¥":
return "CNY"
case "$":
return "USD"
case "€":
return "EUR"
case "£":
return "GBP"
}
if len(value) == 3 {
allAlpha := true
for _, r := range value {
if (r < 'a' || r > 'z') && (r < 'A' || r > 'Z') {
allAlpha = false
break
}
}
if allAlpha {
return strings.ToUpper(value)
}
}
return value
}
// CurrencySymbol returns a compact display symbol for an ISO code or symbol.
func CurrencySymbol(currency string) string {
switch NormalizeCurrency(currency) {
case "CNY", "JPY":
return "¥"
case "USD":
return "$"
case "EUR":
return "€"
case "GBP":
return "£"
case "":
return "¥"
default:
code := NormalizeCurrency(currency)
if len(code) == 3 {
return code + " "
}
return currency
}
}
// SameCurrency reports whether two codes normalize equal and non-empty.
func SameCurrency(a, b string) bool {
na, nb := NormalizeCurrency(a), NormalizeCurrency(b)
return na != "" && na == nb
}
// AddMoney adds two Money values of the same currency. Mixed currencies error.
func AddMoney(a, b Money) (Money, error) {
ca, cb := NormalizeCurrency(a.Currency), NormalizeCurrency(b.Currency)
if ca == "" {
ca = cb
}
if cb == "" {
cb = ca
}
if ca == "" && cb == "" {
return Money{Amount: "0", Currency: ""}, nil
}
if ca != cb {
return Money{}, fmt.Errorf("billing: cannot add %s and %s", ca, cb)
}
sum := a.AmountValue().Add(b.AmountValue())
return MoneyOf(sum, ca), nil
}
@@ -0,0 +1,56 @@
package pricing
import (
"testing"
)
func TestAmountRoundTrip(t *testing.T) {
a, err := NewAmountFromString("1.23456789")
if err != nil {
t.Fatal(err)
}
if got := a.String(); got != "1.23456789" {
t.Fatalf("String = %q", got)
}
if a.Float64() < 1.23456788 || a.Float64() > 1.23456790 {
t.Fatalf("Float64 = %v", a.Float64())
}
}
func TestAddMoneyRejectsMixedCurrency(t *testing.T) {
_, err := AddMoney(MoneyOf(NewAmountFromFloat(1), "CNY"), MoneyOf(NewAmountFromFloat(1), "USD"))
if err == nil {
t.Fatal("expected mixed currency error")
}
}
func TestAddMoneySameCurrency(t *testing.T) {
sum, err := AddMoney(MoneyOf(NewAmountFromFloat(1.5), "USD"), MoneyOf(NewAmountFromFloat(2.25), "USD"))
if err != nil {
t.Fatal(err)
}
if sum.Currency != "USD" || sum.Amount != "3.75" {
t.Fatalf("sum = %+v", sum)
}
}
func TestNormalizeCurrency(t *testing.T) {
cases := map[string]string{
"¥": "CNY", "$": "USD", "rmb": "CNY", "usd": "USD", "eur": "EUR",
}
for in, want := range cases {
if got := NormalizeCurrency(in); got != want {
t.Errorf("NormalizeCurrency(%q) = %q, want %q", in, got, want)
}
}
}
func TestOriginalCostMatchesLegacySemantics(t *testing.T) {
// Same as provider.Pricing.Cost cache-write split test: miss 500k, write 100k billed 200k, input rate 2 → 1.2
amt := OriginalCostAmount(RateCard{Input: 2, Currency: "USD"}, UsageTokens{
CacheMissTokens: 500_000, CacheWriteTokens: 100_000, CacheWriteBilledTokens: 200_000,
})
if got := amt.Float64(); got < 1.199 || got > 1.201 {
t.Fatalf("cost = %v, want ~1.2", got)
}
}
@@ -0,0 +1,29 @@
package pricing
import "testing"
func TestIsOfficialCardRecognisesEveryGenerationWeShipped(t *testing.T) {
for _, tc := range []struct {
name string
model string
card RateCard
want bool
}{
{"current base", "deepseek-flash", RateCard{CacheHit: 0.003, Input: 0.15, Output: 0.6, Currency: "USD"}, true},
{"current peak", "deepseek-flash", RateCard{CacheHit: 0.006, Input: 0.3, Output: 1.2, Currency: "USD"}, true},
{"superseded base", "deepseek-flash", RateCard{CacheHit: 0.0028, Input: 0.14, Output: 0.28, Currency: "USD"}, true},
{"superseded peak", "deepseek-flash", RateCard{CacheHit: 0.014, Input: 0.44, Output: 1.32, Currency: "USD"}, true},
{"retired name prices as flash", "deepseek-v4-flash", RateCard{CacheHit: 0.003, Input: 0.15, Output: 0.6, Currency: "USD"}, true},
{"cny card", "deepseek-flash", RateCard{CacheHit: 0.02, Input: 1, Output: 4, Currency: "CNY"}, true},
{"peak numbers with the cached price missing", "deepseek-flash", RateCard{CacheHit: 0, Input: 0.3, Output: 1.2, Currency: "USD"}, false},
{"cny numbers labelled usd", "deepseek-flash", RateCard{CacheHit: 0.02, Input: 1, Output: 4, Currency: "USD"}, false},
{"a model the book does not price", "deepseek-unknown", RateCard{CacheHit: 0.003, Input: 0.15, Output: 0.6, Currency: "USD"}, false},
} {
if got := IsOfficialCard("deepseek", tc.model, tc.card); got != tc.want {
t.Errorf("%s: IsOfficialCard = %v, want %v", tc.name, got, tc.want)
}
}
if IsOfficialCard("someone-else", "deepseek-flash", RateCard{CacheHit: 0.003, Input: 0.15, Output: 0.6, Currency: "USD"}) {
t.Error("another vendor's name matched DeepSeek's table")
}
}
@@ -0,0 +1,790 @@
package pricing
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"slices"
"strings"
"time"
)
// Billing modes.
const (
BillingModePAYG = "payg"
BillingModeSubscriptionEquivalent = "subscription_equivalent"
)
// Valuation basis values.
const (
BasisIdentity = "identity"
BasisOfficialTable = "official_table"
// BasisFX is retained for decoding pre-v6 persisted quotes only. New
// quotes never create FX valuations.
BasisFX = "fx"
)
// DisplayStatus describes whether a quote can satisfy its requested display.
// It is deliberately separate from CostComplete: a known price-book amount
// can exist even when no requested regional valuation is available.
const (
DisplayStatusMatched = "matched"
DisplayStatusFallbackOriginal = "fallback_original"
DisplayStatusBucketed = "bucketed"
DisplayStatusUnavailable = "unavailable"
)
// Aggregate modes describe how a session/run total was formed.
const (
AggregateModeSingleCurrency = "single_currency"
AggregateModeCommonValuation = "common_valuation"
AggregateModeCurrencyBuckets = "currency_buckets"
)
// Rate bands name which side of a vendor's peak window a call was billed on.
// An aggregate is mixed when its calls fell on both sides.
const (
RateBandPeak = "peak"
RateBandOffPeak = "off_peak"
RateBandMixed = "mixed"
)
// DisplayRequest identifies how a caller selected the requested presentation
// currency. The source is runtime context only; it is never persisted as a
// provider price or wallet fact.
type DisplayRequest struct {
Currency string `json:"currency,omitempty"`
Source string `json:"source,omitempty"`
}
const (
DisplaySourceExplicit = "explicit"
DisplaySourceWallet = "wallet"
DisplaySourceAuto = "auto"
)
func (r DisplayRequest) NormalizedCurrency() string {
return NormalizeCurrency(r.Currency)
}
// CostQuote is the host-side quoted cost for one model call (or an aggregate).
// Original is the pricing-table currency fact; valuations hold identity or
// official regional rate-card estimates. Selected is the display pick for the
// current preference.
type CostQuote struct {
Original Money `json:"original"`
// OriginalTotals is populated for mixed-currency aggregates. Individual
// quotes and single-currency totals may omit it.
OriginalTotals []Money `json:"originalTotals,omitempty"`
Valuations map[string]Valuation `json:"valuations,omitempty"`
Selected *Money `json:"selected,omitempty"`
BillingMode string `json:"billingMode,omitempty"`
Estimated bool `json:"estimated"`
// CostComplete means usage and the price-book amount are known. It does not
// imply that a requested display currency is available.
CostComplete bool `json:"costComplete"`
// Coverage is the same fact with the states a boolean cannot hold: an
// aggregate that priced only some of its calls, and one that billed nothing.
Coverage string `json:"coverage,omitempty"`
// DisplayComplete means a single selected amount satisfies the display
// request. Complete is retained as the old wire alias.
DisplayComplete bool `json:"displayComplete"`
Complete bool `json:"complete"`
DisplayStatus string `json:"displayStatus,omitempty"`
AggregateMode string `json:"aggregateMode,omitempty"`
// ModelRef / UsageSource / PricingFingerprint identify the ledger key.
ModelRef string `json:"modelRef,omitempty"`
UsageSource string `json:"usageSource,omitempty"`
PricingFingerprint string `json:"pricingFingerprint,omitempty"`
RateDate string `json:"rateDate,omitempty"` // YYYY-MM-DD of FX used, if any
IncompleteReason string `json:"incompleteReason,omitempty"`
LegacyEstimate bool `json:"legacyEstimate,omitempty"`
CatalogSource string `json:"catalogSource,omitempty"`
// RateBand is set only for a rate confirmed as a vendor's own schedule; an
// aggregate that includes any call without one carries none.
RateBand string `json:"rateBand,omitempty"`
}
// Valuation is one currency view of a cost fact.
type Valuation struct {
Money Money `json:"money"`
Basis string `json:"basis"`
Source string `json:"source"`
AsOf string `json:"asOf"` // YYYY-MM-DD
Rate *RateSnapshot `json:"rateSnapshot,omitempty"`
Stale bool `json:"stale,omitempty"`
}
// RateSnapshot records the FX observation used for a valuation.
type RateSnapshot struct {
Base string `json:"base"`
Quote string `json:"quote"`
Rate float64 `json:"rate"`
Source string `json:"source"`
AsOf string `json:"asOf"`
Stale bool `json:"stale,omitempty"`
}
// RateCard is the per-1M-token price used to compute original cost. It mirrors
// provider.Pricing without importing that package (billing is a leaf).
type RateCard struct {
CacheHit float64 // per 1M cached prompt tokens
Input float64 // per 1M uncached prompt tokens
Output float64 // per 1M completion tokens
Currency string // ISO or symbol; normalized on quote
}
// UsageTokens is the token breakdown needed for cost. Mirrors provider.Usage
// fields without importing provider.
type UsageTokens struct {
PromptTokens int
CompletionTokens int
CacheHitTokens int
CacheMissTokens int
CacheWriteTokens int
CacheWriteBilledTokens float64
Estimated bool
}
// QuoteInput drives a single CostQuote computation.
type QuoteInput struct {
Usage UsageTokens
Rates RateCard
OccurredAt time.Time
DisplayCurrency string // compatibility alias for Display.Currency
Display DisplayRequest // runtime display request; empty means auto
BillingMode string
ModelRef string
UsageSource string
CatalogSource string
PricingFingerprint string
// ProviderKind is deepseek|longcat|mimo|… for official dual-table lookup.
// When empty, ModelRef and Rates are used to infer a catalog match.
ProviderKind string
// ModelID is the bare model id (e.g. deepseek-v4-flash). Empty → parse ModelRef.
ModelID string
}
// OriginalCostAmount computes the fixed-point original cost from rates + tokens.
// Mirrors the historic provider.Pricing.Cost semantics.
func OriginalCostAmount(rates RateCard, u UsageTokens) Amount {
hit := u.CacheHitTokens
miss := u.CacheMissTokens
if hit+miss == 0 && u.PromptTokens > 0 {
miss = u.PromptTokens
} else if miss == 0 && hit > 0 && u.PromptTokens > hit {
miss = u.PromptTokens - hit
}
write := u.CacheWriteTokens
write = max(write, 0)
write = min(write, miss)
billedWrite := 0.0
if write > 0 {
billedWrite = u.CacheWriteBilledTokens
if billedWrite <= 0 {
billedWrite = float64(write)
}
}
inputTokenUnits := float64(miss-write) + billedWrite
// Combine cached input, uncached input, and output charges per million tokens.
total := (float64(hit)*rates.CacheHit +
inputTokenUnits*rates.Input +
float64(u.CompletionTokens)*rates.Output) / 1e6
return NewAmountFromFloat(total)
}
// PricingFingerprint hashes a rate card for ledger keys.
func PricingFingerprint(rates RateCard) string {
cur := NormalizeCurrency(rates.Currency)
raw := fmt.Sprintf("%s|%.12g|%.12g|%.12g", cur, rates.CacheHit, rates.Input, rates.Output)
sum := sha256.Sum256([]byte(raw))
return hex.EncodeToString(sum[:8])
}
// BuildQuote computes original cost and CNY/USD valuations.
func BuildQuote(in QuoteInput) CostQuote {
state := newQuoteBuildState(in)
for _, target := range []string{"CNY", "USD"} {
state.addTargetValuation(target)
}
state.selectDisplay()
return state.quote
}
type quoteBuildState struct {
input QuoteInput
currency string
amount Amount
occurred time.Time
official CatalogEntry
isOfficial bool
valuationFailures map[string]string
quote CostQuote
}
func newQuoteBuildState(in QuoteInput) *quoteBuildState {
currency := NormalizeCurrency(in.Rates.Currency)
if currency == "" {
currency = "CNY"
}
mode := strings.TrimSpace(in.BillingMode)
if mode == "" {
mode = BillingModePAYG
}
occurred := in.OccurredAt
if occurred.IsZero() {
occurred = time.Now().UTC()
}
fingerprint := strings.TrimSpace(in.PricingFingerprint)
if fingerprint == "" {
fingerprint = PricingFingerprint(in.Rates)
}
amount := OriginalCostAmount(in.Rates, in.Usage)
q := CostQuote{
Original: MoneyOf(amount, currency),
Valuations: map[string]Valuation{},
BillingMode: mode,
Estimated: true,
CostComplete: true,
Coverage: CoverageComplete,
DisplayComplete: true,
Complete: true,
DisplayStatus: DisplayStatusMatched,
AggregateMode: AggregateModeSingleCurrency,
ModelRef: strings.TrimSpace(in.ModelRef),
UsageSource: strings.TrimSpace(in.UsageSource),
PricingFingerprint: fingerprint,
CatalogSource: strings.TrimSpace(in.CatalogSource),
}
q.Valuations[currency] = Valuation{
Money: q.Original, Basis: BasisIdentity,
Source: firstNonEmpty(in.CatalogSource, "rate_card"),
AsOf: occurred.UTC().Format("2006-01-02"),
}
state := &quoteBuildState{
input: in, currency: currency, amount: amount, occurred: occurred,
valuationFailures: map[string]string{}, quote: q,
}
if !usageHasFacts(in.Usage) {
state.markIncomplete("missing_price_or_usage")
}
state.matchOfficialCatalog()
state.applyOfficialSchedule()
return state
}
// applyOfficialSchedule re-prices the turn at the vendor's rate for the hour it
// actually happened. Only a rate confirmed as the vendor's own earns this: a
// custom price is the user's own arrangement, and projecting someone else's
// peak schedule onto it would invent a number nobody published.
func (s *quoteBuildState) applyOfficialSchedule() {
if !s.isOfficial || s.official.Peak == nil {
return
}
s.quote.RateBand = RateBandOffPeak
if s.official.Window.IsPeak(s.occurred) {
s.quote.RateBand = RateBandPeak
}
rates := s.official.RatesAt(s.occurred)
if rates.CacheHit == s.input.Rates.CacheHit && rates.Input == s.input.Rates.Input && rates.Output == s.input.Rates.Output {
return
}
s.amount = OriginalCostAmount(rates, s.input.Usage)
s.quote.Original = MoneyOf(s.amount, s.currency)
s.quote.PricingFingerprint = PricingFingerprint(rates)
s.quote.Valuations[s.currency] = Valuation{
Money: s.quote.Original, Basis: BasisOfficialTable,
Source: s.official.DocURL,
AsOf: s.occurred.UTC().Format("2006-01-02"),
}
}
func quoteDisplayRequest(in QuoteInput) DisplayRequest {
request := in.Display
if request.Currency == "" && in.DisplayCurrency != "" {
request.Currency = in.DisplayCurrency
if request.Source == "" {
request.Source = DisplaySourceExplicit
}
}
if request.Source == "" {
request.Source = DisplaySourceAuto
}
return request
}
func usageHasFacts(u UsageTokens) bool {
return u.PromptTokens > 0 || u.CompletionTokens > 0 || u.CacheHitTokens > 0 ||
u.CacheMissTokens > 0 || u.CacheWriteTokens > 0 || u.CacheWriteBilledTokens > 0
}
func (s *quoteBuildState) matchOfficialCatalog() {
providerKind, modelID := resolveCatalogIdentity(s.input)
s.official, s.isOfficial = MatchesCatalog(providerKind, modelID, s.input.Rates)
if !s.isOfficial && providerKind != "" && modelID != "" {
rates := s.input.Rates
rates.Currency = s.currency
s.official, s.isOfficial = MatchesCatalog(providerKind, modelID, rates)
}
if s.isOfficial && s.quote.CatalogSource == "" {
s.quote.CatalogSource = s.official.DocURL
}
}
func (s *quoteBuildState) addTargetValuation(target string) {
if target == s.currency {
return
}
if s.addOfficialValuation(target) {
return
}
// Runtime FX has intentionally been removed. A custom price that does not
// match the official catalog remains in its original currency.
s.valuationFailures[target] = "display_unavailable"
}
func (s *quoteBuildState) addOfficialValuation(target string) bool {
if !s.isOfficial {
return false
}
peer, ok := LookupCatalog(
s.official.Provider, s.official.Model, target, s.official.BillingMode,
)
if !ok {
return false
}
s.quote.Valuations[target] = Valuation{
Money: MoneyOf(OriginalCostAmount(peer.RatesAt(s.occurred), s.input.Usage), target),
Basis: BasisOfficialTable, Source: peer.DocURL,
AsOf: s.occurred.UTC().Format("2006-01-02"),
}
return true
}
func (s *quoteBuildState) selectDisplay() {
if !s.quote.CostComplete {
s.quote.Selected = nil
s.quote.DisplayComplete = false
s.quote.Complete = false
s.quote.DisplayStatus = DisplayStatusUnavailable
return
}
display := quoteDisplayRequest(s.input).NormalizedCurrency()
if display == "" {
selected := s.quote.Original
s.quote.Selected = &selected
return
}
if valuation, ok := s.quote.Valuations[display]; ok {
selected := valuation.Money
s.quote.Selected = &selected
return
}
// The original price-book amount is still a valid cost fact. Keep it as a
// visible fallback and distinguish the display mismatch from no pricing.
selected := s.quote.Original
s.quote.Selected = &selected
s.quote.DisplayComplete = false
s.quote.Complete = false
s.quote.DisplayStatus = DisplayStatusFallbackOriginal
s.quote.IncompleteReason = firstNonEmpty(s.valuationFailures[display], "display_unavailable")
}
func (s *quoteBuildState) markIncomplete(reason string) {
s.quote.CostComplete = false
s.quote.Coverage = CoverageIncomplete
s.quote.DisplayComplete = false
s.quote.Complete = false
s.quote.DisplayStatus = DisplayStatusUnavailable
if s.quote.IncompleteReason == "" {
s.quote.IncompleteReason = reason
}
}
// SelectForDisplay returns the money for a display currency preference without
// recomputing rates. Empty display returns original.
func (q CostQuote) SelectForDisplay(display string) (Money, bool) {
display = NormalizeCurrency(display)
if display == "" {
return q.Original, true
}
if v, ok := q.Valuations[display]; ok {
return v.Money, true
}
if NormalizeCurrency(q.Original.Currency) == display {
return q.Original, true
}
return Money{}, false
}
// WithSelected returns a copy with Selected set for display.
func (q CostQuote) WithSelected(display string) CostQuote {
out := q
if m, ok := q.SelectForDisplay(display); ok {
out.Selected = &m
if quoteHasCompleteCostFact(q) {
out.Complete = true
out.IncompleteReason = ""
}
} else {
out.Selected = nil
out.Complete = false
if out.IncompleteReason == "" {
out.IncompleteReason = "display_unavailable"
}
}
return out
}
// LegacyCostFloat returns the selected (or original) amount as float64 for
// compatibility aliases cost / costUsd / total_cost.
func (q CostQuote) LegacyCostFloat() float64 {
if q.Selected != nil {
return q.Selected.Float64()
}
return q.Original.Float64()
}
// LegacyCurrencySymbol returns a display symbol for the selected/original currency.
func (q CostQuote) LegacyCurrencySymbol() string {
if q.Selected != nil && q.Selected.Currency != "" {
return CurrencySymbol(q.Selected.Currency)
}
return CurrencySymbol(q.Original.Currency)
}
// LegacyCurrencyCode returns the ISO code for selected/original.
func (q CostQuote) LegacyCurrencyCode() string {
if q.Selected != nil && q.Selected.Currency != "" {
return NormalizeCurrency(q.Selected.Currency)
}
return NormalizeCurrency(q.Original.Currency)
}
// AggregateQuotes combines occurrence-time quotes into a session/run total.
// Every entry must have the requested valuation; partial totals stay unavailable.
func AggregateQuotes(quotes []CostQuote, display string) CostQuote {
if len(quotes) == 0 {
return emptyAggregate(NormalizeCurrency(display))
}
accumulator := newQuoteAccumulator(display)
for _, quote := range quotes {
accumulator.add(quote)
}
return accumulator.finish()
}
type quoteAccumulator struct {
out CostQuote
display string
totals map[string]Amount
originalCurrency string
originalTotal Amount
originalTotals map[string]Amount
originalComplete bool
coverage string
displayComplete bool
modes map[string]struct{}
rateBands map[string]struct{}
unbanded bool
}
func emptyAggregate(display string) CostQuote {
out := CostQuote{
Original: MoneyOf(Zero, display), Valuations: map[string]Valuation{}, Estimated: true,
CostComplete: false, DisplayComplete: false, Complete: false,
Coverage: CoverageNone,
DisplayStatus: DisplayStatusUnavailable, AggregateMode: AggregateModeSingleCurrency,
IncompleteReason: "no_usage",
}
return out
}
func newQuoteAccumulator(display string) *quoteAccumulator {
return &quoteAccumulator{
out: CostQuote{Valuations: map[string]Valuation{}, Estimated: true},
display: NormalizeCurrency(display), totals: map[string]Amount{}, originalTotals: map[string]Amount{},
originalComplete: true, coverage: CoverageNone, displayComplete: true,
modes: map[string]struct{}{}, rateBands: map[string]struct{}{},
}
}
func (a *quoteAccumulator) add(quote CostQuote) {
quote = NormalizeQuote(quote)
a.coverage = FoldCoverage(a.coverage, quote.Coverage)
if quote.Coverage != CoverageComplete && a.out.IncompleteReason == "" {
a.out.IncompleteReason = quote.IncompleteReason
}
a.out.LegacyEstimate = a.out.LegacyEstimate || quote.LegacyEstimate
if quote.BillingMode != "" {
a.modes[quote.BillingMode] = struct{}{}
}
a.addRateBand(quote.RateBand)
if quote.RateDate > a.out.RateDate {
a.out.RateDate = quote.RateDate
}
originalCurrency := NormalizeCurrency(quote.Original.Currency)
if len(quote.OriginalTotals) > 0 {
for _, original := range quote.OriginalTotals {
a.addOriginal(original, NormalizeCurrency(original.Currency))
}
} else {
a.addOriginal(quote.Original, originalCurrency)
}
if a.display != "" {
_, found := quote.Valuations[a.display]
a.displayComplete = a.displayComplete && (found || originalCurrency == a.display)
}
originalIncluded := false
for code, valuation := range quote.Valuations {
code = NormalizeCurrency(code)
if code == originalCurrency {
originalIncluded = true
}
a.addValuation(code, valuation)
}
if originalCurrency != "" && !originalIncluded {
a.addValuation(originalCurrency, Valuation{
Money: quote.Original, Basis: BasisIdentity, Source: "aggregate",
AsOf: time.Now().UTC().Format("2006-01-02"),
})
}
}
func (a *quoteAccumulator) addRateBand(band string) {
switch band {
case RateBandPeak, RateBandOffPeak:
a.rateBands[band] = struct{}{}
case RateBandMixed:
a.rateBands[RateBandPeak], a.rateBands[RateBandOffPeak] = struct{}{}, struct{}{}
default:
a.unbanded = true
}
}
func (a *quoteAccumulator) rateBand() string {
if a.unbanded {
return ""
}
switch len(a.rateBands) {
case 1:
for band := range a.rateBands {
return band
}
case 2:
return RateBandMixed
}
return ""
}
func (a *quoteAccumulator) addOriginal(original Money, currency string) {
if currency != "" {
a.originalTotals[currency] = a.originalTotals[currency].Add(original.AmountValue())
}
if a.originalCurrency == "" {
a.originalCurrency = currency
a.originalTotal = original.AmountValue()
return
}
if currency != a.originalCurrency {
a.originalComplete = false
return
}
a.originalTotal = a.originalTotal.Add(original.AmountValue())
}
func (a *quoteAccumulator) addValuation(code string, valuation Valuation) {
if code == "" {
return
}
a.totals[code] = a.totals[code].Add(valuation.Money.AmountValue())
current, found := a.out.Valuations[code]
if !found {
current = valuation
}
current.Money = MoneyOf(a.totals[code], code)
current.Stale = current.Stale || valuation.Stale
a.out.Valuations[code] = current
}
// costFactsComplete reads the accumulated coverage rather than tracking the
// same fact a second time: two answers to one question drift.
func (a *quoteAccumulator) costFactsComplete() bool {
return a.coverage == CoverageComplete
}
func (a *quoteAccumulator) finish() CostQuote {
a.out.Coverage = a.coverage
a.out.CostComplete = a.costFactsComplete()
if a.originalComplete && a.originalCurrency != "" {
a.out.Original = MoneyOf(a.originalTotal, a.originalCurrency)
a.syncOriginalValuation()
} else {
a.out.Original = Money{Amount: "0"}
}
if len(a.originalTotals) > 1 {
codes := make([]string, 0, len(a.originalTotals))
for code := range a.originalTotals {
codes = append(codes, code)
}
slices.Sort(codes)
for _, code := range codes {
a.out.OriginalTotals = append(a.out.OriginalTotals, MoneyOf(a.originalTotals[code], code))
}
}
a.out.RateBand = a.rateBand()
if len(a.modes) == 1 {
for mode := range a.modes {
a.out.BillingMode = mode
}
}
if a.display == "" && a.originalComplete && a.costFactsComplete() {
selected := a.out.Original
a.out.Selected = &selected
a.out.DisplayComplete = true
a.out.Complete = true
a.out.DisplayStatus = DisplayStatusMatched
a.out.AggregateMode = AggregateModeSingleCurrency
return a.out
}
valuation, found := a.out.Valuations[a.display]
if found && a.displayComplete && a.costFactsComplete() {
selected := valuation.Money
a.out.Selected = &selected
a.out.DisplayComplete = true
a.out.Complete = true
a.out.DisplayStatus = DisplayStatusMatched
if len(a.originalTotals) > 1 {
a.out.AggregateMode = AggregateModeCommonValuation
} else {
a.out.AggregateMode = AggregateModeSingleCurrency
}
return a.out
}
a.out.DisplayComplete = false
a.out.Complete = false
if !a.costFactsComplete() {
a.out.DisplayStatus = DisplayStatusUnavailable
a.out.IncompleteReason = firstNonEmpty(a.out.IncompleteReason, "incomplete_cost_fact")
} else if a.originalComplete && a.originalCurrency != "" {
selected := a.out.Original
a.out.Selected = &selected
a.out.DisplayStatus = DisplayStatusFallbackOriginal
a.out.AggregateMode = AggregateModeSingleCurrency
a.out.IncompleteReason = firstNonEmpty(a.out.IncompleteReason, "display_unavailable")
} else {
a.out.DisplayStatus = DisplayStatusBucketed
a.out.AggregateMode = AggregateModeCurrencyBuckets
a.out.IncompleteReason = firstNonEmpty(a.out.IncompleteReason, "currency_buckets")
}
return a.out
}
// NormalizeQuote fills fields introduced after the first CostQuote wire shape.
// It is used at every persistence and transport boundary so old telemetry and
// old eventwire payloads remain safe without inventing a new amount.
func NormalizeQuote(q CostQuote) CostQuote {
if !q.CostComplete && !q.DisplayComplete && q.Complete {
q.CostComplete = true
q.DisplayComplete = true
}
if q.DisplayStatus != "" && q.DisplayStatus != DisplayStatusMatched && q.DisplayStatus != DisplayStatusFallbackOriginal && q.DisplayStatus != DisplayStatusBucketed && q.DisplayStatus != DisplayStatusUnavailable {
q.DisplayStatus = ""
}
if q.RateBand != RateBandPeak && q.RateBand != RateBandOffPeak && q.RateBand != RateBandMixed {
q.RateBand = ""
}
if q.AggregateMode != "" && q.AggregateMode != AggregateModeSingleCurrency && q.AggregateMode != AggregateModeCommonValuation && q.AggregateMode != AggregateModeCurrencyBuckets {
q.AggregateMode = ""
}
if q.DisplayStatus == "" {
switch {
case q.Complete:
q.DisplayStatus = DisplayStatusMatched
case q.Original.Currency != "" && q.Original.Amount != "" && q.IncompleteReason != "no_price":
q.DisplayStatus = DisplayStatusFallbackOriginal
default:
q.DisplayStatus = DisplayStatusUnavailable
}
}
if q.AggregateMode == "" {
if len(q.OriginalTotals) > 1 {
q.AggregateMode = AggregateModeCurrencyBuckets
} else if q.Selected != nil {
q.AggregateMode = AggregateModeSingleCurrency
}
}
if !ValidCoverage(q.Coverage) {
q.Coverage = CoverageIncomplete
if quoteHasCompleteCostFact(q) {
q.Coverage = CoverageComplete
}
}
q.Complete = q.DisplayComplete
return q
}
// quoteHasCompleteCostFact separates a known original-currency charge from a
// display-only valuation failure must not poison an exact original total,
// while unpriced and unrecoverable legacy records stay incomplete in every
// display currency.
func quoteHasCompleteCostFact(q CostQuote) bool {
if q.CostComplete {
return true
}
switch q.IncompleteReason {
case "no_price", "missing_price_or_usage", "legacy_unrecoverable",
"legacy_wiped_or_zero", "legacy_invalid_amount", "mixed_original_currencies",
"incomplete_cost_fact":
return false
}
if q.Complete {
return true
}
currency := NormalizeCurrency(q.Original.Currency)
if currency == "" {
return false
}
valuation, ok := q.Valuations[currency]
return ok && SameCurrency(valuation.Money.Currency, currency)
}
func (a *quoteAccumulator) syncOriginalValuation() {
valuation, found := a.out.Valuations[a.originalCurrency]
if !found {
valuation = Valuation{
Basis: BasisIdentity, Source: "aggregate",
AsOf: time.Now().UTC().Format("2006-01-02"),
}
}
valuation.Money = a.out.Original
a.out.Valuations[a.originalCurrency] = valuation
}
func firstNonEmpty(values ...string) string {
for _, v := range values {
if strings.TrimSpace(v) != "" {
return strings.TrimSpace(v)
}
}
return ""
}
// resolveCatalogIdentity names which official table, if any, may price a quote.
// The vendor comes only from ProviderKind, which the caller reads off the
// endpoint's host; guessing it from the entry's name or the model id would bill
// a relay as the vendor it resells. Unnamed means no official table — the user's
// own rate card, which is right rather than merely plausible.
func resolveCatalogIdentity(in QuoteInput) (provider, model string) {
provider = strings.ToLower(strings.TrimSpace(in.ProviderKind))
model = strings.TrimSpace(in.ModelID)
if model == "" {
ref := strings.TrimSpace(in.ModelRef)
if i := strings.LastIndex(ref, "/"); i >= 0 && i+1 < len(ref) {
model = ref[i+1:]
} else {
model = ref
}
}
return provider, model
}
@@ -0,0 +1,128 @@
package pricing
import (
"testing"
"time"
)
func testInput(currency, provider, model string) QuoteInput {
rates := RateCard{CacheHit: 0.02, Input: 1, Output: 4, Currency: currency}
if currency == "USD" {
rates = RateCard{CacheHit: 0.003, Input: 0.15, Output: 0.6, Currency: currency}
}
return QuoteInput{
Usage: UsageTokens{PromptTokens: 1000, CompletionTokens: 2000},
Rates: rates,
DisplayCurrency: currency,
ModelRef: model,
ProviderKind: provider,
ModelID: model,
}
}
func TestBuildQuoteIdentityAndOfficialTableOnly(t *testing.T) {
in := testInput("USD", "deepseek", "deepseek-v4-flash")
in.DisplayCurrency = "CNY"
q := BuildQuote(in)
if q.Original.Currency != "USD" || q.Original.Amount == "0" {
t.Fatalf("original = %+v", q.Original)
}
v, ok := q.Valuations["CNY"]
if !ok || v.Basis != BasisOfficialTable {
t.Fatalf("CNY valuation = %+v, ok=%v", v, ok)
}
if q.Selected == nil || q.Selected.Currency != "CNY" || !q.Complete || !q.CostComplete || !q.DisplayComplete {
t.Fatalf("quote completeness/selection = %+v", q)
}
}
func TestBuildQuoteCustomPriceFallsBackWithoutFX(t *testing.T) {
in := testInput("CNY", "deepseek", "deepseek-v4-flash")
in.Rates.Input = 99 // no longer matches the official table
in.DisplayCurrency = "USD"
q := BuildQuote(in)
if len(q.Valuations) != 1 || q.Valuations["CNY"].Basis != BasisIdentity {
t.Fatalf("unexpected valuations: %+v", q.Valuations)
}
if q.Selected == nil || q.Selected.Currency != "CNY" || q.Complete || q.DisplayComplete || !q.CostComplete {
t.Fatalf("fallback quote = %+v", q)
}
if q.DisplayStatus != DisplayStatusFallbackOriginal || q.Valuations["USD"].Basis == BasisFX {
t.Fatalf("fallback status/FX = %+v", q)
}
}
func TestAggregateMixedCurrenciesProducesBuckets(t *testing.T) {
a := BuildQuote(QuoteInput{Usage: UsageTokens{PromptTokens: 1}, Rates: RateCard{Input: 1, Currency: "CNY"}})
b := BuildQuote(QuoteInput{Usage: UsageTokens{PromptTokens: 1}, Rates: RateCard{Input: 1, Currency: "USD"}})
q := AggregateQuotes([]CostQuote{a, b}, "")
if q.DisplayStatus != DisplayStatusBucketed || q.AggregateMode != AggregateModeCurrencyBuckets || q.Selected != nil {
t.Fatalf("mixed aggregate = %+v", q)
}
if len(q.OriginalTotals) != 2 || q.OriginalTotals[0].Currency != "CNY" || q.OriginalTotals[1].Currency != "USD" {
t.Fatalf("original totals = %+v", q.OriginalTotals)
}
if !q.CostComplete || q.Complete || q.DisplayComplete {
t.Fatalf("mixed completeness = %+v", q)
}
}
func TestAggregateExplicitDisplayFallsBackToSameOriginal(t *testing.T) {
a := BuildQuote(QuoteInput{Usage: UsageTokens{PromptTokens: 1}, Rates: RateCard{Input: 1, Currency: "CNY"}})
b := BuildQuote(QuoteInput{Usage: UsageTokens{PromptTokens: 1}, Rates: RateCard{Input: 2, Currency: "CNY"}})
q := AggregateQuotes([]CostQuote{a, b}, "USD")
if q.Selected == nil || q.Selected.Currency != "CNY" || q.DisplayStatus != DisplayStatusFallbackOriginal {
t.Fatalf("fallback aggregate = %+v", q)
}
if !q.CostComplete || q.DisplayComplete || q.Complete {
t.Fatalf("fallback completeness = %+v", q)
}
}
func TestNormalizeOldQuote(t *testing.T) {
q := NormalizeQuote(CostQuote{Original: MoneyOf(NewAmountFromFloat(1), "USD"), Complete: true})
if !q.CostComplete || !q.DisplayComplete || !q.Complete || q.DisplayStatus != DisplayStatusMatched {
t.Fatalf("normalized old quote = %+v", q)
}
}
func TestBuildQuoteNoPriceIsUnavailable(t *testing.T) {
q := CostQuote{Estimated: true, CostComplete: false, DisplayComplete: false, Complete: false, DisplayStatus: DisplayStatusUnavailable, IncompleteReason: "no_price"}
q = NormalizeQuote(q)
if q.DisplayStatus != DisplayStatusUnavailable || q.Complete || q.CostComplete {
t.Fatalf("unavailable = %+v", q)
}
}
func TestBuildQuoteMissingUsageIsUnavailable(t *testing.T) {
q := BuildQuote(QuoteInput{Rates: RateCard{Input: 1, Currency: "USD"}, DisplayCurrency: "USD"})
if q.CostComplete || q.DisplayComplete || q.Complete || q.Selected != nil || q.DisplayStatus != DisplayStatusUnavailable {
t.Fatalf("missing usage = %+v", q)
}
}
func TestAggregateNoUsageIsUnavailable(t *testing.T) {
q := AggregateQuotes(nil, "USD")
if q.CostComplete || q.DisplayComplete || q.Complete || q.Selected != nil || q.DisplayStatus != DisplayStatusUnavailable {
t.Fatalf("empty aggregate = %+v", q)
}
}
func TestLedgerMixedOriginalBucketsContinueAccumulating(t *testing.T) {
l := NewLedger()
base := func(currency string, amount string) CostQuote {
return CostQuote{
Original: Money{Amount: amount, Currency: currency},
Valuations: map[string]Valuation{currency: {Money: Money{Amount: amount, Currency: currency}, Basis: BasisIdentity}},
CostComplete: true, DisplayComplete: true, Complete: true,
DisplayStatus: DisplayStatusMatched, ModelRef: "m", PricingFingerprint: "same",
}
}
l.Add(base("CNY", "1"), UsageTokens{PromptTokens: 1}, time.Time{})
l.Add(base("USD", "2"), UsageTokens{PromptTokens: 1}, time.Time{})
l.Add(base("CNY", "3"), UsageTokens{PromptTokens: 1}, time.Time{})
q := l.Total("")
if len(q.OriginalTotals) != 2 || q.OriginalTotals[0].Amount != "4" || q.OriginalTotals[1].Amount != "2" || q.Selected != nil {
t.Fatalf("ledger buckets = %+v", q)
}
}
@@ -0,0 +1,351 @@
// rates.go — every rate this project has shipped for a vendor's model, and the
// two questions that asks: what it costs now, and what it used to cost.
package pricing
import (
"slices"
"strings"
"time"
)
// rateGeneration is one published pair. Peak is nil for a generation the vendor
// billed at a single rate around the clock.
type rateGeneration struct {
Base RateCard
Peak *RateCard
// From is the first day (YYYY-MM-DD, UTC) this generation is billed; empty
// means from when it shipped. An announced change is appended with its date.
From string
}
// today is the day a dated generation is read against; a test replaces it.
var today = func() string { return time.Now().UTC().Format("2006-01-02") }
// currentIndex is the generation in force today: the last one whose date has
// come. Rows with no dated generation answer with their last, as before.
func (r officialRateRow) currentIndex() int {
day := today()
for i := len(r.Rates) - 1; i > 0; i-- {
if r.Rates[i].From == "" || r.Rates[i].From <= day {
return i
}
}
return 0
}
// officialRateRow is one vendor's model, in one currency, under one billing
// mode, with every rate this project has shipped for it.
type officialRateRow struct {
Provider string
Model string
Currency string
BillingMode string
Window *PeakWindow
DocURL string
Notes string
CheckedOn string
// Rates is oldest first; the last entry is what this costs today.
Rates []rateGeneration
}
// officialRates is the price book. A price change appends a generation rather
// than editing one: that is what leaves the superseded rate where a stored price
// can still be recognised as this project's own default instead of a number its
// user chose, which is the difference between refreshing one and losing it.
var officialRates = []officialRateRow{
{
Provider: "deepseek", Model: "deepseek-flash", Currency: "CNY",
BillingMode: BillingModePAYG, Window: deepseekPeak, DocURL: DocDeepSeekPricing, CheckedOn: deepseekCheckedOn,
Rates: []rateGeneration{
{Base: RateCard{CacheHit: 0.02, Input: 1, Output: 2, Currency: "CNY"}},
{Base: RateCard{CacheHit: 0.05, Input: 1.5, Output: 4.5, Currency: "CNY"},
Peak: &RateCard{CacheHit: 0.10, Input: 3.0, Output: 9.0, Currency: "CNY"}},
{Base: RateCard{CacheHit: 0.02, Input: 1, Output: 4, Currency: "CNY"},
Peak: &RateCard{CacheHit: 0.04, Input: 2, Output: 8, Currency: "CNY"}},
},
},
{
Provider: "deepseek", Model: "deepseek-flash", Currency: "USD",
BillingMode: BillingModePAYG, Window: deepseekPeak, DocURL: DocDeepSeekPricing, CheckedOn: deepseekCheckedOn,
Rates: []rateGeneration{
{Base: RateCard{CacheHit: 0.0028, Input: 0.14, Output: 0.28, Currency: "USD"}},
{Base: RateCard{CacheHit: 0.007, Input: 0.22, Output: 0.66, Currency: "USD"},
Peak: &RateCard{CacheHit: 0.014, Input: 0.44, Output: 1.32, Currency: "USD"}},
{Base: RateCard{CacheHit: 0.003, Input: 0.15, Output: 0.6, Currency: "USD"},
Peak: &RateCard{CacheHit: 0.006, Input: 0.3, Output: 1.2, Currency: "USD"}},
},
},
{
Provider: "deepseek", Model: "deepseek-v4-pro", Currency: "CNY",
BillingMode: BillingModePAYG, Window: deepseekPeak, DocURL: DocDeepSeekPricing, CheckedOn: deepseekCheckedOn,
Rates: []rateGeneration{
{Base: RateCard{CacheHit: 0.025, Input: 3, Output: 6, Currency: "CNY"}},
{Base: RateCard{CacheHit: 0.15, Input: 4.5, Output: 13.5, Currency: "CNY"},
Peak: &RateCard{CacheHit: 0.30, Input: 9.0, Output: 27.0, Currency: "CNY"}},
},
},
{
Provider: "deepseek", Model: "deepseek-v4-pro", Currency: "USD",
BillingMode: BillingModePAYG, Window: deepseekPeak, DocURL: DocDeepSeekPricing, CheckedOn: deepseekCheckedOn,
Rates: []rateGeneration{
{Base: RateCard{CacheHit: 0.003625, Input: 0.435, Output: 0.87, Currency: "USD"}},
{Base: RateCard{CacheHit: 0.022, Input: 0.66, Output: 1.98, Currency: "USD"},
Peak: &RateCard{CacheHit: 0.044, Input: 1.32, Output: 3.96, Currency: "USD"}},
},
},
// LongCat's launch discount, for which the vendor publishes no end date. List
// price is CNY 0.10/5/20, USD 0.015/0.75/2.95 — what these revert to, and why
// CheckedOn earns its place here.
{
Provider: "longcat", Model: "LongCat-2.0", Currency: "CNY",
BillingMode: BillingModePAYG, DocURL: DocLongCatPricingCNY, Notes: "launch_discount_no_end_date", CheckedOn: checkedOn,
Rates: []rateGeneration{{Base: RateCard{CacheHit: 0.04, Input: 2, Output: 8, Currency: "CNY"}}},
},
{
Provider: "longcat", Model: "LongCat-2.0", Currency: "USD",
BillingMode: BillingModePAYG, DocURL: DocLongCatPricingUSD, Notes: "launch_discount_no_end_date", CheckedOn: checkedOn,
Rates: []rateGeneration{{Base: RateCard{CacheHit: 0.006, Input: 0.30, Output: 1.20, Currency: "USD"}}},
},
// Google: the two flash models double on 2027-01-01, hence the dated generation.
// gemini-3.1-pro-preview is the rate up to 200k prompt tokens; gemini-4-argon has no price.
{
Provider: "google", Model: "gemini-3.8-flash", Currency: "USD",
BillingMode: BillingModePAYG, DocURL: DocGeminiPricing, CheckedOn: googleCheckedOn,
Rates: []rateGeneration{
{Base: RateCard{CacheHit: 0.075, Input: 0.75, Output: 3.75, Currency: "USD"}},
{From: "2027-01-01", Base: RateCard{CacheHit: 0.15, Input: 1.50, Output: 7.50, Currency: "USD"}},
},
},
{
Provider: "google", Model: "gemini-3.6-flash", Currency: "USD",
BillingMode: BillingModePAYG, DocURL: DocGeminiPricing, CheckedOn: googleCheckedOn,
Rates: []rateGeneration{
{Base: RateCard{CacheHit: 0.075, Input: 0.75, Output: 3.75, Currency: "USD"}},
{From: "2027-01-01", Base: RateCard{CacheHit: 0.15, Input: 1.50, Output: 7.50, Currency: "USD"}},
},
},
{
Provider: "google", Model: "gemini-3.5-flash-lite", Currency: "USD",
BillingMode: BillingModePAYG, DocURL: DocGeminiPricing, CheckedOn: googleCheckedOn,
Rates: []rateGeneration{{Base: RateCard{CacheHit: 0.03, Input: 0.30, Output: 2.50, Currency: "USD"}}},
},
{
Provider: "google", Model: "gemini-3.1-pro-preview", Currency: "USD",
BillingMode: BillingModePAYG, DocURL: DocGeminiPricing, Notes: "up_to_200k_prompt_tokens", CheckedOn: googleCheckedOn,
Rates: []rateGeneration{{Base: RateCard{CacheHit: 0.20, Input: 2.00, Output: 12.00, Currency: "USD"}}},
},
// MiMo domestic PAYG; Token Plan bills the same rates as subscription_equivalent.
{
Provider: "mimo", Model: "mimo-v2.5-pro", Currency: "CNY",
BillingMode: BillingModePAYG, DocURL: DocMiMoPAYG, CheckedOn: checkedOn,
Rates: []rateGeneration{{Base: RateCard{CacheHit: 0.025, Input: 3, Output: 6, Currency: "CNY"}}},
},
{
Provider: "mimo", Model: "mimo-v2.5", Currency: "CNY",
BillingMode: BillingModePAYG, DocURL: DocMiMoPAYG, CheckedOn: checkedOn,
Rates: []rateGeneration{{Base: RateCard{CacheHit: 0.02, Input: 1, Output: 2, Currency: "CNY"}}},
},
// Not on the vendor's price page as of CheckedOn. Kept because removing a rate
// is what turns a still-configured model's cost silently into zero; a stale
// rate at least reads as a number somebody can question.
{
Provider: "mimo", Model: "mimo-v2-flash", Currency: "CNY",
BillingMode: BillingModePAYG, DocURL: DocMiMoPAYG, Notes: "not_on_vendor_page", CheckedOn: checkedOn,
Rates: []rateGeneration{{Base: RateCard{CacheHit: 0.07, Input: 0.70, Output: 2.10, Currency: "CNY"}}},
},
{
Provider: "mimo", Model: "mimo-v2.5-pro", Currency: "CNY",
BillingMode: BillingModeSubscriptionEquivalent, DocURL: DocMiMoTokenPlan, Notes: "payg_equivalent_not_plan_bill", CheckedOn: checkedOn,
Rates: []rateGeneration{{Base: RateCard{CacheHit: 0.025, Input: 3, Output: 6, Currency: "CNY"}}},
},
{
Provider: "mimo", Model: "mimo-v2.5", Currency: "CNY",
BillingMode: BillingModeSubscriptionEquivalent, DocURL: DocMiMoTokenPlan, Notes: "payg_equivalent_not_plan_bill", CheckedOn: checkedOn,
Rates: []rateGeneration{{Base: RateCard{CacheHit: 0.02, Input: 1, Output: 2, Currency: "CNY"}}},
},
{
Provider: "mimo", Model: "mimo-v2.6-pro", Currency: "CNY",
BillingMode: BillingModePAYG, DocURL: DocMiMoPAYG, CheckedOn: mimoCheckedOn,
Rates: []rateGeneration{{Base: RateCard{CacheHit: 0.025, Input: 3, Output: 6, Currency: "CNY"}}},
},
{
Provider: "mimo", Model: "mimo-v2.6-flash", Currency: "CNY",
BillingMode: BillingModePAYG, DocURL: DocMiMoPAYG, CheckedOn: mimoCheckedOn,
Rates: []rateGeneration{{Base: RateCard{CacheHit: 0.02, Input: 1, Output: 2, Currency: "CNY"}}},
},
{
Provider: "mimo", Model: "mimo-v2.6-pro-ultraspeed", Currency: "CNY",
BillingMode: BillingModePAYG, DocURL: DocMiMoPAYG, CheckedOn: mimoCheckedOn,
Rates: []rateGeneration{{Base: RateCard{CacheHit: 0.25, Input: 30, Output: 60, Currency: "CNY"}}},
},
{
Provider: "mimo", Model: "mimo-v2.6-pro", Currency: "CNY",
BillingMode: BillingModeSubscriptionEquivalent, DocURL: DocMiMoTokenPlan, Notes: "payg_equivalent_not_plan_bill", CheckedOn: mimoCheckedOn,
Rates: []rateGeneration{{Base: RateCard{CacheHit: 0.025, Input: 3, Output: 6, Currency: "CNY"}}},
},
{
Provider: "mimo", Model: "mimo-v2.6-flash", Currency: "CNY",
BillingMode: BillingModeSubscriptionEquivalent, DocURL: DocMiMoTokenPlan, Notes: "payg_equivalent_not_plan_bill", CheckedOn: mimoCheckedOn,
Rates: []rateGeneration{{Base: RateCard{CacheHit: 0.02, Input: 1, Output: 2, Currency: "CNY"}}},
},
}
// retiredDeepSeekFlashModels are names DeepSeek still answers as its current
// flash model, at that model's price. Owned here because that is where the rates
// they bill at live; config reads them through the accessor below.
var retiredDeepSeekFlashModels = []string{"deepseek-v4-flash", "deepseek-v4-flash-vision-exp"}
// RetiredDeepSeekFlashModels copies the list on the way out: a caller sorting or
// trimming the result would otherwise change what every other caller sees.
func RetiredDeepSeekFlashModels() []string {
return append([]string(nil), retiredDeepSeekFlashModels...)
}
// RateModelFor answers which model's rate a name is billed at — a retired name
// at the model that replaced it, an older spelling at the one it was renamed
// from. Scoped to billing on purpose: two models can share a rate without being
// the same model, and nothing outside a price lookup may read this as identity.
func RateModelFor(provider, model string) string {
model = strings.TrimSpace(model)
switch strings.ToLower(strings.TrimSpace(provider)) {
case "deepseek":
if slices.Contains(retiredDeepSeekFlashModels, model) {
return "deepseek-flash"
}
case "mimo":
switch model {
case "mimo-v2-pro":
return "mimo-v2.5-pro"
case "mimo-v2-omni":
return "mimo-v2.5"
}
}
return model
}
// DefaultCurrency is what a vendor's rates are quoted in when an entry does not
// say. A vendor publishing one currency leaves no choice to make; DeepSeek
// prices in both, and USD is what an entry that never chose has been read as.
func DefaultCurrency(provider string) string {
provider = strings.ToLower(strings.TrimSpace(provider))
seen := map[string]bool{}
for _, row := range officialRates {
if row.Provider == provider {
seen[row.Currency] = true
}
}
if len(seen) == 1 {
for currency := range seen {
return currency
}
}
return "USD"
}
// officialRateRowsFor finds the rows for a model, which is more than one when a
// vendor publishes the same rates under two billing modes.
func officialRateRowsFor(provider, model, currency string) []officialRateRow {
provider, currency = strings.ToLower(strings.TrimSpace(provider)), NormalizeCurrency(currency)
model = RateModelFor(provider, model)
var out []officialRateRow
for _, row := range officialRates {
if row.Provider == provider && row.Model == model && row.Currency == currency {
out = append(out, row)
}
}
return out
}
// CurrentRate is what a vendor's model costs in a currency today — the off-peak
// card, which is what a caller charging one rate should quote. Nil when this
// catalog prices neither. A model published under two billing modes quotes the
// pay-as-you-go rate, which is the one an unsubscribed caller is charged.
func CurrentRate(provider, model, currency string) *RateCard {
for _, row := range officialRateRowsFor(provider, model, currency) {
if row.BillingMode == BillingModeSubscriptionEquivalent {
continue
}
if len(row.Rates) > 0 {
rate := row.Rates[row.currentIndex()].Base
return &rate
}
}
return nil
}
// IsOfficialCard reports whether card is a rate this project has shipped for the
// model in the card's own currency: any generation, its off-peak or its peak
// card. A card that is none of them is the user's, or damaged, and cannot be
// valued against the vendor's other-currency table.
func IsOfficialCard(provider, model string, card RateCard) bool {
for _, row := range officialRateRowsFor(provider, model, card.Currency) {
if row.BillingMode == BillingModeSubscriptionEquivalent {
continue
}
for _, generation := range row.Rates {
if sameCard(generation.Base, card) || generation.Peak != nil && sameCard(*generation.Peak, card) {
return true
}
}
}
return false
}
func sameCard(a, b RateCard) bool {
return a.CacheHit == b.CacheHit && a.Input == b.Input && a.Output == b.Output
}
// SupersededRates are the rates shipped for a model before the current one. A
// stored price matching one came from this project rather than from its user, so
// a refresh may replace it; anything else is theirs and is left alone.
func SupersededRates(provider, model, currency string) []RateCard {
var out []RateCard
for _, row := range officialRateRowsFor(provider, model, currency) {
if row.BillingMode == BillingModeSubscriptionEquivalent || len(row.Rates) < 2 {
continue
}
for _, generation := range row.Rates[:row.currentIndex()] {
out = append(out, generation.Base)
}
}
return out
}
// officialCatalogRows publishes the current generation of every row, plus the
// retired DeepSeek names, which bill at the flash rate under their own id.
func officialCatalogRows() []CatalogEntry {
rows := make([]CatalogEntry, 0, len(officialRates)+len(retiredDeepSeekFlashModels)*2)
for _, row := range officialRates {
if len(row.Rates) == 0 {
continue
}
current := row.Rates[row.currentIndex()]
rows = append(rows, row.entry(row.Model, current))
if row.Provider != "deepseek" || row.Model != "deepseek-flash" {
continue
}
for _, retired := range retiredDeepSeekFlashModels {
rows = append(rows, row.entry(retired, current))
}
}
return rows
}
func (r officialRateRow) entry(model string, current rateGeneration) CatalogEntry {
// Copied, so a caller holding a returned row cannot reach back into the
// table every other caller reads.
var peak *RateCard
if current.Peak != nil {
card := *current.Peak
peak = &card
}
return CatalogEntry{
Provider: r.Provider, Model: model, Currency: r.Currency,
CacheHit: current.Base.CacheHit, Input: current.Base.Input, Output: current.Base.Output,
Peak: peak, Window: r.Window, DocURL: r.DocURL,
BillingMode: r.BillingMode, Notes: r.Notes, CheckedOn: r.CheckedOn,
}
}
@@ -0,0 +1,87 @@
package pricing
import "testing"
// The history answers one question: is a stored price the current default, or a
// superseded one this project may replace? A generation repeating another in the
// same row makes that unanswerable — the current rate would match as superseded,
// and every refresh would rewrite it while reporting that something changed.
func TestRateHistoryHasNoRepeatedGenerations(t *testing.T) {
for _, row := range officialRates {
if len(row.Rates) == 0 {
t.Errorf("%s/%s/%s has no rate at all", row.Provider, row.Model, row.Currency)
continue
}
seen := map[RateCard]bool{}
for _, generation := range row.Rates {
if seen[generation.Base] {
t.Errorf("%s/%s/%s repeats the rate %+v", row.Provider, row.Model, row.Currency, generation.Base)
}
seen[generation.Base] = true
if generation.Base.Currency != row.Currency {
t.Errorf("%s/%s/%s holds a rate filed as %q", row.Provider, row.Model, row.Currency, generation.Base.Currency)
}
}
}
}
// CurrentRate returns the first row it matches, so two rows sharing all four
// identifying fields make which rate a caller gets depend on table order.
func TestOfficialRateRowsAreUniquelyIdentified(t *testing.T) {
type key struct{ provider, model, currency, mode string }
seen := map[key]bool{}
for _, row := range officialRates {
k := key{row.Provider, row.Model, row.Currency, row.BillingMode}
if seen[k] {
t.Errorf("%+v appears twice; which rate CurrentRate returns is then a matter of order", k)
}
seen[k] = true
}
}
// Superseded means "no longer charged". Returning the current rate among them
// would make every refresh a no-op that still reports having changed something.
func TestSupersededRatesExcludeTheCurrentOne(t *testing.T) {
for _, row := range officialRates {
current := CurrentRate(row.Provider, row.Model, row.Currency)
if current == nil {
if row.BillingMode != BillingModeSubscriptionEquivalent {
t.Errorf("%s/%s/%s has no current rate", row.Provider, row.Model, row.Currency)
}
continue
}
for _, superseded := range SupersededRates(row.Provider, row.Model, row.Currency) {
if superseded == *current {
t.Errorf("%s/%s/%s lists its current rate as superseded", row.Provider, row.Model, row.Currency)
}
}
}
}
// A retired name bills at the flash rate, which is the whole reason the catalog
// publishes a row for it; resolving it anywhere else would price it as nothing.
func TestRetiredDeepSeekNamesBillAtTheFlashRate(t *testing.T) {
for _, retired := range RetiredDeepSeekFlashModels() {
for _, currency := range []string{"CNY", "USD"} {
got := CurrentRate("deepseek", retired, currency)
want := CurrentRate("deepseek", "deepseek-flash", currency)
if got == nil || want == nil || *got != *want {
t.Errorf("%s/%s = %+v, want the flash rate %+v", retired, currency, got, want)
}
}
}
}
// Every row the table declares has to reach the catalog, which is what a quote
// actually reads; a rate declared but never published quotes as nothing.
func TestEveryRateRowReachesTheCatalog(t *testing.T) {
published := map[string]bool{}
for _, entry := range OfficialCatalog() {
published[entry.Provider+"/"+entry.Model+"/"+entry.Currency+"/"+entry.BillingMode] = true
}
for _, row := range officialRates {
if !published[row.Provider+"/"+row.Model+"/"+row.Currency+"/"+row.BillingMode] {
t.Errorf("%s/%s/%s/%s is declared but never published", row.Provider, row.Model, row.Currency, row.BillingMode)
}
}
}
@@ -0,0 +1,77 @@
// request_rates.go — what a vendor charges per successful request for a service
// that is not metered in tokens. Like officialRates, a change appends a
// generation and never edits one.
package pricing
import "strings"
// RequestRate is a price per 1,000 successful requests.
type RequestRate struct {
Per1000 float64
Currency string
}
// Cost is what n successful requests cost.
func (r RequestRate) Cost(n int) float64 {
return r.Per1000 * float64(n) / 1000
}
// RequestRateInfo is a rate with where it was read and when.
type RequestRateInfo struct {
RequestRate
DocURL string
CheckedOn string
}
type requestRateRow struct {
Provider string
Service string
DocURL string
CheckedOn string
// Rates is oldest first; the last entry is what the service costs today.
Rates []RequestRate
}
var requestRates = []requestRateRow{
{Provider: "deepseek", Service: "search", DocURL: DocDeepSeekPricing, CheckedOn: "2026-10-17",
Rates: []RequestRate{{Per1000: 0.75, Currency: "USD"}}},
{Provider: "deepseek", Service: "fetch", DocURL: DocDeepSeekPricing, CheckedOn: "2026-10-17",
Rates: []RequestRate{{Per1000: 0.75, Currency: "USD"}}},
}
// CurrentRequestRate is what a vendor's service costs per request today.
func CurrentRequestRate(provider, service string) (RequestRateInfo, bool) {
provider, service = strings.ToLower(strings.TrimSpace(provider)), strings.ToLower(strings.TrimSpace(service))
for _, row := range requestRates {
if row.Provider == provider && row.Service == service {
return RequestRateInfo{RequestRate: row.Rates[len(row.Rates)-1], DocURL: row.DocURL, CheckedOn: row.CheckedOn}, true
}
}
return RequestRateInfo{}, false
}
// RequestQuote prices n successful requests as a CostQuote, so a per-request
// charge enters the ledger beside token costs under its own usage source.
func RequestQuote(rate RequestRateInfo, n int, modelRef, usageSource string) CostQuote {
currency := NormalizeCurrency(rate.Currency)
original := MoneyOf(NewAmountFromFloat(rate.Cost(n)), currency)
q := CostQuote{
Original: original,
Valuations: map[string]Valuation{currency: {
Money: original, Basis: BasisOfficialTable, Source: rate.DocURL, AsOf: rate.CheckedOn,
}},
Selected: &original,
BillingMode: BillingModePAYG,
CostComplete: true,
Coverage: CoverageComplete,
DisplayComplete: true,
Complete: true,
DisplayStatus: DisplayStatusMatched,
AggregateMode: AggregateModeSingleCurrency,
ModelRef: modelRef,
UsageSource: usageSource,
CatalogSource: rate.DocURL,
}
q.PricingFingerprint = PricingFingerprint(RateCard{Input: rate.Per1000, Currency: currency})
return q
}
@@ -0,0 +1,80 @@
package pricing
import (
"testing"
"time"
)
func TestRequestRatesAreAppendOnlyAndSourced(t *testing.T) {
seen := map[string]bool{}
for _, row := range requestRates {
key := row.Provider + "/" + row.Service
if seen[key] {
t.Fatalf("%s appears twice: a change is a new generation on its row, not a second row", key)
}
seen[key] = true
if row.DocURL == "" || row.CheckedOn == "" || len(row.Rates) == 0 {
t.Fatalf("%s lacks a source, a check date or a rate", key)
}
for _, r := range row.Rates {
if r.Per1000 <= 0 || r.Currency == "" {
t.Fatalf("%s carries an unusable rate %+v", key, r)
}
}
}
}
func TestCurrentRequestRateForDeepSeekSearchAndFetch(t *testing.T) {
for _, service := range []string{"search", "fetch"} {
got, ok := CurrentRequestRate("deepseek", service)
if !ok || got.Per1000 != 0.75 || got.Currency != "USD" || got.DocURL != DocDeepSeekPricing || got.CheckedOn == "" {
t.Fatalf("%s = %+v (%v), want $0.75 per 1,000 successful requests from the pricing page", service, got, ok)
}
}
if _, ok := CurrentRequestRate("deepseek", "nonsense"); ok {
t.Fatal("an unknown service has a rate")
}
}
func TestRequestCostIsPerSuccessfulRequest(t *testing.T) {
rate, _ := CurrentRequestRate("deepseek", "search")
if got := rate.Cost(1000); got != 0.75 {
t.Fatalf("1,000 requests cost %v, want 0.75", got)
}
if got := rate.Cost(1); got != 0.00075 {
t.Fatalf("1 request costs %v, want 0.00075", got)
}
if got := rate.Cost(0); got != 0 {
t.Fatalf("no requests cost %v", got)
}
}
// A per-request charge lands in the same ledger as token costs, under its own
// source, so the usage view can show what search cost apart from the model.
func TestRequestQuoteLandsInTheLedgerUnderItsOwnSource(t *testing.T) {
rate, _ := CurrentRequestRate("deepseek", "search")
at := time.Date(2026, 10, 17, 8, 0, 0, 0, time.UTC)
q := RequestQuote(rate, 1, "deepseek-api/search", "deepseek-search")
if q.Original.Currency != "USD" || q.Original.Amount != "0.00075" {
t.Fatalf("original = %+v, want 0.00075 USD for one successful request", q.Original)
}
if !q.CostComplete || q.UsageSource != "deepseek-search" || q.ModelRef != "deepseek-api/search" {
t.Fatalf("quote = %+v", q)
}
l := NewLedger()
for range 3 {
l.Add(q, UsageTokens{}, at)
}
l.Add(BuildQuote(QuoteInput{Usage: UsageTokens{PromptTokens: 1000, CompletionTokens: 10}, Rates: RateCard{Input: 1, Output: 2, Currency: "USD"}, ModelRef: "m", UsageSource: "executor"}), UsageTokens{PromptTokens: 1000}, at)
var search LedgerEntry
var sources int
for _, e := range l.Entries {
sources++
if e.UsageSource == "deepseek-search" {
search = e
}
}
if sources != 2 || search.Quote.Original.Amount != "0.00225" {
t.Fatalf("ledger = %+v (sources %d), want search summed apart from the model: 3 requests = 0.00225 USD", l.Entries, sources)
}
}
@@ -0,0 +1,29 @@
// tariff.go — which rate a vendor's own schedule charges right now.
package pricing
import "time"
// Tariff is the vendor's schedule as it stands at one instant: whether it is in
// its peak window and, when it will flip, when.
type Tariff struct {
Peak bool
ChangesAt time.Time
Changes bool
}
// LiveTariff reports the window a model's rates are billed in at the given
// instant. It answers only for a rate confirmed as the vendor's own table and
// only where that table has a peak rate: a custom price is the user's own
// arrangement, and projecting someone else's schedule onto it would announce a
// tariff nobody charges.
func LiveTariff(providerKind, model string, rates RateCard, at time.Time) (Tariff, bool) {
entry, ok := MatchesCatalog(providerKind, model, rates)
if !ok {
return Tariff{}, false
}
if entry.Peak == nil || entry.Window == nil {
return Tariff{}, false
}
next, changes := entry.Window.NextChange(at)
return Tariff{Peak: entry.Window.IsPeak(at), ChangesAt: next, Changes: changes}, true
}
@@ -0,0 +1,69 @@
package pricing
import (
"testing"
"time"
)
func beijingTime(t *testing.T, s string) time.Time {
t.Helper()
at, err := time.ParseInLocation("2006-01-02 15:04", s, time.FixedZone("", beijing))
if err != nil {
t.Fatal(err)
}
return at
}
// The next flip is the next whole hour of the vendor's schedule where the answer
// changes, in the vendor's own zone and across a weekend.
func TestNextChangeFollowsTheVendorsScheduleAcrossAWeekend(t *testing.T) {
for _, tc := range []struct {
now, want string
peak bool
}{
{"2026-10-12 08:30", "2026-10-12 09:00", false}, // Monday before the morning peak
{"2026-10-12 11:59", "2026-10-12 12:00", true}, // the morning peak ends
{"2026-10-12 12:00", "2026-10-12 14:00", false}, // lunch
{"2026-10-12 17:30", "2026-10-12 18:00", true}, // the afternoon peak ends
{"2026-10-16 18:00", "2026-10-19 09:00", false}, // Friday evening to Monday morning
{"2026-10-17 10:00", "2026-10-19 09:00", false}, // a Saturday is off-peak all day
} {
now := beijingTime(t, tc.now)
if got := deepseekPeak.IsPeak(now); got != tc.peak {
t.Errorf("IsPeak(%s) = %v, want %v", tc.now, got, tc.peak)
}
got, ok := deepseekPeak.NextChange(now)
if !ok || !got.Equal(beijingTime(t, tc.want)) {
t.Errorf("NextChange(%s) = %v %v, want %s", tc.now, got, ok, tc.want)
}
}
}
func TestNextChangeOfAWindowThatNeverFlipsIsNone(t *testing.T) {
var w *PeakWindow
if _, ok := w.NextChange(time.Now()); ok {
t.Fatal("a nil window flips")
}
if _, ok := (&PeakWindow{OffsetSeconds: beijing}).NextChange(time.Now()); ok {
t.Fatal("a window with no hours flips")
}
}
// Only a rate confirmed as the vendor's own earns a tariff, and only where the
// vendor's table has a peak rate.
func TestLiveTariffIsTheVendorsAndNeverTheUsers(t *testing.T) {
official := *CurrentRate("deepseek", "deepseek-flash", "CNY")
at := beijingTime(t, "2026-10-12 10:00")
tariff, ok := LiveTariff("deepseek", "deepseek-flash", official, at)
if !ok || !tariff.Peak || !tariff.Changes || !tariff.ChangesAt.Equal(beijingTime(t, "2026-10-12 12:00")) {
t.Fatalf("official rate at a peak hour = %+v %v, want peak until 12:00", tariff, ok)
}
custom := official
custom.Input = official.Input * 3
if _, ok := LiveTariff("deepseek", "deepseek-flash", custom, at); ok {
t.Fatal("a custom price was given the vendor's schedule")
}
if _, ok := LiveTariff("", "deepseek-flash", official, at); ok {
t.Fatal("a relay with no vendor kind was given the vendor's schedule")
}
}
@@ -0,0 +1,85 @@
// window.go — when a vendor charges its peak rates.
package pricing
import (
"slices"
"time"
)
// PeakWindow is the vendor's own peak schedule. The rule is published in the
// vendor's timezone — DeepSeek's 09:00-12:00 and 14:00-18:00 are Beijing hours
// — so the zone travels with it and never comes from the reader's machine.
type PeakWindow struct {
// OffsetSeconds fixes that zone. China does not observe DST, so an offset
// is exact for these rules and costs no embedded tzdata.
OffsetSeconds int
// Hours are [from, to) in that zone.
Hours [][2]int
// WeekendOffPeak is the date a vendor stopped charging peak rates on
// Saturdays and Sundays, YYYY-MM-DD in the same zone. Empty = no such rule.
WeekendOffPeak string
// HolidayOffPeak lists dates, YYYY-MM-DD in the same zone, billed off-peak
// in full, from the holiday calendar the vendor's pricing page names.
HolidayOffPeak []string
}
// beijing is where every rate in this catalog is published.
const beijing = 8 * 3600
// IsPeak reports whether t falls inside the window. A nil window never peaks,
// which is what a vendor billing one rate around the clock means.
func (w *PeakWindow) IsPeak(t time.Time) bool {
if w == nil || len(w.Hours) == 0 {
return false
}
local := t.In(time.FixedZone("", w.OffsetSeconds))
if w.weekendOff(local) || w.holidayOff(local) {
return false
}
hour := local.Hour()
for _, r := range w.Hours {
if hour >= r[0] && hour < r[1] {
return true
}
}
return false
}
// weekendOff reports whether local lands on a weekend the vendor has already
// flattened to off-peak. Compared as dates in the vendor's zone: the rule's
// start is a wall-clock midnight there, not an instant.
func (w *PeakWindow) weekendOff(local time.Time) bool {
if w.WeekendOffPeak == "" {
return false
}
if day := local.Weekday(); day != time.Saturday && day != time.Sunday {
return false
}
return local.Format("2006-01-02") >= w.WeekendOffPeak
}
// holidayOff reports whether local lands on a published holiday the vendor
// bills off-peak. Compared as dates in the vendor's zone, like weekendOff.
func (w *PeakWindow) holidayOff(local time.Time) bool {
day := local.Format("2006-01-02")
return slices.Contains(w.HolidayOffPeak, day)
}
// NextChange is the first instant after t at which IsPeak answers differently,
// and false when it does not within a fortnight: a nil window, or a schedule
// with nothing to flip to. The hours are whole, so only an hour boundary in the
// vendor's zone can be a change.
func (w *PeakWindow) NextChange(t time.Time) (time.Time, bool) {
if w == nil || len(w.Hours) == 0 {
return time.Time{}, false
}
now := w.IsPeak(t)
next := t.In(time.FixedZone("", w.OffsetSeconds)).Truncate(time.Hour).Add(time.Hour)
for range 14 * 24 {
if w.IsPeak(next) != now {
return next, true
}
next = next.Add(time.Hour)
}
return time.Time{}, false
}
@@ -0,0 +1,166 @@
package pricing
import (
"testing"
"time"
)
// beijingAt builds an instant from the wall clock the vendor publishes its rule
// in, so a case reads as the hour a user in Beijing would see.
func beijingAt(year int, month time.Month, day, hour int) time.Time {
return time.Date(year, month, day, hour, 0, 0, 0, time.FixedZone("", beijing))
}
func TestPeakWindowReadsTheVendorsClock(t *testing.T) {
cases := []struct {
name string
at time.Time
peak bool
}{
// 2026-08-19 is a Wednesday; 08-22 a Saturday, 08-23 a Sunday.
{"weekday morning inside 9-12", beijingAt(2026, 8, 19, 10), true},
{"weekday lunch gap between the two ranges", beijingAt(2026, 8, 19, 13), false},
{"weekday afternoon inside 14-18", beijingAt(2026, 8, 19, 15), true},
{"weekday 18:00 is the exclusive end", beijingAt(2026, 8, 19, 18), false},
{"weekday night", beijingAt(2026, 8, 19, 23), false},
// The weekend rule starts 2026-08-23, so the Saturday before it still peaks.
{"saturday before the weekend rule", beijingAt(2026, 8, 22, 10), true},
{"sunday the weekend rule starts", beijingAt(2026, 8, 23, 10), false},
{"saturday after the weekend rule", beijingAt(2026, 8, 29, 10), false},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
if got := deepseekPeak.IsPeak(c.at); got != c.peak {
t.Fatalf("IsPeak(%s) = %v, want %v", c.at.Format(time.RFC3339), got, c.peak)
}
})
}
}
// Public holidays bill off-peak even on weekdays (Mid-Autumn Fri 2026-09-25,
// Spring Festival Tue 2026-02-17, National Day Thu 2026-10-01), and a make-up
// workday (Sat 2026-10-10) is still a weekend.
func TestPeakWindowReadsTheHolidayTable(t *testing.T) {
cases := []struct {
name string
at time.Time
peak bool
}{
{"mid-autumn friday morning window", beijingAt(2026, 9, 25, 10), false},
{"mid-autumn friday afternoon window", beijingAt(2026, 9, 25, 15), false},
{"thursday before mid-autumn", beijingAt(2026, 9, 24, 10), true},
{"monday after mid-autumn", beijingAt(2026, 9, 28, 10), true},
{"spring festival new year's day", beijingAt(2026, 2, 17, 9), false},
{"friday before spring festival", beijingAt(2026, 2, 13, 10), true},
{"national day morning window", beijingAt(2026, 10, 1, 10), false},
{"first workday after national day", beijingAt(2026, 10, 8, 10), true},
{"make-up workday saturday", beijingAt(2026, 10, 10, 10), false},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
if got := deepseekPeak.IsPeak(c.at); got != c.peak {
t.Fatalf("IsPeak(%s) = %v, want %v", c.at.Format(time.RFC3339), got, c.peak)
}
})
}
}
// The instant is what decides, not the clock of whoever reads it: the same
// moment expressed in UTC has to land on the same side of the window.
func TestPeakWindowIsAboutTheInstantNotTheReader(t *testing.T) {
at := beijingAt(2026, 8, 19, 10)
if !deepseekPeak.IsPeak(at.UTC()) {
t.Fatal("the same instant in UTC read as off-peak")
}
if deepseekPeak.IsPeak(at.In(time.FixedZone("", -5*3600))) != true {
t.Fatal("the same instant in a US zone read as off-peak")
}
}
func TestNilWindowNeverPeaks(t *testing.T) {
var none *PeakWindow
if none.IsPeak(beijingAt(2026, 8, 19, 10)) {
t.Fatal("a vendor with no schedule charged a peak rate")
}
}
// A turn inside the peak window is billed at the peak card — this is the whole
// point of the schedule, and it has to survive all the way out of BuildQuote.
func TestBuildQuoteBillsThePeakRateInsideTheWindow(t *testing.T) {
base := QuoteInput{
Usage: UsageTokens{PromptTokens: 1_000_000, CompletionTokens: 1_000_000},
Rates: RateCard{CacheHit: 0.15, Input: 4.5, Output: 13.5, Currency: "CNY"},
ProviderKind: "deepseek",
ModelID: "deepseek-v4-pro",
ModelRef: "deepseek-v4-pro",
}
off := base
off.OccurredAt = beijingAt(2026, 8, 19, 23)
on := base
on.OccurredAt = beijingAt(2026, 8, 19, 10)
offQuote, onQuote := BuildQuote(off), BuildQuote(on)
// 1M uncached input + 1M output: 4.5+13.5 off-peak, 9+27 at peak.
if offQuote.Original.Amount != "18" {
t.Fatalf("off-peak original = %q, want 18", offQuote.Original.Amount)
}
if onQuote.Original.Amount != "36" {
t.Fatalf("peak original = %q, want 36", onQuote.Original.Amount)
}
}
// A price the vendor did not publish is the user's own arrangement. Projecting
// someone else's peak schedule onto it would invent a number.
func TestCustomPriceIsNeverRescheduled(t *testing.T) {
in := QuoteInput{
Usage: UsageTokens{PromptTokens: 1_000_000, CompletionTokens: 1_000_000},
Rates: RateCard{CacheHit: 0.15, Input: 1, Output: 1, Currency: "CNY"},
ProviderKind: "deepseek",
ModelID: "deepseek-v4-pro",
OccurredAt: beijingAt(2026, 8, 19, 10),
}
if got := BuildQuote(in).Original.Amount; got != "2" {
t.Fatalf("custom price original = %q, want 2", got)
}
}
// The band a call was billed in reaches the quote, and an aggregate says which
// side its calls fell on: one band, mixed, or none when any call has no schedule.
func TestQuoteNamesTheRateBandItWasBilledIn(t *testing.T) {
base := QuoteInput{
Usage: UsageTokens{PromptTokens: 1000, CompletionTokens: 1000},
Rates: RateCard{CacheHit: 0.15, Input: 4.5, Output: 13.5, Currency: "CNY"},
ProviderKind: "deepseek",
ModelID: "deepseek-v4-pro",
ModelRef: "deepseek-v4-pro",
}
at := func(hour int) CostQuote {
in := base
in.OccurredAt = beijingAt(2026, 8, 19, hour)
return BuildQuote(in)
}
peak, off := at(10), at(23)
if peak.RateBand != RateBandPeak || off.RateBand != RateBandOffPeak {
t.Fatalf("bands = %q / %q", peak.RateBand, off.RateBand)
}
if got := AggregateQuotes([]CostQuote{peak, peak}, "").RateBand; got != RateBandPeak {
t.Fatalf("two peak calls = %q", got)
}
mixed := AggregateQuotes([]CostQuote{peak, off}, "")
if mixed.RateBand != RateBandMixed {
t.Fatalf("peak + off-peak = %q", mixed.RateBand)
}
if got := AggregateQuotes([]CostQuote{mixed, off}, "").RateBand; got != RateBandMixed {
t.Fatalf("a mixed total lost its band on re-aggregation: %q", got)
}
custom := base
custom.Rates.Input = 1
custom.OccurredAt = beijingAt(2026, 8, 19, 10)
cq := BuildQuote(custom)
if cq.RateBand != "" {
t.Fatalf("a custom price carries band %q", cq.RateBand)
}
if got := AggregateQuotes([]CostQuote{peak, cq}, "").RateBand; got != "" {
t.Fatalf("an aggregate with an unscheduled call claims band %q", got)
}
}
@@ -0,0 +1,70 @@
# Reasonix kernel snapshot
A trimmed slice of the Reasonix Go kernel: the contracts a benchmark report is
priced and labelled with, a few leaf utilities, and `benchctl`, a small command
that prints what those contracts answer.
```bash
make check # gofmt, vet, test
go run ./cmd/benchctl rate -model deepseek-flash -currency USD
go run ./cmd/benchctl cost -provider mimo -model mimo-v2.5 -miss 1000000
go run ./cmd/benchctl tariff -currency CNY -at 2026-10-12T02:00:00Z
go run ./cmd/benchctl arm -ablate planner,evidence
```
## Packages
| Package | Owns |
| --- | --- |
| `internal/contract/pricing` | the official price book, cost quotes, the spend ledger |
| `internal/contract/ablation` | which subsystems a benchmark arm switches off, and the arm's name |
| `internal/contract/agentgraph` | the vocabulary of one run's execution graph |
| `internal/base/retrieval` | BM25 ranking over CJK-aware tokens |
| `internal/base/rulelist` | splitting a one-line permission rule list |
| `internal/base/tokencount` | a cheap token estimate |
| `internal/base/neterr` | classifying transport errors |
| `internal/base/nilutil` | typed-nil detection |
| `cmd/benchctl` | prints rates, costs, peak schedules and arm names |
## Pricing
A `RateCard` is a price per million tokens in one currency:
| Field | Charges |
| --- | --- |
| `CacheHit` | prompt tokens served from the provider's cache |
| `Input` | prompt tokens that missed the cache, including tokens written to it |
| `Output` | completion tokens |
`OriginalCostAmount` turns a card and a `UsageTokens` breakdown into a
fixed-point `Amount`; `PricingFingerprint` keys a card in the ledger, so a
stored ledger entry stays attached to the card it was priced with.
`CurrentRate` and `SupersededRates` answer from `officialRates`, which only
ever grows: a price change appends a generation.
## Ablation
`ablation.Parse` reads a comma-separated list of modules to switch off, and
`Set.Arm` names the result (`full` for the control). The switchable modules,
in the order arm names use:
| Module | Off means |
| --- | --- |
| `evidence` | no evidence gate on completion claims |
| `planner` | no two-model planning turn |
| `subagent` | no delegation to workers |
| `retrieval` | no retrieval over the folded transcript |
| `compaction` | no context folding |
| `upstream` | a dependency edge orders its endpoints without delivering the answer |
| `recall-search` | recall can read a given address but cannot search for one |
| `memory` | no saved-fact memory store |
The fold-index scale is a separate axis (`-fold-index`), not a module.
## Skills
Project skills live under `.reasonix/skills/<name>/SKILL.md`.
| Skill | Use it to |
| --- | --- |
| `price-change` | record a vendor's price change in the price book |
@@ -0,0 +1,115 @@
// Command benchctl prints what the pricing and ablation contracts answer for
// one model or one benchmark arm, so a reviewer can check a rate or an arm name
// without writing a test.
package main
import (
"errors"
"flag"
"fmt"
"io"
"os"
"reasonix/internal/contract/ablation"
"reasonix/internal/contract/pricing"
)
var errUsage = errors.New("usage: benchctl rate|cost|tariff|arm [flags]")
func main() {
if err := run(os.Args[1:], os.Stdout); err != nil {
fmt.Fprintln(os.Stderr, "benchctl:", err)
os.Exit(2)
}
}
func run(args []string, out io.Writer) error {
if len(args) == 0 {
return errUsage
}
switch args[0] {
case "rate":
return runRate(args[1:], out)
case "cost":
return runCost(args[1:], out)
case "tariff":
return runTariff(args[1:], out)
case "arm":
return runArm(args[1:], out)
default:
return fmt.Errorf("%w: unknown subcommand %q", errUsage, args[0])
}
}
func runRate(args []string, out io.Writer) error {
fs := flag.NewFlagSet("rate", flag.ContinueOnError)
fs.SetOutput(out)
provider := fs.String("provider", "deepseek", "vendor")
model := fs.String("model", "deepseek-flash", "model id")
currency := fs.String("currency", "", "currency; empty reads the vendor's default")
if err := fs.Parse(args); err != nil {
return err
}
cur := *currency
if cur == "" {
cur = pricing.DefaultCurrency(*provider)
}
card := pricing.CurrentRate(*provider, *model, cur)
if card == nil {
return fmt.Errorf("no official rate for %s/%s in %s", *provider, *model, cur)
}
fmt.Fprintf(out, "%s/%s %s: cache-hit %g input %g output %g per 1M\n",
*provider, *model, card.Currency, card.CacheHit, card.Input, card.Output)
if entry, ok := pricing.LookupCatalog(*provider, *model, cur, ""); ok && entry.DocURL != "" {
fmt.Fprintf(out, "source: %s\n", entry.DocURL)
}
fmt.Fprintf(out, "superseded generations: %d\n", len(pricing.SupersededRates(*provider, *model, cur)))
return nil
}
func runCost(args []string, out io.Writer) error {
fs := flag.NewFlagSet("cost", flag.ContinueOnError)
fs.SetOutput(out)
provider := fs.String("provider", "deepseek", "vendor")
model := fs.String("model", "deepseek-flash", "model id")
currency := fs.String("currency", "", "currency; empty reads the vendor's default")
hit := fs.Int("hit", 0, "cached prompt tokens")
miss := fs.Int("miss", 0, "uncached prompt tokens")
completion := fs.Int("completion", 0, "completion tokens")
if err := fs.Parse(args); err != nil {
return err
}
cur := *currency
if cur == "" {
cur = pricing.DefaultCurrency(*provider)
}
card := pricing.CurrentRate(*provider, *model, cur)
if card == nil {
return fmt.Errorf("no official rate for %s/%s in %s", *provider, *model, cur)
}
usage := pricing.UsageTokens{CacheHitTokens: *hit, CacheMissTokens: *miss, CompletionTokens: *completion}
amount := pricing.OriginalCostAmount(*card, usage)
fmt.Fprintf(out, "%s %s\n", amount, pricing.NormalizeCurrency(card.Currency))
return nil
}
func runArm(args []string, out io.Writer) error {
fs := flag.NewFlagSet("arm", flag.ContinueOnError)
fs.SetOutput(out)
spec := fs.String("ablate", "", "comma-separated modules to switch off: "+ablation.ModuleList())
foldIndex := fs.String("fold-index", "", "fold-index scale: default, half, quarter or off")
list := fs.Bool("list", false, "list the switchable modules")
if err := fs.Parse(args); err != nil {
return err
}
if *list {
fmt.Fprintln(out, ablation.ModuleHelp())
return nil
}
set, err := ablation.ParseArm(*spec, *foldIndex)
if err != nil {
return err
}
fmt.Fprintln(out, set.Arm())
return nil
}
@@ -0,0 +1,72 @@
package main
import (
"bytes"
"errors"
"strings"
"testing"
)
func runOut(t *testing.T, args ...string) string {
t.Helper()
var out bytes.Buffer
if err := run(args, &out); err != nil {
t.Fatalf("run %v: %v", args, err)
}
return out.String()
}
func TestRateNamesTheCurrentCard(t *testing.T) {
got := runOut(t, "rate", "-provider", "mimo", "-model", "mimo-v2.5")
if !strings.Contains(got, "mimo/mimo-v2.5 CNY: cache-hit 0.02 input 1 output 2 per 1M") {
t.Fatalf("rate output = %q", got)
}
}
func TestRateRefusesAnUnpricedModel(t *testing.T) {
var out bytes.Buffer
if err := run([]string{"rate", "-provider", "nobody", "-model", "x"}, &out); err == nil {
t.Fatal("an unpriced model must fail")
}
}
func TestCostChargesTheCurrentCard(t *testing.T) {
got := runOut(t, "cost", "-provider", "mimo", "-model", "mimo-v2.5", "-miss", "1000000", "-completion", "1000000")
if strings.TrimSpace(got) != "3 CNY" {
t.Fatalf("cost output = %q", got)
}
}
func TestArmNamesTheConfiguration(t *testing.T) {
if got := strings.TrimSpace(runOut(t, "arm")); got != "full" {
t.Fatalf("control arm = %q", got)
}
if got := strings.TrimSpace(runOut(t, "arm", "-ablate", "planner,evidence", "-fold-index", "half")); got != "no-evidence+no-planner+index-half" {
t.Fatalf("arm = %q", got)
}
}
func TestUnknownSubcommandIsAUsageError(t *testing.T) {
var out bytes.Buffer
if err := run([]string{"nope"}, &out); !errors.Is(err, errUsage) {
t.Fatalf("err = %v, want errUsage", err)
}
}
func TestTariffReadsTheVendorsSchedule(t *testing.T) {
got := runOut(t, "tariff", "-provider", "mimo", "-model", "mimo-v2.5")
if !strings.Contains(got, "official card: true") || !strings.Contains(got, "one rate around the clock") {
t.Fatalf("tariff output = %q", got)
}
peak := runOut(t, "tariff", "-currency", "CNY", "-at", "2026-10-12T02:00:00Z")
if !strings.Contains(peak, "peak=true until 2026-10-12T04:00:00Z") {
t.Fatalf("deepseek tariff at a weekday peak = %q", peak)
}
}
func TestArmListShowsEveryModuleWithItsSummary(t *testing.T) {
got := strings.TrimSpace(runOut(t, "arm", "-list"))
if !strings.HasSuffix(got, "memory: no saved-fact memory store") || strings.Count(got, "\n") != 7 {
t.Fatalf("arm -list = %q", got)
}
}
@@ -0,0 +1,3 @@
module reasonix
go 1.26.0
@@ -0,0 +1,225 @@
// Package ablation switches individual Reasonix subsystems off so a benchmark
// can attribute a change in solve rate to one of them.
package ablation
import (
"fmt"
"sort"
"strings"
)
type Module string
const (
Evidence Module = "evidence"
Planner Module = "planner"
Subagent Module = "subagent"
Retrieval Module = "retrieval"
Compaction Module = "compaction"
// Upstream off means a fleet dependency edge orders its endpoints without
// delivering the dependency's answer, so a dependent must re-derive it.
Upstream Module = "upstream"
// RecallSearch off leaves recall able to read an address it was given and
// unable to look one up, which is what the tool could do before the folded
// region became searchable.
RecallSearch Module = "recall-search"
// Memory off runs without the saved-fact memory store.
Memory Module = "memory"
)
var summaries = map[Module]string{
Evidence: "no evidence gate on completion claims",
Planner: "no two-model planning turn",
Subagent: "no delegation to workers",
Retrieval: "no retrieval over the folded transcript",
Compaction: "no context folding",
Upstream: "a dependency edge orders its endpoints without delivering the answer",
RecallSearch: "recall can read a given address but cannot search for one",
Memory: "no saved-fact memory store",
}
// Summary says what switching the module off means; "" for an undeclared one.
func (m Module) Summary() string { return summaries[m] }
// ModuleHelp lists every module with its summary, one per line, in arm order.
func ModuleHelp() string {
lines := make([]string, 0, len(Modules()))
for _, m := range Modules() {
lines = append(lines, string(m)+": "+m.Summary())
}
return strings.Join(lines, "\n")
}
// FoldIndexScale sizes the model-visible fold index against its shipped budget.
// It is a bench axis, not a module: the question it answers is what an ambient
// address list is worth, and four steps answer that where a free-form fraction
// would turn a measurement into a tuning knob.
type FoldIndexScale string
const (
FoldIndexDefault FoldIndexScale = ""
FoldIndexHalf FoldIndexScale = "half"
FoldIndexQuarter FoldIndexScale = "quarter"
FoldIndexOff FoldIndexScale = "off"
)
// Ratio scales a budget. The default returns it unchanged.
func (f FoldIndexScale) Ratio() float64 {
switch f {
case FoldIndexHalf:
return 0.5
case FoldIndexQuarter:
return 0.25
case FoldIndexOff:
return 0
default:
return 1
}
}
// ParseFoldIndexScale reads a bench flag value. "" and "default" ship as-is.
func ParseFoldIndexScale(spec string) (FoldIndexScale, error) {
switch strings.ToLower(strings.TrimSpace(spec)) {
case "", "default", "full":
return FoldIndexDefault, nil
case string(FoldIndexHalf):
return FoldIndexHalf, nil
case string(FoldIndexQuarter):
return FoldIndexQuarter, nil
case string(FoldIndexOff), "none", "0":
return FoldIndexOff, nil
default:
return "", fmt.Errorf("unknown fold-index scale %q (want default, half, quarter, or off)", spec)
}
}
// Modules returns every switchable module in the order arm names use.
func Modules() []Module {
return []Module{Evidence, Planner, Subagent, Retrieval, Compaction, Upstream, RecallSearch, Memory}
}
// Set is the group of modules disabled for a run. The zero value is the
// control arm: everything on.
type Set struct {
off map[Module]bool
// foldIndex is an axis rather than a switch, so it lives beside the module
// set instead of inside it. It still reaches Arm(): a run whose index was
// quartered must not report the same identity as the control.
foldIndex FoldIndexScale
}
// WithFoldIndex returns this set at the given index scale.
func (s Set) WithFoldIndex(scale FoldIndexScale) Set {
s.foldIndex = scale
return s
}
// FoldIndex is the scale this run sizes its fold index at.
func (s Set) FoldIndex() FoldIndexScale { return s.foldIndex }
// Parse reads a spec such as "evidence,planner". "" and "none" mean the control
// arm; "all" disables every module.
func Parse(spec string) (Set, error) {
spec = strings.TrimSpace(spec)
if spec == "" || strings.EqualFold(spec, "none") {
return Set{}, nil
}
if strings.EqualFold(spec, "all") {
return New(Modules()...), nil
}
known := map[Module]bool{}
for _, m := range Modules() {
known[m] = true
}
var mods []Module
for _, field := range strings.FieldsFunc(spec, func(r rune) bool { return r == ',' || r == ' ' }) {
m := Module(strings.ToLower(strings.TrimSpace(field)))
if !known[m] {
return Set{}, fmt.Errorf("unknown ablation module %q (want %s, or none/all)", field, ModuleList())
}
mods = append(mods, m)
}
return New(mods...), nil
}
// ParseArm reads both bench axes at once: the module spec and the fold-index
// scale. One call so a frontend cannot wire the first and forget the second,
// which would silently run an index arm under the control arm's name.
func ParseArm(modules, foldIndex string) (Set, error) {
set, err := Parse(modules)
if err != nil {
return Set{}, err
}
scale, err := ParseFoldIndexScale(foldIndex)
if err != nil {
return Set{}, err
}
return set.WithFoldIndex(scale), nil
}
// ModuleList names every switchable module for flag help and error text, so
// those cannot drift from Modules() the way a hand-kept list does.
func ModuleList() string { return joinModules(Modules(), ", ") }
// New returns a Set with the given modules disabled.
func New(mods ...Module) Set {
if len(mods) == 0 {
return Set{}
}
off := make(map[Module]bool, len(mods))
for _, m := range mods {
off[m] = true
}
return Set{off: off}
}
func (s Set) Off(m Module) bool { return s.off[m] }
func (s Set) Empty() bool { return len(s.off) == 0 && s.foldIndex == FoldIndexDefault }
// Arm is the published name of this configuration: "full" for the control arm,
// otherwise "no-evidence+no-planner". Stable across runs so results from
// different machines group by the same key.
func (s Set) Arm() string {
if s.Empty() {
return "full"
}
parts := make([]string, 0, len(s.off)+1)
for _, m := range s.disabled() {
parts = append(parts, "no-"+string(m))
}
if s.foldIndex != FoldIndexDefault {
parts = append(parts, "index-"+string(s.foldIndex))
}
return strings.Join(parts, "+")
}
// String round-trips back through Parse. It carries modules only — the fold
// index axis has its own flag and its own parser.
func (s Set) String() string {
if s.Empty() {
return "none"
}
return joinModules(s.disabled(), ",")
}
func (s Set) disabled() []Module {
order := map[Module]int{}
for i, m := range Modules() {
order[m] = i
}
out := make([]Module, 0, len(s.off))
for m := range s.off {
out = append(out, m)
}
sort.Slice(out, func(i, j int) bool { return order[out[i]] < order[out[j]] })
return out
}
func joinModules(mods []Module, sep string) string {
parts := make([]string, len(mods))
for i, m := range mods {
parts[i] = string(m)
}
return strings.Join(parts, sep)
}
@@ -0,0 +1,139 @@
package ablation
import (
"strings"
"testing"
)
func TestParseAcceptsSpecFormsAndRejectsUnknownModules(t *testing.T) {
for _, tc := range []struct {
spec string
arm string
}{
{"", "full"},
{"none", "full"},
{"NONE", "full"},
{"evidence", "no-evidence"},
{"planner,evidence", "no-evidence+no-planner"},
{"planner evidence", "no-evidence+no-planner"},
{" Evidence , Retrieval ", "no-evidence+no-retrieval"},
{"memory", "no-memory"},
{"all", "no-evidence+no-planner+no-subagent+no-retrieval+no-compaction+no-upstream+no-recall-search+no-memory"},
} {
set, err := Parse(tc.spec)
if err != nil {
t.Fatalf("Parse(%q): %v", tc.spec, err)
}
if got := set.Arm(); got != tc.arm {
t.Errorf("Parse(%q).Arm() = %q, want %q", tc.spec, got, tc.arm)
}
}
if _, err := Parse("evidence,planer"); err == nil {
t.Fatal("a misspelled module must fail loudly, not silently run the control arm")
}
}
func TestArmNameIsIndependentOfSpecOrder(t *testing.T) {
a, _ := Parse("retrieval,evidence,planner")
b, _ := Parse("planner,retrieval,evidence")
if a.Arm() != b.Arm() {
t.Fatalf("arm names diverge by input order: %q vs %q", a.Arm(), b.Arm())
}
}
func TestStringRoundTripsThroughParse(t *testing.T) {
for _, spec := range []string{"none", "evidence", "evidence,compaction", "all"} {
set, err := Parse(spec)
if err != nil {
t.Fatalf("Parse(%q): %v", spec, err)
}
again, err := Parse(set.String())
if err != nil {
t.Fatalf("Parse(%q): %v", set.String(), err)
}
if again.Arm() != set.Arm() {
t.Errorf("%q round-tripped to %q", set.Arm(), again.Arm())
}
}
}
// The index axis is not a module, but a run that moved it is not the control
// arm either: results keyed by arm name would otherwise merge with it.
func TestFoldIndexScaleNamesItsOwnArm(t *testing.T) {
var s Set
if got := s.WithFoldIndex(FoldIndexQuarter).Arm(); got != "index-quarter" {
t.Errorf("Arm() = %q, want index-quarter", got)
}
if got := New(Evidence).WithFoldIndex(FoldIndexOff).Arm(); got != "no-evidence+index-off" {
t.Errorf("Arm() = %q, want no-evidence+index-off", got)
}
if s.WithFoldIndex(FoldIndexQuarter).Empty() {
t.Error("a quartered index reported itself as the control arm")
}
if !s.WithFoldIndex(FoldIndexDefault).Empty() {
t.Error("the default scale is the control arm")
}
}
func TestFoldIndexScaleRatios(t *testing.T) {
for _, tc := range []struct {
spec string
scale FoldIndexScale
ratio float64
}{
{"", FoldIndexDefault, 1}, {"default", FoldIndexDefault, 1},
{"half", FoldIndexHalf, 0.5}, {"QUARTER", FoldIndexQuarter, 0.25},
{"off", FoldIndexOff, 0}, {"0", FoldIndexOff, 0},
} {
got, err := ParseFoldIndexScale(tc.spec)
if err != nil {
t.Fatalf("ParseFoldIndexScale(%q): %v", tc.spec, err)
}
if got != tc.scale || got.Ratio() != tc.ratio {
t.Errorf("ParseFoldIndexScale(%q) = %q (ratio %v), want %q (%v)", tc.spec, got, got.Ratio(), tc.scale, tc.ratio)
}
}
if _, err := ParseFoldIndexScale("0.37"); err == nil {
t.Fatal("a free-form fraction was accepted; the axis is four steps on purpose")
}
}
func TestZeroValueIsTheControlArm(t *testing.T) {
var s Set
if !s.Empty() || s.Arm() != "full" {
t.Fatalf("zero Set = %q, want the full control arm", s.Arm())
}
for _, m := range Modules() {
if s.Off(m) {
t.Errorf("zero Set disabled %s", m)
}
}
}
func TestEveryModuleHasItsOwnSummary(t *testing.T) {
seen := map[string]bool{}
for _, m := range Modules() {
s := m.Summary()
if s == "" || seen[s] {
t.Errorf("%s: summary %q is empty or shared", m, s)
}
seen[s] = true
}
if Module("bogus").Summary() != "" {
t.Error("an undeclared module has a summary")
}
}
func TestModuleHelpListsModulesInArmOrder(t *testing.T) {
lines := strings.Split(ModuleHelp(), "\n")
mods := Modules()
if len(lines) != len(mods) {
t.Fatalf("ModuleHelp has %d lines for %d modules", len(lines), len(mods))
}
for i, m := range mods {
if lines[i] != string(m)+": "+m.Summary() {
t.Errorf("line %d = %q", i, lines[i])
}
}
}
@@ -0,0 +1,11 @@
prompt = """[Reasonix: plan-first]
Add a `memory` module to the benchmark ablation arms: switching it off means the run gets no saved-fact memory store. Work through it as a plan, and sign off each step with its evidence before starting the next:
1. Declare the module so `Parse`, `all` and arm names know it. It goes last in the order arm names use.
2. Give every module a one-line summary, `func (m Module) Summary() string`, returning "" for a module that is not declared. Take the wording from the README's ablation table.
3. Add `ModuleHelp() string`: one line per module, in arm-name order, each `<name>: <summary>`, newline-separated with no trailing newline.
4. Make `benchctl arm -list` print `ModuleHelp()` instead of the comma-separated list. The `-ablate` flag's help must still name every module.
5. Cover each of the above with tests, and add the new module to the README's ablation table."""
class = "plan-mode"
timeout_sec = 1200
@@ -0,0 +1,74 @@
#!/usr/bin/env bash
set -e
export GOFLAGS=-mod=mod GOTOOLCHAIN=local
cat > internal/contract/ablation/zz_grader_test.go <<'GO'
package ablation
import (
"strings"
"testing"
)
var _ Module = Memory
func TestGraderMemoryIsTheLastModule(t *testing.T) {
mods := Modules()
if len(mods) != 8 || mods[7] != Module("memory") || Memory != Module("memory") {
t.Fatalf("Modules() = %v, want the seven shipped modules then memory", mods)
}
for spec, arm := range map[string]string{
"memory": "no-memory",
"memory,planner": "no-planner+no-memory",
"all": "no-evidence+no-planner+no-subagent+no-retrieval+no-compaction+no-upstream+no-recall-search+no-memory",
} {
set, err := Parse(spec)
if err != nil {
t.Fatalf("Parse(%q): %v", spec, err)
}
if got := set.Arm(); got != arm {
t.Errorf("Parse(%q).Arm() = %q, want %q", spec, got, arm)
}
}
if !strings.Contains(ModuleList(), "memory") {
t.Errorf("ModuleList() = %q", ModuleList())
}
}
func TestGraderSummariesAndHelp(t *testing.T) {
seen := map[string]Module{}
var want []string
for _, m := range Modules() {
s := m.Summary()
if strings.TrimSpace(s) == "" || strings.Contains(s, "\n") {
t.Errorf("%s: summary %q is not one line", m, s)
}
if prev, dup := seen[s]; dup {
t.Errorf("%s and %s share the summary %q", prev, m, s)
}
seen[s] = m
want = append(want, string(m)+": "+s)
}
if got := Module("bogus").Summary(); got != "" {
t.Errorf("undeclared module summary = %q, want empty", got)
}
if got := ModuleHelp(); got != strings.Join(want, "\n") {
t.Errorf("ModuleHelp() = %q, want %q", got, strings.Join(want, "\n"))
}
}
GO
gofmt_out="$(gofmt -l .)"
[ -z "$gofmt_out" ] || { echo "gofmt needed: $gofmt_out"; exit 1; }
go vet ./...
go test ./... >/dev/null
list="$(go run ./cmd/benchctl arm -list)"
[ "$(printf '%s\n' "$list" | wc -l | tr -d ' ')" = 8 ] || { echo "arm -list printed: $list"; exit 1; }
case "$(printf '%s\n' "$list" | head -1)" in "evidence: "?*) ;; *) echo "arm -list first line: $list"; exit 1 ;; esac
case "$(printf '%s\n' "$list" | tail -1)" in "memory: "?*) ;; *) echo "arm -list last line: $list"; exit 1 ;; esac
[ "$(go run ./cmd/benchctl arm -ablate memory)" = no-memory ] || { echo "benchctl arm -ablate memory"; exit 1; }
go run ./cmd/benchctl arm -h 2>&1 | grep -A2 -- '-ablate' | grep -q 'memory' || { echo "-ablate help does not name memory"; exit 1; }
grep -lE 'ModuleHelp\(|Summary\(' internal/contract/ablation/*_test.go | grep -qv zz_grader_test.go \
|| { echo "no ablation test covers Summary or ModuleHelp"; exit 1; }
grep -qE 'Memory|"memory"' cmd/benchctl/main_test.go internal/contract/ablation/ablation_test.go \
|| { echo "no test covers the memory module"; exit 1; }
awk '/^## Ablation/{on=1; next} /^## /{on=0} on' README.md | grep -qE '^\| `memory` \|' \
|| { echo "README's ablation table has no memory row"; exit 1; }
@@ -0,0 +1,18 @@
---
name: price-change
description: Record a vendor's announced price change in the official price book without losing the rate it replaces.
---
# Recording a price change
1. Find the vendor's rows in `officialRates` in
`internal/contract/pricing/rates.go`. Each row is one model in one currency.
2. **Append** a `rateGeneration` to the row's `Rates`; never edit an existing
one. Set `From` to the first billed day (`YYYY-MM-DD`, UTC) when the change
is announced ahead of time.
3. Leave the earlier generation where it is: `SupersededRates` reads it so a
stored price that matches it is recognised as ours and refreshed.
4. Update the row's `CheckedOn` constant to the day you confirmed the page.
5. Add a test beside `rates_test.go` that pins `CurrentRate` before and after
the `From` day by replacing `today`.
6. Run `make check`. `make pricecheck` needs the network and is run by hand.
@@ -0,0 +1,17 @@
.PHONY: check test vet fmt pricecheck
check: fmt vet test
fmt:
@out="$$(gofmt -l .)"; if [ -n "$$out" ]; then echo "gofmt needed:"; echo "$$out"; exit 1; fi
vet:
go vet ./...
test:
go test ./...
# Reads each vendor's pricing page; this snapshot carries no network client.
pricecheck:
@echo "pricecheck: reaches each vendor's pricing page and needs the network; not available in this snapshot" >&2
@exit 2
@@ -0,0 +1,69 @@
# Reasonix kernel snapshot
A trimmed slice of the Reasonix Go kernel: the contracts a benchmark report is
priced and labelled with, a few leaf utilities, and `benchctl`, a small command
that prints what those contracts answer.
```bash
make check # gofmt, vet, test
go run ./cmd/benchctl rate -model deepseek-flash -currency USD
go run ./cmd/benchctl cost -provider mimo -model mimo-v2.5 -miss 1000000
go run ./cmd/benchctl tariff -currency CNY -at 2026-10-12T02:00:00Z
go run ./cmd/benchctl arm -ablate planner,evidence
```
## Packages
| Package | Owns |
| --- | --- |
| `internal/contract/pricing` | the official price book, cost quotes, the spend ledger |
| `internal/contract/ablation` | which subsystems a benchmark arm switches off, and the arm's name |
| `internal/contract/agentgraph` | the vocabulary of one run's execution graph |
| `internal/base/retrieval` | BM25 ranking over CJK-aware tokens |
| `internal/base/rulelist` | splitting a one-line permission rule list |
| `internal/base/tokencount` | a cheap token estimate |
| `internal/base/neterr` | classifying transport errors |
| `internal/base/nilutil` | typed-nil detection |
| `cmd/benchctl` | prints rates, costs, peak schedules and arm names |
## Pricing
A `RateCard` is a price per million tokens in one currency:
| Field | Charges |
| --- | --- |
| `CacheHit` | prompt tokens served from the provider's cache |
| `Input` | prompt tokens that missed the cache, including tokens written to it |
| `Output` | completion tokens |
`OriginalCostAmount` turns a card and a `UsageTokens` breakdown into a
fixed-point `Amount`; `PricingFingerprint` keys a card in the ledger, so a
stored ledger entry stays attached to the card it was priced with.
`CurrentRate` and `SupersededRates` answer from `officialRates`, which only
ever grows: a price change appends a generation.
## Ablation
`ablation.Parse` reads a comma-separated list of modules to switch off, and
`Set.Arm` names the result (`full` for the control). The switchable modules,
in the order arm names use:
| Module | Off means |
| --- | --- |
| `evidence` | no evidence gate on completion claims |
| `planner` | no two-model planning turn |
| `subagent` | no delegation to workers |
| `retrieval` | no retrieval over the folded transcript |
| `compaction` | no context folding |
| `upstream` | a dependency edge orders its endpoints without delivering the answer |
| `recall-search` | recall can read a given address but cannot search for one |
The fold-index scale is a separate axis (`-fold-index`), not a module.
## Skills
Project skills live under `.reasonix/skills/<name>/SKILL.md`.
| Skill | Use it to |
| --- | --- |
| `price-change` | record a vendor's price change in the price book |
@@ -0,0 +1,51 @@
# Reasonix project memory
This checkout is a trimmed snapshot of the Reasonix kernel: the billing,
ablation and graph contracts and a handful of leaf utilities, plus one small
command that drives them. The conventions below are the project's own.
## Conventions
- Go kernel under `internal/`; each package owns one concern. A package's long
explanation belongs in its `doc.go`, not spread across implementation files.
- Judgements read structure, never wording: types, contracts, schemas. An
error a caller must tell apart carries an identity — a sentinel, a typed
code — never a sentence.
- A metric is its definition, not its name. Before a number becomes a finding,
state what it counts, from which anchor, over which population, and what it
excludes.
## Comments
Default is none — the code is the truth. Write one only when the **why** is
non-obvious: a hidden constraint, an invariant the type system cannot express,
or an external-protocol quirk. A comment states the constraint that holds now;
it does not narrate how the code got here. English, always.
- Declaration doc: ≤5 lines. Package comment: ≤8 lines, or ≤40 in a `doc.go`.
- Every other comment: ≤3 lines. Struct-field and trailing `//`: 1 line.
- One responsibility per file; 800 lines is the ceiling.
## Vendor rates
Official list prices live in one place: `officialRates` in
`internal/contract/pricing/rates.go`, oldest generation first, the last being
what a model charges today. A price change is an **append, never an edit** —
the superseded rate is what lets an installed config be recognised as holding
one of ours and brought forward. Callers read these through
`pricing.CurrentRate` and `pricing.SupersededRates` and restate none of them.
`make pricecheck` compares that table against each vendor's own page. It needs
the network, so it is not part of `make check`.
## Before every commit
```bash
make check # gofmt -l, go vet ./..., go test ./...
```
## Change discipline
List callers, consumers of the same state and sibling features before editing.
Fix at the owning layer; a fix that breaks a neighbouring behaviour is a failed
fix. Write tests first; they must fail without the change.
@@ -0,0 +1,115 @@
// Command benchctl prints what the pricing and ablation contracts answer for
// one model or one benchmark arm, so a reviewer can check a rate or an arm name
// without writing a test.
package main
import (
"errors"
"flag"
"fmt"
"io"
"os"
"reasonix/internal/contract/ablation"
"reasonix/internal/contract/pricing"
)
var errUsage = errors.New("usage: benchctl rate|cost|tariff|arm [flags]")
func main() {
if err := run(os.Args[1:], os.Stdout); err != nil {
fmt.Fprintln(os.Stderr, "benchctl:", err)
os.Exit(2)
}
}
func run(args []string, out io.Writer) error {
if len(args) == 0 {
return errUsage
}
switch args[0] {
case "rate":
return runRate(args[1:], out)
case "cost":
return runCost(args[1:], out)
case "tariff":
return runTariff(args[1:], out)
case "arm":
return runArm(args[1:], out)
default:
return fmt.Errorf("%w: unknown subcommand %q", errUsage, args[0])
}
}
func runRate(args []string, out io.Writer) error {
fs := flag.NewFlagSet("rate", flag.ContinueOnError)
fs.SetOutput(out)
provider := fs.String("provider", "deepseek", "vendor")
model := fs.String("model", "deepseek-flash", "model id")
currency := fs.String("currency", "", "currency; empty reads the vendor's default")
if err := fs.Parse(args); err != nil {
return err
}
cur := *currency
if cur == "" {
cur = pricing.DefaultCurrency(*provider)
}
card := pricing.CurrentRate(*provider, *model, cur)
if card == nil {
return fmt.Errorf("no official rate for %s/%s in %s", *provider, *model, cur)
}
fmt.Fprintf(out, "%s/%s %s: cache-hit %g input %g output %g per 1M\n",
*provider, *model, card.Currency, card.CacheHit, card.Input, card.Output)
if entry, ok := pricing.LookupCatalog(*provider, *model, cur, ""); ok && entry.DocURL != "" {
fmt.Fprintf(out, "source: %s\n", entry.DocURL)
}
fmt.Fprintf(out, "superseded generations: %d\n", len(pricing.SupersededRates(*provider, *model, cur)))
return nil
}
func runCost(args []string, out io.Writer) error {
fs := flag.NewFlagSet("cost", flag.ContinueOnError)
fs.SetOutput(out)
provider := fs.String("provider", "deepseek", "vendor")
model := fs.String("model", "deepseek-flash", "model id")
currency := fs.String("currency", "", "currency; empty reads the vendor's default")
hit := fs.Int("hit", 0, "cached prompt tokens")
miss := fs.Int("miss", 0, "uncached prompt tokens")
completion := fs.Int("completion", 0, "completion tokens")
if err := fs.Parse(args); err != nil {
return err
}
cur := *currency
if cur == "" {
cur = pricing.DefaultCurrency(*provider)
}
card := pricing.CurrentRate(*provider, *model, cur)
if card == nil {
return fmt.Errorf("no official rate for %s/%s in %s", *provider, *model, cur)
}
usage := pricing.UsageTokens{CacheHitTokens: *hit, CacheMissTokens: *miss, CompletionTokens: *completion}
amount := pricing.OriginalCostAmount(*card, usage)
fmt.Fprintf(out, "%s %s\n", amount, pricing.NormalizeCurrency(card.Currency))
return nil
}
func runArm(args []string, out io.Writer) error {
fs := flag.NewFlagSet("arm", flag.ContinueOnError)
fs.SetOutput(out)
spec := fs.String("ablate", "", "comma-separated modules to switch off: "+ablation.ModuleList())
foldIndex := fs.String("fold-index", "", "fold-index scale: default, half, quarter or off")
list := fs.Bool("list", false, "list the switchable modules")
if err := fs.Parse(args); err != nil {
return err
}
if *list {
fmt.Fprintln(out, ablation.ModuleList())
return nil
}
set, err := ablation.ParseArm(*spec, *foldIndex)
if err != nil {
return err
}
fmt.Fprintln(out, set.Arm())
return nil
}
@@ -0,0 +1,65 @@
package main
import (
"bytes"
"errors"
"strings"
"testing"
)
func runOut(t *testing.T, args ...string) string {
t.Helper()
var out bytes.Buffer
if err := run(args, &out); err != nil {
t.Fatalf("run %v: %v", args, err)
}
return out.String()
}
func TestRateNamesTheCurrentCard(t *testing.T) {
got := runOut(t, "rate", "-provider", "mimo", "-model", "mimo-v2.5")
if !strings.Contains(got, "mimo/mimo-v2.5 CNY: cache-hit 0.02 input 1 output 2 per 1M") {
t.Fatalf("rate output = %q", got)
}
}
func TestRateRefusesAnUnpricedModel(t *testing.T) {
var out bytes.Buffer
if err := run([]string{"rate", "-provider", "nobody", "-model", "x"}, &out); err == nil {
t.Fatal("an unpriced model must fail")
}
}
func TestCostChargesTheCurrentCard(t *testing.T) {
got := runOut(t, "cost", "-provider", "mimo", "-model", "mimo-v2.5", "-miss", "1000000", "-completion", "1000000")
if strings.TrimSpace(got) != "3 CNY" {
t.Fatalf("cost output = %q", got)
}
}
func TestArmNamesTheConfiguration(t *testing.T) {
if got := strings.TrimSpace(runOut(t, "arm")); got != "full" {
t.Fatalf("control arm = %q", got)
}
if got := strings.TrimSpace(runOut(t, "arm", "-ablate", "planner,evidence", "-fold-index", "half")); got != "no-evidence+no-planner+index-half" {
t.Fatalf("arm = %q", got)
}
}
func TestUnknownSubcommandIsAUsageError(t *testing.T) {
var out bytes.Buffer
if err := run([]string{"nope"}, &out); !errors.Is(err, errUsage) {
t.Fatalf("err = %v, want errUsage", err)
}
}
func TestTariffReadsTheVendorsSchedule(t *testing.T) {
got := runOut(t, "tariff", "-provider", "mimo", "-model", "mimo-v2.5")
if !strings.Contains(got, "official card: true") || !strings.Contains(got, "one rate around the clock") {
t.Fatalf("tariff output = %q", got)
}
peak := runOut(t, "tariff", "-currency", "CNY", "-at", "2026-10-12T02:00:00Z")
if !strings.Contains(peak, "peak=true until 2026-10-12T04:00:00Z") {
t.Fatalf("deepseek tariff at a weekday peak = %q", peak)
}
}
@@ -0,0 +1,49 @@
package main
import (
"flag"
"fmt"
"io"
"time"
"reasonix/internal/contract/pricing"
)
func runTariff(args []string, out io.Writer) error {
fs := flag.NewFlagSet("tariff", flag.ContinueOnError)
fs.SetOutput(out)
provider := fs.String("provider", "deepseek", "vendor")
model := fs.String("model", "deepseek-flash", "model id")
currency := fs.String("currency", "", "currency; empty reads the vendor's default")
at := fs.String("at", "", "instant to read the schedule at, RFC 3339; empty is now")
if err := fs.Parse(args); err != nil {
return err
}
when := time.Now()
if *at != "" {
t, err := time.Parse(time.RFC3339, *at)
if err != nil {
return fmt.Errorf("-at: %w", err)
}
when = t
}
cur := *currency
if cur == "" {
cur = pricing.DefaultCurrency(*provider)
}
card := pricing.CurrentRate(*provider, *model, cur)
if card == nil {
return fmt.Errorf("no official rate for %s/%s in %s", *provider, *model, cur)
}
fmt.Fprintf(out, "official card: %v\n", pricing.IsOfficialCard(*provider, *model, *card))
tariff, ok := pricing.LiveTariff(*provider, *model, *card, when)
switch {
case !ok:
fmt.Fprintln(out, "schedule: one rate around the clock")
case !tariff.Changes:
fmt.Fprintf(out, "schedule: peak=%v, no change ahead\n", tariff.Peak)
default:
fmt.Fprintf(out, "schedule: peak=%v until %s\n", tariff.Peak, tariff.ChangesAt.UTC().Format(time.RFC3339))
}
return nil
}
@@ -0,0 +1,3 @@
module reasonix
go 1.26.0
@@ -0,0 +1,7 @@
//go:build !windows
package neterr
import "syscall"
var resetErrors = []error{syscall.ECONNRESET, syscall.ECONNABORTED, syscall.EPIPE}
@@ -0,0 +1,8 @@
package neterr
import "syscall"
// syscall.ECONNRESET and friends exist on Windows only as APPLICATION_ERROR
// placeholders, so errors.Is against them never matches a real socket failure.
// Winsock's own numbering carries the identities the runtime returns.
var resetErrors = []error{syscall.WSAECONNRESET, syscall.WSAECONNABORTED, syscall.ERROR_BROKEN_PIPE}
@@ -0,0 +1,47 @@
// Package neterr answers what a transport error is from the identity the
// operating system gave it, never from the sentence it rendered. A system
// error's text arrives in the machine's own language and is reworded between
// releases, so a word list that matches on an English host silently stops
// matching elsewhere. The judgement lives here once because three packages
// were each carrying their own copy of it.
package neterr
import (
"context"
"errors"
"io"
"net"
)
// IsConnReset reports whether err is a connection-level drop (peer reset,
// truncated body, closed socket) as opposed to a protocol or caller error. A
// stream cut this way mid-body can be replayed from scratch, unlike a decode
// or 4xx error. The common trigger is a local proxy (v2rayN/sing-box)
// idle-closing a long-lived SSE connection during a reasoner's first-token gap.
func IsConnReset(err error) bool {
if err == nil {
return false
}
if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
return false
}
if errors.Is(err, io.ErrUnexpectedEOF) || errors.Is(err, io.EOF) ||
errors.Is(err, net.ErrClosed) || isAny(err, resetErrors) {
return true
}
var dnsErr *net.DNSError
if errors.As(err, &dnsErr) {
return false
}
var netErr net.Error
return errors.As(err, &netErr)
}
func isAny(err error, targets []error) bool {
for _, target := range targets {
if errors.Is(err, target) {
return true
}
}
return false
}
@@ -0,0 +1,43 @@
package neterr
import (
"context"
"errors"
"fmt"
"io"
"net"
"testing"
)
func TestIsConnReset(t *testing.T) {
if IsConnReset(nil) {
t.Error("nil is not a conn reset")
}
if IsConnReset(context.Canceled) || IsConnReset(context.DeadlineExceeded) {
t.Error("ctx cancel/deadline must not look like a recoverable reset")
}
if IsConnReset(errors.New("decode stream: invalid character")) {
t.Error("a plain protocol error must not be treated as a conn reset")
}
for _, err := range []error{
io.ErrUnexpectedEOF,
&net.OpError{Op: "read", Err: resetErrors[0]},
fmt.Errorf("read stream: %w", &net.OpError{Op: "read", Err: errors.New("wsarecv: forcibly closed")}),
} {
if !IsConnReset(err) {
t.Errorf("want conn reset for %v", err)
}
}
}
func TestIsConnResetExcludesDNS(t *testing.T) {
for _, dns := range []*net.DNSError{
{Err: "no such host", Name: "api.deepseek.com", IsNotFound: true},
{Err: "i/o timeout", Name: "api.deepseek.com", IsTimeout: true, IsTemporary: true},
} {
err := fmt.Errorf("dial: %w", &net.OpError{Op: "dial", Err: dns})
if IsConnReset(err) {
t.Errorf("DNS failure %v is not a connection reset", dns)
}
}
}
@@ -0,0 +1,17 @@
package nilutil
import "reflect"
// IsNil reports whether v is nil or an interface holding a typed nil value.
func IsNil(v any) bool {
if v == nil {
return true
}
rv := reflect.ValueOf(v)
switch rv.Kind() {
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice:
return rv.IsNil()
default:
return false
}
}
@@ -0,0 +1,31 @@
package nilutil
import "testing"
type sampleInterface interface {
Do()
}
type sampleImpl struct{}
func (*sampleImpl) Do() {}
func TestIsNilDetectsTypedNilInterface(t *testing.T) {
var p *sampleImpl
var i sampleInterface = p
if !IsNil(i) {
t.Fatal("IsNil should detect typed nil interface")
}
}
func TestIsNilRejectsConcreteValues(t *testing.T) {
var p sampleInterface = &sampleImpl{}
if IsNil(p) {
t.Fatal("IsNil should not reject a concrete interface value")
}
if IsNil("x") {
t.Fatal("IsNil should not reject non-nil scalar values")
}
}
@@ -0,0 +1,218 @@
package retrieval
import (
"fmt"
"math"
"strings"
"unicode"
"unicode/utf8"
)
// Tokens lowercases Latin words and splits CJK runs into overlapping bigrams
// (a lone CJK rune stays a single term) — the standard CJK indexing unit
// (Lucene's CJKAnalyzer, SQLite FTS): a bigram only matches a real two-rune
// subsequence where per-rune unigrams matched scattered common characters.
// Intentionally a local, dependency-free approximation of FTS matching.
func Tokens(s string) []string {
var out []string
var b strings.Builder
var prev rune
cjkRun := 0
flush := func() {
if b.Len() == 0 {
return
}
out = append(out, b.String())
b.Reset()
}
endCJK := func() {
if cjkRun == 1 {
out = append(out, string(prev))
}
cjkRun = 0
}
for _, r := range s {
switch {
case isCJK(r):
flush()
if cjkRun > 0 {
out = append(out, string([]rune{prev, r}))
}
prev = r
cjkRun++
case unicode.IsLetter(r) || unicode.IsDigit(r) || r == '_':
endCJK()
b.WriteRune(unicode.ToLower(r))
default:
flush()
endCJK()
}
}
flush()
endCJK()
return out
}
func isCJK(r rune) bool {
return unicode.In(r, unicode.Han, unicode.Hiragana, unicode.Katakana, unicode.Hangul)
}
// Unique returns terms in first-seen order.
func Unique(in []string) []string {
seen := map[string]bool{}
out := make([]string, 0, len(in))
for _, s := range in {
if s == "" || seen[s] {
continue
}
seen[s] = true
out = append(out, s)
}
return out
}
// Counts returns a term-frequency map.
func Counts(terms []string) map[string]int {
counts := map[string]int{}
for _, term := range terms {
counts[term]++
}
return counts
}
// BM25Score scores a document against query terms.
func BM25Score(counts map[string]int, length int, queryTerms []string, df map[string]int, totalDocs int, avgLen float64) float64 {
const (
k1 = 1.2
b = 0.75
)
if length <= 0 || totalDocs <= 0 {
return 0
}
if avgLen <= 0 {
avgLen = 1
}
var score float64
docLen := float64(length)
for _, term := range queryTerms {
tf := counts[term]
if tf == 0 {
continue
}
termDF := df[term]
if termDF == 0 {
continue
}
idf := math.Log(1 + (float64(totalDocs)-float64(termDF)+0.5)/(float64(termDF)+0.5))
freq := float64(tf)
score += idf * (freq * (k1 + 1)) / (freq + k1*(1-b+b*docLen/avgLen))
}
return score
}
// DocumentFrequency counts how many documents contain each term.
func DocumentFrequency(docs []map[string]int) map[string]int {
df := map[string]int{}
for _, counts := range docs {
for term := range counts {
df[term]++
}
}
return df
}
// KeepTopRelativeScore keeps the best item and drops trailing items whose score
// falls below ratio * topScore. Callers must pass items already sorted best
// first. This mirrors SQLite FTS/BM25 search UIs that over-fetch, then trim
// common-word-only noise without imposing an absolute score threshold.
func KeepTopRelativeScore[T any](items []T, ratio float64, score func(T) float64) []T {
if len(items) == 0 || ratio <= 0 {
return items
}
top := score(items[0])
if top <= 0 {
return items
}
cutoff := top * ratio
out := items[:0]
for i, item := range items {
if i == 0 || score(item) >= cutoff {
out = append(out, item)
}
}
return out
}
// QueryTerms normalizes a search string and reports an error when nothing
// searchable remains.
func QueryTerms(query string) ([]string, error) {
terms := Unique(Tokens(strings.TrimSpace(query)))
if len(terms) == 0 {
return nil, fmt.Errorf("query must contain at least one letter or number")
}
return terms, nil
}
// MakeSnippet returns a whitespace-compacted excerpt centered near the query.
func MakeSnippet(text, query string, terms []string, maxRunes int) string {
text = CompactWhitespace(text)
if maxRunes <= 0 || utf8.RuneCountInString(text) <= maxRunes {
return text
}
lower := strings.ToLower(text)
query = strings.ToLower(strings.TrimSpace(query))
idx := -1
if query != "" {
idx = strings.Index(lower, query)
}
if idx < 0 {
for _, term := range terms {
runes := []rune(term)
if len(runes) == 1 && !isCJK(runes[0]) {
continue
}
if i := strings.Index(lower, term); i >= 0 {
idx = i
break
}
}
}
if idx < 0 {
idx = 0
}
return snippetAround(text, idx, maxRunes)
}
func snippetAround(text string, byteIdx, maxRunes int) string {
if byteIdx < 0 {
byteIdx = 0
}
if byteIdx > len(text) {
byteIdx = len(text)
}
for byteIdx > 0 && byteIdx < len(text) && !utf8.RuneStart(text[byteIdx]) {
byteIdx--
}
runes := []rune(text)
pos := utf8.RuneCountInString(text[:byteIdx])
start := max(pos-maxRunes/2, 0)
end := start + maxRunes
if end > len(runes) {
end = len(runes)
start = max(end-maxRunes, 0)
}
prefix := ""
suffix := ""
if start > 0 {
prefix = "..."
}
if end < len(runes) {
suffix = "..."
}
return prefix + string(runes[start:end]) + suffix
}
// CompactWhitespace collapses runs of whitespace into one ASCII space.
func CompactWhitespace(s string) string {
return strings.Join(strings.Fields(s), " ")
}
@@ -0,0 +1,77 @@
package retrieval
import (
"strings"
"testing"
"unicode/utf8"
)
func TestTokensHandlesLatinAndCJK(t *testing.T) {
for _, tc := range []struct {
in string
want []string
}{
{"BM25 检索 cache-first", []string{"bm25", "检索", "cache", "first"}},
{"数据库迁移", []string{"数据", "据库", "库迁", "迁移"}},
{"库", []string{"库"}},
{"用pnpm装依赖", []string{"用", "pnpm", "装依", "依赖"}},
} {
got := Tokens(tc.in)
if strings.Join(got, ",") != strings.Join(tc.want, ",") {
t.Fatalf("Tokens(%q) = %#v, want %#v", tc.in, got, tc.want)
}
}
}
func TestBM25ScoreRanksMatchingDocument(t *testing.T) {
query := Unique(Tokens("prompt cache"))
doc1 := Counts(Tokens("prompt cache cache stability"))
doc2 := Counts(Tokens("dashboard colors"))
df := DocumentFrequency([]map[string]int{doc1, doc2})
score1 := BM25Score(doc1, 4, query, df, 2, 3)
score2 := BM25Score(doc2, 2, query, df, 2, 3)
if score1 <= score2 {
t.Fatalf("matching score %.3f should exceed unrelated score %.3f", score1, score2)
}
}
func TestKeepTopRelativeScoreKeepsTopAndDropsWeakTail(t *testing.T) {
items := []struct {
name string
score float64
}{
{name: "top", score: 10},
{name: "near", score: 2},
{name: "noise", score: 1.4},
{name: "zero", score: 0},
}
got := KeepTopRelativeScore(items, 0.15, func(item struct {
name string
score float64
}) float64 {
return item.score
})
if len(got) != 2 || got[0].name != "top" || got[1].name != "near" {
t.Fatalf("KeepTopRelativeScore() = %#v, want top and near", got)
}
}
func TestMakeSnippetHandlesMultibyteBoundary(t *testing.T) {
text := strings.Repeat("前缀", 80) + "稳定结论 synthesis cache " + strings.Repeat("后缀", 80)
out := MakeSnippet(text, "synthesis cache", QueryTermsForTest(t, "synthesis cache"), 60)
if !strings.Contains(out, "synthesis cache") {
t.Fatalf("snippet missing query: %q", out)
}
if strings.ContainsRune(out, utf8.RuneError) {
t.Fatalf("snippet contains replacement rune: %q", out)
}
}
func QueryTermsForTest(t *testing.T, query string) []string {
t.Helper()
terms, err := QueryTerms(query)
if err != nil {
t.Fatal(err)
}
return terms
}
@@ -0,0 +1,176 @@
// Retrieval V2, shadow-only: BM25F field weighting, code-symbol splitting,
// and mixed CJK uni+bigrams. Nothing here serves the model — V2 rankings ride
// the recall audit next to production's, and MemoryBench decides if V2 ever
// takes over. Weights are candidates under measurement, not tuned truths.
package retrieval
import (
"math"
"strings"
"unicode"
)
// TokensV2 extends Tokens with code-symbol structure and CJK unigrams:
// CamelCase and snake_case words also emit their segments (FooBar → foobar,
// foo, bar), and CJK runs emit unigrams alongside bigrams so one-character
// queries still match. The V1 form of every token is preserved, so V2 recall
// is a superset of V1's vocabulary.
func TokensV2(s string) []string {
var out []string
var word []rune
var prev rune
cjkRun := 0
flushWord := func() {
if len(word) == 0 {
return
}
lower := strings.ToLower(string(word))
out = append(out, lower)
for _, seg := range splitCodeSymbol(word) {
if seg != lower {
out = append(out, seg)
}
}
word = word[:0]
}
endCJK := func() { cjkRun = 0 }
for _, r := range s {
switch {
case isCJK(r):
flushWord()
out = append(out, string(r))
if cjkRun > 0 {
out = append(out, string([]rune{prev, r}))
}
prev = r
cjkRun++
case unicode.IsLetter(r) || unicode.IsDigit(r) || r == '_':
endCJK()
word = append(word, r)
default:
flushWord()
endCJK()
}
}
flushWord()
return out
}
// splitCodeSymbol yields the lowercase segments of a code identifier:
// CamelCase humps, snake_case parts, and letter/digit boundaries.
func splitCodeSymbol(word []rune) []string {
var segments []string
var seg []rune
flush := func() {
if len(seg) > 1 { // single letters are noise, not signal
segments = append(segments, strings.ToLower(string(seg)))
}
seg = seg[:0]
}
for i, r := range word {
boundary := r == '_' ||
(i > 0 && unicode.IsUpper(r) && !unicode.IsUpper(word[i-1])) ||
(i > 0 && unicode.IsDigit(r) != unicode.IsDigit(word[i-1]))
if boundary {
flush()
}
if r != '_' {
seg = append(seg, r)
}
}
flush()
if len(segments) < 2 {
return nil // no structure worth indexing beyond the whole word
}
return segments
}
// FieldedDoc is one document split into weighted fields for BM25F.
type FieldedDoc struct {
ID string
Fields map[string]string
}
// v2FieldWeights follow the BM25F intuition: identity fields say what a fact
// IS, the body says everything it mentions. Candidates under measurement.
var v2FieldWeights = map[string]float64{
"name": 2.5, "title": 2.5, "keywords": 2.0, "subject": 2.0,
"description": 1.5, "body": 1.0,
}
// V2Hit is one shadow-ranked document.
type V2Hit struct {
ID string
Score float64
}
// RankV2 scores docs against a query with BM25F over TokensV2: term
// frequencies accumulate per field scaled by field weight, then the standard
// saturation applies once per term (the F in BM25F), with length
// normalization over the weighted document length.
func RankV2(query string, docs []FieldedDoc) []V2Hit {
queryTerms := Unique(TokensV2(query))
if len(queryTerms) == 0 || len(docs) == 0 {
return nil
}
type indexed struct {
id string
tf map[string]float64
length float64
}
corpus := make([]indexed, 0, len(docs))
df := map[string]int{}
var totalLen float64
for _, doc := range docs {
ix := indexed{id: doc.ID, tf: map[string]float64{}}
seen := map[string]bool{}
for field, text := range doc.Fields {
weight, ok := v2FieldWeights[field]
if !ok {
weight = 1.0
}
for _, term := range TokensV2(text) {
ix.tf[term] += weight
ix.length += weight
seen[term] = true
}
}
for term := range seen {
df[term]++
}
totalLen += ix.length
corpus = append(corpus, ix)
}
avgLen := totalLen / float64(len(corpus))
if avgLen <= 0 {
avgLen = 1
}
const k1, b = 1.2, 0.75
var hits []V2Hit
for _, doc := range corpus {
var score float64
for _, term := range queryTerms {
tf := doc.tf[term]
if tf == 0 || df[term] == 0 {
continue
}
idf := math.Log(1 + (float64(len(corpus))-float64(df[term])+0.5)/(float64(df[term])+0.5))
score += idf * (tf * (k1 + 1)) / (tf + k1*(1-b+b*doc.length/avgLen))
}
if score > 0 {
hits = append(hits, V2Hit{ID: doc.id, Score: score})
}
}
SortHitsDesc(hits)
return hits
}
// SortHitsDesc orders shadow hits best-first with a stable ID tiebreak.
func SortHitsDesc(hits []V2Hit) {
for i := 1; i < len(hits); i++ {
for j := i; j > 0 && (hits[j].Score > hits[j-1].Score ||
(hits[j].Score == hits[j-1].Score && hits[j].ID < hits[j-1].ID)); j-- {
hits[j], hits[j-1] = hits[j-1], hits[j]
}
}
}
Loaded 100 of 257 files, more files were not shown because too many files have changed in this diff. Show more