wulf-pulse/lib/services/llm/pricing.ts

53 lines
1.9 KiB
TypeScript
Raw Normal View History

/**
* Per-model token pricing for cost estimation in analyzer_analyses.estimated_cost_usd.
*
* Rates are USD per 1,000,000 tokens.
*
* VERIFY QUARTERLY against https://docs.claude.com/en/docs/about-claude/pricing
* Last verified: 2026-04-15
*/
import { HAIKU, SONNET, OPUS, type ModelId } from './models';
interface ModelRate {
/** USD per 1M input tokens */
input: number;
/** USD per 1M output tokens */
output: number;
/** USD per 1M tokens read from prompt cache (~0.1× input) */
cacheRead: number;
/** USD per 1M tokens written to 5-minute prompt cache (~1.25× input) */
cacheWrite5m: number;
}
export const PRICING: Record<ModelId, ModelRate> = {
[HAIKU]: { input: 1.0, output: 5.0, cacheRead: 0.1, cacheWrite5m: 1.25 },
[SONNET]: { input: 3.0, output: 15.0, cacheRead: 0.3, cacheWrite5m: 3.75 },
[OPUS]: { input: 5.0, output: 25.0, cacheRead: 0.5, cacheWrite5m: 6.25 },
};
export interface TokenUsage {
input_tokens: number;
output_tokens: number;
cache_creation_input_tokens?: number;
cache_read_input_tokens?: number;
}
/**
* USD cost of a single LLM call given its token usage. Caches are billed
* separately from raw input tokens cache_read at ~0.1×, cache_write at ~1.25×.
*/
export function estimateCostUsd(model: ModelId, usage: TokenUsage): number {
const rate = PRICING[model];
const cacheRead = usage.cache_read_input_tokens ?? 0;
const cacheWrite = usage.cache_creation_input_tokens ?? 0;
const uncachedInput = usage.input_tokens; // SDK reports this as the uncached remainder
const cost =
(uncachedInput / 1_000_000) * rate.input +
(usage.output_tokens / 1_000_000) * rate.output +
(cacheRead / 1_000_000) * rate.cacheRead +
(cacheWrite / 1_000_000) * rate.cacheWrite5m;
// Round to 4 decimals (matches analyzer_analyses.estimated_cost_usd numeric(10,4)).
return Math.round(cost * 10_000) / 10_000;
}