Skip to content
Closed
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
28 changes: 25 additions & 3 deletions vllm/tool_parsers/qwen3coder_tool_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,12 +157,16 @@ def _convert_param_value(
and "type" in param_config[param_name]
):
param_type = str(param_config[param_name]["type"]).strip().lower()
elif (
isinstance(param_config[param_name], dict)
and "anyOf" in param_config[param_name]
elif isinstance(param_config[param_name], dict) and (
"anyOf" in param_config[param_name]
):
# anyOf has no top-level "type"; treat as object to trigger json.loads.
param_type = "object"
Comment on lines +160 to 164
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This change correctly identifies parameters defined with $ref, but making the broad assumption that they are always of object type could lead to incorrect behavior.

A $ref can point to a definition of any type, including primitives like string or integer. If a $ref points to a string type, but the model returns a value that looks like a dictionary (e.g., "{'key': 'value'}"), the current logic would incorrectly parse it as a dictionary via ast.literal_eval in the fallback path, instead of treating it as a string. This would result in a type mismatch and potential runtime errors in the tool-using code.

A safer approach would be to handle $ref parameters separately, perhaps by attempting to parse them with json.loads and falling back to treating them as a raw string if parsing fails, without attempting ast.literal_eval. This would correctly handle object-like values while not misinterpreting string literals that happen to look like Python literals.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switched to more restrictive parsing, avoiding the ast.literal_eval for refs.

elif isinstance(param_config[param_name], dict) and (
"$ref" in param_config[param_name]
):
# $ref has no type information at this level; treat it separately.
param_type = "$ref"
else:
param_type = "string"
if param_type in ["string", "str", "text", "varchar", "char", "enum"]:
Expand Down Expand Up @@ -214,6 +218,24 @@ def _convert_param_value(
func_name,
)
return param_value == "true"
elif param_type == "$ref":
# For $ref types, we don't have direct type information here.
# Attempt JSON parsing first, then fallback to string.
# We avoid ast.literal_eval for $ref types due to potential complexity and
# security concerns.
try:
param_value = json.loads(param_value)
return param_value
except (json.JSONDecodeError, TypeError, ValueError):
logger.debug(
"Parsed value '%s' of parameter '%s' cannot be "
"parsed with json.loads for $ref type in tool '%s', "
"degenerating to string.",
param_value,
param_name,
func_name,
)
return param_value
else:
if (
param_type in ["object", "array", "arr"]
Expand Down
Loading