Skip to content

perf(rust): move pricing snapshots into a leaf crate - #1922

Merged
ryoppippi merged 3 commits into
ci/speed-up-windows-buildfrom
perf/isolate-pricing-snapshots
Oct 18, 2026
Merged

ryoppippi merged 3 commits into
ci/speed-up-windows-buildfrom
perf/isolate-pricing-snapshots

Conversation

@ryoppippi

@ryoppippi ryoppippi commented Oct 18, 2026 •

Copy link
Copy Markdown
Member

Summary

Most commits that land on main are the scheduled chore(pricing): update models.dev snapshot / update LiteLLM snapshot commits (776 of the last 878 first-parent commits). Each one invalidated the foundation and adapters Crane layers. As a result, every PR opened after one of those commits rebuilt ccusage-core, every adapter, the config-schema generator, and the clippy/hawk/test bases, even when the PR touched no Rust.

This PR moves the snapshot data into a new leaf crate, ccusage-pricing-snapshots. Only the binary depends on it, so a snapshot refresh now changes only the final ccusage derivation (and the test derivation, which builds the whole workspace anyway).

Evidence (before)

I compared drvPaths with nix eval on f6d28c56 (aarch64-darwin), before and after a simulated update:

layer baseline (a) models-dev-pricing.json +newline (b) --override-input litellm (patched copy)
dependencies hb71ghcg… hb71ghcg… (same) hb71ghcg… (same)
foundation ny2qqzh4… rcddc4mj… changed 0bhh48gk… changed
adapters bj2ym9f8… b0b9m72c… changed i0v49lls… changed
final p973nwwv… changed changed

Causes:

  • (a) nix/cargo-artifacts.nix put models-dev-pricing.json and models-dev-catalog-rules.json into every layer's source. ccusage-core/build.rs embeds them, so Cargo reruns the build script and then recompiles ccusage-core and everything that depends on it.
  • (b) CCUSAGE_PRICING_JSON_PATH (the litellm input) stayed in artifactCommonArgs. It was only stripped from the deps-only args, so a lock bump changed every layer's environment. ccusage-core's build script also declares rerun-if-env-changed for it.

What changed

  • New rust/crates/ccusage-pricing-snapshots: it owns the moved build.rs (unchanged apart from its test import), the committed models-dev-pricing.json / models-dev-catalog-rules.json, and the fetch-litellm-pricing feature. It exports three &[u8] constants and has no runtime dependencies.
  • ccusage-core no longer has a build script. It exposes pricing::EmbeddedSnapshots and pricing::set_embedded_snapshots, which main() calls next to the existing set_json_fetcher.
  • Tests: a test-only test-snapshots feature on ccusage-core falls back to the snapshot crate when nothing was installed. Crates whose tests load pricing enable it from [dev-dependencies]. Cargo does not activate dev-dependency features for cargo build, so the layers and the shipped binary never compile it.
  • nix/cargo-artifacts.nix: layers drop CCUSAGE_PRICING_JSON_PATH and the snapshot JSON. They still include the new crate's manifest and .rs files, because ccusage-core dev-depends on it and Cargo resolves the whole workspace.
  • antigravity's stray dev-dependency on ccusage-core/fetch-litellm-pricing is removed. Before this change it pulled the rustls build stack into every cargo test --workspace core build. The binary's fetch-litellm-pricing feature (used by the Windows build) now forwards to the new crate.
  • Path updates in justfile (gen-models-dev-pricing), .github/scripts/update-models-dev-lock.nu, nix/treefmt.nix (typos excludes), nix/rust-src.nix, the crate READMEs, and the rust/testing skills.

Evidence (after)

Rebased on 12dfc20e:

layer baseline (a) models-dev-pricing.json +newline (b) --override-input litellm
dependencies 7nq7vmr9… same same
foundation 62mks729… same same
adapters a1kxdf5r… same same
final i6zz2741… s3rs3acy… lzl24hjm…

The foundation drvPath was also identical before and after rebasing across the real 12dfc20e models.dev snapshot commit. Editing models-dev-catalog-rules.json likewise left foundation/adapters unchanged.

After building .#ccusage, I simulated a snapshot change and rebuilt. Only ccusage-20.0.35.drv was built (36.6s wall locally on a busy machine), with no foundation or adapters derivation. I also listed the foundation/adapters target.tar.zst archives: the only ccusage-pricing-snapshots entries are Crane's dummyBuild ones from the deps layer.

