|
|
@@ -8,7 +8,7 @@ import { Context, Service } from 'cordis'
|
|
|
import z from 'schemastery'
|
|
|
import { BlockAssembler, deepFreeze } from '@deepseek-ai/dsh-llm'
|
|
|
import type { Message, TokenUsage } from '@deepseek-ai/dsh-llm'
|
|
|
-import type { EpochHeader, Session, SessionEvent, SurfaceEvent } from '@deepseek-ai/dsh-session'
|
|
|
+import type { EpochHeader, Session, SessionEvent } from '@deepseek-ai/dsh-session'
|
|
|
import { canonicalHeader, headerEquals, isSurfaceEvent } from '@deepseek-ai/dsh-session'
|
|
|
// Type-only: resolves the optional projection registry Context seam.
|
|
|
import type {} from '@deepseek-ai/dsh-session-projection'
|
|
|
@@ -21,6 +21,7 @@ import type {
|
|
|
import { contextBreakdownProjectionDefinition } from './breakdown-projection.ts'
|
|
|
import { contextPressureProjectionDefinition, tokenUsageProjectionDefinition } from './usage-projection.ts'
|
|
|
import { estimateContent, estimateHeader, estimateMessage, ROLE_OVERHEAD } from './estimate.ts'
|
|
|
+import { foldSurfaceTokens } from './surface-fold.ts'
|
|
|
|
|
|
export type * from './types.ts'
|
|
|
|
|
|
@@ -39,11 +40,6 @@ interface ReplayState {
|
|
|
anchor: MeasurementAnchor | undefined
|
|
|
}
|
|
|
|
|
|
-interface PreparedSurfaceMutation {
|
|
|
- readonly tokens: number
|
|
|
- commit(state: ReplayState): void
|
|
|
-}
|
|
|
-
|
|
|
/** Sum disjoint provider usage buckets without double-counting reasoning output. */
|
|
|
function usageTokens(usage: TokenUsage): number {
|
|
|
return usage.inputTokens
|
|
|
@@ -219,7 +215,7 @@ export class TokenMeterService extends Service {
|
|
|
}
|
|
|
|
|
|
const surface = isSurfaceEvent(event)
|
|
|
- ? this._prepareSurfaceMutation(session, state, event)
|
|
|
+ ? foldSurfaceTokens(state.surface, event)
|
|
|
: undefined
|
|
|
|
|
|
if (event.type === 'assistant/message') {
|
|
|
@@ -266,51 +262,11 @@ export class TokenMeterService extends Service {
|
|
|
|
|
|
state.header = nextHeader
|
|
|
state.stepStart = nextStepStart
|
|
|
- if (surface !== undefined) surface.commit(state)
|
|
|
- state.anchor = nextAnchor
|
|
|
- }
|
|
|
-
|
|
|
- /** Validate one surface operation and return its allocation-light commit. */
|
|
|
- private _prepareSurfaceMutation(
|
|
|
- session: Session,
|
|
|
- state: ReplayState,
|
|
|
- event: SurfaceEvent,
|
|
|
- ): PreparedSurfaceMutation {
|
|
|
- const tokens = this._estimateSurfaceEvent(session, event)
|
|
|
- const op = event.surfaceOp
|
|
|
- if (op === 'append') {
|
|
|
- return {
|
|
|
- tokens,
|
|
|
- commit(target) {
|
|
|
- target.surface.push({ seq: event.seq, tokens })
|
|
|
- target.surfaceTokens += tokens
|
|
|
- },
|
|
|
- }
|
|
|
+ if (surface !== undefined) {
|
|
|
+ state.surface = surface.nodes
|
|
|
+ state.surfaceTokens += surface.deltaTokens
|
|
|
}
|
|
|
-
|
|
|
- const startIdx = state.surface.findIndex(node => node.seq === op.start)
|
|
|
- const endIdx = state.surface.findIndex(node => node.seq === op.end)
|
|
|
- if (startIdx === -1 || endIdx === -1 || startIdx > endIdx) {
|
|
|
- throw new Error(
|
|
|
- `token meter: replace at seq ${event.seq} has invalid current range ${op.start}-${op.end}`,
|
|
|
- )
|
|
|
- }
|
|
|
- const removedTokens = state.surface
|
|
|
- .slice(startIdx, endIdx + 1)
|
|
|
- .reduce((total, node) => total + node.tokens, 0)
|
|
|
- return {
|
|
|
- tokens,
|
|
|
- commit(target) {
|
|
|
- target.surface.splice(startIdx, endIdx - startIdx + 1, { seq: event.seq, tokens })
|
|
|
- target.surfaceTokens += tokens - removedTokens
|
|
|
- },
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /** Price one current surface event exactly as it projects to a request. */
|
|
|
- private _estimateSurfaceEvent(session: Session, event: SurfaceEvent): number {
|
|
|
- const message = session.deriveEventMessage(event)
|
|
|
- return message === null ? 0 : this.estimateMessage(message)
|
|
|
+ state.anchor = nextAnchor
|
|
|
}
|
|
|
|
|
|
/**
|