/** * One folded day of analytics rows, and the arithmetic that folds them. * * WHY A DAY OF ROWS CAN BECOME ONE ROW AT ALL. The savings report classifies * every row on its own -- `classifySavings` reads content hashes, byte counts * and a schema version off the row's metadata and returns one of four classes * -- and then adds up only what the class allows. So a sum of tokens is NOT * enough to reproduce the report: two rows with the same tokens and different * classes contribute different amounts. * * The fold key is what makes it exact. A group fixes the day, the dimensions * every reader groups by, and the CLASSIFICATION, so within a group every row * contributes the same way and the contributions add. What is stored is the * result of the row-level arithmetic, not the inputs to it. * * PRICE IS SUMMED AT FOLD TIME, PER ROW, and that is deliberate on two counts. * Pricing is not linear in tokens -- a long-context tier charges a higher rate * past a per-request threshold -- so pricing a group's summed tokens once would * read as a request nobody made. And a figure already reported should not move * when a vendor changes a rate: the fold is where history stops being * re-derived and starts being remembered. */ import type { AnalyticsEntry } from './analytics-types.js'; import { type SavingsClassification } from './savings-classification.js'; /** The dimensions a fold may not mix, because a reader groups by them. */ export interface RollupKey { /** Local calendar day, `YYYY-MM-DD`. */ readonly day: string; readonly hookPhase: string; readonly toolName: string; readonly mcpServer: string; readonly client: string; readonly clientVersion: string; readonly model: string; readonly modelVersion: string; /** From metadata, and kept because pricing and its disclosure name it. */ readonly provider: string; readonly route: string; /** What `classifySavings` returned for every row in this group. */ readonly classification: SavingsClassification; /** * The measurement contract the folded rows were recorded under, or 0 for * rows written before the stamp existed. * * A KEY DIMENSION, NOT AN ANNOTATION. Once a day is folded its rows are * gone, so whatever is not in the key is unrecoverable -- and "which * contract produced this number" is exactly the question a reader needs * when the contract changes under them. Folding two contracts into one * total answers it with a number that belongs to neither. */ readonly measurementSchemaVersion: number; } /** What a folded group contributes to every figure the readers compute. */ export interface RollupSums { /** Rows folded. This is the report's operation count. */ readonly operations: number; /** Rows whose verified delta was not zero. */ readonly eligibleOperations: number; /** Signed sum of `verifiedTransportDelta`, which is the report's savings. */ readonly tokensSaved: number; /** `originalTokens` over the eligible rows only, as the report does it. */ readonly tokensBefore: number; /** Over every row, for the surfaces that show gross traffic. */ readonly originalTokens: number; readonly optimizedTokens: number; /** Sum of `reportedSavings`, the unverified figure shown beside the real one. */ readonly reportedSavings: number; /** Rows that carried an observed return, whatever their class. */ readonly observedReturns: number; /** USD over the rows that priced, summed one row at a time. */ readonly costUsd: number; readonly pricedOperations: number; /** Eligible rows no catalog could price -- what keeps a model disclosed. */ readonly unpricedOperations: number; /** * The dashboard's own splits, each gated by the predicate it reads with. * * SEPARATE FROM THE REPORT'S SUMS, NOT DERIVED FROM THEM. The dashboard and * the savings report ask different questions of the same rows: the report * adds a signed verified delta, while the dashboard shows a gross credit and * the debit beside it, and prices the context a row returned as well as the * context it saved. Deriving one set from the other would mean inferring a * per-row gate from a sum, which is the mistake the fold key exists to make * impossible -- so each figure is summed under its own predicate, by the same * functions the dashboard would have called on the rows. */ readonly verifiedOperations: number; readonly expansionOperations: number; readonly unverifiedOperations: number; /** `originalTokens` over the verified rows -- the dashboard's denominator. */ readonly verifiedOriginalTokens: number; /** Gross credit: `reportedSavings` over the verified rows. */ readonly verifiedReportedSavings: number; /** The debit: `optimizedTokens` over the expansion rows. */ readonly expansionOptimizedTokens: number; /** Context actually returned: `optimizedTokens` over the observed rows. */ readonly observedOptimizedTokens: number; /** `optimizedTokens` over the measured rows -- verified and debit together. */ readonly measuredOptimizedTokens: number; /** USD for that returned context, priced one row at a time. */ readonly contextUsd: number; readonly pricedContextOperations: number; /** What the unverified rows claimed, which stays visible as an audit figure. */ readonly unverifiedReportedSavings: number; /** * Tokens the caller did not spend because a tool answered instead of handing * them the file, over the rows where the recorder measured both sides. * * SUMMED APART FROM `tokensSaved` ON PURPOSE: that figure is the signed * transport delta, whose baseline is a payload, while this one's baseline is * a file on disk. One reply against two different befores is not additive, * and a single column would invite exactly that addition. */ readonly inputDisplacementTokens: number; /** * Tokens a tool DECLARED it displaced, kept apart from the measured figure * above because the evidence behind them differs: one was counted twice by * this process, the other once here and once by the tool. */ readonly declaredDisplacementTokens: number; readonly declaredOperations: number; /** Rows that carried a measured displacement. */ readonly displacementOperations: number; /** Earliest row in the group, kept so a fold can be audited against rows. */ readonly firstTimestamp: string; /** Latest row in the group, which is the dashboard's `lastSeen`. */ readonly lastTimestamp: string; } /** A folded day: the dimensions, and what they contributed. */ export interface AnalyticsRollup extends RollupKey, RollupSums { } /** * The local calendar day a timestamp falls in, as `YYYY-MM-DD`. * * THE SAME KEY THE PROXY HALF USES, from the same function, because the two * stores are read side by side: a window that meant one thing for one of them * and something else for the other would make the report's own totals * disagree across the boundary between them. */ export declare function localDayOf(timestamp: string): string | null; /** * The contract a row was recorded under, or 0 for one written before the * stamp existed. * * ZERO IS A LABEL, NOT A DEFAULT. Treating an unstamped row as current would * fold the history of an older contract into the totals of a newer one, which * is the one thing a version stamp exists to prevent. */ export declare function schemaVersionOf(metadata: Record): number; /** * The group one row belongs to. * * EVERY DIMENSION A READER GROUPS BY IS HERE, and the cost of being wrong is * asymmetric: a dimension left out silently merges two groups a report shows * apart, while one left in only costs a row. */ export declare function rollupKeyOf(entry: AnalyticsEntry): RollupKey | null; /** One string that identifies a group, for grouping in memory. */ export declare function rollupKeyText(key: RollupKey): string; /** Zero of the fold. */ export declare function emptySums(firstTimestamp: string): RollupSums; /** * Adds one row to a group's sums. * * THE ORDER OF THE CHECKS MIRRORS `accumulate` IN THE REPORT, deliberately: a * row with a zero delta counts as an operation and contributes nothing else, * including no `tokensBefore`. Folding its original tokens in anyway would * raise the denominator of every savings percentage for the folded days and * show the product getting worse with age. */ export declare function foldEntry(sums: RollupSums, entry: AnalyticsEntry): RollupSums; /** Merges two folds of the same group, which is what a second prune does. */ export declare function mergeSums(into: RollupSums, from: RollupSums): RollupSums; /** Folds a batch of rows into one rollup per group. */ export declare function foldEntries(entries: readonly AnalyticsEntry[]): AnalyticsRollup[]; /** The model name a folded group discloses when nothing could price it. */ export declare function rollupModelName(rollup: AnalyticsRollup): string; //# sourceMappingURL=analytics-rollup.d.ts.map