Skip to content

Add multiple pricing data sources support - #745

Closed
LeslieLeung wants to merge 2 commits into
ccusage:mainfrom
LeslieLeung:feat/pricing-source
Closed

Add multiple pricing data sources support#745
LeslieLeung wants to merge 2 commits into
ccusage:mainfrom
LeslieLeung:feat/pricing-source

Conversation

@LeslieLeung

@LeslieLeung LeslieLeung commented Dec 9, 2025

Copy link
Copy Markdown
  • Add models.dev integration alongside LiteLLM pricing
  • Support auto-merge, litellm-only, and modelsdev-only modes
  • Add --pricing-source CLI option for all commands
  • Update configuration schema with pricingSource field
  • Add comprehensive documentation in docs/guide/pricing-sources.md
  • Implement pricing fetcher with fallback and merge strategies

Mainland China have some trouble accessing litellm APIs #13 , and a previous PR to add pricing source #51 is closed. I found a site https://models.dev/ that supports multiple provider and more diverse model IDs.

Summary by CodeRabbit

  • New Features

    • Added a configurable pricing source (litellm, modelsdev, auto) with CLI flag -P/--pricing-source and config support across commands and providers.
    • Added support for fetching and merging models.dev pricing; "auto" tries LiteLLM first then merges models.dev results.
  • Documentation

    • Added Pricing Source docs, examples, and guidance about live vs cached pricing.

Review Change Stack

@coderabbitai

coderabbitai Bot commented Dec 9, 2025

Copy link
Copy Markdown

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 026c2b3b-e0e4-44ce-96c1-14bfa39e0f69

📥 Commits

Reviewing files that changed from the base of the PR and between fa3e7aa and 4573843.

📒 Files selected for processing (3)
  • docs/guide/cli-options.md
  • docs/guide/configuration.md
  • packages/internal/src/pricing.ts

📝 Walkthrough

Walkthrough

This PR adds a selectable pricing backend (LiteLLM, models.dev, or auto) via config and CLI, implements models.dev ingestion and conversion, routes/merges pricing in the core fetcher, and threads the selection through commands, adapters, and data loaders.

Changes

Multi-source pricing backend selection

Layer / File(s) Summary
Models.dev pricing ingestion module
packages/internal/src/models-dev-pricing.ts
New module with Valibot schema for models.dev JSON, convertModelsDevToLiteLLMPricing, fetchModelsDevPricing client, and inline Vitest conversion tests.
Pricing fetcher backend selection and merging
packages/internal/src/pricing.ts
Adds PricingSources/PricingSource, extends fetcher options with modelsDevUrl and pricingSource, refactors LiteLLM loader, implements models.dev loader and auto merge behavior with tiered-field preservation, and adds tests for modelsdev and auto modes.
Type definitions and CLI argument surface
apps/ccusage/src/shared-args.ts, apps/ccusage/src/types.ts, apps/ccusage/src/adapter/types.ts
Introduces --pricing-source enum CLI option (short -P, default litellm) sourced from PricingSources; re-exports PricingSource type; extends AdapterOptions with pricingSource?: PricingSource.
PricingFetcher wrapper and command CLI exposure
apps/ccusage/src/pricing-fetcher.ts, apps/ccusage/src/commands/{agent,all,statusline,codex,session,blocks}.ts, apps/ccusage/src/commands/session_id.ts
Extends PricingFetcher constructor to accept/forward pricingSource; exposes --pricing-source in commands; threads mergedOptions.pricingSource into loadSessionUsageById, loadDailyUsageData, loadSessionBlockData, and calculateContextTokens; adds pricingSource to session-id lookup context.
Claude data loader pricingSource propagation
apps/ccusage/src/adapter/claude/data-loader.ts, apps/ccusage/src/adapter/claude/index.ts
Adds pricingSource to LoadOptions and worker payload; updates collectWithUsageWorkers, loadDailyUsageData, loadSessionData, loadSessionUsageById, calculateContextTokens, and loadSessionBlockData to accept/forward pricingSource; worker run uses provided pricingSource.
Adapter pricing context integration
apps/ccusage/src/adapter/{amp,codex,opencode}/index.ts
Passes options.pricingSource into LiteLLMPricingFetcher construction for AMP, Codex, and OpenCode adapters.
Configuration schema pricingSource propagation
apps/ccusage/config-schema.json
Adds pricingSource property (enum: litellm, modelsdev, auto; default litellm) to global defaults, top-level commands, and per-provider sections (claude, codex, opencode, amp, pi) and their command overrides.
User-facing documentation
docs/guide/cli-options.md, docs/guide/configuration.md, docs/guide/statusline.md
Documents --pricing-source values and --offline interaction; updates configuration examples and statusline technical details and cost-source wording; adds short alias -P.

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • ryoppippi

Poem

🐰 A pricing feast of choices new,
LiteLLM, models.dev, or both will do!
Auto-merges the best of each,
CLI flags keep config within reach.
Hop on, select — the data’s in view.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 53.85% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add multiple pricing data sources support' clearly and accurately summarizes the main change: introducing support for multiple pricing backends (LiteLLM, models.dev, and auto-merge).
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

packages/internal/src/pricing.ts

[baseline-browser-mapping] The data in this module is over two months old. To ensure accurate Baseline data, please update: npm i baseline-browser-mapping@latest -D
tsconfig.json is not found. we cannot use type-aware rules.