The embedded payload is byte-identical. The deflated litellm-pricing, models-dev-pricing, and models-dev-catalog-rules outputs from ccusage-core's build script at f6d28c56 match the new crate's outputs by SHA-256 (e632d11f…, cf148b37…, 11bf0111…).

Not fixed here: the final layer still rebuilds core and every adapter

While measuring this, I found that the final cargo build -p ccusage --bin ccusage re-checks/recompiles ccusage-core and all adapters even when the layers are fresh. This already happens on main: I reproduced it with cargo check on the same -p sequence the layers use, at f6d28c56. The cause is feature unification. The layer builds resolve features for a subset of the workspace, and the final graph adds e.g. ureq → flate2 → miniz_oxide/simd, syn features from schemars_derive, and libc/default. That changes the metadata hash of serde, miniz_oxide, etc., so ccusage-core becomes a different unit. So for the final binary, this PR saves the layer builds themselves (CI measured foundation 9s + adapters 4.5s on mac-arm64, 33s + 8s on linux-arm64, plus the test job's layer rebuilds and the config-schema generator in preflight). It does not shrink the final layer. Making the layers reusable by the final build (for example a workspace-hack crate, or building the layers with the binary's feature set) is a separate follow-up.

Testing

  • nix eval drvPath comparisons above (before/after, both update kinds)
  • nix build .#ccusage, then rebuilt after a simulated snapshot change (only the final derivation rebuilt)
  • release binary smoke: ccusage daily --offline --json --breakdown --mode calculate prices claude-sonnet-4-20250514 (LiteLLM), claude-opus-4-8, and glm-5 from the embedded snapshots
  • cargo fmt --all --check, cargo clippy --workspace --all-targets -- -D warnings, just hawk (0 findings), just fmt (no changes)
  • cargo test --workspace with HOME pointed at an empty dir and XDG_DATA_HOME/XDG_CONFIG_HOME/CLAUDE_CONFIG_DIR/CODEX_HOME unset: 1033 passed, 0 failed
  • pre-push hooks (clippy, treefmt, gitleaks, cargo test) passed with XDG_DATA_HOME pointed at an empty dir, because the adapter-all tests otherwise read the developer's real OpenCode data

Summary by cubic

Moves pricing snapshot data into a new leaf crate, ccusage-pricing-snapshots, so scheduled snapshot-refresh commits no longer invalidate the foundation and adapters Crane layers. Those commits (776 of the last 878 on main) previously recompiled ccusage-core, every adapter, and the config-schema generator; now a snapshot refresh rebuilds only the final binary derivation. This also cuts CI build and cache time: a minimal rustup profile skips rust-docs (~730 MB), and the macOS and Windows caches are saved only from pushes to main when their dependency keys change.

  • The new crate owns the moved build.rs, the committed models.dev JSON snapshots, and the fetch-litellm-pricing feature, and exports three byte constants with no runtime dependencies.
  • The binary installs the bytes at startup via pricing::set_embedded_snapshots; ccusage-core keeps the loaders and drops its build script.
  • A test-only test-snapshots feature on ccusage-core falls back to the snapshot crate when nothing was installed; crates whose tests load pricing enable it from [dev-dependencies], which Cargo leaves out of cargo build.
  • nix/cargo-artifacts.nix no longer injects CCUSAGE_PRICING_JSON_PATH or the snapshot JSON into the layers, so their derivation paths are stable across both kinds of snapshot update.
  • Nix flake inputs and the Crane cache root are rooted in the store's gcroots before a macOS save, so each cached entry holds one build closure instead of accumulating every commit's artifacts.
  • The Apple and Windows CI jobs install the minimal Rust profile, key Cargo caches on toolchain and manifest hashes rather than the commit, and save only from shared dependency builds on main; the Windows cache drops workspace outputs before saving.

This fixes the layer rebuilds but not the final layer: the last cargo build still recompiles ccusage-core and the adapters due to feature-unification differences between the layer build and the full workspace graph. That already happens on main and is a separate follow-up.

Written for commit a36c243. Summary will update on new commits.

View guided diff Turn on auto-fix

@coderabbitai

coderabbitai Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: c694755b-bfad-4286-ab95-d07a83c9c216
📥 Commits

Reviewing files that changed from the base of the PR and between 69ac221 and a36c243.

⛔ Files ignored due to path filters: 1
  • rust/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing: 37
  • .agents/skills/rust/SKILL.md
  • .agents/skills/testing/references/rust.md
  • .github/scripts/update-models-dev-lock.nu
  • justfile
  • nix/cargo-artifacts.nix
  • nix/rust-src.nix
  • nix/treefmt.nix
  • rust/Cargo.toml
  • rust/adapters/antigravity/Cargo.toml
  • rust/adapters/codebuff/Cargo.toml
  • rust/adapters/codex/Cargo.toml
  • rust/adapters/copilot/Cargo.toml
  • rust/adapters/droid/Cargo.toml
  • rust/adapters/gemini/Cargo.toml
  • rust/adapters/goose/Cargo.toml
  • rust/adapters/grok/Cargo.toml
  • rust/adapters/hermes/Cargo.toml
  • rust/adapters/kilo/Cargo.toml
  • rust/adapters/kimi/Cargo.toml
  • rust/adapters/openclaw/Cargo.toml
  • rust/adapters/opencode/Cargo.toml
  • rust/adapters/pi/Cargo.toml
  • rust/adapters/zcode/Cargo.toml
  • rust/crates/ccusage-adapter-all/Cargo.toml
  • rust/crates/ccusage-core/Cargo.toml
  • rust/crates/ccusage-core/README.md
  • rust/crates/ccusage-core/src/lib.rs
  • rust/crates/ccusage-core/src/pricing.rs
  • rust/crates/ccusage-pricing-snapshots/Cargo.toml
  • rust/crates/ccusage-pricing-snapshots/README.md
  • rust/crates/ccusage-pricing-snapshots/build.rs
  • rust/crates/ccusage-pricing-snapshots/src/lib.rs
  • rust/crates/ccusage-pricing-snapshots/src/models-dev-catalog-rules.json
  • rust/crates/ccusage-pricing-snapshots/src/models-dev-pricing.json
  • rust/crates/ccusage/Cargo.toml
  • rust/crates/ccusage/README.md
  • rust/crates/ccusage/src/main.rs
+----------------+
| CSI: Git Diff. |
+----------------+
  \
   \     /)/)
        ( '')
        /  |
      o(___)
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
ccusage-guide a36c243 Commit Preview URL

Branch Preview URL
Oct 18 2026, 03:26 PM

@pkg-pr-new

pkg-pr-new Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

ccusage

npx https://pkg.pr.new/ccusage@1922

@ccusage/ccusage-darwin-arm64

npx https://pkg.pr.new/@ccusage/ccusage-darwin-arm64@1922

@ccusage/ccusage-darwin-x64

npx https://pkg.pr.new/@ccusage/ccusage-darwin-x64@1922

@ccusage/ccusage-linux-arm64

npx https://pkg.pr.new/@ccusage/ccusage-linux-arm64@1922

@ccusage/ccusage-linux-x64

npx https://pkg.pr.new/@ccusage/ccusage-linux-x64@1922

@ccusage/ccusage-win32-x64

npx https://pkg.pr.new/@ccusage/ccusage-win32-x64@1922

commit: a36c243

@github-actions

Copy link
Copy Markdown
Contributor

ccusage performance comparison

PR SHA: 925883309469
Base SHA: 12dfc20e16b9

Performance comparison skipped.

Base package URL was not ready before 300.000s. Fixture performance comparison requires a base package when --base-dir is not provided.

Base package: 12dfc20e16b9

@github-actions

Copy link
Copy Markdown
Contributor

ccusage performance comparison

PR SHA: 925883309469
Base SHA: 12dfc20e16b9

Performance comparison skipped.

Base package URL was not ready before 300.000s. Fixture performance comparison requires a base package when --base-dir is not provided.

Base package: 12dfc20e16b9

@github-actions

Copy link
Copy Markdown
Contributor

ccusage performance comparison

PR SHA: 0ac934c5c867
Base SHA: 12dfc20e16b9

Performance comparison skipped.

Base package URL was not ready before 300.000s. Fixture performance comparison requires a base package when --base-dir is not provided.

Base package: 12dfc20e16b9

@github-actions

Copy link
Copy Markdown
Contributor

ccusage performance comparison

PR SHA: 0ac934c5c867
Base SHA: 12dfc20e16b9

Performance comparison skipped.

Base package URL was not ready before 300.000s. Fixture performance comparison requires a base package when --base-dir is not provided.

Base package: 12dfc20e16b9

Most commits on main are the scheduled "chore(pricing)" snapshot
updates, and each one invalidated the foundation and adapters Crane
layers: ccusage-core embedded the models.dev JSON through its build
script, and CCUSAGE_PRICING_JSON_PATH (the litellm flake input) was
passed to every layer. A models.dev or LiteLLM bump therefore rebuilt
ccusage-core, every adapter, the config-schema generator, and the
clippy, hawk and test bases.

