const assert = require('assert'); const { DeltaAccumulator } = require('../../../dist/glmt/delta-accumulator'); describe('DeltaAccumulator', () => { describe('Initialization', () => { it('has correct initial state', () => { const acc = new DeltaAccumulator(); assert(acc.messageId.startsWith('msg_'), 'Message ID should start with msg_'); assert.strictEqual(acc.role, 'assistant'); assert.strictEqual(acc.contentBlocks.length, 0); assert.strictEqual(acc.currentBlockIndex, -1); assert.strictEqual(acc.messageStarted, false); assert.strictEqual(acc.finalized, false); }); it('accepts config options', () => { const acc = new DeltaAccumulator({}, { loopDetectionThreshold: 5 }); assert.strictEqual(acc.loopDetectionThreshold, 5); }); }); describe('Block management', () => { it('starts thinking block', () => { const acc = new DeltaAccumulator(); const block = acc.startBlock('thinking'); assert.strictEqual(block.type, 'thinking'); assert.strictEqual(block.index, 0); assert.strictEqual(block.started, true); assert.strictEqual(block.stopped, false); assert.strictEqual(acc.contentBlocks.length, 1); assert.strictEqual(acc.currentBlockIndex, 0); }); it('starts text block', () => { const acc = new DeltaAccumulator(); const block = acc.startBlock('text'); assert.strictEqual(block.type, 'text'); assert.strictEqual(block.index, 0); }); it('adds delta to thinking block', () => { const acc = new DeltaAccumulator(); acc.startBlock('thinking'); acc.addDelta('Hello '); acc.addDelta('world'); const block = acc.getCurrentBlock(); assert.strictEqual(block.content, 'Hello world'); assert.strictEqual(acc.thinkingBuffer, 'Hello world'); }); it('adds delta to text block', () => { const acc = new DeltaAccumulator(); acc.startBlock('text'); acc.addDelta('Answer: '); acc.addDelta('42'); const block = acc.getCurrentBlock(); assert.strictEqual(block.content, 'Answer: 42'); assert.strictEqual(acc.textBuffer, 'Answer: 42'); }); it('handles multiple blocks (thinking → text)', () => { const acc = new DeltaAccumulator(); acc.startBlock('thinking'); acc.addDelta('Analyzing...'); acc.startBlock('text'); acc.addDelta('The answer is 42'); assert.strictEqual(acc.contentBlocks.length, 2); assert.strictEqual(acc.currentBlockIndex, 1); assert.strictEqual(acc.contentBlocks[0].type, 'thinking'); assert.strictEqual(acc.contentBlocks[1].type, 'text'); assert.strictEqual(acc.contentBlocks[0].content, 'Analyzing...'); assert.strictEqual(acc.contentBlocks[1].content, 'The answer is 42'); }); it('stops current block', () => { const acc = new DeltaAccumulator(); acc.startBlock('thinking'); acc.addDelta('Done'); acc.stopCurrentBlock(); const block = acc.getCurrentBlock(); assert.strictEqual(block.stopped, true); }); it('returns null when no blocks exist', () => { const acc = new DeltaAccumulator(); const block = acc.getCurrentBlock(); assert.strictEqual(block, null); }); }); describe('Usage statistics', () => { it('updates usage statistics', () => { const acc = new DeltaAccumulator(); acc.updateUsage({ prompt_tokens: 100, completion_tokens: 50 }); assert.strictEqual(acc.inputTokens, 100); assert.strictEqual(acc.outputTokens, 50); }); }); describe('Summary', () => { it('gets correct summary', () => { const acc = new DeltaAccumulator(); acc.model = 'glm-4.6'; acc.startBlock('thinking'); acc.addDelta('test'); acc.updateUsage({ prompt_tokens: 10, completion_tokens: 20 }); const summary = acc.getSummary(); assert.strictEqual(summary.messageId, acc.messageId); assert.strictEqual(summary.model, 'glm-4.6'); assert.strictEqual(summary.role, 'assistant'); assert.strictEqual(summary.blockCount, 1); assert.strictEqual(summary.usage.input_tokens, 10); assert.strictEqual(summary.usage.output_tokens, 20); }); }); describe('Message lifecycle', () => { it('tracks lifecycle flags', () => { const acc = new DeltaAccumulator(); assert.strictEqual(acc.messageStarted, false); acc.messageStarted = true; assert.strictEqual(acc.messageStarted, true); acc.finalized = true; assert.strictEqual(acc.finalized, true); }); it('tracks finish reason', () => { const acc = new DeltaAccumulator(); assert.strictEqual(acc.finishReason, null); acc.finishReason = 'stop'; assert.strictEqual(acc.finishReason, 'stop'); }); }); describe('Loop detection', () => { it('does not detect loop below threshold', () => { const acc = new DeltaAccumulator(); acc.startBlock('thinking'); acc.addDelta('Thinking 1'); acc.startBlock('thinking'); acc.addDelta('Thinking 2'); const hasLoop = acc.checkForLoop(); assert.strictEqual(hasLoop, false); assert.strictEqual(acc.loopDetected, false); }); it('detects loop with 3 consecutive thinking blocks', () => { const acc = new DeltaAccumulator(); acc.startBlock('thinking'); acc.addDelta('Planning step 1...'); acc.startBlock('thinking'); acc.addDelta('Planning step 2...'); acc.startBlock('thinking'); acc.addDelta('Planning step 3...'); const hasLoop = acc.checkForLoop(); assert.strictEqual(hasLoop, true); assert.strictEqual(acc.loopDetected, true); const summary = acc.getSummary(); assert.strictEqual(summary.loopDetected, true); }); it('does not detect loop when tool calls present', () => { const acc = new DeltaAccumulator(); acc.startBlock('thinking'); acc.addDelta('Thinking 1'); acc.startBlock('thinking'); acc.addDelta('Thinking 2'); acc.addToolCallDelta({ index: 0, id: 'call_123', type: 'function', function: { name: 'read_file', arguments: '{"path": "test.js"}' } }); acc.startBlock('thinking'); acc.addDelta('Thinking 3'); const hasLoop = acc.checkForLoop(); assert.strictEqual(hasLoop, false); assert.strictEqual(acc.loopDetected, false); }); it('does not detect loop with mixed block types', () => { const acc = new DeltaAccumulator(); acc.startBlock('thinking'); acc.addDelta('Thinking 1'); acc.startBlock('text'); acc.addDelta('Some text'); acc.startBlock('thinking'); acc.addDelta('Thinking 2'); acc.startBlock('thinking'); acc.addDelta('Thinking 3'); const hasLoop = acc.checkForLoop(); assert.strictEqual(hasLoop, false); }); it('respects custom threshold', () => { const acc = new DeltaAccumulator({}, { loopDetectionThreshold: 5 }); for (let i = 0; i < 4; i++) { acc.startBlock('thinking'); acc.addDelta(`Thinking ${i + 1}`); } let hasLoop = acc.checkForLoop(); assert.strictEqual(hasLoop, false); acc.startBlock('thinking'); acc.addDelta('Thinking 5'); hasLoop = acc.checkForLoop(); assert.strictEqual(hasLoop, true); }); it('resets loop detection state', () => { const acc = new DeltaAccumulator(); acc.startBlock('thinking'); acc.startBlock('thinking'); acc.startBlock('thinking'); acc.checkForLoop(); assert.strictEqual(acc.loopDetected, true); acc.resetLoopDetection(); assert.strictEqual(acc.loopDetected, false); const hasLoop = acc.checkForLoop(); assert.strictEqual(hasLoop, true); }); it('persists after first detection', () => { const acc = new DeltaAccumulator(); acc.startBlock('thinking'); acc.startBlock('thinking'); acc.startBlock('thinking'); acc.checkForLoop(); assert.strictEqual(acc.loopDetected, true); acc.startBlock('thinking'); acc.startBlock('thinking'); const hasLoop = acc.checkForLoop(); assert.strictEqual(hasLoop, true); }); }); describe('Tool call tracking', () => { it('tracks tool calls correctly', () => { const acc = new DeltaAccumulator(); acc.addToolCallDelta({ index: 0, id: 'call_1', type: 'function', function: { name: 'test', arguments: '{"a":' } }); acc.addToolCallDelta({ index: 0, function: { arguments: '1}' } }); const toolCalls = acc.getToolCalls(); assert.strictEqual(toolCalls.length, 1); assert.strictEqual(toolCalls[0].function.arguments, '{"a":1}'); const summary = acc.getSummary(); assert.strictEqual(summary.toolCallCount, 1); }); }); });