Skip to content
Draft
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
10 changes: 5 additions & 5 deletions renderers/parsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,7 @@ def _parse_xml_tool_calls(
r"<parameter=([^>]+)>\n?(.*?)\n?</parameter>", block_text, re.DOTALL
):
arg_name = pm.group(1)
arg_value = pm.group(2).strip()
arg_value = pm.group(2)
value, used_fallback = _coerce_arg_value(
arg_value, params.get(arg_name)
)
Expand Down Expand Up @@ -597,7 +597,7 @@ def _parse_glm_tool_call(
if ave == -1:
structure_broke = True
break
val_text = _decode(tokenizer, block[av + 1 : ave]).strip()
val_text = _decode(tokenizer, block[av + 1 : ave])
value, used_fallback = _coerce_arg_value(val_text, params.get(key))
arguments[key] = value
any_json_fallback = any_json_fallback or used_fallback
Expand Down Expand Up @@ -799,7 +799,7 @@ def _parse_hy3_tool_calls(
if ave == -1:
structure_broke = True
break
val_text = _decode(tokenizer, arg_ids[av + 1 : ave]).strip()
val_text = _decode(tokenizer, arg_ids[av + 1 : ave])
value, used_fallback = _coerce_arg_value(val_text, params.get(key))
arguments[key] = value
any_json_fallback = any_json_fallback or used_fallback
Expand Down Expand Up @@ -978,7 +978,7 @@ def _parse_laguna_xs2_tool_calls(
re.DOTALL,
):
k = m.group(1).strip()
v = m.group(2).strip()
v = m.group(2)
value, used_fallback = _coerce_arg_value(v, params.get(k))
arguments[k] = value
any_json_fallback = any_json_fallback or used_fallback
Expand Down Expand Up @@ -1417,7 +1417,7 @@ def parse_minimax(
re.DOTALL,
):
pname = pm.group(1)
pval = pm.group(2).strip()
pval = pm.group(2)
value, used_fallback = _coerce_arg_value(
pval, params.get(pname)
)
Expand Down
14 changes: 8 additions & 6 deletions tests/test_tool_arg_type_preservation.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,17 @@ def renderer(model, renderer_name):
]


# Each case: a single ``x`` argument whose value is a STRING that
# happens to be valid JSON of another type. With a schema declaring
# ``x: string``, the parser must return the string verbatim.
JSON_LOOKING_STRING_ARGS = [
# String values retain their type and whitespace when the schema declares
# ``x: string``, including values that happen to be valid JSON of another type.
STRING_ARG_CASES = [
pytest.param({"x": "true"}, id="string-bool"),
pytest.param({"x": "42"}, id="string-int"),
pytest.param({"x": "null"}, id="string-null"),
pytest.param({"x": "[1,2,3]"}, id="string-array"),
pytest.param({"x": '{"k": 1}'}, id="string-object"),
pytest.param({"x": " Paris\nMünchen ☁ "}, id="string-whitespace"),
pytest.param({"x": "\n leading and trailing newline\n"}, id="string-newline"),
pytest.param({"x": ""}, id="string-empty"),
]


Expand Down Expand Up @@ -116,7 +118,7 @@ def _extract_assistant_tokens(renderer, prompt, assistant_msg, *, tools=None):
return full_ids[len(prompt_ids) :]


@pytest.mark.parametrize("args", JSON_LOOKING_STRING_ARGS)
@pytest.mark.parametrize("args", STRING_ARG_CASES)
def test_string_arg_follows_parser_contract(model, renderer_name, renderer, args):
"""Preserve declared strings unless the reference parser decodes JSON."""
msg = {
Expand All @@ -135,7 +137,7 @@ def test_string_arg_follows_parser_contract(model, renderer_name, renderer, args
assert parsed.tool_calls, f"{model}: parser returned no tool_calls"
got = _normalize_args(parsed.tool_calls[0].arguments)
expected = (
{key: json.loads(value) for key, value in args.items()}
{key: _normalize_args(value) for key, value in args.items()}
if isinstance(renderer, MuseGlimmerRenderer)
else args
)
Expand Down
Loading