The snapshot build script and the committed models.dev files now live in
ccusage-pricing-snapshots, a dependency-free crate only the binary
links. ccusage-core keeps the loaders and receives the deflated bytes
through pricing::set_embedded_snapshots, installed in main() next to the
existing set_json_fetcher. Tests reach the snapshots through a
test-only `test-snapshots` feature on ccusage-core, enabled from
[dev-dependencies], which Cargo leaves out of `cargo build`, so neither
the layers nor the shipped binary compile it.

The artifact layers no longer receive CCUSAGE_PRICING_JSON_PATH or the
snapshot JSON, so their derivation paths stay the same across both
kinds of snapshot update. The embedded bytes are unchanged: the moved
build script produces byte-identical deflated snapshots.

antigravity's dev-dependency on the removed fetch-litellm-pricing
feature of ccusage-core is dropped; the binary's feature now forwards to
the new crate.
The rust skill and the testing reference still described the snapshots
as part of ccusage-core. Record where they live now, why snapshot data
must stay out of ccusage-core, and that a crate whose tests load pricing
enables ccusage-core's test-snapshots feature.
@ryoppippi
ryoppippi force-pushed the perf/isolate-pricing-snapshots branch from 0ac934c to a36c243 Compare October 18, 2026 15:25
@ryoppippi
ryoppippi changed the base branch from main to ci/speed-up-windows-build October 18, 2026 15:25
@ryoppippi
ryoppippi added this pull request to stack #1926 October 18, 2026 15:25
@github-actions

Copy link
Copy Markdown
Contributor

ccusage performance comparison

PR SHA: a36c2435daf9
Base SHA: 6548615e30b4

Performance comparison skipped.

Base package URL was not ready before 300.000s. Fixture performance comparison requires a base package when --base-dir is not provided.

Base package: 6548615e30b4

@github-actions

Copy link
Copy Markdown
Contributor

ccusage performance comparison

PR SHA: a36c2435daf9
Base SHA: 6548615e30b4

Performance comparison skipped.

Base package URL was not ready before 300.000s. Fixture performance comparison requires a base package when --base-dir is not provided.

Base package: 6548615e30b4

@ryoppippi
ryoppippi marked this pull request as ready for review October 18, 2026 17:03
@ryoppippi
ryoppippi merged commit 85ebca3 into main Oct 18, 2026
47 of 48 checks passed
@ryoppippi
ryoppippi deleted the perf/isolate-pricing-snapshots branch October 18, 2026 17:03

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found.

Reviewed changes — reviewed the pricing snapshot isolation on perf/isolate-pricing-snapshots (3 commits, reconstructed from the commit range since the base branch is deleted).

  • New ccusage-pricing-snapshots leaf crate — owns the moved build.rs, the committed models.dev JSON, the fetch-litellm-pricing feature, and three deflated &[u8] constants with no runtime dependencies.
  • ccusage-core becomes snapshot-free — drops its build script, exposes EmbeddedSnapshots plus set_embedded_snapshots behind a OnceLock, and adds a test-only test-snapshots fallback enabled from [dev-dependencies].
  • Binary installs snapshots at startup — main() hands the three constants to pricing::set_embedded_snapshots next to the existing fetcher setup, and the binary feature now forwards to the new crate.
  • Crane layers stay cached — nix/cargo-artifacts.nix strips CCUSAGE_PRICING_JSON_PATH and the snapshot JSON from layer inputs while keeping the snapshot crate manifests for workspace resolution.
  • Tooling and docs follow the move — justfile, lock script, treefmt excludes, rust-src comment, crate READMEs, and the rust/testing skills point at the new crate; antigravity's stray fetch-feature dev-dep is removed.

Verified no stale ccusage-core/src/models-dev or ccusage-core/fetch-litellm-pricing references remain, the workspace-root --features fetch-litellm-pricing Windows invocation still resolves against the binary target, and the amp/claude/qwen adapters without the test feature have no load_embedded test usage.

Pullfrog  | View workflow run | Using muse-spark-1.3-contributor (free via Pullfrog for OSS) | 𝕏

Buster210 added a commit to Buster210/ccusage that referenced this pull request Oct 20, 2026
Upstream tip: c76874e (2026-10-20, chore(pricing): update models.dev snapshot)
Previous fork base: 0c58b88 (the upstream commit this fork was synced to)