Oops! Something went wrong! :(

ESLint: 9.35.0

Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'eslint-plugin-format' imported from /node_modules/.pnpm/@antfu+eslint-config@4.19.0_@vue[email protected][email protected][email protected]_vit_670a2c5c75d4275eabd7bc195a173ee6/node_modules/@antfu/eslint-config/dist/index.js
at Object.getPackageJSONURL (node:internal/modules/package_json_reader:301:9)
at packageResolve (node:internal/modules/esm/resolve:764:81)
at moduleResolve (node:internal/modules/esm/resolve:855:18)
at defaultResolve (node:internal/modules/esm/resolve:988:11)
at #cachedDefaultResolve (node:internal/modules/esm/loader:697:20)
at #resolveAndMaybeBlockOnLoaderThread (node:internal/modules/esm/loader:714:38)
at ModuleLoader.resolveSync (node:internal/modules/esm/loader:746:52)
at #resolve (node:internal/modules/esm/loader:679:17)
at ModuleLoader.getOrCreateModuleJob (node:internal/modules/esm/loader:599:35)
at node:internal/modules/esm/loader:628:32


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 and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
apps/codex/package.json (1)

20-20: Revert the bin entry to point to the compiled output.

The bin entry was changed from "./dist/index.js" to "./src/index.ts", but for a published package, the bin entry must point to the compiled/built output, not the TypeScript source. When installed from npm/registry, consumers will not have a TypeScript loader available, and the package will fail to execute. This also contradicts the publishConfig section (lines 59–61), which correctly points to "./dist/index.js".

Apply this diff to fix the bin entry:

 	"bin": {
-		"ccusage-codex": "./src/index.ts"
+		"ccusage-codex": "./dist/index.js"
 	},
packages/internal/src/pricing.ts (1)

59-71: useModelsDev boolean cannot express a true “models.dev only” mode

The models.dev integration here is generally well‑structured: fetchModelsDevPricing is wrapped in Result.try, merge semantics correctly prefer LiteLLM data, and failures gracefully fall back to LiteLLM‑only pricing.

However, with LiteLLMPricingFetcherOptions.useModelsDev?: boolean and:

this.useModelsDev = Boolean(options.useModelsDev);
...
Result.andThen(async (liteLLMPricing) => {
  if (!this.useModelsDev) return Result.succeed(liteLLMPricing);
  const modelsDevResult = await this.fetchModelsDevPricing();
  ...
  const merged = this.mergePricingMaps(liteLLMPricing, modelsDevResult.value);
  return Result.succeed(merged);
})

the fetcher only supports two behaviors:

  • LiteLLM only (useModelsDev === false)
  • LiteLLM as the base, plus extra models from models.dev (useModelsDev === true)

There is no code path that returns models.dev pricing alone or skips the LiteLLM HTTP fetch altogether, which means an upstream "modelsdev" source cannot be implemented faithfully with this API.

To support the three intended modes, this class likely needs a richer option, for example:

  • sourceMode?: "litellm" | "auto" | "modelsdev" (preferred), or
  • A separate modelsDevOnly?: boolean that changes ensurePricingLoaded to, in "modelsdev" mode, fetch and cache models.dev pricing even if LiteLLM is unreachable or not contacted at all.

Once that’s in place, callers like PricingFetcher in apps/ccusage can pass through the exact mode instead of a boolean and you can add tests that verify:

  • "modelsdev" does not invoke LiteLLM,
  • "auto" merges as it does now, and
  • failures in models.dev still leave you with a sane LiteLLM‑only map.

Also applies to: 96-112, 150-175, 217-231

🧹 Nitpick comments (5)
apps/ccusage/config-schema.json (1)

76-86: pricingSource schema additions are consistent across commands

The new pricingSource properties (enum: auto / litellm / modelsdev, default auto) on defaults and all relevant commands line up with the CLI and docs and should validate config correctly. The duplication of the same schema block per command is acceptable here, though in the future you could consider a shared $ref to keep them perfectly in sync.

Also applies to: 191-201, 317-327, 427-437, 542-552, 657-667, 735-745

packages/internal/src/models-dev-pricing.ts (1)

78-122: Please re‑check Result/async usage in fetchModelsDevPricing and consider adding tests

Two things worth a closer look:

  1. Result.try / Result.pipe pattern
    In debug.ts, Result.try is used with a thunk and then invoked (e.g., const parser = Result.try({ try: () => JSON.parse(...), ... }); const result = parser();). Here you’re passing fetch(MODELS_DEV_API_URL) and response.json() directly as the try values inside Result.try, and feeding those into Result.pipe/Result.andThen. That’s a different pattern and may not behave as intended depending on the exact @praha/byethrow API (e.g., whether try is expected to be a function vs an already-started Promise). It would be good to confirm against the library docs and, if needed, align this with the same thunk style you use elsewhere. As per coding guidelines, prefer the established Result patterns for clarity and correctness.

  2. async + Result.ResultAsync return type
    Declaring export async function fetchModelsDevPricing(): Result.ResultAsync<...> means the resolved value of the function is a ResultAsync, so callers see Promise<ResultAsync<...>>. If ResultAsync is itself promise-like (common pattern), this could lead to nested promises. You may want to either:

    • Drop the async keyword and return the Result.pipe(...) expression directly, or
    • Change the return type to the resolved type you actually want callers to await.

Additionally, fetchModelsDevPricing is not currently covered by tests. A small test that mocks globalThis.fetch to return a minimal models.dev‑style payload and asserts that the resulting Map contains both provider‑prefixed and unprefixed keys would lock in the behavior and catch any pipeline/regression issues.

Also applies to: 124-180

apps/ccusage/src/commands/session.ts (1)

62-71: Pricing source plumbing is correct; consider unifying timezone/locale usage

The new loadSessionData call correctly passes mergedOptions.pricingSource (and other merged fields), so pricing-source selection and config defaults will affect data loading as intended.

However, the table’s dateFormatter still uses ctx.values.timezone / ctx.values.locale, so timezone/locale coming from config (via mergeConfigWithArgs) may influence loading but not display. Consider switching the formatter to mergedOptions.timezone / mergedOptions.locale for consistency.

Also applies to: 130-134

apps/ccusage/src/commands/blocks.ts (1)

166-176: Blocks command correctly threads pricingSource; consider harmonizing locale/timezone usage

Using mergedOptions.* for since, until, mode, order, offline, pricingSource, timezone, and locale in loadSessionBlockData, and for mode/order/pricingSource in startLiveMonitoring, cleanly integrates the new pricing-source selection into both report and live-monitor flows.

As in session.ts, you now have a mix of mergedOptions (for loading) and ctx.values (for things like ctx.values.locale in formatting). If you want config-file overrides for locale/timezone to be fully respected, it would be worth standardizing those remaining call sites on mergedOptions in a follow-up.

Also applies to: 251-259

apps/ccusage/src/data-loader.ts (1)

3566-3566: Updated tests match the extended PricingFetcher constructor

All the updated test cases now instantiate new PricingFetcher(false, "auto"), which aligns with the new (offline, pricingSource = "auto") signature and preserves the previous behavior of using online pricing in auto/merged mode. Once the underlying "modelsdev" semantics are finalized, you may want to add a few tests that exercise non‑default pricingSource values, but the current changes are consistent.

Also applies to: 3575-3575, 3583-3583, 3602-3602, 3609-3609, 3625-3625, 3636-3636, 3657-3657, 3670-3670, 3687-3687, 3701-3701, 3710-3710, 3717-3717, 3739-3739, 3747-3747, 3753-3753

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 54d98f7 and 98b6218.

📒 Files selected for processing (21)
  • apps/ccusage/config-schema.json (7 hunks)
  • apps/ccusage/package.json (2 hunks)
  • apps/ccusage/src/_live-monitor.ts (5 hunks)
  • apps/ccusage/src/_live-rendering.ts (2 hunks)
  • apps/ccusage/src/_pricing-fetcher.ts (2 hunks)
  • apps/ccusage/src/_shared-args.ts (5 hunks)
  • apps/ccusage/src/_types.ts (1 hunks)
  • apps/ccusage/src/commands/_blocks.live.ts (1 hunks)
  • apps/ccusage/src/commands/blocks.ts (2 hunks)
  • apps/ccusage/src/commands/session.ts (1 hunks)
  • apps/ccusage/src/commands/statusline.ts (2 hunks)
  • apps/ccusage/src/data-loader.ts (20 hunks)
  • apps/ccusage/src/debug.ts (1 hunks)
  • apps/codex/package.json (2 hunks)
  • apps/mcp/package.json (2 hunks)
  • docs/guide/cli-options.md (1 hunks)
  • docs/guide/configuration.md (2 hunks)
  • docs/guide/pricing-sources.md (1 hunks)
  • packages/internal/package.json (1 hunks)
  • packages/internal/src/models-dev-pricing.ts (1 hunks)
  • packages/internal/src/pricing.ts (5 hunks)
🧰 Additional context used
📓 Path-based instructions (15)
apps/ccusage/src/**/*.ts

📄 CodeRabbit inference engine (apps/ccusage/CLAUDE.md)

apps/ccusage/src/**/*.ts: Write tests in-source using if (import.meta.vitest != null) blocks instead of separate test files
Use Vitest globals (describe, it, expect) without imports in test blocks
In tests, use current Claude 4 models (sonnet-4, opus-4)
Use fs-fixture with createFixture() to simulate Claude data in tests
Only export symbols that are actually used by other modules
Do not use console.log; use the logger utilities from src/logger.ts instead

Files:

  • apps/ccusage/src/_live-rendering.ts
  • apps/ccusage/src/debug.ts
  • apps/ccusage/src/commands/_blocks.live.ts
  • apps/ccusage/src/commands/statusline.ts
  • apps/ccusage/src/_types.ts
  • apps/ccusage/src/_pricing-fetcher.ts
  • apps/ccusage/src/_live-monitor.ts
  • apps/ccusage/src/data-loader.ts
  • apps/ccusage/src/commands/session.ts
  • apps/ccusage/src/_shared-args.ts
  • apps/ccusage/src/commands/blocks.ts
apps/ccusage/**/*.ts

📄 CodeRabbit inference engine (apps/ccusage/CLAUDE.md)

apps/ccusage/**/*.ts: NEVER use await import() dynamic imports anywhere (especially in tests)
Prefer @praha/byethrow Result type for error handling instead of try-catch
Use .ts extensions for local imports (e.g., import { foo } from './utils.ts')

Files:

  • apps/ccusage/src/_live-rendering.ts
  • apps/ccusage/src/debug.ts
  • apps/ccusage/src/commands/_blocks.live.ts
  • apps/ccusage/src/commands/statusline.ts
  • apps/ccusage/src/_types.ts
  • apps/ccusage/src/_pricing-fetcher.ts
  • apps/ccusage/src/_live-monitor.ts
  • apps/ccusage/src/data-loader.ts
  • apps/ccusage/src/commands/session.ts
  • apps/ccusage/src/_shared-args.ts
  • apps/ccusage/src/commands/blocks.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,js,jsx}: Use ESLint for linting and formatting with tab indentation and double quotes
