@@ -347,3 +347,101 @@ def test_cross_path_bare_end_tag():
347347 assert len (parts ) == 1
348348 assert isinstance (parts [0 ], ThinkingPart )
349349 assert parts [0 ].content == 'donex'
350+
351+
352+ def test_invalid_partial_tag_prefix ():
353+ """Test content starting with '<' but not matching tag prefix (branch 109->113)."""
354+ events , parts = stream_text_deltas (['<xyz' ])
355+
356+ assert len (parts ) == 1
357+ assert isinstance (parts [0 ], TextPart )
358+ assert parts [0 ].content == '<xyz'
359+ assert len (events ) == 1
360+ assert isinstance (events [0 ], PartStartEvent )
361+
362+
363+ def test_bare_start_tag_simple_path ():
364+ """Test isolated start tag through simple path (branch 319->321)."""
365+ manager = ModelResponsePartsManager ()
366+ thinking_tags = ('<think>' , '</think>' )
367+
368+ events = list (manager .handle_text_delta (vendor_part_id = None , content = '<think>' , thinking_tags = thinking_tags ))
369+
370+ assert len (events ) == 0
371+
372+ final_events = list (manager .finalize ())
373+ assert len (final_events ) == 1
374+ assert isinstance (final_events [0 ], PartStartEvent )
375+ assert isinstance (final_events [0 ].part , TextPart )
376+ assert final_events [0 ].part .content == '<think>'
377+
378+
379+ def test_complete_thinking_block_with_trailing_text_single_chunk ():
380+ """Test complete thinking block and text in one chunk (branch 411->386)."""
381+ events , parts = stream_text_deltas (['<think>reasoning</think>final text' ])
382+
383+ assert len (parts ) == 2
384+ assert isinstance (parts [0 ], ThinkingPart )
385+ assert parts [0 ].content == 'reasoning'
386+ assert isinstance (parts [1 ], TextPart )
387+ assert parts [1 ].content == 'final text'
388+ assert len (events ) == 2
389+
390+
391+ def test_thinking_delta_after_tool_call ():
392+ """Test creating ThinkingPart when latest part is a ToolCallPart (branch 515->528)."""
393+ manager = ModelResponsePartsManager ()
394+
395+ manager .handle_tool_call_part (
396+ vendor_part_id = 'tool1' , tool_name = 'test_tool' , args = {'key' : 'value' }, tool_call_id = 'call_123'
397+ )
398+
399+ events = list (manager .handle_thinking_delta (vendor_part_id = None , content = 'some thinking' ))
400+
401+ assert len (events ) == 1
402+ assert isinstance (events [0 ], PartStartEvent )
403+ assert isinstance (events [0 ].part , ThinkingPart )
404+
405+ parts = manager .get_parts ()
406+ assert len (parts ) == 2
407+ assert isinstance (parts [1 ], ThinkingPart )
408+ assert parts [1 ].content == 'some thinking'
409+
410+
411+ def test_text_part_update_via_handle_part_then_emit ():
412+ """Test updating a TextPart created via handle_part (lines 471-472)."""
413+ manager = ModelResponsePartsManager ()
414+
415+ manager .handle_part (vendor_part_id = 'text1' , part = TextPart (content = 'initial' ))
416+
417+ events = list (manager .handle_text_delta (vendor_part_id = 'text1' , content = ' more' , thinking_tags = None ))
418+
419+ assert len (events ) == 1
420+ assert isinstance (events [0 ], PartStartEvent )
421+ assert isinstance (events [0 ].part , TextPart )
422+ assert events [0 ].part .content == 'initial more'
423+
424+ parts = manager .get_parts ()
425+ assert len (parts ) == 1
426+ assert isinstance (parts [0 ], TextPart )
427+ assert parts [0 ].content == 'initial more'
428+
429+
430+ def test_bare_end_tag_chunk ():
431+ """Test chunk containing only the closing tag (branch 411->386)."""
432+ events , parts = stream_text_deltas (['<think>' , 'content' , '</think>' ])
433+
434+ assert len (parts ) == 1
435+ assert isinstance (parts [0 ], ThinkingPart )
436+ assert parts [0 ].content == 'content'
437+ assert len (events ) == 1
438+
439+
440+ def test_stream_without_finalize ():
441+ """Test streaming without finalization (branch 49->53)."""
442+ events , parts = stream_text_deltas (['<thi' ], finalize = False )
443+
444+ # Incomplete tag should be buffered, not emitted
445+ assert len (events ) == 0
446+ # No parts yet since not finalized
447+ assert len (parts ) == 0
0 commit comments