diff --git a/tests/tool_parsers/test_gemma4_tool_parser.py b/tests/tool_parsers/test_gemma4_tool_parser.py index 54f1e7eed8..16450c6a8d 100644 --- a/tests/tool_parsers/test_gemma4_tool_parser.py +++ b/tests/tool_parsers/test_gemma4_tool_parser.py @@ -85,6 +85,14 @@ class TestParseGemma4Args: result = _parse_gemma4_args("flag:false") assert result == {"flag": False} + def test_null_value(self): + # Bare `null` must parse as None (Python), not the string "null". + # Without this, tool_choice=auto would emit `{"param": "null"}` + # instead of `{"param": null}` for nullable tool parameters. + result = _parse_gemma4_args("param:null") + assert result == {"param": None} + assert json.dumps(result) == '{"param": null}' + def test_mixed_types(self): result = _parse_gemma4_args( 'name:<|"|>test<|"|>,count:42,active:true,score:3.14' diff --git a/vllm/tool_parsers/gemma4_tool_parser.py b/vllm/tool_parsers/gemma4_tool_parser.py index b96fb919c8..ac48ef26cc 100644 --- a/vllm/tool_parsers/gemma4_tool_parser.py +++ b/vllm/tool_parsers/gemma4_tool_parser.py @@ -66,6 +66,10 @@ def _parse_gemma4_value(value_str: str) -> object: if value_str == "false": return False + # Null + if value_str.lower() in ("null", "none", "nil"): + return None + # Number (int or float) try: if "." in value_str: