breakdown-projection.ts 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. /**
  2. * Heuristic composition of the current retained surface, independent of route
  3. * image pricing and provider usage. Positional entries preserve system-prompt
  4. * classification across replacements without retaining historical messages.
  5. */
  6. import { z } from 'zod'
  7. import { canonicalHeader, isSurfaceEvent, SessionSeq } from '@deepseek-ai/dsh-session'
  8. import type { ProjectionDefinition } from '@deepseek-ai/dsh-session-projection'
  9. import { estimateToolsTokens } from './estimate.ts'
  10. import { commitSurfaceTokens, planSurfaceTokens } from './surface-fold.ts'
  11. // Import for the `contextBreakdown` SessionProjectionStateMap key merge.
  12. import type {} from './projection.ts'
  13. declare module '@deepseek-ai/dsh-session-projection/types' {
  14. interface SessionProjectionStateMap {
  15. contextBreakdown: ContextBreakdownState
  16. }
  17. }
  18. const tokenCount = z.number().int().nonnegative()
  19. const sessionSeq = z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER).transform(SessionSeq)
  20. const breakdownSchema = z.object({
  21. systemTokens: tokenCount,
  22. toolsTokens: tokenCount,
  23. messageTokens: tokenCount,
  24. }).strict()
  25. /** Plain-JSON checkpoint: one compact entry per retained surface position. */
  26. const contextBreakdownStateSchema = z.object({
  27. nodes: z.array(z.object({
  28. seq: sessionSeq,
  29. heuristicTokens: tokenCount,
  30. system: z.boolean(),
  31. }).strict()),
  32. breakdown: breakdownSchema,
  33. }).strict()
  34. type ContextBreakdownState = z.infer<typeof contextBreakdownStateSchema>
  35. /**
  36. * Context composition with the last nonempty surviving system in surface
  37. * order classified as system tokens; all other visible prices are messages.
  38. * Replacements use the measurement planner, not shadow-price claims. State
  39. * and surface transitions cost O(current retained surface), not O(log length).
  40. * Tools are priced from the latest request header. No route pricing applies.
  41. */
  42. export const contextBreakdownProjectionDefinition = {
  43. key: 'contextBreakdown',
  44. stateVersion: 4,
  45. stateSchema: contextBreakdownStateSchema,
  46. init: (): ContextBreakdownState => ({
  47. nodes: [],
  48. breakdown: { systemTokens: 0, toolsTokens: 0, messageTokens: 0 },
  49. }),
  50. apply: (state, event) => {
  51. if (event.type === 'request/header') {
  52. const toolsTokens = estimateToolsTokens(canonicalHeader(event.data.header))
  53. return toolsTokens === state.breakdown.toolsTokens
  54. ? state
  55. : { ...state, breakdown: { ...state.breakdown, toolsTokens } }
  56. }
  57. if (!isSurfaceEvent(event)) return state
  58. const plan = planSurfaceTokens(state.nodes, event)
  59. const nodes = [...state.nodes]
  60. commitSurfaceTokens(nodes, {
  61. ...plan,
  62. node: { seq: event.seq, heuristicTokens: plan.tokens, system: event.type === 'system/message' },
  63. })
  64. const systemTokens = nodes.findLast(node => node.system && node.heuristicTokens > 0)?.heuristicTokens ?? 0
  65. const messageTokens = state.breakdown.systemTokens + state.breakdown.messageTokens + plan.deltaTokens - systemTokens
  66. const breakdown = systemTokens === state.breakdown.systemTokens && messageTokens === state.breakdown.messageTokens
  67. ? state.breakdown
  68. : { systemTokens, toolsTokens: state.breakdown.toolsTokens, messageTokens }
  69. return { nodes, breakdown }
  70. },
  71. wire: {
  72. viewSchema: breakdownSchema,
  73. view: state => state.breakdown,
  74. },
  75. } satisfies ProjectionDefinition<'contextBreakdown', ContextBreakdownState>