/** * attribution.ts * * CostAttributionService, a cost view over the platform's existing LLM usage * records (the LLM_RESPONSE_RECEIVED turn events), with cache-aware pricing and * 24h/7d aggregation windows, attributable across every dimension a usage * record carries (agent, tool, hook, MCP server, model, provider, session). * * HONESTY IDIOM (non-negotiable, mirrors services.ts `priceUsage` and the fleet * cost-state contract): a model the pricing catalog does not know is `unpriced` *, its cost is null and it is counted separately, never folded into a * fabricated dollar amount. An aggregate over a mix of priced and unpriced * records reports costState `estimated` (some contributors unpriced) so a * surface never mistakes a partial total for a complete one. * * CACHE ECONOMICS: fresh input, cache-read, and cache-write tokens are priced * distinctly. The catalog carries only the fresh input/output rates, so the * cache-read/write rates are derived from the fresh input rate via a documented * per-provider multiplier table ({@link CACHE_MULTIPLIERS}), these are the * providers' own published cache ratios (e.g. Anthropic cache-read 0.1x, * cache-write 1.25x), NOT a guessed base price: the base rate is always the * catalog's, and an unknown model stays unpriced regardless. */ /** A single usage record. Every dimension is optional, the service attributes over whatever a record carries. */ export interface CostUsageRecord { /** Epoch ms the usage occurred. */ readonly at: number; readonly provider?: string | undefined; readonly model?: string | undefined; readonly sessionId?: string | undefined; readonly agentId?: string | undefined; readonly tool?: string | undefined; readonly hook?: string | undefined; readonly mcpServer?: string | undefined; readonly inputTokens: number; readonly outputTokens: number; readonly cacheReadTokens: number; readonly cacheWriteTokens: number; } /** The dimension an attribution query groups by. */ export type CostDimension = 'agent' | 'tool' | 'hook' | 'mcp' | 'model' | 'provider' | 'session'; /** Aggregation window. */ export type CostWindow = '24h' | '7d'; /** Whether an aggregate's `costUsd` is complete. Mirrors the fleet ProcessCostState. */ export type CostAttributionState = 'priced' | 'estimated' | 'unpriced'; /** Token totals for an aggregate. */ export interface CostTokenTotals { readonly inputTokens: number; readonly outputTokens: number; readonly cacheReadTokens: number; readonly cacheWriteTokens: number; } /** * Where an aggregate's priced dollars came from: the resolver's source when * every priced contributor shared one ('user' = a manually configured or * registration price, 'provider' = the provider's own served rates, * 'catalog' = the dated pricing catalog), 'mixed' when priced contributors * disagree, or null when nothing was priced. Lets a surface render "your * price" vs "catalog price, as of " without deriving it client-side. */ export type CostAttributionSource = 'user' | 'provider' | 'catalog' | 'mixed' | null; /** One row of the attribution breakdown. */ export interface CostAttributionRow { /** The dimension value (e.g. an agentId, a model id, a provider name), or '(unattributed)' when a record lacked the queried dimension. */ readonly key: string; readonly costUsd: number | null; readonly costState: CostAttributionState; readonly costSource: CostAttributionSource; /** Oldest ISO date (YYYY-MM-DD) among the dated (catalog/provider) pricing snapshots that contributed; null when none carried a date. */ readonly pricingAsOf: string | null; readonly pricedRecordCount: number; readonly unpricedRecordCount: number; readonly tokens: CostTokenTotals; } /** The full attribution result. */ export interface CostAttributionResult { readonly window: CostWindow; readonly windowStartMs: number; readonly dimension: CostDimension; /** Sum of priced contributors, or null when every contributor is unpriced. */ readonly totalCostUsd: number | null; readonly costState: CostAttributionState; readonly costSource: CostAttributionSource; /** Oldest ISO date (YYYY-MM-DD) among the dated (catalog/provider) pricing snapshots that contributed; null when none carried a date. */ readonly pricingAsOf: string | null; readonly pricedRecordCount: number; readonly unpricedRecordCount: number; readonly tokens: CostTokenTotals; readonly rows: readonly CostAttributionRow[]; } /** * Rates per 1M tokens, or null when the model is unpriced. Wire from * providerRegistry.resolveModelPricing (the one pricing resolver: manual -> * registration -> provider-served -> catalog -> honest null). Explicit * cacheRead/cacheWrite rates, when the source carried them, take precedence * over the CACHE_MULTIPLIERS fallback in priceRecord. */ export type ResolvePricing = (model: string | undefined, provider?: string | undefined) => { readonly input: number; readonly output: number; readonly cacheRead?: number | undefined; readonly cacheWrite?: number | undefined; /** Where the rates came from ('user' manual/registration, 'provider' served, 'catalog'); absent when the wiring predates provenance. */ readonly source?: 'user' | 'provider' | 'catalog' | undefined; /** ISO date (YYYY-MM-DD) of the catalog/provider pricing snapshot; absent for user prices. */ readonly asOf?: string | undefined; } | null; /** * Published per-provider cache ratios relative to the fresh input rate. Keyed by * a provider substring (matched case-insensitively). `read`/`write` multiply the * catalog input rate to price cache-read/cache-write tokens. The default (no * match) is 1.0/1.0, cache tokens priced at full input rate, the conservative * honest choice when the provider's ratio is unknown. */ export declare const CACHE_MULTIPLIERS: Readonly>; export interface CostAttributionServiceOptions { readonly resolvePricing: ResolvePricing; /** Ceiling on retained records (oldest pruned first). Default 50000. */ readonly maxRecords?: number | undefined; readonly now?: (() => number) | undefined; } export declare class CostAttributionService { private readonly resolvePricing; private readonly maxRecords; private readonly now; /** Append-only ring, oldest-first; pruned by count and (on read) by the 7d ceiling. */ private records; constructor(opts: CostAttributionServiceOptions); /** Ingest one usage record. Zero-token records are dropped (nothing to attribute). */ record(rec: CostUsageRecord): void; /** Price one record with cache-aware rates, honestly unpriced when the model is unknown. */ priceRecord(rec: CostUsageRecord): { costUsd: number | null; state: 'priced' | 'unpriced'; source?: 'user' | 'provider' | 'catalog' | undefined; asOf?: string | undefined; }; /** Aggregate cost + tokens over a window, grouped by `dimension`. */ attribution(window: CostWindow, dimension: CostDimension): CostAttributionResult; private tokensOf; } //# sourceMappingURL=attribution.d.ts.map