mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
139 lines
5.3 KiB
TypeScript
139 lines
5.3 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import { renderContentBlocks, renderTranscript } from '@deepseek-ai/dsh-compact'
|
|
import { Session, SessionId } from '@deepseek-ai/dsh-session'
|
|
import type { ContentBlock } from '@deepseek-ai/dsh-llm'
|
|
import { CallId } from '@deepseek-ai/dsh-llm'
|
|
|
|
function session(): Session {
|
|
return new Session(SessionId('render-spec'))
|
|
}
|
|
|
|
describe('renderContentBlocks', () => {
|
|
it('renders text blocks verbatim and skips empty ones', () => {
|
|
expect(renderContentBlocks([
|
|
{ type: 'text', text: 'hello' },
|
|
{ type: 'text', text: '' },
|
|
{ type: 'text', text: 'world' },
|
|
])).toBe('hello\nworld')
|
|
})
|
|
|
|
it('wraps reasoning, skipping empty reasoning', () => {
|
|
expect(renderContentBlocks([
|
|
{ type: 'reasoning', text: 'think' },
|
|
{ type: 'reasoning', text: '' },
|
|
])).toBe('[reasoning: think]')
|
|
})
|
|
|
|
it('renders tool-call as a name(args) placeholder', () => {
|
|
expect(renderContentBlocks([
|
|
{ type: 'tool-call', id: CallId('c1'), name: 'read', arguments: '{"filePath":"a"}' },
|
|
])).toBe('[tool-call: read({"filePath":"a"})]')
|
|
})
|
|
|
|
it('renders tool-result with nested content, and bare when empty', () => {
|
|
expect(renderContentBlocks([
|
|
{ type: 'tool-result', toolCallId: CallId('c1'), content: [{ type: 'text', text: 'ok' }] },
|
|
{ type: 'tool-result', toolCallId: CallId('c2'), content: [] },
|
|
])).toBe('[tool-result: ok]\n[tool-result]')
|
|
})
|
|
|
|
it('renders an unknown (merge-extended) block type as a bare type tag', () => {
|
|
const unknown = { type: 'image', data: 'zzz' } as unknown as ContentBlock
|
|
expect(renderContentBlocks([unknown])).toBe('[image]')
|
|
})
|
|
|
|
it('returns the empty string for no blocks', () => {
|
|
expect(renderContentBlocks([])).toBe('')
|
|
})
|
|
})
|
|
|
|
describe('renderTranscript', () => {
|
|
it('renders each surface event type with its label, in the seq order given', () => {
|
|
const s = session()
|
|
const user = s.append('user/message', {
|
|
content: [{ type: 'text', text: 'fix the bug' }],
|
|
source: { kind: 'user' },
|
|
}, { surfaceOp: 'append' })
|
|
const assistant = s.append('assistant/message', { provenance: { provider: 'mock', model: 'mock' },
|
|
turn: 0, step: 0,
|
|
content: [{ type: 'text', text: 'looking' }],
|
|
}, { surfaceOp: 'append' })
|
|
const result = s.append('tool/result', {
|
|
turn: 0, step: 0, callId: CallId('c1'),
|
|
content: [{ type: 'text', text: 'exit 0' }],
|
|
isError: false,
|
|
}, { surfaceOp: 'append' })
|
|
const context = s.append('context/message', {
|
|
content: [{ type: 'text', text: 'file changed' }],
|
|
source: { kind: 'plugin', plugin: 'fs' },
|
|
}, { surfaceOp: 'append' })
|
|
const steering = s.append('steering/message', {
|
|
turn: 0,
|
|
content: [{ type: 'text', text: 'stop that' }],
|
|
source: { kind: 'user' },
|
|
}, { surfaceOp: 'append' })
|
|
|
|
expect(renderTranscript(s.events, [user.seq, assistant.seq, result.seq, context.seq, steering.seq])).toBe([
|
|
'User: fix the bug',
|
|
'Assistant: looking',
|
|
'Tool result (call c1): exit 0',
|
|
'[Context: file changed]',
|
|
'[Steering: stop that]',
|
|
].join('\n\n'))
|
|
})
|
|
|
|
it('labels an error tool result "Tool error"', () => {
|
|
const s = session()
|
|
const result = s.append('tool/result', {
|
|
turn: 0, step: 0, callId: CallId('c9'),
|
|
content: [{ type: 'text', text: 'boom' }],
|
|
isError: true,
|
|
}, { surfaceOp: 'append' })
|
|
expect(renderTranscript(s.events, [result.seq])).toBe('Tool error (call c9): boom')
|
|
})
|
|
|
|
it('renders NON-log-order seqs in the order given (surface order after a replace)', () => {
|
|
const s = session()
|
|
const first = s.append('user/message', {
|
|
content: [{ type: 'text', text: 'first' }],
|
|
source: { kind: 'user' },
|
|
}, { surfaceOp: 'append' })
|
|
const second = s.append('user/message', {
|
|
content: [{ type: 'text', text: 'second' }],
|
|
source: { kind: 'user' },
|
|
}, { surfaceOp: 'append' })
|
|
expect(renderTranscript(s.events, [second.seq, first.seq])).toBe('User: second\n\nUser: first')
|
|
})
|
|
|
|
it('skips events that render to nothing, non-message events, and seqs with no event', () => {
|
|
const s = session()
|
|
const empty = s.append('user/message', {
|
|
content: [{ type: 'text', text: '' }],
|
|
source: { kind: 'user' },
|
|
}, { surfaceOp: 'append' })
|
|
const emptyAssistant = s.append('assistant/message', { provenance: { provider: 'mock', model: 'mock' },
|
|
turn: 0, step: 0,
|
|
content: [{ type: 'text', text: '' }],
|
|
}, { surfaceOp: 'append' })
|
|
const emptyResult = s.append('tool/result', {
|
|
turn: 0, step: 0, callId: CallId('c3'),
|
|
content: [{ type: 'text', text: '' }],
|
|
isError: false,
|
|
}, { surfaceOp: 'append' })
|
|
const emptyContext = s.append('context/message', {
|
|
content: [{ type: 'text', text: '' }],
|
|
source: { kind: 'plugin', plugin: 'fs' },
|
|
}, { surfaceOp: 'append' })
|
|
const emptySteering = s.append('steering/message', {
|
|
turn: 0,
|
|
content: [{ type: 'text', text: '' }],
|
|
source: { kind: 'user' },
|
|
}, { surfaceOp: 'append' })
|
|
// A log-only (non-surface) event type: contributes nothing to a transcript.
|
|
const lock = s.append('compact/start', { turn: 0 })
|
|
expect(renderTranscript(s.events, [
|
|
empty.seq, emptyAssistant.seq, emptyResult.seq, emptyContext.seq, emptySteering.seq, lock.seq, 9999,
|
|
])).toBe('')
|
|
})
|
|
})
|