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_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.
-
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
1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 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 | |
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
771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 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 | |
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_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
444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 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 | |
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
910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 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 | |
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
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.