Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Changelog

## Unreleased

### Fixed
- **Grok 4.6 prices from day one** — xAI's launch-day rates
($2 in / $0.50 cached / $6 out per MTok, doubling for ≥200k-token
prompts; the fast variant at 2x) are baked in until the public
catalogs learn the model, so spend from Grok 4.6 sessions shows
dollars instead of the unpriced ⚠ — including Cursor sessions, whose
usage export brands the model `cursor-grok-4.6-xhigh`.

## 0.4.33 — 2026-08-12

### Fixed
Expand Down
50 changes: 49 additions & 1 deletion src-tauri/src/pricing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ pub fn generation() -> u64 {
/// fingerprinted below — an app update that reprices the same files would
/// otherwise leave history at the old dollars until upstream happens to
/// rewrite a catalog.
const CORRECTIONS_REV: u32 = 3; // 3: zero-rate catalog placeholders no longer price at $0.00
const CORRECTIONS_REV: u32 = 4; // 4: grok-4.6 builtin launch pricing

/// Stable fingerprint of the effective pricing inputs: the on-disk catalog
/// files plus this binary's corrections revision. The persistent spend
Expand Down Expand Up @@ -668,13 +668,34 @@ fn builtin_price(canonical: &str) -> Option<Price> {
let bare = canonical
.strip_prefix("moonshot/")
.or_else(|| canonical.strip_prefix("moonshot-ai/"))
.or_else(|| canonical.strip_prefix("xai/"))
.unwrap_or(canonical);
match bare {
"kimi-k3" | "kimi-k3-code" => Some(Price::flat(3.0, 15.0, 0.3, 3.0)),
// Alibaba Model Studio, GA'd 2026-08-03 (USD/MTok): input $2,
// output $6, implicit cache read $0.25, explicit cache write $2.50.
// Public catalogs still carry 0/0 placeholders for these slugs.
"qwen3.8-max" | "qwen3.8-max-preview" => Some(Price::flat(2.0, 6.0, 0.25, 2.5)),
// Grok 4.6, released 2026-08-12 — docs.x.ai/docs/pricing (USD/MTok):
// $2 in / $0.50 cached / $6 out; prompts ≥200k bill $4 / $1 / $12
// for the WHOLE request (xAI's long-context rule matches
// request_cost's tiering, and Grok spend passes the default 200k
// threshold). xAI bills no separate cache-write rate — writes are
// plain input. The announced 2x "-fast" variant needs no entry:
// the -fast resolution path applies the default 2x multiplier to
// this rate card. Public catalogs don't carry 4.6 yet.
// The cursor- spellings are Cursor's CSV branding for the same
// vendor-billed model ("cursor-grok-4.6-xhigh" on launch day) —
// matched EXPLICITLY rather than via a generic cursor- prefix
// strip, so other cursor-branded slugs (which Cursor may bill at
// its own rates) can never silently price off this baked table.
"grok-4.6" | "grok-4-6" | "cursor-grok-4.6" | "cursor-grok-4-6" => Some(Price {
input_200k: Some(4.0),
output_200k: Some(12.0),
cache_read_200k: Some(1.0),
cache_write_200k: Some(4.0),
..Price::flat(2.0, 6.0, 0.5, 2.0)
}),
_ => None,
}
}
Expand Down Expand Up @@ -768,6 +789,33 @@ mod tests {
assert_eq!((terra.input, terra.output), (1.75, 11.0));
}

#[test]
fn grok_46_builtin_prices_with_long_context_tier() {
// Vendor rates (docs.x.ai/docs/pricing): $2/$0.50/$6, doubling for
// ≥200k prompts — resolvable in every spelling before the public
// catalogs learn the model. Empty store = builtin only.
let store = super::Store::default();
// cursor-grok-4.6-xhigh is the exact slug Cursor's CSV logged on
// launch day — 20.6M real tokens showed $0.00 until it resolved.
for slug in
["grok-4.6", "grok-4-6", "xai/grok-4.6", "grok-4.6-high", "cursor-grok-4.6-xhigh"]
{
let p = super::resolve(&store, slug, 0)
.unwrap_or_else(|| panic!("{slug} did not price"));
assert_eq!((p.input, p.output, p.cache_read, p.cache_write), (2.0, 6.0, 0.5, 2.0), "{slug}");
assert_eq!(
(p.input_200k, p.output_200k, p.cache_read_200k),
(Some(4.0), Some(12.0), Some(1.0)),
"{slug}"
);
}
// The fast variant is "twice the price" (launch post): the -fast
// path scales the whole rate card, long-context tier included.
let fast = super::resolve(&store, "grok-4.6-fast", 0).unwrap();
assert_eq!((fast.input, fast.output, fast.cache_read), (4.0, 12.0, 1.0));
assert_eq!(fast.input_200k, Some(8.0));
}

#[test]
fn kimi_k3_builtin_prices_every_spelling() {
// Vendor-documented rates (platform.kimi.ai): $3 in, $15 out, $0.30
Expand Down
13 changes: 12 additions & 1 deletion src-tauri/src/spend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,18 @@ fn codex_price(model: &str) -> (f64, f64, f64) {

fn grok_price(model: &str) -> (f64, f64) {
let m = model.to_lowercase();
if m.contains("code") || m.contains("fast") {
// Grok 4.6's "fast" is a 2x PREMIUM speed tier (launch post: "twice
// the price"), the opposite of the older grok-4-fast/grok-code-fast
// line where "fast" meant a smaller, cheaper model — 4.6 slugs must
// never fall into that cheap branch. (Normally unreachable: the
// baked-in catalog entry resolves 4.6 before this backstop.)
if m.contains("4.6") || m.contains("4-6") {
if m.contains("fast") {
(4.0, 12.0)
} else {
(2.0, 6.0)
}
} else if m.contains("code") || m.contains("fast") {
(0.2, 1.5)
} else {
(3.0, 15.0)
Expand Down