mirror of
https://github.com/JuliusBrussee/caveman.git
synced 2026-08-11 13:21:09 +02:00
fix(stats): Opus output price $75->$25 for 4.5+ era; Windows statusline UTF-8
Folds in #466 (correct Opus 4.5-4.8 output price to $25/M, keep legacy 4.0/4.1 carve-outs at $75/M; fixes #465) and #459 (UTF-8 console + Get-Content so the pickaxe renders on Windows; +spacing). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
f06348cbd3
commit
f68111acc3
@@ -22,13 +22,20 @@ const COMPRESSION = { 'full': 0.65 };
|
||||
// Match by model id prefix so this stays correct across point releases
|
||||
// (e.g. claude-sonnet-4-20250514, claude-sonnet-4-7). Update from
|
||||
// https://www.anthropic.com/pricing if a release changes the tier.
|
||||
// Most-specific prefixes MUST come first — priceForModel returns the first match.
|
||||
const MODEL_OUTPUT_PRICE_PER_M = [
|
||||
['claude-opus-4', 75.00],
|
||||
['claude-sonnet-4', 15.00],
|
||||
['claude-haiku-4', 4.00],
|
||||
['claude-3-5-sonnet', 15.00],
|
||||
['claude-3-5-haiku', 4.00],
|
||||
['claude-3-opus', 75.00],
|
||||
// Legacy Opus 4.0 / 4.1 (pre-4.5) billed at the old $75/M output tier,
|
||||
// including the dated ids (e.g. claude-opus-4-20250514).
|
||||
['claude-opus-4-0', 75.00],
|
||||
['claude-opus-4-1', 75.00],
|
||||
['claude-opus-4-2025', 75.00],
|
||||
// Opus 4.5–4.8 dropped to $25/M output (rate card held since 4.5).
|
||||
['claude-opus-4', 25.00],
|
||||
['claude-sonnet-4', 15.00],
|
||||
['claude-haiku-4', 5.00], // Haiku 4.5 = $5/M output
|
||||
['claude-3-5-sonnet', 15.00],
|
||||
['claude-3-5-haiku', 4.00],
|
||||
['claude-3-opus', 75.00],
|
||||
];
|
||||
|
||||
function priceForModel(model) {
|
||||
@@ -324,7 +331,7 @@ function main() {
|
||||
// Routed through safeWriteFlag — the suffix path is predictable and
|
||||
// user-owned, same symlink-clobber surface as the .caveman-active flag.
|
||||
const agg = aggregateHistory(historyPath, null);
|
||||
const suffix = agg.estSavedTokens > 0 ? `⛏ ${humanizeTokens(agg.estSavedTokens)}` : '';
|
||||
const suffix = agg.estSavedTokens > 0 ? `⛏ ${humanizeTokens(agg.estSavedTokens)}` : '';
|
||||
safeWriteFlag(path.join(claudeDir, '.caveman-statusline-suffix'), suffix);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
$ClaudeDir = if ($env:CLAUDE_CONFIG_DIR) { $env:CLAUDE_CONFIG_DIR } else { Join-Path $HOME ".claude" }
|
||||
$Flag = Join-Path $ClaudeDir ".caveman-active"
|
||||
if (-not (Test-Path $Flag)) { exit 0 }
|
||||
@@ -50,7 +51,7 @@ if ($env:CAVEMAN_STATUSLINE_SAVINGS -ne "0") {
|
||||
$SavingsItem = Get-Item -LiteralPath $SavingsFile -Force -ErrorAction Stop
|
||||
if (-not ($SavingsItem.Attributes -band [System.IO.FileAttributes]::ReparsePoint) -and
|
||||
$SavingsItem.Length -le 64) {
|
||||
$Savings = (Get-Content -LiteralPath $SavingsFile -Raw -ErrorAction Stop).TrimEnd()
|
||||
$Savings = (Get-Content -LiteralPath $SavingsFile -Encoding UTF8 -Raw -ErrorAction Stop).TrimEnd()
|
||||
$Savings = ($Savings -replace '[\x00-\x1F]', '')
|
||||
if ($Savings.Length -gt 0) {
|
||||
[Console]::Write(" ${Esc}[38;5;172m$Savings${Esc}[0m")
|
||||
|
||||
@@ -160,10 +160,12 @@ test('omits USD line when model is unknown', (tmp) => {
|
||||
|
||||
test('priceForModel matches by prefix across point releases', () => {
|
||||
const { priceForModel } = require(path.join(ROOT, 'src', 'hooks', 'caveman-stats.js'));
|
||||
assert.strictEqual(priceForModel('claude-opus-4-7'), 75.00);
|
||||
assert.strictEqual(priceForModel('claude-opus-4-7'), 25.00);
|
||||
assert.strictEqual(priceForModel('claude-opus-4-8'), 25.00);
|
||||
assert.strictEqual(priceForModel('claude-opus-4-20250101'), 75.00);
|
||||
assert.strictEqual(priceForModel('claude-opus-4-1-20250805'), 75.00);
|
||||
assert.strictEqual(priceForModel('claude-sonnet-4-7-20260315'), 15.00);
|
||||
assert.strictEqual(priceForModel('claude-haiku-4-5'), 4.00);
|
||||
assert.strictEqual(priceForModel('claude-haiku-4-5'), 5.00);
|
||||
assert.strictEqual(priceForModel('claude-3-5-sonnet-20241022'), 15.00);
|
||||
assert.strictEqual(priceForModel(null), null);
|
||||
assert.strictEqual(priceForModel('gpt-4'), null);
|
||||
@@ -342,9 +344,9 @@ test('writes statusline suffix file after a stats run', (tmp) => {
|
||||
});
|
||||
const suffixPath = path.join(claudeDir, '.caveman-statusline-suffix');
|
||||
assert.ok(fs.existsSync(suffixPath));
|
||||
// 1500 / 0.35 = 4286, saved = 2786 → "⛏ 2.8k"
|
||||
// 1500 / 0.35 = 4286, saved = 2786 → "⛏ 2.8k" (two spaces after ⛏, #459)
|
||||
const suffix = fs.readFileSync(suffixPath, 'utf8');
|
||||
assert.match(suffix, /^⛏ 2\.8k$/);
|
||||
assert.match(suffix, /^⛏ 2\.8k$/);
|
||||
});
|
||||
|
||||
test('humanizeTokens formats small/medium/large correctly', () => {
|
||||
|
||||
Reference in New Issue
Block a user