No console.log allowed except where explicitly disabled with eslint-disable; use logger.ts instead
Use file paths with Node.js path utilities for cross-platform compatibility
Use variables starting with lowercase (camelCase) for variable names
Can use UPPER_SNAKE_CASE for constants

Files:

  • apps/ccusage/src/_live-rendering.ts
  • apps/ccusage/src/debug.ts
  • apps/ccusage/src/commands/_blocks.live.ts
  • apps/ccusage/src/commands/statusline.ts
  • apps/ccusage/src/_types.ts
  • packages/internal/src/pricing.ts
  • packages/internal/src/models-dev-pricing.ts
  • apps/ccusage/src/_pricing-fetcher.ts
  • apps/ccusage/src/_live-monitor.ts
  • apps/ccusage/src/data-loader.ts
  • apps/ccusage/src/commands/session.ts
  • apps/ccusage/src/_shared-args.ts
  • apps/ccusage/src/commands/blocks.ts
**/*.ts{,x}

📄 CodeRabbit inference engine (CLAUDE.md)

Use TypeScript with strict mode and bundler module resolution

Files:

  • apps/ccusage/src/_live-rendering.ts
  • apps/ccusage/src/debug.ts
  • apps/ccusage/src/commands/_blocks.live.ts
  • apps/ccusage/src/commands/statusline.ts
  • apps/ccusage/src/_types.ts
  • packages/internal/src/pricing.ts
  • packages/internal/src/models-dev-pricing.ts
  • apps/ccusage/src/_pricing-fetcher.ts
  • apps/ccusage/src/_live-monitor.ts
  • apps/ccusage/src/data-loader.ts
  • apps/ccusage/src/commands/session.ts
  • apps/ccusage/src/_shared-args.ts
  • apps/ccusage/src/commands/blocks.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use .ts extensions for local file imports (e.g., import { foo } from './utils.ts')
Prefer @praha/byethrow Result type over traditional try-catch for functional error handling
Use Result.try() for wrapping operations that may throw (JSON parsing, etc.)
Use Result.isFailure() for checking errors (more readable than !Result.isSuccess())
Use early return pattern (if (Result.isFailure(result)) continue;) instead of ternary operators when checking Results
Keep traditional try-catch only for file I/O with complex error handling or legacy code that's hard to refactor
Always use Result.isFailure() and Result.isSuccess() type guards for better code clarity
Use uppercase (PascalCase) for type names
Only export constants, functions, and types that are actually used by other modules - internal constants used only within the same file should NOT be exported
In-source testing pattern: write tests directly in source files using if (import.meta.vitest != null) blocks
CRITICAL: DO NOT use await import() dynamic imports anywhere in the codebase - this causes tree-shaking issues
CRITICAL: Never use dynamic imports with await import() in vitest test blocks - this is particularly problematic for test execution
Vitest globals (describe, it, expect) are enabled and available without imports since globals are configured
Create mock data using fs-fixture with createFixture() for Claude data directory simulation in tests
All test files must use current Claude 4 models (claude-sonnet-4-20250514, claude-opus-4-20250514), not outdated Claude 3 models
Model names in tests must exactly match LiteLLM's pricing database entries

Files:

  • apps/ccusage/src/_live-rendering.ts
  • apps/ccusage/src/debug.ts
  • apps/ccusage/src/commands/_blocks.live.ts
  • apps/ccusage/src/commands/statusline.ts
  • apps/ccusage/src/_types.ts
  • packages/internal/src/pricing.ts
  • packages/internal/src/models-dev-pricing.ts
  • apps/ccusage/src/_pricing-fetcher.ts
  • apps/ccusage/src/_live-monitor.ts
  • apps/ccusage/src/data-loader.ts
  • apps/ccusage/src/commands/session.ts
  • apps/ccusage/src/_shared-args.ts
  • apps/ccusage/src/commands/blocks.ts
**/*.{ts,tsx,json}

📄 CodeRabbit inference engine (CLAUDE.md)

Claude model naming convention: claude-{model-type}-{generation}-{date} (e.g., claude-sonnet-4-20250514, NOT claude-4-sonnet-20250514)

Files:

  • apps/ccusage/src/_live-rendering.ts
  • packages/internal/package.json
  • apps/ccusage/src/debug.ts
  • apps/ccusage/src/commands/_blocks.live.ts
  • apps/ccusage/src/commands/statusline.ts
  • apps/ccusage/src/_types.ts
  • packages/internal/src/pricing.ts
  • packages/internal/src/models-dev-pricing.ts
  • apps/ccusage/src/_pricing-fetcher.ts
  • apps/ccusage/src/_live-monitor.ts
  • apps/ccusage/src/data-loader.ts
  • apps/ccusage/config-schema.json
  • apps/ccusage/src/commands/session.ts
  • apps/ccusage/src/_shared-args.ts
  • apps/mcp/package.json
  • apps/codex/package.json
  • apps/ccusage/src/commands/blocks.ts
  • apps/ccusage/package.json
**/package.json

📄 CodeRabbit inference engine (CLAUDE.md)

Dependencies should always be added as devDependencies unless explicitly requested otherwise

Files:

  • packages/internal/package.json
  • apps/mcp/package.json
  • apps/codex/package.json
  • apps/ccusage/package.json
docs/guide/**/*.md

📄 CodeRabbit inference engine (docs/CLAUDE.md)

docs/guide/**/*.md: Place screenshots immediately after the main heading (H1) on guide pages that include screenshots
User-facing guides should live under the docs/guide/ directory

Files:

  • docs/guide/pricing-sources.md
  • docs/guide/configuration.md
  • docs/guide/cli-options.md
docs/**/*.md

📄 CodeRabbit inference engine (docs/CLAUDE.md)

docs/**/*.md: Use image paths relative to the docs public root (e.g., /screenshot.png for assets in /docs/public/)
Always include descriptive alt text for images and screenshots
For code blocks that should skip ESLint parsing (e.g., containing ...), add immediately before the code block

Files:

  • docs/guide/pricing-sources.md
  • docs/guide/configuration.md
  • docs/guide/cli-options.md
**/*.md

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.md: Place screenshots immediately after the main heading (H1) in documentation pages for immediate visual context
Use relative image paths like /screenshot.png for images stored in /docs/public/ in documentation
Always include descriptive alt text for images in documentation for accessibility

Files:

  • docs/guide/pricing-sources.md
  • docs/guide/configuration.md
  • docs/guide/cli-options.md
**/data-loader.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Silently skip malformed JSONL lines during parsing in data loading operations

Files:

  • apps/ccusage/src/data-loader.ts
apps/mcp/**/package.json

📄 CodeRabbit inference engine (apps/mcp/CLAUDE.md)

Add new dependencies as devDependencies unless explicitly requested otherwise

Files:

  • apps/mcp/package.json
apps/*/package.json

📄 CodeRabbit inference engine (CLAUDE.md)

All projects under apps/ ship as bundled CLIs/binaries - treat runtime dependencies as bundled assets by listing everything in each app's devDependencies (never dependencies)

Files:

  • apps/mcp/package.json
  • apps/codex/package.json
  • apps/ccusage/package.json
apps/codex/**/package.json

📄 CodeRabbit inference engine (apps/codex/CLAUDE.md)

Package Codex as a bundled CLI and keep every runtime dependency in devDependencies so the bundle includes shipped code

Files:

  • apps/codex/package.json
apps/ccusage/**/package.json

📄 CodeRabbit inference engine (apps/ccusage/CLAUDE.md)

apps/ccusage/**/package.json: Add dependencies as devDependencies unless explicitly required otherwise
Because the CLI is bundled, keep all runtime libraries in devDependencies so the bundler captures them

Files:

  • apps/ccusage/package.json
🧠 Learnings (30)
📓 Common learnings
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/codex/CLAUDE.md:0-0
Timestamp: 2025-09-18T16:07:16.293Z
Learning: Fetch per-model pricing from LiteLLM model_prices_and_context_window.json via LiteLLMPricingFetcher using an offline cache scoped to Codex-prefixed models; handle aliases (e.g., gpt-5-codex → gpt-5) in CodexPricingSource
📚 Learning: 2025-11-25T14:42:34.734Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T14:42:34.734Z
Learning: Applies to **/*.{ts,tsx} : Only export constants, functions, and types that are actually used by other modules - internal constants used only within the same file should NOT be exported

Applied to files:

  • packages/internal/package.json
📚 Learning: 2025-09-17T18:29:15.764Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/mcp/CLAUDE.md:0-0
Timestamp: 2025-09-17T18:29:15.764Z
Learning: Applies to apps/mcp/**/*.ts : Only export what is actually used

Applied to files:

  • packages/internal/package.json
  • apps/mcp/package.json
📚 Learning: 2025-09-18T16:06:37.474Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/ccusage/CLAUDE.md:0-0
Timestamp: 2025-09-18T16:06:37.474Z
Learning: Applies to apps/ccusage/**/package.json : Because the CLI is bundled, keep all runtime libraries in devDependencies so the bundler captures them

Applied to files:

  • packages/internal/package.json
  • apps/codex/package.json
  • apps/ccusage/package.json
📚 Learning: 2025-09-18T16:06:37.474Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/ccusage/CLAUDE.md:0-0
Timestamp: 2025-09-18T16:06:37.474Z
Learning: Applies to apps/ccusage/src/**/*.ts : Only export symbols that are actually used by other modules

Applied to files:

  • packages/internal/package.json
  • apps/codex/package.json
  • apps/ccusage/package.json
📚 Learning: 2025-09-18T16:06:37.474Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/ccusage/CLAUDE.md:0-0
Timestamp: 2025-09-18T16:06:37.474Z
Learning: Applies to apps/ccusage/**/package.json : Add dependencies as devDependencies unless explicitly required otherwise

Applied to files:

  • packages/internal/package.json
  • apps/codex/package.json
  • apps/ccusage/package.json
📚 Learning: 2025-09-18T16:06:37.474Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/ccusage/CLAUDE.md:0-0
Timestamp: 2025-09-18T16:06:37.474Z
Learning: Applies to apps/ccusage/**/*.ts : Use `.ts` extensions for local imports (e.g., `import { foo } from './utils.ts'`)

Applied to files:

  • packages/internal/package.json
  • apps/ccusage/package.json
📚 Learning: 2025-09-18T16:07:16.293Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/codex/CLAUDE.md:0-0
Timestamp: 2025-09-18T16:07:16.293Z
Learning: Applies to apps/codex/**/package.json : Package Codex as a bundled CLI and keep every runtime dependency in devDependencies so the bundle includes shipped code

Applied to files:

  • packages/internal/package.json
  • apps/codex/package.json
  • apps/ccusage/package.json
📚 Learning: 2025-09-18T16:06:37.474Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/ccusage/CLAUDE.md:0-0
Timestamp: 2025-09-18T16:06:37.474Z
Learning: Applies to apps/ccusage/src/**/*.ts : Write tests in-source using `if (import.meta.vitest != null)` blocks instead of separate test files

Applied to files:

  • packages/internal/package.json
  • packages/internal/src/models-dev-pricing.ts
📚 Learning: 2025-11-25T14:42:34.734Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T14:42:34.734Z
Learning: Applies to apps/*/package.json : All projects under `apps/` ship as bundled CLIs/binaries - treat runtime dependencies as bundled assets by listing everything in each app's `devDependencies` (never `dependencies`)

Applied to files:

  • packages/internal/package.json
  • apps/codex/package.json
  • apps/ccusage/package.json
📚 Learning: 2025-09-18T16:07:16.293Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/codex/CLAUDE.md:0-0
Timestamp: 2025-09-18T16:07:16.293Z
Learning: Fetch per-model pricing from LiteLLM model_prices_and_context_window.json via LiteLLMPricingFetcher using an offline cache scoped to Codex-prefixed models; handle aliases (e.g., gpt-5-codex → gpt-5) in CodexPricingSource

Applied to files:

  • apps/ccusage/src/commands/statusline.ts
  • docs/guide/pricing-sources.md
  • packages/internal/src/pricing.ts
  • packages/internal/src/models-dev-pricing.ts
  • apps/ccusage/src/_pricing-fetcher.ts
  • apps/ccusage/src/data-loader.ts
📚 Learning: 2025-11-25T14:42:34.734Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T14:42:34.734Z
Learning: Applies to **/calculate-cost.ts : Cost calculations require exact model name matches with LiteLLM's database

Applied to files:

  • apps/ccusage/src/_types.ts
  • packages/internal/src/pricing.ts
  • packages/internal/src/models-dev-pricing.ts
  • apps/ccusage/src/_pricing-fetcher.ts
  • apps/ccusage/src/data-loader.ts
📚 Learning: 2025-11-25T14:42:34.734Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T14:42:34.734Z
Learning: Refer to individual CLAUDE.md files in apps/ccusage/CLAUDE.md, apps/mcp/CLAUDE.md, and docs/CLAUDE.md for package-specific guidance

Applied to files:

  • docs/guide/pricing-sources.md
📚 Learning: 2025-11-25T14:42:34.734Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T14:42:34.734Z
Learning: Applies to **/*.{ts,tsx} : Model names in tests must exactly match LiteLLM's pricing database entries

Applied to files:

  • packages/internal/src/pricing.ts
  • packages/internal/src/models-dev-pricing.ts
📚 Learning: 2025-11-25T14:42:34.734Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T14:42:34.734Z
Learning: Applies to **/*.{ts,tsx} : Use `Result.try()` for wrapping operations that may throw (JSON parsing, etc.)

Applied to files:

  • packages/internal/src/pricing.ts
📚 Learning: 2025-11-25T14:42:34.734Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T14:42:34.734Z
Learning: The application cannot calculate costs for models not supported by LiteLLM

Applied to files:

  • packages/internal/src/pricing.ts
📚 Learning: 2025-11-25T14:42:34.734Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T14:42:34.734Z
Learning: Applies to **/*.{ts,tsx} : In-source testing pattern: write tests directly in source files using `if (import.meta.vitest != null)` blocks

Applied to files:

  • packages/internal/src/models-dev-pricing.ts
📚 Learning: 2025-09-18T16:06:37.474Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/ccusage/CLAUDE.md:0-0
Timestamp: 2025-09-18T16:06:37.474Z
Learning: Applies to apps/ccusage/src/**/*.ts : Use Vitest globals (`describe`, `it`, `expect`) without imports in test blocks

Applied to files:

  • packages/internal/src/models-dev-pricing.ts
📚 Learning: 2025-09-18T16:06:37.474Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/ccusage/CLAUDE.md:0-0
Timestamp: 2025-09-18T16:06:37.474Z
Learning: Applies to apps/ccusage/src/**/*.ts : In tests, use current Claude 4 models (sonnet-4, opus-4)

Applied to files:

  • apps/ccusage/src/_pricing-fetcher.ts
📚 Learning: 2025-09-18T16:07:16.293Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/codex/CLAUDE.md:0-0
Timestamp: 2025-09-18T16:07:16.293Z
Learning: Pricing tests must inject stub offline loaders to avoid network access

Applied to files:

  • apps/ccusage/src/_pricing-fetcher.ts
  • apps/ccusage/src/data-loader.ts
📚 Learning: 2025-09-18T16:07:16.293Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/codex/CLAUDE.md:0-0
Timestamp: 2025-09-18T16:07:16.293Z
Learning: Command flag --offline forces use of the embedded pricing snapshot

Applied to files:

  • docs/guide/configuration.md
📚 Learning: 2025-09-17T18:29:15.764Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/mcp/CLAUDE.md:0-0
Timestamp: 2025-09-17T18:29:15.764Z
Learning: Applies to apps/mcp/**/package.json : Add new dependencies as `devDependencies` unless explicitly requested otherwise

Applied to files:

  • apps/mcp/package.json
  • apps/codex/package.json
  • apps/ccusage/package.json
📚 Learning: 2025-09-17T18:29:15.764Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/mcp/CLAUDE.md:0-0
Timestamp: 2025-09-17T18:29:15.764Z
Learning: Applies to apps/mcp/**/*.{test,spec}.ts : Use `fs-fixture` for mock data in tests of MCP server functionality

Applied to files:

  • apps/mcp/package.json
📚 Learning: 2025-09-17T18:29:15.764Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/mcp/CLAUDE.md:0-0
Timestamp: 2025-09-17T18:29:15.764Z
Learning: Applies to apps/mcp/**/*.ts : Use `.ts` extensions for local (relative) imports

Applied to files:

  • apps/mcp/package.json
📚 Learning: 2025-09-18T16:07:16.293Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/codex/CLAUDE.md:0-0
Timestamp: 2025-09-18T16:07:16.293Z
Learning: Treat Codex as a sibling to apps/ccusage; reuse shared packages, command names, and flag semantics; diverge only when Codex-specific data requires it and document inline

Applied to files:

  • apps/codex/package.json
📚 Learning: 2025-09-18T16:06:37.474Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/ccusage/CLAUDE.md:0-0
Timestamp: 2025-09-18T16:06:37.474Z
Learning: After any code change, run format, typecheck, and tests in parallel (`pnpm run format`, `pnpm typecheck`, `pnpm run test`)

Applied to files:

  • apps/codex/package.json
  • apps/ccusage/package.json
📚 Learning: 2025-11-25T14:42:34.734Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T14:42:34.734Z
Learning: After making any code changes, run `pnpm run format`, `pnpm typecheck`, and `pnpm run test` in parallel

Applied to files:

  • apps/codex/package.json
  • apps/ccusage/package.json
📚 Learning: 2025-11-25T14:42:34.734Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T14:42:34.734Z
Learning: Run `pnpm run format` to format code with ESLint, which writes changes

Applied to files:

  • apps/codex/package.json
📚 Learning: 2025-09-17T18:29:15.764Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: apps/mcp/CLAUDE.md:0-0
Timestamp: 2025-09-17T18:29:15.764Z
Learning: After code changes, always run `pnpm run format`, `pnpm typecheck`, and `pnpm run test` in parallel

Applied to files:

  • apps/codex/package.json
  • apps/ccusage/package.json
📚 Learning: 2025-11-25T14:42:34.734Z
Learnt from: CR
Repo: ryoppippi/ccusage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T14:42:34.734Z
Learning: Run `pnpm typecheck` to type check code with TypeScript

Applied to files:

  • apps/codex/package.json
🧬 Code graph analysis (8)
apps/ccusage/src/_live-rendering.ts (2)
apps/ccusage/src/_types.ts (1)
  • PricingSource (154-154)
apps/codex/src/_types.ts (1)
  • PricingSource (53-55)
apps/ccusage/src/debug.ts (1)
apps/ccusage/src/_pricing-fetcher.ts (1)
  • PricingFetcher (40-54)
apps/ccusage/src/commands/statusline.ts (2)
apps/ccusage/src/_shared-args.ts (1)
  • sharedArgs (24-132)
apps/ccusage/src/data-loader.ts (1)
  • calculateContextTokens (1237-1313)
apps/ccusage/src/_types.ts (1)
apps/codex/src/_types.ts (1)
  • PricingSource (53-55)
packages/internal/src/pricing.ts (2)
packages/internal/src/logger.ts (1)
  • createLogger (5-17)
packages/internal/src/models-dev-pricing.ts (1)
  • fetchModelsDevPricing (78-122)
apps/ccusage/src/_pricing-fetcher.ts (2)
apps/ccusage/src/_types.ts (1)
  • PricingSource (154-154)
packages/internal/src/pricing.ts (1)
  • LiteLLMPricingFetcher (96-406)
apps/ccusage/src/data-loader.ts (3)
apps/ccusage/src/_types.ts (1)
  • PricingSource (154-154)
apps/codex/src/_types.ts (1)
  • PricingSource (53-55)
apps/ccusage/src/_pricing-fetcher.ts (1)
  • PricingFetcher (40-54)
apps/ccusage/src/_shared-args.ts (1)
apps/ccusage/src/_types.ts (2)
  • PricingSource (154-154)
  • PricingSources (149-149)
🪛 markdownlint-cli2 (0.18.1)
docs/guide/pricing-sources.md

107-107: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


174-174: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

docs/guide/configuration.md

86-86: Hard tabs
Column: 1

(MD010, no-hard-tabs)


87-87: Hard tabs
Column: 1

(MD010, no-hard-tabs)

🔇 Additional comments (17)
apps/codex/package.json (2)

6-8: Field reordering is acceptable.

The metadata fields (bugs, funding, license, author, type, engines) have been reordered and the engines section relocated, but the content remains unchanged. These are purely cosmetic changes and do not affect functionality.

Also applies to: 13-16, 55-57


35-54: Runtime dependencies are correctly placed in devDependencies.

All dependencies are properly configured as devDependencies, which is correct for a bundled CLI where the bundler will capture all runtime code at build time.

apps/ccusage/src/debug.ts (1)

94-96: Explicitly setting offline and pricingSource looks correct

Using new PricingFetcher(false, "auto") matches the new constructor signature and preserves prior behavior while making the defaults explicit. No issues from this change.

packages/internal/package.json (1)

7-14: New models-dev-pricing export is wired correctly

Exporting "./models-dev-pricing": "./src/models-dev-pricing.ts" is consistent with the existing internal API surface and points to the correct TS source file.

docs/guide/configuration.md (1)

81-88: pricingSource documentation matches config & CLI wiring

Adding "pricingSource": "auto" to the defaults example and documenting “Pricing Source: auto/litellm/modelsdev with --pricing-source aligns with the new schema and shared CLI option. The example command mirrors the actual flags, and the link to the Pricing Sources guide is a good discoverability touch.

Also applies to: 142-149, 151-152

apps/ccusage/package.json (1)

6-8: Metadata and engines updates look consistent

Adding bugs, funding, license, author, and "type": "module" improves package metadata, and the engines.node: ">=20.19.4" constraint is reasonable given the modern Node features used elsewhere in the app. No issues from these changes.

Also applies to: 13-16, 90-92

apps/mcp/package.json (1)

6-8: MCP package metadata/engines updates are fine

New bugs, funding, license, author, and "type": "module" fields plus the engines.node constraint keep the MCP package in line with the main app’s metadata and runtime assumptions. No functional concerns.

Also applies to: 13-16, 65-67

packages/internal/src/models-dev-pricing.ts (1)

5-73: Conversion and schema typing for models.dev pricing look solid

The valibot schemas for cost/limit/model/provider/API and the ModelsDev* types are clear, and convertModelsDevToLiteLLM correctly converts per‑million prices to per‑token and maps context/output into the LiteLLM limit fields. The in-source tests exercise both the happy path and missing-field behavior and match the in-repo testing guidelines.

docs/guide/cli-options.md (1)

78-99: Short flag -p is documented for two different options

The "Pricing Source" section introduces --pricing-source with short flag -p, but the "Daily Command" section and the aliases table still document --project as also using -p. Verify that distinct short flags are used in the CLI implementation and confirm the aliases table reflects the correct mappings for both options.

apps/ccusage/src/commands/_blocks.live.ts (1)

33-39: Propagating pricingSource into live monitor state looks correct

Forwarding config.pricingSource into monitorConfig keeps the live monitor’s pricing behavior aligned with the CLI/config selection and the updated LiveMonitorConfig type, without changing any other control flow.

apps/ccusage/src/_live-rendering.ts (1)

11-12: LiveMonitoringConfig extension is consistent with upstream/downstream usage

Adding pricingSource: PricingSource and importing PricingSource matches how startLiveMonitoring is called in blocks.ts and how LiveMonitorConfig is defined in _live-monitor.ts, without altering rendering logic.

Also applies to: 45-53

apps/ccusage/src/_types.ts (1)

143-155: Pricing source enum and union follow existing patterns

Defining PricingSources and deriving PricingSource via TupleToUnion mirrors CostModes/CostMode and SortOrders/SortOrder, giving a tight, type-safe source of truth for CLI and config without adding runtime complexity.

apps/ccusage/src/commands/statusline.ts (1)

101-110: Statusline wiring for pricingSource into context calculation is sound

Exposing pricingSource via sharedArgs.pricingSource and passing mergedOptions.pricingSource into calculateContextTokens aligns this command with the new pricing-source-aware context limit logic, without perturbing existing cost-source behavior.

Also applies to: 435-438

apps/ccusage/src/_live-monitor.ts (1)

12-18: Live monitor now respects the configured pricing source

Extending LiveMonitorConfig with pricingSource and passing it into new PricingFetcher(false, config.pricingSource) ensures live monitoring uses the same pricing-source selection as the rest of ccusage, without changing retention, file scanning, or block-identification logic. The in-source tests’ configs were correctly updated to include pricingSource: 'auto', keeping them in sync with the new type.

Also applies to: 32-38, 75-77, 295-301, 333-339

docs/guide/pricing-sources.md (1)

107-111: Add languages to fenced code blocks and verify configuration guide link

The price-conversion and log-output snippets use bare ``` fences, which trigger MD040; adding a language like text (or `bash` if you prefer) will satisfy markdownlint and improve readability:

-``` 
+```text
 models.dev:  $3 per million tokens
 Converted:   $0.000003 per token

...
- +text
ℹ Fetching latest model pricing from LiteLLM...
...

Also, the "Configuration Files" link points to /guide/config-files; please verify that this slug exists and resolves correctly in your site structure.

apps/ccusage/src/_shared-args.ts (1)

2-10: Pricing source CLI wiring looks consistent and type‑safe

PricingSource / PricingSources are correctly imported and used for the new pricingSource enum arg; default "auto" as const satisfies PricingSource matches the union, and sharedCommandConfig.toKebab = true will expose this as --pricing-source as intended. The other description tweaks remain accurate given the current behavior.

Also applies to: 44-52, 58-62, 76-83, 84-91

apps/ccusage/src/data-loader.ts (1)

13-21: PricingSource is correctly threaded through data loading and context‑limit paths

Importing PricingSource, extending LoadOptions with pricingSource?: PricingSource, and passing options?.pricingSource ?? "auto" into every new PricingFetcher(...) call (loadDailyUsageData, loadSessionData, loadSessionUsageById, loadSessionBlockData, and calculateContextTokens) keeps behavior backward‑compatible while allowing callers to control the data source. The new calculateContextTokens parameter and default also look consistent with the rest of the API.

Also applies to: 728-740, 748-782, 890-941, 1098-1120, 1353-1363, 1237-1241, 1278-1293

Comment thread apps/ccusage/src/_pricing-fetcher.ts Outdated
Comment on lines +17 to +38
/**
* Determines whether to use models.dev based on pricing source setting
* @param pricingSource - The pricing source mode ('auto', 'litellm', or 'modelsdev')
* @param offline - Whether offline mode is enabled
* @returns true if models.dev should be used, false otherwise
*/
function shouldUseModelsDev(pricingSource: PricingSource, offline: boolean): boolean {
if (offline) {
return false; // Never use models.dev in offline mode
}

switch (pricingSource) {
case 'auto':
return true; // Use both sources (merged)
case 'litellm':
return false; // LiteLLM only
case 'modelsdev':
return true; // models.dev only (will be handled by fetcher options)
default:
return true; // Default to auto
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

modelsdev mode is indistinguishable from auto in current wiring

shouldUseModelsDev collapses both "auto" and "modelsdev" into useModelsDev = true, and PricingFetcher only passes this boolean into LiteLLMPricingFetcher. Since the underlying fetcher always fetches LiteLLM first and then optionally merges models.dev data, there is no way for pricingSource: "modelsdev" to mean “models.dev only” as advertised; it still hits LiteLLM and prefers LiteLLM data on key collisions.

To align behavior with the CLI modes, consider:

  • Passing the full PricingSource (or an explicit "auto" | "litellm" | "modelsdev" mode) into LiteLLMPricingFetcher instead of a boolean, and
  • Branching in the internal fetch logic so "modelsdev" only ever calls models.dev (with an optional offline fallback) and does not require LiteLLM to be reachable.

This will also let you add tests that assert LiteLLM is not contacted when pricingSource === "modelsdev".

Also applies to: 41-52

🤖 Prompt for AI Agents
In apps/ccusage/src/_pricing-fetcher.ts around lines 17-38 (and likewise 41-52),
the function shouldUseModelsDev currently collapses "auto" and "modelsdev" into
a boolean that causes the fetcher to always contact LiteLLM first; change the
wiring to pass the full PricingSource ("auto" | "litellm" | "modelsdev") into
LiteLLMPricingFetcher instead of a boolean, update LiteLLMPricingFetcher to
branch on that mode so that "modelsdev" triggers only models.dev calls (with an
optional offline fallback) and never contacts LiteLLM, keep "auto" as the merged
behavior and "litellm" as LiteLLM-only, and add tests asserting that when
pricingSource === "modelsdev" LiteLLM is not contacted.

@ryoppippi

Copy link
Copy Markdown
Member

AFAIK, motels.dev does not support models that have two‑stage pricing changes, such as Sonnet’s 1MB model. Are you taking that into consideration?
@LeslieLeung

Add a pricingSource option across the shared CLI, config schema, all-agent loaders, Claude loaders, and agent adapters so callers can choose LiteLLM, models.dev, or an auto merge mode for calculated costs.

Implement models.dev fetching in the internal pricing layer by validating the provider/model API shape and converting per-million token prices into the existing LiteLLM per-token pricing format. The auto mode preserves LiteLLM precedence and fills missing entries from models.dev, while offline mode continues to use embedded pricing snapshots instead of fetching models.dev.

Document the new --pricing-source flag and regenerate the ccusage config schema. models.dev remains outside macro prefetching, so it is only used for live pricing fetches.
@ryoppippi
ryoppippi force-pushed the feat/pricing-source branch from 98b6218 to fa3e7aa Compare May 17, 2026 11:43

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/ccusage/src/commands/session.ts (1)

49-57: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Propagate pricingSource to the non---id session load path.

Line 56 threads pricingSource only for --id, but Line 74-81 still calls loadSessionData without it. Regular session output will ignore --pricing-source/config and diverge from session --id.

Suggested fix
 		sessionData = await loadSessionData({
 			since: mergedOptions.since,
 			until: mergedOptions.until,
 			mode: mergedOptions.mode,
 			offline: mergedOptions.offline,
+			pricingSource: mergedOptions.pricingSource,
 			singleThread: mergedOptions.singleThread,
 			timezone: mergedOptions.timezone,
 		});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/ccusage/src/commands/session.ts` around lines 49 - 57, The non-`--id`
