[Model] Support Ling-3.0-flash (BailingMoeV3) (#33561)
Signed-off-by: JustinTong <justintong0323@gmail.com> Signed-off-by: Xinyuan Tong <xinyuantong.cs@gmail.com> Co-authored-by: luoyuan.luo <luoyuan.luo@antgroup.com> Co-authored-by: 得泽 <zhangkaihong.zkh@antgroup.com> Co-authored-by: 翎悦 <vito.yy@antgroup.com> Co-authored-by: 羽癫 <yudian.zy@antgroup.com> Co-authored-by: tiwei.btw <tiwei.btw@antgroup.com> Co-authored-by: Liangsheng Yin <hnyls2002@gmail.com> Co-authored-by: 文赋 <zibin.zb@antgroup.com> Co-authored-by: JustinTong <justintong0323@gmail.com>
This commit is contained in:
co-authored by
luoyuan.luo
得泽
翎悦
羽癫
tiwei.btw
Liangsheng Yin
文赋
JustinTong
parent
8739d56a31
commit
20621aa14b
@@ -28,6 +28,7 @@ from sglang.srt.function_call.inkling_detector import InklingDetector
|
||||
from sglang.srt.function_call.json_array_parser import JsonArrayParser
|
||||
from sglang.srt.function_call.kimik2_detector import KimiK2Detector
|
||||
from sglang.srt.function_call.lfm2_detector import Lfm2Detector
|
||||
from sglang.srt.function_call.ling3_detector import Ling3Detector
|
||||
from sglang.srt.function_call.llama32_detector import Llama32Detector
|
||||
from sglang.srt.function_call.mistral_detector import MistralDetector
|
||||
from sglang.srt.function_call.pythonic_detector import PythonicDetector
|
||||
@@ -2944,6 +2945,84 @@ class TestGlm4MoeDetector(unittest.TestCase):
|
||||
)
|
||||
self.assertEqual(result.normal_text, "")
|
||||
|
||||
def test_streaming_tool_call(self):
|
||||
chunks = [
|
||||
"<tool_call>get_weather\n",
|
||||
"<arg_key>city</arg_key>\n<arg_value>Beijing</arg_value>\n",
|
||||
"<arg_key>date</arg_key>\n<arg_value>2024-06-27</arg_value>\n",
|
||||
"</tool_call>",
|
||||
]
|
||||
tool_calls = []
|
||||
for chunk in chunks:
|
||||
result = self.detector.parse_streaming_increment(chunk, self.tools)
|
||||
for tool_call_chunk in result.calls:
|
||||
if (
|
||||
hasattr(tool_call_chunk, "tool_index")
|
||||
and tool_call_chunk.tool_index is not None
|
||||
):
|
||||
while len(tool_calls) <= tool_call_chunk.tool_index:
|
||||
tool_calls.append({"name": "", "parameters": ""})
|
||||
tc = tool_calls[tool_call_chunk.tool_index]
|
||||
if tool_call_chunk.name:
|
||||
tc["name"] = tool_call_chunk.name
|
||||
if tool_call_chunk.parameters:
|
||||
tc["parameters"] += tool_call_chunk.parameters
|
||||
self.assertEqual(len(tool_calls), 1)
|
||||
self.assertEqual(tool_calls[0]["name"], "get_weather")
|
||||
self.assertEqual(
|
||||
tool_calls[0]["parameters"], '{"city": "Beijing", "date": "2024-06-27"}'
|
||||
)
|
||||
|
||||
def test_streaming_tool_call_without_arguments(self):
|
||||
chunks = [
|
||||
"<tool_call>get_weather\n",
|
||||
"</tool_call>",
|
||||
]
|
||||
tool_calls = []
|
||||
for chunk in chunks:
|
||||
result = self.detector.parse_streaming_increment(chunk, self.tools)
|
||||
for tool_call_chunk in result.calls:
|
||||
if (
|
||||
hasattr(tool_call_chunk, "tool_index")
|
||||
and tool_call_chunk.tool_index is not None
|
||||
):
|
||||
while len(tool_calls) <= tool_call_chunk.tool_index:
|
||||
tool_calls.append({"name": "", "parameters": ""})
|
||||
tc = tool_calls[tool_call_chunk.tool_index]
|
||||
if tool_call_chunk.name:
|
||||
tc["name"] = tool_call_chunk.name
|
||||
if tool_call_chunk.parameters:
|
||||
tc["parameters"] += tool_call_chunk.parameters
|
||||
|
||||
self.assertEqual(len(tool_calls), 1)
|
||||
self.assertEqual(tool_calls[0]["name"], "get_weather")
|
||||
self.assertEqual(tool_calls[0]["parameters"], "{}")
|
||||
self.assertEqual(self.detector.streamed_args_for_tool[0], "{}")
|
||||
|
||||
def test_streaming_tool_call_without_arguments_single_chunk(self):
|
||||
"""Test no-argument tool call when name and end token arrive together."""
|
||||
chunks = ["<tool_call>get_weather\n</tool_call>"]
|
||||
tool_calls = []
|
||||
for chunk in chunks:
|
||||
result = self.detector.parse_streaming_increment(chunk, self.tools)
|
||||
for tool_call_chunk in result.calls:
|
||||
if (
|
||||
hasattr(tool_call_chunk, "tool_index")
|
||||
and tool_call_chunk.tool_index is not None
|
||||
):
|
||||
while len(tool_calls) <= tool_call_chunk.tool_index:
|
||||
tool_calls.append({"name": "", "parameters": ""})
|
||||
tc = tool_calls[tool_call_chunk.tool_index]
|
||||
if tool_call_chunk.name:
|
||||
tc["name"] = tool_call_chunk.name
|
||||
if tool_call_chunk.parameters:
|
||||
tc["parameters"] += tool_call_chunk.parameters
|
||||
|
||||
self.assertEqual(len(tool_calls), 1)
|
||||
self.assertEqual(tool_calls[0]["name"], "get_weather")
|
||||
self.assertEqual(tool_calls[0]["parameters"], "{}")
|
||||
self.assertEqual(self.detector.streamed_args_for_tool[0], "{}")
|
||||
|
||||
def test_streaming_multiple_tool_calls(self):
|
||||
"""Test streaming incremental parsing of multiple tool calls."""
|
||||
chunks = [
|
||||
@@ -3602,6 +3681,119 @@ class TestGlm47MoeDetector(unittest.TestCase):
|
||||
_glm47_native_structural_tag_available.cache_clear()
|
||||
|
||||
|
||||
class TestLing3Detector(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.tools = [
|
||||
Tool(
|
||||
type="function",
|
||||
function=Function(
|
||||
name="get_weather",
|
||||
description="Get weather information",
|
||||
parameters={
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"city": {"type": "string"},
|
||||
"date": {"type": "string"},
|
||||
},
|
||||
},
|
||||
),
|
||||
),
|
||||
Tool(
|
||||
type="function",
|
||||
function=Function(
|
||||
name="get_date",
|
||||
description="Get current date",
|
||||
parameters={"type": "object", "properties": {}},
|
||||
),
|
||||
),
|
||||
]
|
||||
self.detector = Ling3Detector()
|
||||
|
||||
def _collect_streaming_tool_calls(self, chunks):
|
||||
tool_calls = []
|
||||
for chunk in chunks:
|
||||
result = self.detector.parse_streaming_increment(chunk, self.tools)
|
||||
for tool_call_chunk in result.calls:
|
||||
while len(tool_calls) <= tool_call_chunk.tool_index:
|
||||
tool_calls.append({"name": "", "parameters": ""})
|
||||
tc = tool_calls[tool_call_chunk.tool_index]
|
||||
if tool_call_chunk.name:
|
||||
tc["name"] = tool_call_chunk.name
|
||||
if tool_call_chunk.parameters:
|
||||
tc["parameters"] += tool_call_chunk.parameters
|
||||
return tool_calls
|
||||
|
||||
def test_detect_and_parse_newline_and_compact_tool_call(self):
|
||||
cases = {
|
||||
"newline": (
|
||||
"<tool_call>get_weather\n"
|
||||
"<arg_key>city</arg_key><arg_value>Beijing</arg_value>"
|
||||
"<arg_key>date</arg_key><arg_value>2024-06-27</arg_value>"
|
||||
"</tool_call>",
|
||||
'{"city": "Beijing", "date": "2024-06-27"}',
|
||||
),
|
||||
"compact": (
|
||||
"<tool_call>get_weather"
|
||||
"<arg_key>city</arg_key><arg_value>Shanghai</arg_value>"
|
||||
"<arg_key>date</arg_key><arg_value>2024-06-28</arg_value>"
|
||||
"</tool_call>",
|
||||
'{"city": "Shanghai", "date": "2024-06-28"}',
|
||||
),
|
||||
}
|
||||
for layout, (text, expected) in cases.items():
|
||||
with self.subTest(layout=layout):
|
||||
result = self.detector.detect_and_parse(text, self.tools)
|
||||
self.assertEqual(len(result.calls), 1)
|
||||
self.assertEqual(result.calls[0].name, "get_weather")
|
||||
self.assertEqual(result.calls[0].parameters, expected)
|
||||
|
||||
def test_detect_and_parse_empty_args(self):
|
||||
result = self.detector.detect_and_parse(
|
||||
"<tool_call>get_date</tool_call>", self.tools
|
||||
)
|
||||
self.assertEqual(len(result.calls), 1)
|
||||
self.assertEqual(result.calls[0].name, "get_date")
|
||||
self.assertEqual(json.loads(result.calls[0].parameters), {})
|
||||
|
||||
def test_streaming_empty_args_emits_single_empty_object(self):
|
||||
tool_calls = self._collect_streaming_tool_calls(
|
||||
["<tool_call>get_date", "</tool_call>"]
|
||||
)
|
||||
self.assertEqual(len(tool_calls), 1)
|
||||
self.assertEqual(tool_calls[0]["name"], "get_date")
|
||||
self.assertEqual(tool_calls[0]["parameters"], "{}")
|
||||
self.assertEqual(self.detector.streamed_args_for_tool[0], "{}")
|
||||
|
||||
def test_streaming_newline_and_compact_tool_call(self):
|
||||
cases = {
|
||||
"newline": (
|
||||
[
|
||||
"<tool_call>get_weather\n",
|
||||
"<arg_key>city</arg_key><arg_value>Beijing</arg_value>",
|
||||
"<arg_key>date</arg_key><arg_value>2024-06-27</arg_value>",
|
||||
"</tool_call>",
|
||||
],
|
||||
'{"city": "Beijing", "date": "2024-06-27"}',
|
||||
),
|
||||
"compact": (
|
||||
[
|
||||
"<tool_call>get_weather",
|
||||
"<arg_key>city</arg_key><arg_value>Shanghai</arg_value>",
|
||||
"<arg_key>date</arg_key><arg_value>2024-06-28</arg_value>",
|
||||
"</tool_call>",
|
||||
],
|
||||
'{"city": "Shanghai", "date": "2024-06-28"}',
|
||||
),
|
||||
}
|
||||
for layout, (chunks, expected) in cases.items():
|
||||
with self.subTest(layout=layout):
|
||||
self.setUp()
|
||||
tool_calls = self._collect_streaming_tool_calls(chunks)
|
||||
self.assertEqual(len(tool_calls), 1)
|
||||
self.assertEqual(tool_calls[0]["name"], "get_weather")
|
||||
self.assertEqual(tool_calls[0]["parameters"], expected)
|
||||
|
||||
|
||||
class TestJsonArrayParser(unittest.TestCase):
|
||||
def setUp(self):
|
||||
# Create sample tools for testing
|
||||
|
||||
Reference in New Issue
Block a user