Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion tensorrt_llm/serve/tool_parser/deepseekv32_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,10 @@ def _parse_parameters_from_xml(self, invoke_content: str) -> dict:
for param_name, param_type, param_value in param_matches:
# Convert value based on type
if param_type == "true": # string type
parameters[param_name] = param_value.strip()
# Taken as written: the encoder renders a string value verbatim
# between the tags, so its whitespace (a file's last newline, a
# line's indentation) is part of the value.
parameters[param_name] = param_value
else:
# Try to parse as JSON for other types
try:
Expand Down
47 changes: 47 additions & 0 deletions tests/unittest/llmapi/apps/test_tool_parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3160,6 +3160,53 @@ def test_deepseek_streaming_keeps_markdown_fences(
assert parser_cls().detect_and_parse(text, sample_tools).normal_text == text


def _dsml_tool_call(tag_prefix: str, block_name: str, name: str,
string_arguments: dict[str, str]) -> str:
"""A tool call as the DSML encoders render it: each string value verbatim."""
parameters = "\n".join(
f'<{tag_prefix}parameter name="{key}" string="true">{value}'
f"</{tag_prefix}parameter>" for key, value in string_arguments.items())
return (f"<{tag_prefix}{block_name}>\n"
f'<{tag_prefix}invoke name="{name}">\n{parameters}\n'
f"</{tag_prefix}invoke>\n</{tag_prefix}{block_name}>")


@pytest.mark.parametrize("parser_cls,tag_prefix,block_name", [
(DeepSeekV32Parser, "|DSML|", "function_calls"),
(DeepSeekV4Parser, "|DSML|", "tool_calls"),
(DeepSeekV41Parser, "|DSML| ", "calls"),
])
@pytest.mark.parametrize("streaming", [False, True])
def test_dsml_string_parameter_values_are_kept_as_written(
sample_tools: list[ChatCompletionToolsParam],
parser_cls: type[BaseToolParser], tag_prefix: str, block_name: str,
streaming: bool) -> None:
"""A string value's leading and trailing whitespace is part of the value.

An edit's ``old_string`` must match the file including its indentation,
and a written file keeps its last newline.
"""
arguments = {
"location": " indented line\n second line",
"unit": "file content\n",
}
text = _dsml_tool_call(tag_prefix, block_name, "get_weather", arguments)

parser = parser_cls()
if streaming:
results = [
parser.parse_streaming_increment(text[i:i + 7], sample_tools)
for i in range(0, len(text), 7)
]
calls = [call for result in results for call in result.calls]
else:
calls = parser.detect_and_parse(text, sample_tools).calls

assert [call.name for call in calls if call.name] == ["get_weather"]
assert json.loads("".join(call.parameters or ""
for call in calls)) == arguments


@pytest.mark.parametrize(
"parser_cls, tool_call_text",
[
Expand Down
Loading