Files
deepseek-harness/packages/llm/token-meter/src/breakdown-projection.ts
T
imccyu 59df683ef1 fix(token-meter): bound projection state via logged shadow prices
The contextBreakdown and contextPressure units carried the full priced
surface, so each session's persisted projection checkpoint grew without
bound. A surface replacement is now priced by the shadow-price event
logged directly before it — compact/summary for compaction, the new
compact/prune from tool-result pruning (priced through the injected
token meter) — and the unit states shrink to a fixed handful of numbers.
Regenerate the persistence/cordis/module/config catalogs.
2026-08-06 02:50:38 +08:00

70 lines
2.8 KiB
TypeScript

/**
* Pure fold for the heuristic context-composition projection: system prompt
* and tool schemas from the newest request envelope, conversation from the
* live surface. Prices with the same shared estimator as the meter service,
* so the three figures match `measure()`'s heuristic vocabulary exactly.
*/
import { z } from 'zod'
import { canonicalHeader } from '@deepseek-ai/dsh-session'
import type { ProjectionDefinition } from '@deepseek-ai/dsh-session-projection'
import { estimateSystemTokens, estimateToolsTokens } from './estimate.ts'
import { foldSurfaceProjection } from './surface-projection.ts'
import type { ShadowPriceClaim } from './surface-projection.ts'
// Import for the `contextBreakdown` SessionProjectionMap key merge.
import type {} from './projection.ts'
interface ContextBreakdownState {
systemTokens: number
toolsTokens: number
messageTokens: number
/** Shadow price armed by the immediately preceding metering event. */
claim?: ShadowPriceClaim
}
const breakdownSchema = z.object({
systemTokens: z.number().int().nonnegative(),
toolsTokens: z.number().int().nonnegative(),
messageTokens: z.number().int().nonnegative(),
}).strict()
/**
* Token-meter's context-composition projection unit.
*
* 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.
*/
export const contextBreakdownProjectionDefinition:
ProjectionDefinition<'contextBreakdown', ContextBreakdownState> = {
key: 'contextBreakdown',
schema: breakdownSchema,
init: () => ({ systemTokens: 0, toolsTokens: 0, messageTokens: 0 }),
apply: (state, event) => {
const fold = foldSurfaceProjection(state.claim, event)
let systemTokens = state.systemTokens
let toolsTokens = state.toolsTokens
if (event.type === 'request/header') {
const header = canonicalHeader(event.data.header)
systemTokens = estimateSystemTokens(header)
toolsTokens = estimateToolsTokens(header)
}
if (systemTokens === state.systemTokens
&& toolsTokens === state.toolsTokens
&& fold.deltaTokens === 0
&& fold.claim === undefined
&& state.claim === undefined) return state
return {
systemTokens,
toolsTokens,
messageTokens: state.messageTokens + fold.deltaTokens,
...fold.claim === undefined ? {} : { claim: fold.claim },
}
},
view: ({ systemTokens, toolsTokens, messageTokens }) => ({ systemTokens, toolsTokens, messageTokens }),
stateVersion: 2,
}