diff --git a/tests/parser/test_chat_parsing.py b/tests/parser/test_chat_parsing.py index 679d97f15b..e3b8dba172 100644 --- a/tests/parser/test_chat_parsing.py +++ b/tests/parser/test_chat_parsing.py @@ -1555,6 +1555,14 @@ class ToolArgCoercionTest(unittest.TestCase): def test_coerce_falls_back_to_raw_on_failure(self): self.assertEqual(_coerce("not-a-number", ("integer",)), "not-a-number") + def test_coerce_integer_accepts_whole_number_floats(self): + self.assertEqual(_coerce("3.0", ("integer",)), 3) + self.assertIsInstance(_coerce("3.0", ("integer",)), int) + self.assertEqual(_coerce("3.5", ("integer",)), "3.5") + self.assertEqual(_coerce("3.5", ("integer", "number")), 3.5) + self.assertEqual(_coerce("inf", ("integer",)), "inf") + self.assertEqual(_coerce("NaN", ("integer",)), "NaN") + def test_coerce_handles_any_of_and_null(self): self.assertEqual(_coerce("5", ("integer", "null")), 5) self.assertIsNone(_coerce("null", ("integer", "null"))) diff --git a/vllm/parser/chat_parsing/response_parser.py b/vllm/parser/chat_parsing/response_parser.py index 556df803a7..0416fe0fe3 100644 --- a/vllm/parser/chat_parsing/response_parser.py +++ b/vllm/parser/chat_parsing/response_parser.py @@ -50,7 +50,14 @@ def _coerce(raw: str, types: tuple[str, ...]) -> Any: for type_name in types: try: if type_name == "integer": - return int(raw) + try: + return int(raw) + except ValueError: + # JSON Schema integers include whole numbers written as floats, e.g. "3.0" + number = float(raw) + if not number.is_integer(): + continue + return int(number) if type_name == "number": number = float(raw) if number != number or number in (float("inf"), float("-inf")):