Synced 83 upstream commits on top of 0c58b88: 56 automated pricing
snapshot refreshes and the following meaningful ones:

  1cd29f5 chore(deps): update pnpm to v12.13.0 (ccusage#1907)
  e4f63ba chore(deps): update node.js to >=24.22.0 (ccusage#1908)
  130e502 ci: regenerate bun.nix automatically (ccusage#1909)
  259ec33 feat(adapter): report stored cost in JSON model breakdowns (ccusage#1903)
  c1eb448 chore: release v20.0.35 (ccusage#1901)
  2a0875f ci(pricing): update snapshots on push to main and daily instead of hourly (ccusage#1914)
  b090428 chore(deps): update github/codeql-action action to v4.39.0 (ccusage#1912)
  4dcf7f5 fix(nix): pin models-dev-gen remeda to models.dev's version and stop Renovate drift (ccusage#1915)
  46edb28 fix(nix): stop the dev shell from copying the whole repository (ccusage#1917)
  58ec8f1 fix(nix): let commits succeed in worktrees without a loaded dev shell (ccusage#1920)
  a570a6f fix(nix): vendor Cargo deps without import-from-derivation (ccusage#1918)
  f6d28c5 fix(antigravity): map model IDs 1418/1419 to gemini-4-argon (ccusage#1905)
  ce439e5 fix(antigravity): map model ID 1050 to gemini-3.1-flash-lite (ccusage#1902)
  e62a66f chore(deps): update pnpm to v12.14.0 (ccusage#1923)
  117ae68 ci(nix): make the macOS Nix cache save a trimmed store with the Crane layers (ccusage#1921)
  7a063a0 ci: shorten the Windows native build critical path (ccusage#1924)
  85ebca3 perf(rust): move pricing snapshots into a leaf crate (ccusage#1922)
  8fe3f47 perf(nix): build Crane layers with the binary's feature resolution (ccusage#1925)
  ac289aa perf(pricing): give the pricing refresh a verify-only TLS 1.3 rustls provider (ccusage#1927)
  d097f74 chore(deps): update rust crate rustls to 0.23.46 (ccusage#1928)
  f8aaf89 docs: use the compact pkg.pr.new URL for bunx previews [ci skip] (ccusage#1929)
  f56bb3d chore: release v20.0.36 (ccusage#1916)
  aaafb41 chore(deps): update dependency vitepress-plugin-group-icons to ^1.8.1 (ccusage#1931)
  0861e7f fix(claude): count requestless resp_ content blocks once (ccusage#1935)
  867c9f3 chore: release v20.0.37 (ccusage#1933)
  fa40bfd chore(deps): update songmu/tagpr action to v1.21.3 (ccusage#1937)
  de99d15 chore(deps): update rust crate zstd to 0.14.3 (ccusage#1936)

Fork wins kept: the SQLite on-disk cache with --live-only and the per-namespace
ledger, the pi adapter and its per-path namespaces, the OpenCode V2 loader, the
FxHash/Cow hot paths, the table renderer, clear-cache, and the tag-only release
workflow. Upstream's own release and tagpr machinery stays out.

Hand-resolved conflicts:

  rust/adapters/claude/src/lib.rs: upstream's resp_ message-id dedupe (ccusage#1935) lands
    beside the fork's requestless replay and collapse_replays rules.
  rust/adapters/claude/src/README.md: upstream's wording for the msg_/resp_ exception.
  rust/Cargo.toml: zstd 0.14.3, keeping the fork's exact `=` pin style.
  .github/workflows/release.yaml: the fork's tag-only release, without upstream's
    tagpr job or the songmu/tagpr action bump.
  apps/ccusage/package.json, docs/package.json, package.json: keep 20.0.34;
    upstream's 20.0.37 bump is dropped.
  packages/ccusage-*/package.json (6 files): stay deleted; the fork ships no npm
    wrapper packages and the release workflow does not read them.
  rust/adapters/claude/src/daily.rs: stays deleted; the fork's daily path lives in
    lib.rs and shares the dedupe helper the fix changes.

Fixes made while landing this sync, folded in so the merge stands green on its own:

  fix(ci): save the Nix store from the native build composites. The nested
    cache-nix-action read an empty primary-key at post-job and every native
    build failed; upstream's defer-save pattern is the fix.
  fix(rust): keep the agent-recorded cost for deleted sources. The recorded_cost
    ledger column carries it, so storedCost survives a deleted file. The ledger
    schema version stays at 2: the column is not a LedgerEntry field, so the blob
    layout is unchanged and the previously released binary keeps working.
  fix(rust): make the file fingerprint helper private, the one hawk finding. It is
    inherited from the pre-sync master, not introduced here.

Keep 20.0.34 numbering; DO NOT push a v20.0.34 tag from this fork (release.yaml
would publish fork bytes under upstream identity).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant