@@ -2933,10 +2933,62 @@ def test_postprocess_messages_null_arguments_string():
29332933 assert tool_calls [0 ]["function" ]["arguments" ] == {}
29342934
29352935
2936+ @pytest .mark .asyncio
2937+ async def test_resolve_items_runs_modalities_concurrently_and_preserves_order ():
2938+ """Media fetches overlap while modality and item order are preserved."""
2939+
2940+ active_fetches = 0
2941+ max_active_fetches = 0
2942+
2943+ async def _fetch (name : str , delay : float ):
2944+ nonlocal active_fetches , max_active_fetches
2945+ active_fetches += 1
2946+ max_active_fetches = max (max_active_fetches , active_fetches )
2947+ try :
2948+ await asyncio .sleep (delay )
2949+ return name , f"{ name } -uuid"
2950+ finally :
2951+ active_fetches -= 1
2952+
2953+ tracker = AsyncMultiModalItemTracker (MagicMock ())
2954+ tracker ._model_config .is_multimodal_model = True
2955+ tracker .__dict__ ["mm_processor" ] = MagicMock ()
2956+ tracker ._items_by_modality ["video" ] = [
2957+ lambda : _fetch ("video-0" , 0.02 ),
2958+ lambda : _fetch ("video-1" , 0.01 ),
2959+ ]
2960+ tracker ._items_by_modality ["image" ] = [
2961+ lambda : _fetch ("image-0" , 0.02 ),
2962+ lambda : _fetch ("image-1" , 0.01 ),
2963+ ]
2964+ tracker ._items_by_modality ["audio" ] = [
2965+ lambda : _fetch ("audio-0" , 0.02 ),
2966+ lambda : _fetch ("audio-1" , 0.01 ),
2967+ ]
2968+
2969+ mm_data , mm_uuids = await tracker .resolve_items ()
2970+
2971+ assert mm_data is not None
2972+ assert mm_uuids is not None
2973+ assert max_active_fetches == 6
2974+ assert list (mm_data ) == ["image" , "audio" , "video" ]
2975+ assert list (mm_uuids ) == ["image" , "audio" , "video" ]
2976+ assert mm_data == {
2977+ "image" : ["image-0" , "image-1" ],
2978+ "audio" : ["audio-0" , "audio-1" ],
2979+ "video" : ["video-0" , "video-1" ],
2980+ }
2981+ assert mm_uuids == {
2982+ "image" : ["image-0-uuid" , "image-1-uuid" ],
2983+ "audio" : ["audio-0-uuid" , "audio-1-uuid" ],
2984+ "video" : ["video-0-uuid" , "video-1-uuid" ],
2985+ }
2986+
2987+
29362988@pytest .mark .asyncio
29372989async def test_resolve_items_does_not_leak_tasks_on_partial_failure ():
29382990 """Regression test: one failing media fetch must not abandon the other
2939- still-in-flight fetches in the same modality batch .
2991+ still-in-flight fetches across modality batches .
29402992
29412993 Before the fix, `resolve_items` gathered per-modality fetches with plain
29422994 `asyncio.gather`, so the first exception propagated immediately while
@@ -2957,6 +3009,8 @@ async def _fetch(should_fail: bool, delay: float):
29573009 lambda : _fetch (False , 0.2 ),
29583010 lambda : _fetch (False , 0.2 ),
29593011 ]
3012+ tracker ._items_by_modality ["audio" ] = [lambda : _fetch (False , 0.2 )]
3013+ tracker ._items_by_modality ["video" ] = [lambda : _fetch (False , 0.2 )]
29603014
29613015 tasks_before = asyncio .all_tasks ()
29623016 with pytest .raises (ValueError , match = "simulated fetch failure" ):
0 commit comments