import json import pytest from src.email_reply_stream import reply_body, stream_reply def test_analysis_is_not_a_reply(): assert reply_body('The user wants me to draft a reply. Context Analysis: ...', complete=True) == '' assert reply_body('analysis', complete=True) == '' assert reply_body('<<>>Hi Jonathan', complete=True) == '' assert reply_body('<<>>analysisHi<<>>', complete=True) == '' assert reply_body('analysis\n<<>>Hi Jonathan,\nThanks.<<>>', complete=True) == 'Hi Jonathan,\nThanks.' @pytest.mark.asyncio async def test_stream_filters_reasoning_and_split_markers(monkeypatch): from src import llm_core calls, events = [], [] async def model(*args, **kwargs): calls.append(kwargs) for delta in ['Private analysis.', '<<>>Hi Jonathan,', '\nThanks.', '<<>>']: yield 'data: ' + json.dumps({'delta': delta}) + '\n\n' async def emit(event): events.append(event) monkeypatch.setattr(llm_core, 'stream_llm', model) raw, used = await stream_reply([('http://local', 'Ajax', {})], [], emit) assert used == 'Ajax' assert calls[0]['thinking_mode'] == 'off' assert events[-1]['text'] == 'Hi Jonathan,\nThanks.' assert all('analysis' not in e['text'] and '<<<' not in e['text'] for e in events) @pytest.mark.asyncio async def test_incomplete_stream_clears_partial_draft(monkeypatch): from src import llm_core events = [] async def model(*args, **kwargs): yield 'data: ' + json.dumps({'delta': '<<>>Partial'}) + '\n\n' async def emit(event): events.append(event) monkeypatch.setattr(llm_core, 'stream_llm', model) with pytest.raises(ValueError, match='incomplete'): await stream_reply([('http://local', 'Ajax', {})], [], emit) assert events[-1]['text'] == ''