diff --git a/python/sglang/srt/function_call/multi_format_detector.py b/python/sglang/srt/function_call/multi_format_detector.py index 14a540a4706e..fae320a38c2c 100644 --- a/python/sglang/srt/function_call/multi_format_detector.py +++ b/python/sglang/srt/function_call/multi_format_detector.py @@ -1046,7 +1046,10 @@ def _deserialize_glm_value(value: str) -> Any: except Exception: pass try: - return ast.literal_eval(value) + result = ast.literal_eval(value) + # Keep literal_eval results only when they remain JSON-serializable. + json.dumps(result) + return result except Exception: pass return value diff --git a/test/registered/function_call/test_multi_format_detector.py b/test/registered/function_call/test_multi_format_detector.py index a80f49def64f..d94b1cfda03f 100644 --- a/test/registered/function_call/test_multi_format_detector.py +++ b/test/registered/function_call/test_multi_format_detector.py @@ -276,6 +276,18 @@ def test_python_tuple_literal_value(self): # ast.literal handles tuple syntax; serialized as a JSON array. self.assertEqual(args["days"], [1, 2]) + def test_python_set_literal_value_stays_string(self): + text = ( + "get_weather" + "days{1, 2}" + "" + ) + result = self.det.detect_and_parse(text, self.tools) + args = json.loads(result.calls[0].parameters) + # ast.literal_eval yields a set, which JSON cannot represent; the raw + # string is kept so the arguments dict stays serializable. + self.assertEqual(args["days"], "{1, 2}") + class TestGptOssDialect(unittest.TestCase): def setUp(self):