import extractReasoning from './extractReasoning'; describe('extractReasoning', () => { test.each([ [ 'Gemma channel markers', '<|channel>thought\nI should greet them.Hello there!', 'Hello there!', 'thought\nI should greet them.', ], [ 'think tags', 'Let me work this out.The answer is 42.', 'The answer is 42.', 'Let me work this out.', ], [ 'thinking tags', 'Considering options.Done.', 'Done.', 'Considering options.', ], [ 'no markers', 'Just a plain reply.', 'Just a plain reply.', '', ], [ 'empty content', '', '', '', ], [ 'leading whitespace before the marker', '\n Deliberating.The answer.', 'The answer.', 'Deliberating.', ], [ 'a marker-like tag that is part of the reply', 'The `` tag marks internal reasoning, like this.', 'The `` tag marks internal reasoning, like this.', '', ], [ 'a reply whose code block shows the tag', 'They want an example.Here:\n\n```\nhello\n```', 'Here:\n\n```\nhello\n```', 'They want an example.', ], ])('should split %s', (_name, content, expectedText, expectedReasoning) => { const result = extractReasoning(content); expect(result.text).toBe(expectedText); expect(result.reasoning).toBe(expectedReasoning); }); it('should treat an unterminated marker as reasoning rather than leaking it as the reply', () => { const result = extractReasoning('<|channel>thought\nI was cut off mid-sent'); expect(result.text).toBe(''); expect(result.reasoning).toBe('thought\nI was cut off mid-sent'); }); it('should preserve a reply that opens with a fenced block showing the tag', () => { const content = '```\nhello\n```\n\nThat tag marks internal reasoning.'; const result = extractReasoning(content); expect(result.text).toBe(content.trim()); expect(result.reasoning).toBe(''); }); });