fix: projection error

This commit is contained in:
imccyu
2026-08-06 19:35:24 +08:00
parent 4d241d37e4
commit ed8431cdd0
9 changed files with 134 additions and 24 deletions
@@ -33,10 +33,11 @@ const breakdownSchema = z.object({
*
* Envelope figures are last-wins per `request/header`; the message figure
* rides {@link foldSurfaceProjection} — the same O(1) fold the occupancy
* projection uses — so it equals `measure().surfaceTokens` at every event
* boundary and compaction shrinks it by its logged shadow price, the way it
* shrinks the next request. The state is a fixed handful of numbers, so the
* persisted checkpoint stays O(1) over the session's life.
* projection uses — so fully metered logs equal `measure().surfaceTokens` at
* every event boundary and compaction shrinks the figure by its logged shadow
* price. A replacement without a claim preserves the previous total. The
* state is a fixed handful of numbers, so the persisted checkpoint stays
* O(1) over the session's life.
*/
export const contextBreakdownProjectionDefinition:
ProjectionDefinition<'contextBreakdown', ContextBreakdownState> = {
+4 -3
View File
@@ -3,9 +3,10 @@
* surface `measure()` serves and compaction plans against. The projection
* units deliberately do NOT share this fold — their state must stay O(1)
* for the persisted checkpoint, so they ride `surface-projection.ts`'s
* shadow-price protocol instead. The two stay in agreement by construction:
* both price through `estimate.ts`, and every logged shadow price is derived
* from THIS fold's nodes by the replace producer.
* shadow-price protocol instead. Fully metered logs stay in agreement by
* construction: both price through `estimate.ts`, and every logged shadow
* price is derived from THIS fold's nodes by the replace producer. A
* projection replacement without a claim deliberately folds with zero delta.
*
* @module @deepseek-ai/dsh-token-meter/surface-fold
*/
@@ -10,7 +10,9 @@
* 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.
* 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
*/
@@ -47,16 +49,19 @@ export interface SurfaceTokensFold {
* 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` must consume a claim naming its exact range — the
* 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 without a claim for its exact range —
* every in-repo replace producer meters its replacement, so an unpriced
* replacement is a shadow-price contract violation and must fail loud
* rather than let the total drift.
* @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,
@@ -74,10 +79,15 @@ export function foldSurfaceProjection(
const tokens = message === null ? 0 : estimateMessage(message)
const op = event.surfaceOp
if (op === 'append') return { deltaTokens: tokens, claim: undefined }
if (claim === undefined || claim.start !== op.start || claim.end !== op.end) {
// 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`
+ (claim === undefined ? '' : ` (armed claim covers ${claim.start}-${claim.end})`),
+ ` (armed claim covers ${claim.start}-${claim.end})`,
)
}
return { deltaTokens: tokens - claim.tokens, claim: undefined }
@@ -155,9 +155,10 @@ ProjectionDefinition<'tokenUsage', TokenUsageState> = {
* `projectedTokens` — the sample plus the surface's signed movement since it
* was taken — so occupancy answers for the next request rather than the last
* one. The total rides {@link foldSurfaceProjection}, so the state stays O(1)
* and a replacement shrinks it by its logged shadow price. A usage sample is
* stamped BEFORE the same event joins the surface, so an `assistant/message`
* anchors against the surface its own request saw.
* and a replacement shrinks it by its logged shadow price. A replacement
* without a claim preserves the previous total. A usage sample is stamped
* BEFORE the same event joins the surface, so an `assistant/message` anchors
* against the surface its own request saw.
*/
export const contextPressureProjectionDefinition:
ProjectionDefinition<'contextPressure', ContextPressureState> = {