execution path currently omits mergedOptions.pricingSource when calling
loadSessionData, so the CLI ignores --pricing-source/config for the regular
session command while handleSessionIdLookup passes it for the --id path; update
the branch that calls loadSessionData (the call that uses mergedOptions.mode,
mergedOptions.offline, mergedOptions.timezone) to also pass
mergedOptions.pricingSource so loadSessionData receives pricingSource
consistently across both code paths (mirror the values object keys used in
handleSessionIdLookup).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/internal/src/pricing.ts`:
- Around line 278-284: The offline check is skipped when pricingSource ===
'modelsdev', causing network calls even in offline mode; update the condition
order in the method that decides pricing by checking this.offline before testing
this.pricingSource so that if this.offline is true you call loadOfflinePricing()
first (and return), otherwise proceed to handle pricingSource and call
loadModelsDevPricing() when appropriate; locate the decision logic referencing
pricingSource, loadModelsDevPricing, offline, and loadOfflinePricing and reorder
the conditionals so offline takes precedence.

---

Outside diff comments:
In `@apps/ccusage/src/commands/session.ts`:
- Around line 49-57: The non-`--id` execution path currently omits
mergedOptions.pricingSource when calling loadSessionData, so the CLI ignores
--pricing-source/config for the regular session command while
handleSessionIdLookup passes it for the --id path; update the branch that calls
loadSessionData (the call that uses mergedOptions.mode, mergedOptions.offline,
mergedOptions.timezone) to also pass mergedOptions.pricingSource so
loadSessionData receives pricingSource consistently across both code paths
(mirror the values object keys used in handleSessionIdLookup).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8fa54f0f-bec1-4948-90a8-07eeab809319

📥 Commits

Reviewing files that changed from the base of the PR and between 98b6218 and fa3e7aa.

📒 Files selected for processing (22)
  • apps/ccusage/config-schema.json
  • apps/ccusage/src/adapter/amp/index.ts
  • apps/ccusage/src/adapter/claude/data-loader.ts
  • apps/ccusage/src/adapter/claude/index.ts
  • apps/ccusage/src/adapter/codex/index.ts
  • apps/ccusage/src/adapter/opencode/index.ts
  • apps/ccusage/src/adapter/types.ts
  • apps/ccusage/src/commands/agent.ts
  • apps/ccusage/src/commands/all.ts
  • apps/ccusage/src/commands/blocks.ts
  • apps/ccusage/src/commands/codex.ts
  • apps/ccusage/src/commands/session.ts
  • apps/ccusage/src/commands/session_id.ts
  • apps/ccusage/src/commands/statusline.ts
  • apps/ccusage/src/pricing-fetcher.ts
  • apps/ccusage/src/shared-args.ts
  • apps/ccusage/src/types.ts
  • docs/guide/cli-options.md
  • docs/guide/configuration.md
  • docs/guide/statusline.md
  • packages/internal/src/models-dev-pricing.ts
  • packages/internal/src/pricing.ts
✅ Files skipped from review due to trivial changes (3)
  • docs/guide/statusline.md
  • docs/guide/cli-options.md
  • docs/guide/configuration.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/internal/src/models-dev-pricing.ts

Comment thread packages/internal/src/pricing.ts
models.dev currently exposes flat token prices, so using it as the primary pricing source could bypass LiteLLM tier metadata for models such as Claude Sonnet with higher prices above large context thresholds.

Fetch LiteLLM metadata alongside models.dev for the explicit modelsdev source, copy only the known tiered pricing fields when present, and fall back to flat models.dev pricing if LiteLLM cannot be loaded. This keeps models.dev as the selected price source while preserving the existing tiered cost calculation path.

Add a regression test for a models.dev Claude Sonnet row with LiteLLM above-200k fields and document the behavior in the CLI and configuration guides.
@ryoppippi

Copy link
Copy Markdown
Member

Closing this stale pricing-source PR as not planned. The models.dev fallback path is risky for ccusage because current pricing logic depends on LiteLLM fields such as tiered/cache pricing, and this PR still has the unresolved tiered-pricing concern noted earlier. A future pricing-source change should be designed against the current pricing module and preserve tiered model behavior explicitly.

@ryoppippi ryoppippi closed this May 17, 2026
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.

2 participants