vllm.tool_parsers.utils
¶
Classes:
-
UnexpectedAstError–Raised when the AST structure does not match the expected
Functions:
-
coerce_to_schema_type–Best-effort coercion of a raw string value to a JSON Schema type.
-
compute_tool_delta–Compute the incremental delta between previously streamed arguments
-
contains_broken_string_literal–Whether some string literal's first closing quote cannot close it.
-
escape_ctrl_chars_in_strings–Escape literal control chars inside string literals of pythonic text.
-
escape_nested_quotes_in_strings–Close a broken string literal at the only closing quote that works.
-
extract_intermediate_diff–Given two strings, extract the difference in the middle between two strings
-
extract_types_from_schema–Extract all possible type strings from a JSON Schema definition.
-
find_common_prefix–Finds a common prefix that is shared between two strings, if there is one.
-
find_common_suffix–Finds a common suffix shared between two strings, if there is one. Order of
-
find_tool_name–Return whether a function tool with tool_name exists.
-
find_tool_properties–Find a tool by name and return its properties dict, or {}.
-
get_function_tools–Return the function tools the model is shown, in request order.
-
get_parameter_value–Extract a Python literal value from an AST expression node.
-
handle_single_tool–Convert a single AST function call node into a ToolCall object.
-
make_valid_python–Attempt to close all open brackets/quotes to make partial Python valid.
-
normalize_leading_zero_ints–Strip leading zeros from decimal integer literals so the text parses.
-
partial_tag_overlap–Length of the longest prefix of tag that matches a suffix of text.
-
rename_reserved_kwargs–Rename Python-keyword parameter names so pythonic tool text parses.
-
require_function_tools–Return
get_function_tools(tools), rejecting a request that has none. -
restore_reserved_kwarg_names–Undo :func:
rename_reserved_kwargson a decoded arguments dict.
UnexpectedAstError
¶
_ast_callable_dotted_name(node)
¶
Return the dotted name for a call target, walking ast.Attribute
chains so a.b.c(...) becomes "a.b.c".
Raises:
-
UnexpectedAstError–If the chain does not bottom out in an
ast.Name(e.g. subscript or call expression as receiver).
Source code in vllm/tool_parsers/utils.py
_is_escaped(text, index)
¶
Whether the character at index is backslash-escaped.
A character is escaped iff it is preceded by an odd number of consecutive
backslashes. Checking only the single preceding character is wrong for even
runs: in 'ab\' the closing quote follows an escaped backslash (\\)
and is therefore NOT escaped — it closes the string. Common in regex/code
arguments such as r'\\b'.
Source code in vllm/tool_parsers/utils.py
_is_json_finite(obj)
¶
Whether obj can be serialized to valid JSON.
json.dumps(..., allow_nan=False) raises ValueError on any
non-finite float (inf/-inf/nan) anywhere in the value, so this
detects non-finite floats nested inside parsed lists/dicts too.
Source code in vllm/tool_parsers/utils.py
coerce_to_schema_type(value, schema_type)
¶
Best-effort coercion of a raw string value to a JSON Schema type.
Tries each type in priority order (null > integer > number > boolean > object > array > string) and returns the first successful coercion. Falls back to the original string when no coercion succeeds.
Parameters:
-
(value¶str) –The raw string value from the model output.
-
(schema_type¶str | list[str]) –One or more JSON Schema type strings (e.g.
"string"or["string", "null"]).
Source code in vllm/tool_parsers/utils.py
1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 | |
compute_tool_delta(previously_sent_args, new_call, index, withheld_suffix)
¶
Compute the incremental delta between previously streamed arguments and the current tool call state.
Returns:
-
DeltaToolCall | None–A DeltaToolCall with only the new argument characters, or None
-
DeltaToolCall | None–if there is no difference from what was previously sent.
Source code in vllm/tool_parsers/utils.py
contains_broken_string_literal(text)
¶
Whether some string literal's first closing quote cannot close it.
Streaming guard companion to escape_nested_quotes_in_strings: completion-based partial parses of nested-quote text read the string as implicit concatenation and stream argument prefixes that can never be retracted. Callers should withhold tool deltas while this returns True and let the requote recovery run on the final text instead. A string whose closing quote has not arrived yet is NOT broken (normal streaming); a closing quote at the very end of the text counts as broken only because its follower is still unknown.
Source code in vllm/tool_parsers/utils.py
escape_ctrl_chars_in_strings(text)
¶
Escape literal control chars inside string literals of pythonic text.
Models emitting pythonic tool calls frequently place raw newlines inside a
string argument (e.g. exec(command='line1\nline2') written with a real
line break). That is invalid Python — ast.parse fails with "unterminated
string literal" — so the call would be dropped even though the intent is
unambiguous. A NUL byte is worse: ast.parse rejects it anywhere in the
source with ValueError. Escaping \n/\r/\t/\x00 only
inside string literals makes the text parseable while preserving the
argument value exactly (the escape sequences evaluate back to the original
control chars).
Text outside string literals is returned unchanged.
Source code in vllm/tool_parsers/utils.py
escape_nested_quotes_in_strings(text)
¶
Close a broken string literal at the only closing quote that works.
Models emitting shell commands frequently nest unescaped same-style
quotes inside a string argument — command='sed -n '360,450p' f.py',
or a quoted python3 -c payload that itself contains quoted strings —
which Python reads as juxtaposed garbage, so the call is dropped even
though the intent is unambiguous. A string is treated as broken when
its first unescaped quote cannot syntactically close it (what follows
is none of ,, ), ], }, :). For a broken string,
every syntactically plausible closing quote is tried: interior quotes
escaped, the rest of the text kept verbatim, and the result (with
control chars escaped) validated with ast.parse. Exactly one
candidate parsing means recovery — the decoded value is exactly the
text the model wrote. Zero or several parsing candidates means the
nesting is genuinely ambiguous and the text is returned unchanged
rather than guessed at.
Returns (rewritten_text, changed); run the result through escape_ctrl_chars_in_strings before parsing — quotes chosen here can move raw control chars inside the string.
Source code in vllm/tool_parsers/utils.py
810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 | |
extract_intermediate_diff(curr, old)
¶
Given two strings, extract the difference in the middle between two strings that are known to have a common prefix and/or suffix.
This function is provided as a UTILITY for extracting information from JSON generated by partial_json_parser, to help in ensuring that the right tokens are returned in streaming, so that close-quotes, close-brackets and close-braces are not returned prematurely. The order of arguments IS important - the new version of the partially-parsed JSON must be the first argument, and the secnod argument must be from the previous generation.
What it returns, is tokens that should be streamed to the client.
e.g. extract_intermediate_diff('{"fruit": "apple"}', '{"fruit": "ap"}') -> 'ple'
Source code in vllm/tool_parsers/utils.py
extract_types_from_schema(schema)
¶
Extract all possible type strings from a JSON Schema definition.
Handles type (string or list), enum value inference, and
recursive anyOf/oneOf/allOf. Returns ["string"]
when no type information can be determined.
Source code in vllm/tool_parsers/utils.py
find_common_prefix(s1, s2)
¶
Finds a common prefix that is shared between two strings, if there is one. Order of arguments is NOT important.
This function is provided as a UTILITY for extracting information from JSON generated by partial_json_parser, to help in ensuring that the right tokens are returned in streaming, so that close-quotes, close-brackets and close-braces are not returned prematurely.
e.g. find_common_prefix('{"fruit": "ap"}', '{"fruit": "apple"}') -> '{"fruit": "ap'
Source code in vllm/tool_parsers/utils.py
find_common_suffix(s1, s2)
¶
Finds a common suffix shared between two strings, if there is one. Order of arguments is NOT important. Stops when the suffix ends OR it hits an alphanumeric character
e.g. find_common_suffix('{"fruit": "ap"}', '{"fruit": "apple"}') -> '"}'
Source code in vllm/tool_parsers/utils.py
find_tool_name(tools, tool_name)
¶
Return whether a function tool with tool_name exists.
Source code in vllm/tool_parsers/utils.py
find_tool_properties(tools, tool_name)
¶
Find a tool by name and return its properties dict, or {}.
Source code in vllm/tool_parsers/utils.py
get_function_tools(tools)
¶
Return the function tools the model is shown, in request order.
This is the single source for both the chat template's tool list and the
tools a tool-call grammar constrains, so the two cannot drift apart.
Namespace functions are flattened to <namespace>__<name>; other
Responses tool types are never rendered for the model and are dropped.
Source code in vllm/tool_parsers/utils.py
get_parameter_value(val)
¶
Extract a Python literal value from an AST expression node.
Handles constants, dicts, lists, and JSON-style name literals (null, true, false) that some models produce instead of Python literals (None, True, False).
Raises:
-
UnexpectedAstError–If the AST node is not a supported literal type.
Source code in vllm/tool_parsers/utils.py
480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 | |
handle_single_tool(call)
¶
Convert a single AST function call node into a ToolCall object.
Accepts both bare names (foo(...)) and dotted attribute chains
(a.b.c(...)); the resulting tool call name field preserves the
dotted form.
Raises:
-
UnexpectedAstError–If the call target is neither a simple name nor a chain of attribute accesses bottoming out in a name.
Source code in vllm/tool_parsers/utils.py
make_valid_python(text)
¶
Attempt to close all open brackets/quotes to make partial Python valid.
Used during streaming to parse incomplete tool call expressions by appending the necessary closing characters.
Returns:
-
tuple[str, str] | None–A tuple of (completed_text, added_suffix) if the text can be
-
tuple[str, str] | None–made valid, or None if the text is too incomplete to complete
-
tuple[str, str] | None–meaningfully (e.g. mid-parameter-name or mid-dict-key).
Raises:
-
UnexpectedAstError–If mismatched brackets or parentheses are detected.
Source code in vllm/tool_parsers/utils.py
949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 | |
normalize_leading_zero_ints(text)
¶
Strip leading zeros from decimal integer literals so the text parses.
Models emit zero-padded integers (month=07), which Python rejects
("leading zeros in decimal integer literals are not permitted"), so the
whole call would be dropped. Rewrite 07 to 7 outside string
literals only. Tokens that are already valid Python are left alone:
all-zero literals (00), floats and fractional parts (07.5,
1.07), exponents (1e07, consumed as a name run), and 0x/
0o/0b prefixes (the digit run stops at the prefix letter).
Source code in vllm/tool_parsers/utils.py
partial_tag_overlap(text, tag)
¶
Length of the longest prefix of tag that matches a suffix of text.
E.g. text ending in "<tool_" returns 6 when tag is "<tool_call>".
Returns 0 when there is no overlap.
Source code in vllm/tool_parsers/utils.py
rename_reserved_kwargs(text)
¶
Rename Python-keyword parameter names so pythonic tool text parses.
Tools legitimately name parameters from, in, class — but
memory_get(from=1) is a Python SyntaxError, so the whole call
would be dropped. Rename from= to from_pyreservedkw_= (outside
string literals only, and only in keyword-argument position: preceded by
( or , and followed by a single =), parse, then restore the
original name with :func:restore_reserved_kwarg_names.
Returns (rewritten_text, changed). Keyword values (x=True) and
keywords inside string arguments are never touched.
Source code in vllm/tool_parsers/utils.py
require_function_tools(tools)
¶
Return get_function_tools(tools), rejecting a request that has none.
Raises:
-
VLLMValidationError–toolshas no tool the model can call, so a tool choice that requires a call can never be satisfied.
Source code in vllm/tool_parsers/utils.py
restore_reserved_kwarg_names(arguments)
¶
Undo :func:rename_reserved_kwargs on a decoded arguments dict.
Only keys that carry the rename suffix and whose stem is a Python keyword are restored, making this an exact inverse of the rename.