@@ -1019,3 +1019,26 @@ def test_thought_signature_round_trip():
10191019 tool_call = LiteLLMModel .format_request_message_tool_call (internal_tool_use )
10201020 assert "__thought__" in tool_call ["id" ]
10211021 assert signature in tool_call ["id" ]
1022+
1023+
1024+ @pytest .mark .asyncio
1025+ async def test_stream_generates_tool_call_id_when_null (litellm_acompletion , model , agenerator , alist ):
1026+ mock_tool_call = unittest .mock .Mock (index = 0 )
1027+ mock_tool_call .id = None
1028+ mock_tool_call .function .name = "test_tool"
1029+ mock_tool_call .function .arguments = '{"arg": "value"}'
1030+
1031+ mock_delta_1 = unittest .mock .Mock (content = None , tool_calls = [mock_tool_call ], reasoning_content = None )
1032+ mock_delta_2 = unittest .mock .Mock (content = None , tool_calls = None , reasoning_content = None )
1033+
1034+ mock_event_1 = unittest .mock .Mock (choices = [unittest .mock .Mock (finish_reason = None , delta = mock_delta_1 )])
1035+ mock_event_2 = unittest .mock .Mock (choices = [unittest .mock .Mock (finish_reason = "tool_calls" , delta = mock_delta_2 )])
1036+
1037+ litellm_acompletion .side_effect = unittest .mock .AsyncMock (return_value = agenerator ([mock_event_1 , mock_event_2 ]))
1038+
1039+ response = model .stream ([{"role" : "user" , "content" : [{"text" : "test" }]}])
1040+ events = await alist (response )
1041+
1042+ start = next (e for e in events if "contentBlockStart" in e and "toolUse" in e ["contentBlockStart" ]["start" ])
1043+ tool_id = start ["contentBlockStart" ]["start" ]["toolUse" ]["toolUseId" ]
1044+ assert tool_id and tool_id .startswith ("call_" )
0 commit comments