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');
});
});