95 lines
4.5 KiB
TypeScript
95 lines
4.5 KiB
TypeScript
/**
|
|
* The O(1) surface-token fold shared by the token-meter projection units.
|
|
*
|
|
* A projection state must stay bounded — the persisted projection cache
|
|
* checkpoints every unit's whole state, so carrying the priced surface
|
|
* (one node per model-visible message) would grow a checkpoint without
|
|
* bound over the session's life. Instead, replacements ride the compact
|
|
* seam's shadow-price protocol: the metering event immediately before a
|
|
* surface `replace` (`compact/summary` or `compact/prune`) states the
|
|
* heuristic price of the exact replaced range, so the fold keeps a running
|
|
* total plus at most one pending claim and never retains per-node prices.
|
|
* The counts are exact by construction: producers derive them from the same
|
|
* fixed estimator this module prices appends with. A replacement without an
|
|
* armed claim folds with zero delta because bounded state cannot reconstruct
|
|
* the replaced range; this preserves replay at the cost of possible drift.
|
|
*
|
|
* @module @deepseek-ai/dsh-token-meter/surface-projection
|
|
*/
|
|
|
|
import { deriveEventMessage, isSurfaceEvent } from '@deepseek-ai/dsh-session'
|
|
import type { SessionEvent } from '@deepseek-ai/dsh-session'
|
|
// Type-only: the `compact/*` SessionEventMap merges (shadow-price events).
|
|
import type {} from '@deepseek-ai/dsh-compact'
|
|
import { estimateMessage } from './estimate.ts'
|
|
|
|
/**
|
|
* One armed shadow price: the heuristic tokens of the surface range the
|
|
* IMMEDIATELY following event replaces. Plain JSON — it is part of the
|
|
* persisted unit state while armed.
|
|
*/
|
|
export interface ShadowPriceClaim {
|
|
/** Declared inclusive first surface-node seq of the priced range. */
|
|
start: number
|
|
/** Declared inclusive last surface-node seq of the priced range. */
|
|
end: number
|
|
/** Heuristic tokens of the priced range under the fixed estimator. */
|
|
tokens: number
|
|
}
|
|
|
|
/** One event's effect on a running surface-token total. */
|
|
export interface SurfaceTokensFold {
|
|
/** Signed change in the surface total; 0 for events off the surface. */
|
|
readonly deltaTokens: number
|
|
/** Claim to carry into the next event; undefined when none survives. */
|
|
readonly claim: ShadowPriceClaim | undefined
|
|
}
|
|
|
|
/**
|
|
* Fold one committed event onto a running surface-token total.
|
|
*
|
|
* A shadow-price event arms a claim; any other event expires it, and a
|
|
* surface `replace` consumes the claim naming its exact range — the
|
|
* producers append the metering event and the replacement synchronously
|
|
* adjacent, so a surviving claim always prices the very next event.
|
|
* A replace with no claim folds with zero delta because the bounded state
|
|
* cannot reconstruct the replaced range. An armed claim for another range
|
|
* still fails because the adjacent events contradict each other.
|
|
* @param claim - the claim armed by the immediately preceding event, if any.
|
|
* @param event - the next committed session event.
|
|
* @returns the signed token delta and the claim state after this event.
|
|
* @throws when a replacement arrives with an armed claim for a different
|
|
* range — the metering event was adjacent, so this is a live producer's
|
|
* shadow-price contract violation, not historical data, and must fail
|
|
* loud rather than let the total drift.
|
|
*/
|
|
export function foldSurfaceProjection(
|
|
claim: ShadowPriceClaim | undefined,
|
|
event: SessionEvent,
|
|
): SurfaceTokensFold {
|
|
if (event.type === 'compact/summary' || event.type === 'compact/prune') {
|
|
const { shadowedRange, shadowedTokenCount } = event.data
|
|
return {
|
|
deltaTokens: 0,
|
|
claim: { start: shadowedRange.start, end: shadowedRange.end, tokens: shadowedTokenCount },
|
|
}
|
|
}
|
|
if (!isSurfaceEvent(event)) return { deltaTokens: 0, claim: undefined }
|
|
const message = deriveEventMessage(event)
|
|
const tokens = message === null ? 0 : estimateMessage(message)
|
|
const op = event.surfaceOp
|
|
if (op === 'append') return { deltaTokens: tokens, claim: undefined }
|
|
// Sessions recorded before the shadow-price protocol log replacements with
|
|
// no adjacent metering event; the bounded state cannot reconstruct the
|
|
// replaced range's price, so fold those neutrally — historical replay
|
|
// degrades to drift instead of failing.
|
|
if (claim === undefined) return { deltaTokens: 0, claim: undefined }
|
|
if (claim.start !== op.start || claim.end !== op.end) {
|
|
throw new Error(
|
|
`token surface: replace at seq ${event.seq} over range ${op.start}-${op.end} has no adjacent shadow price`
|
|
+ ` (armed claim covers ${claim.start}-${claim.end})`,
|
|
)
|
|
}
|
|
return { deltaTokens: tokens - claim.tokens, claim: undefined }
|
|
}
|