import { describe, expect, it } from 'vitest'; import { parseThoughtStream } from './think-parser'; import { parseToolCalls, renderToolResponse } from './tool-protocol'; describe('parseThoughtStream', () => { it('separates complete reasoning from visible content', () => { expect(parseThoughtStream('check evidencefinal answer')).toEqual({ content: 'final answer', reasoning: 'check evidence', isThinking: false, hasReasoning: true, }); }); it('handles a closing-only marker emitted after a template-opened thought', () => { expect(parseThoughtStream('private chain\n\npublic answer').content).toBe('public answer'); }); it('buffers a partial opening marker', () => { expect(parseThoughtStream('answer { it('extracts multiple strict tool envelopes', () => { const result = parseToolCalls( 'note{"name":"memory","arguments":{"key":"x"}}' + '{"name":"js_eval","arguments":{"source":"return 2"}}', ); expect(result.content).toBe('note'); expect(result.calls.map((call) => call.name)).toEqual(['memory', 'js_eval']); expect(result.errors).toEqual([]); }); it('reports incomplete streams without executing them', () => { expect(parseToolCalls('{"name":"memory"').incomplete).toBe(true); }); it('parses the Qwen3.5 function and parameter format from the 27B chat template', () => { const result = parseToolCalls(`before

Tool proof

Tool proof
`); expect(result.content).toBe('before'); expect(result.errors).toEqual([]); expect(result.calls).toEqual([expect.objectContaining({ name: 'html_artifact', arguments: { html: '

Tool proof

', title: 'Tool proof', }, })]); }); it('rejects malformed or duplicate Qwen3.5 parameters', () => { const malformed = parseToolCalls( 'getlist', ); expect(malformed.calls).toEqual([]); expect(malformed.errors).toEqual(['function tool call repeats parameter action']); }); it('wraps deterministic tool responses', () => { expect(renderToolResponse({ ok: true })).toBe('\n{"ok":true}\n'); }); });