context-branches.spec.ts 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. import { describe, expect, it } from 'vitest'
  2. import type {
  3. ConversationContext, ConversationNode, RequestView,
  4. } from '@deepseek-ai/dsh-client-runtime/client'
  5. import {
  6. deriveTrajectoryContextBranches,
  7. trajectoryBranchContainsRequest,
  8. } from '../src/client/context-branches.ts'
  9. const checkpoint = {
  10. kind: 'context',
  11. seq: 100,
  12. time: 100,
  13. content: [],
  14. source: { kind: 'plugin', plugin: 'compact' },
  15. } as ConversationNode
  16. const abandoned = {
  17. kind: 'assistant',
  18. seq: 20,
  19. time: 20,
  20. turn: 1,
  21. step: 1,
  22. blocks: [{ kind: 'text', text: 'abandoned' }],
  23. } as ConversationNode
  24. const current = {
  25. kind: 'user',
  26. seq: 110,
  27. time: 110,
  28. content: [{ type: 'text', text: 'rewound' }],
  29. source: { kind: 'plugin', plugin: 'rewind' },
  30. } as ConversationNode
  31. function request(
  32. purpose: RequestView['purpose'],
  33. startSeq: number,
  34. resultSeq?: number,
  35. replacementSeq?: number,
  36. ): RequestView {
  37. return {
  38. purpose,
  39. startSeq,
  40. turn: 1,
  41. step: purpose === 'assistant' ? 1 : 0,
  42. startedAt: startSeq,
  43. completedAt: startSeq + 1,
  44. status: 'complete',
  45. ...(resultSeq === undefined ? {} : { resultSeq }),
  46. ...(replacementSeq === undefined ? {} : { replacementSeq }),
  47. }
  48. }
  49. describe('trajectory context branches', () => {
  50. it('inherits nodes and requests by retained surface position rather than seq cutoff', () => {
  51. const contexts: ConversationContext[] = [
  52. { id: 0, nodes: [checkpoint, abandoned] },
  53. {
  54. id: 1,
  55. parentId: 0,
  56. origin: 'rewind',
  57. originSeq: 110,
  58. nodes: [checkpoint, current],
  59. },
  60. ]
  61. const branches = deriveTrajectoryContextBranches(contexts)
  62. const successor = branches[1]!
  63. expect(successor.nodes.map(node => node.seq)).toEqual([110])
  64. expect(trajectoryBranchContainsRequest(
  65. successor,
  66. request('assistant', 10, 20),
  67. )).toBe(false)
  68. expect(trajectoryBranchContainsRequest(
  69. successor,
  70. request('compaction', 90, 95, 100),
  71. )).toBe(true)
  72. expect(trajectoryBranchContainsRequest(
  73. successor,
  74. request('assistant', 111),
  75. )).toBe(true)
  76. })
  77. })