77"""Unit tests for Generator's _to_completions and _extract_tool_calls logic."""
88
99import json
10+ from dataclasses import dataclass
11+ from typing import Any
1012from unittest .mock import MagicMock
1113
1214import pytest
@@ -24,18 +26,19 @@ def _import_error():
2426 return True
2527
2628
29+ @dataclass
2730class TokenizerWrapper :
2831 """Wrapper to mimic vLLM's tokenizer structure (has .tokenizer attr)."""
2932
30- def __init__ ( self , tokenizer ):
31- self . tokenizer = tokenizer
33+ tokenizer : Any
34+
3235
3336class _StubTokenizer :
3437 """Minimal stub tokenizer for initializing the Hermes tool parser in tests.
35-
38+
3639 The Hermes tool parser from vLLM requires:
3740 - get_vocab(): Returns vocab dict mapping tokens to ids
38- - vocab: Direct vocab attribute
41+ - vocab: Direct vocab attribute
3942 - eos_token_id: End of sequence token id
4043 - <tool_call> and </tool_call> tokens in vocab (for streaming support)
4144 """
@@ -83,7 +86,9 @@ def make_mock_request_output(
8386) -> RequestOutput :
8487 """Create a mock vLLM RequestOutput for testing _to_completions."""
8588 if outputs is None :
86- outputs = [{"text" : "test response" , "token_ids" : [1 , 2 , 3 ], "finish_reason" : "stop" }]
89+ outputs = [
90+ {"text" : "test response" , "token_ids" : [1 , 2 , 3 ], "finish_reason" : "stop" }
91+ ]
8792
8893 mock_outputs = []
8994 for out in outputs :
@@ -162,9 +167,9 @@ def test_extract_single_tool_call(self, generator_with_hermes):
162167 """Test extracting a single tool call."""
163168 generator = generator_with_hermes
164169
165- model_output = ''' <tool_call>
170+ model_output = """ <tool_call>
166171{"name": "calculator", "arguments": {"equation": "2 + 2"}}
167- </tool_call>'''
172+ </tool_call>"""
168173
169174 result = generator ._extract_tool_calls (model_output )
170175
@@ -179,10 +184,10 @@ def test_extract_tool_call_with_content_prefix(self, generator_with_hermes):
179184 """Test extracting tool call when there's content before it."""
180185 generator = generator_with_hermes
181186
182- model_output = ''' Let me calculate that for you.
187+ model_output = """ Let me calculate that for you.
183188<tool_call>
184189{"name": "calculator", "arguments": {"equation": "15 * 7"}}
185- </tool_call>'''
190+ </tool_call>"""
186191
187192 result = generator ._extract_tool_calls (model_output )
188193
@@ -195,13 +200,13 @@ def test_extract_tool_call_with_think_prefix(self, generator_with_hermes):
195200 """Test extracting tool call when there's <think> tags before it."""
196201 generator = generator_with_hermes
197202
198- model_output = ''' <think>
203+ model_output = """ <think>
199204The user is asking for a math calculation. I should use the calculator tool.
200205Let me compute 2 + 2.
201206</think>
202207<tool_call>
203208{"name": "calculator", "arguments": {"equation": "2 + 2"}}
204- </tool_call>'''
209+ </tool_call>"""
205210
206211 result = generator ._extract_tool_calls (model_output )
207212
@@ -213,27 +218,28 @@ def test_extract_tool_call_with_think_prefix(self, generator_with_hermes):
213218 assert """<think>
214219The user is asking for a math calculation. I should use the calculator tool.
215220Let me compute 2 + 2.
216- </think>""" in (result .content )
221+ </think>""" in (
222+ result .content
223+ )
217224
218225 def test_extract_multiple_tool_calls (self , generator_with_hermes ):
219226 """Test extracting multiple tool calls."""
220227 generator = generator_with_hermes
221228
222- model_output = ''' <tool_call>
229+ model_output = """ <tool_call>
223230{"name": "calculator", "arguments": {"equation": "2 + 2"}}
224231</tool_call>
225232<tool_call>
226233{"name": "calculator", "arguments": {"equation": "3 * 4"}}
227- </tool_call>'''
234+ </tool_call>"""
228235
229236 result = generator ._extract_tool_calls (model_output )
230237
231238 assert result .tools_called is True
232239 assert len (result .tool_calls ) == 2
233240
234241 equations = [
235- json .loads (tc .function .arguments )["equation" ]
236- for tc in result .tool_calls
242+ json .loads (tc .function .arguments )["equation" ] for tc in result .tool_calls
237243 ]
238244 assert "2 + 2" in equations
239245 assert "3 * 4" in equations
@@ -308,7 +314,9 @@ def test_to_completions_no_tool_call_with_parser(self, generator_with_hermes):
308314
309315 request_output = make_mock_request_output (
310316 prompt = "What is the capital of France?" ,
311- outputs = [{"text" : "Paris is the capital of France." , "token_ids" : [10 , 20 ]}],
317+ outputs = [
318+ {"text" : "Paris is the capital of France." , "token_ids" : [10 , 20 ]}
319+ ],
312320 )
313321
314322 completions = generator ._to_completions (request_output )
@@ -327,9 +335,12 @@ def test_to_completions_multiple_outputs(self, generator_with_hermes):
327335 request_output = make_mock_request_output (
328336 prompt = "Calculate something" ,
329337 outputs = [
330- {"text" : '''<tool_call>
338+ {
339+ "text" : """<tool_call>
331340{"name": "calculator", "arguments": {"equation": "1 + 1"}}
332- </tool_call>''' , "token_ids" : [1 , 2 ]},
341+ </tool_call>""" ,
342+ "token_ids" : [1 , 2 ],
343+ },
333344 {"text" : "The answer is obviously 2." , "token_ids" : [3 , 4 ]},
334345 ],
335346 )
@@ -345,4 +356,3 @@ def test_to_completions_multiple_outputs(self, generator_with_hermes):
345356 # Second completion has no tool call
346357 assert not completions [1 ].has_tool_calls
347358 assert completions [1 ].content == "The answer is obviously 2."
348-
0 commit comments