history-records.client.spec.ts 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. /** V2 history records become one event-shaped Client value per wire record. */
  2. import { describe, expect, it } from 'vitest'
  3. import { ToolCallId } from '@deepseek-ai/dsh-llm/brand'
  4. import type { SessionHistoryRecord } from '../src/types.ts'
  5. import {
  6. historyEntries,
  7. historyRecordFirstSeq,
  8. historyRecordLastSeq,
  9. } from '../src/client/sessions/history-records.ts'
  10. describe('Session history record projection', () => {
  11. it('retains an ordinary event and its point cursor', () => {
  12. const ordinary: SessionHistoryRecord = {
  13. type: 'event',
  14. event: { type: 'turn/start', seq: 7, time: 1, data: { turn: 1 } },
  15. }
  16. const records = [ordinary]
  17. const [entry] = historyEntries(records)
  18. expect(historyEntries(records)).toBe(records)
  19. expect(entry).toBe(ordinary)
  20. expect(historyRecordFirstSeq(ordinary)).toBe(7)
  21. expect(entry?.event.time).toBe(1)
  22. expect(historyRecordLastSeq(ordinary)).toBe(7)
  23. })
  24. it('retains one message with an embedded compact text stream', () => {
  25. const message: SessionHistoryRecord = {
  26. type: 'event',
  27. event: {
  28. type: 'assistant/message',
  29. seq: 11,
  30. time: 20,
  31. data: {
  32. turn: 1,
  33. step: 2,
  34. message: {
  35. id: 'message-1',
  36. role: 'assistant',
  37. content: [{ type: 'text', text: 'abcd' }],
  38. source: { kind: 'model', provider: 'fixture', model: 'fixture-v2' },
  39. },
  40. stream: [{ type: 'text-chunks', time0: 20, index: 0, dt: [1, 2, 3], texts: ['a', 'b', 'c', 'd'] }],
  41. },
  42. },
  43. }
  44. const [entry] = historyEntries([message])
  45. if (entry?.type !== 'event') throw new Error('expected v2 history entry')
  46. const { event } = entry
  47. expect(entry).toBe(message)
  48. expect(event).toBe(message.event)
  49. expect(historyRecordFirstSeq(message)).toBe(11)
  50. expect(event.time).toBe(20)
  51. expect(historyRecordLastSeq(message)).toBe(11)
  52. })
  53. it('preserves an attempt stream and optional tool-name absence', () => {
  54. const attempt: SessionHistoryRecord = {
  55. type: 'event',
  56. event: {
  57. type: 'assistant/attempt',
  58. seq: 20,
  59. time: 200,
  60. data: {
  61. turn: 2,
  62. step: 4,
  63. stream: [{
  64. type: 'tool-call-chunks',
  65. time0: 200,
  66. index: 1,
  67. id: ToolCallId('call-1'),
  68. dt: [2, 3],
  69. args: ['', '{"x":', '1}'],
  70. }],
  71. },
  72. },
  73. }
  74. const [entry] = historyEntries([attempt])
  75. if (entry?.type !== 'event') throw new Error('expected v2 history entry')
  76. const { event } = entry
  77. if (event.type !== 'assistant/attempt') throw new Error('expected Assistant attempt event')
  78. expect(event).toBe(attempt.event)
  79. const [record] = event.data.stream
  80. expect(record).toMatchObject({ type: 'tool-call-chunks', id: 'call-1' })
  81. expect(Object.hasOwn(record as object, 'name')).toBe(false)
  82. expect(historyRecordLastSeq(attempt)).toBe(20)
  83. })
  84. })