jarrodwatts--claude-hud
127 行
4.8 KiB
JavaScript
127 行
4.8 KiB
JavaScript
import { isBedrockModelId, isVertexModelId } from './stdin.js';
|
|
const TOKENS_PER_MILLION = 1_000_000;
|
|
const CACHE_WRITE_MULTIPLIER = 1.25;
|
|
const CACHE_READ_MULTIPLIER = 0.1;
|
|
// Patterns are tried in order; the first match wins. Families with more specific
|
|
// model lines (Haiku 4.x differs from Haiku 3.5) must come before any broader
|
|
// fallback patterns to avoid silent under-pricing.
|
|
const ANTHROPIC_MODEL_PRICING = [
|
|
{ pattern: /\bopus 4 (?:[5-9]|\d{2,})\b/i, pricing: { inputUsdPerMillion: 5, outputUsdPerMillion: 25 } },
|
|
{ pattern: /\bopus 4(?: \d+)?\b/i, pricing: { inputUsdPerMillion: 15, outputUsdPerMillion: 75 } },
|
|
{ pattern: /\bsonnet 4(?: \d+)?\b/i, pricing: { inputUsdPerMillion: 3, outputUsdPerMillion: 15 } },
|
|
{ pattern: /\bsonnet 3 7\b/i, pricing: { inputUsdPerMillion: 3, outputUsdPerMillion: 15 } },
|
|
{ pattern: /\bsonnet 3 5\b/i, pricing: { inputUsdPerMillion: 3, outputUsdPerMillion: 15 } },
|
|
{ pattern: /\bhaiku 4(?: \d+)?\b/i, pricing: { inputUsdPerMillion: 1, outputUsdPerMillion: 5 } },
|
|
{ pattern: /\bhaiku 3 5\b/i, pricing: { inputUsdPerMillion: 0.8, outputUsdPerMillion: 4 } },
|
|
// Enterprise plan aliases (e.g. opusplan, sonnetplan, haikuplan)
|
|
{ pattern: /\bopusplan\b/i, pricing: { inputUsdPerMillion: 15, outputUsdPerMillion: 75 } },
|
|
{ pattern: /\bsonnetplan\b/i, pricing: { inputUsdPerMillion: 3, outputUsdPerMillion: 15 } },
|
|
{ pattern: /\bhaikuplan\b/i, pricing: { inputUsdPerMillion: 0.8, outputUsdPerMillion: 4 } },
|
|
];
|
|
function normalizeModelName(modelName) {
|
|
return modelName
|
|
.toLowerCase()
|
|
.replace(/^claude\s+/, '')
|
|
.replace(/\([^)]*\)/g, ' ')
|
|
.replace(/[._-]+/g, ' ')
|
|
.replace(/\s+/g, ' ')
|
|
.trim();
|
|
}
|
|
function matchAnthropicPricing(modelName) {
|
|
const normalized = normalizeModelName(modelName);
|
|
for (const entry of ANTHROPIC_MODEL_PRICING) {
|
|
if (entry.pattern.test(normalized)) {
|
|
return entry.pricing;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
function calculateUsd(tokens, usdPerMillion) {
|
|
return (tokens * usdPerMillion) / TOKENS_PER_MILLION;
|
|
}
|
|
function getAnthropicPricing(stdin) {
|
|
const candidates = [
|
|
stdin.model?.display_name?.trim(),
|
|
stdin.model?.id?.trim(),
|
|
];
|
|
for (const candidate of candidates) {
|
|
if (!candidate) {
|
|
continue;
|
|
}
|
|
const pricing = matchAnthropicPricing(candidate);
|
|
if (pricing) {
|
|
return pricing;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
export function estimateSessionCost(stdin, sessionTokens, options) {
|
|
if (!sessionTokens) {
|
|
return null;
|
|
}
|
|
if (!options?.allowRoutedCost && (isBedrockModelId(stdin.model?.id) || isVertexModelId(stdin.model?.id))) {
|
|
return null;
|
|
}
|
|
const pricing = getAnthropicPricing(stdin);
|
|
if (!pricing) {
|
|
return null;
|
|
}
|
|
const totalTokens = sessionTokens.inputTokens
|
|
+ sessionTokens.cacheCreationTokens
|
|
+ sessionTokens.cacheReadTokens
|
|
+ sessionTokens.outputTokens;
|
|
if (totalTokens === 0) {
|
|
return null;
|
|
}
|
|
const inputUsd = calculateUsd(sessionTokens.inputTokens, pricing.inputUsdPerMillion);
|
|
const cacheCreationUsd = calculateUsd(sessionTokens.cacheCreationTokens, pricing.inputUsdPerMillion * CACHE_WRITE_MULTIPLIER);
|
|
const cacheReadUsd = calculateUsd(sessionTokens.cacheReadTokens, pricing.inputUsdPerMillion * CACHE_READ_MULTIPLIER);
|
|
const outputUsd = calculateUsd(sessionTokens.outputTokens, pricing.outputUsdPerMillion);
|
|
return {
|
|
totalUsd: inputUsd + cacheCreationUsd + cacheReadUsd + outputUsd,
|
|
inputUsd,
|
|
cacheCreationUsd,
|
|
cacheReadUsd,
|
|
outputUsd,
|
|
};
|
|
}
|
|
function getNativeCostUsd(stdin, options) {
|
|
const nativeCost = stdin.cost?.total_cost_usd;
|
|
if (typeof nativeCost !== 'number' || !Number.isFinite(nativeCost)) {
|
|
return null;
|
|
}
|
|
if (isBedrockModelId(stdin.model?.id) || isVertexModelId(stdin.model?.id)) {
|
|
// Routed native billing reads $0.00 until the first response; use it only when opted in and positive.
|
|
if (!options?.allowRoutedCost || nativeCost <= 0) {
|
|
return null;
|
|
}
|
|
}
|
|
return nativeCost;
|
|
}
|
|
export function resolveSessionCost(stdin, sessionTokens, options) {
|
|
const nativeCostUsd = getNativeCostUsd(stdin, options);
|
|
if (nativeCostUsd !== null) {
|
|
return {
|
|
totalUsd: nativeCostUsd,
|
|
source: 'native',
|
|
};
|
|
}
|
|
const estimate = estimateSessionCost(stdin, sessionTokens, options);
|
|
if (!estimate) {
|
|
return null;
|
|
}
|
|
return {
|
|
totalUsd: estimate.totalUsd,
|
|
source: 'estimate',
|
|
};
|
|
}
|
|
export function formatUsd(amount) {
|
|
if (amount >= 1) {
|
|
return `$${amount.toFixed(2)}`;
|
|
}
|
|
if (amount >= 0.1) {
|
|
return `$${amount.toFixed(3)}`;
|
|
}
|
|
return `$${amount.toFixed(4)}`;
|
|
}
|
|
//# sourceMappingURL=cost.js.map
|