/** * The provenance gate for the OTHER input: what the proxy saved on the wire. * * WHY A SECOND GATE AND NOT THE EXISTING ONE. `savings-classification.ts` * decides whether an MCP tool row proves a saving, and the evidence it looks * for is two materialized payloads with matching hashes. A proxy request has * entirely different evidence available, and more of it: * * - the exact byte length of the body we were given and the one we sent, * because we held both in memory at the same instant; * - our own token count of both of those bodies under a named encoder; * - the provider's billed prompt count for the body we actually sent. * * That last one is the part no other surface in this product has. Nobody will * ever bill us for a request we chose not to make, so the before-side can only * ever be a local estimate -- but the after-side is counted twice, once by us * and once by the party sending the invoice, and the two can be compared on * every single request for free. A systematic error in our encoder shows up as * a systematic gap between those two columns, which makes this the only savings * claim in the product that carries its own calibration. * * ONLY A 2xx PROVES A BILL. A request that failed in transport was never * delivered, and a 4xx or 429 is generally not charged -- so a token delta on * one of those rows is a counterfactual about a request nobody paid for either * way. Those rows are counted and named, never folded into a saving. This is a * strictly harder gate than the MCP path applies, and it is affordable here * precisely because the status is on the row. * * A REFUSAL IS NAMED, NEVER A ZERO -- the same rule the ledger writer follows. * Every row lands in exactly one class, and the classes that cannot support a * claim say which one they are rather than contributing nothing silently. */ import type { AccountingRecord, RequestUsage } from '../proxy/accounting.js'; /** * Version 1 is the first proxy ledger line that carries a token count at all. * Rows written before it are retained and classified `uncounted`, because a * ledger whose older half silently vanished would make the proxy look as * though it had only just started working. */ export declare const PROXY_MEASUREMENT_SCHEMA_VERSION = 1; export declare const PROXY_SAVINGS: Readonly<{ /** Counted both sides, request billed, and the provider's count to check it. */ readonly CalibratedReduction: "verified-wire-reduction-provider-calibrated"; /** Counted both sides and billed, but the response reported no usage. */ readonly Reduction: "verified-wire-reduction"; /** We made the request bigger -- an injected knowledge block, usually. */ readonly ExpansionDebit: "verified-wire-expansion-debit"; /** Counted, billed, and identical: a pass-through, not a failure. */ readonly NoChange: "counted-no-change"; /** Not a 2xx: nothing was charged for either body, so nothing was saved. */ readonly Unbilled: "unbilled-request"; /** The row carries no token count; `tokens.reason` says why. */ readonly Uncounted: "uncounted-request"; }>; export type ProxySavingsClassification = (typeof PROXY_SAVINGS)[keyof typeof PROXY_SAVINGS]; /** * What the provider says the prompt we sent cost it, or null. * * THE TWO DIALECTS COUNT CACHING OPPOSITELY, and getting this wrong does not * produce a slightly-off number -- it discredits the instrument. Anthropic * reports `input_tokens` EXCLUSIVE of cache reads and cache writes, which are * their own fields; a cache-heavy turn can bill 150,000 read tokens and report * `input_tokens: 4`. Comparing our count of that body against the 4 would say * our encoder read four orders of magnitude high. The Responses dialect does * the reverse: `input_tokens` already INCLUDES the cached subset that * `cached_input_tokens` reports separately, so adding them double-counts. * * The presence of the exclusive fields is what distinguishes them, which is * the same signal `mergeUsage` uses when it keeps the two sets apart instead * of normalising them into one. */ export declare function billedPromptTokens(usage: RequestUsage): number | null; /** Whether the provider accepted and charged for this request. */ export declare function wasBilled(record: AccountingRecord): boolean; export declare function classifyProxySavings(record: AccountingRecord): ProxySavingsClassification; /** Signed contribution to prompt tokens avoided on the wire. */ export declare function proxyTransportDelta(record: AccountingRecord): number; /** The prompt size the request would have had, in tokens, or 0. */ export declare function proxyTokensBefore(record: AccountingRecord): number; /** * Our count of the body we sent against the provider's count of the same body. * * THE INSTRUMENT MEASURING ITSELF. Both numbers describe one identical byte * sequence, so any gap between them is encoder disagreement and nothing else: * there is no sampling, no modelling and no assumption in it. That is a claim * about the rows this admits, and it is why it admits so few: a request whose * tools were deferred is billed for a prompt the provider assembled, holding a * search schema we never sent and omitting the schemas we did, so its gap is * not encoder disagreement and `classifyProxySavings` keeps it out. Summed over a * window it is the answer to "how far should I trust the column next to this * one", which is a question no other savings surface in this product can * answer at all. * * RETURNED AS A PAIR, NOT A RATIO. A ratio from one request is noise, and a * mean of per-request ratios weights a 300-token request the same as a * 300,000-token one. The caller sums the two sides and divides once. */ export declare function proxyCalibration(record: AccountingRecord): { readonly ours: number; readonly billed: number; } | null; /** * What the saving on one request was worth, or null. * * EACH SIDE IS PRICED AS A WHOLE PROMPT AND THE PRICES ARE SUBTRACTED, rather * than pricing the delta on its own. The unit is identical -- one immediate * uncached-input equivalent, the same definition `priceVerifiedDelta` uses for * the MCP path -- but provider prices are TIERED on prompt length, and the * delta is not a prompt. Pricing 50,000 saved tokens in isolation prices them * at the short-context rate even when the request they came out of was 250,000 * tokens long and billed at the long-context one, which understates the * saving; and when compression carries a request back across that threshold, * the tier change IS part of what was saved and only this form captures it. * * NULL IS NOT ZERO. A model with no exact catalog entry -- and a row with no * model at all, which is every row a pass-through path wrote -- has an unknown * price, not a free one. */ export declare function priceProxyDelta(record: AccountingRecord): number | null; //# sourceMappingURL=proxy-savings.d.ts.map