Skip to content

vllm_mlx.tool_parsers.qwen3_xml_tool_parser

Qwen 3.5 XML tool call parser for vllm-mlx.

View the complete module source at #L1-L1559.

API details

Each callable below includes its exact signature, type annotations, inputs, defaults, return contract, documented exceptions, implementation source, and parsed docstring sections when the source provides them.

vllm_mlx.tool_parsers.qwen3_xml_tool_parser

Qwen 3.5 XML tool call parser for vllm-mlx.

Handles Qwen 3.5's XML parameter format:

<tool_call>
<function=func_name>
<parameter=param1>value1</parameter>
</function>
</tool_call>

Authority: Qwen 3.5 HF chat template, vLLM PR #25028 (from Qwen API team).

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.logger module-attribute

logger = logging.getLogger(__name__)

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.ChatCompletionToolsParam module-attribute

ChatCompletionToolsParam = Any

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.DeltaFunctionCall dataclass

DeltaFunctionCall(name: Optional[str] = None, arguments: str = '')

Incremental function name and argument payload used by the XML parser.

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.DeltaFunctionCall.name class-attribute instance-attribute

name: Optional[str] = None

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.DeltaFunctionCall.arguments class-attribute instance-attribute

arguments: str = ''

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.DeltaToolCall dataclass

DeltaToolCall(index: int = 0, id: Optional[str] = None, type: str = 'function', function: Optional[DeltaFunctionCall] = None)

Incremental indexed tool call produced by the XML parser shim.

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.DeltaToolCall.index class-attribute instance-attribute

index: int = 0

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.DeltaToolCall.id class-attribute instance-attribute

id: Optional[str] = None

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.DeltaToolCall.type class-attribute instance-attribute

type: str = 'function'

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.DeltaToolCall.function class-attribute instance-attribute

function: Optional[DeltaFunctionCall] = None

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.DeltaMessage dataclass

DeltaMessage(content: Optional[str] = None, tool_calls: Optional[list[DeltaToolCall]] = None, role: Optional[str] = None, reasoning_content: Optional[str] = None)

Incremental content, reasoning, and tool calls from the parser shim.

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.DeltaMessage.content class-attribute instance-attribute

content: Optional[str] = None

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.DeltaMessage.tool_calls class-attribute instance-attribute

tool_calls: Optional[list[DeltaToolCall]] = None

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.DeltaMessage.role class-attribute instance-attribute

role: Optional[str] = None

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.DeltaMessage.reasoning_content class-attribute instance-attribute

reasoning_content: Optional[str] = None

vllm_mlx.tool_parsers.qwen3_xml_tool_parser._FunctionDef

_FunctionDef(d: dict)

Wrap a function definition dict for attribute access.

Source code in vllm_mlx/tool_parsers/qwen3_xml_tool_parser.py
def __init__(self, d: dict):
    self._d = d

vllm_mlx.tool_parsers.qwen3_xml_tool_parser._FunctionDef.__slots__ class-attribute instance-attribute

__slots__ = ('_d',)

vllm_mlx.tool_parsers.qwen3_xml_tool_parser._FunctionDef._d instance-attribute

_d = d

vllm_mlx.tool_parsers.qwen3_xml_tool_parser._FunctionDef.name property

name: str

vllm_mlx.tool_parsers.qwen3_xml_tool_parser._FunctionDef.parameters property

parameters: dict

vllm_mlx.tool_parsers.qwen3_xml_tool_parser._ToolDef

_ToolDef(d: dict)

Wrap a tool definition dict for attribute access.

Source code in vllm_mlx/tool_parsers/qwen3_xml_tool_parser.py
def __init__(self, d: dict):
    self._d = d
    self._func = _FunctionDef(d.get("function", {}))

vllm_mlx.tool_parsers.qwen3_xml_tool_parser._ToolDef.__slots__ class-attribute instance-attribute

__slots__ = ('_d', '_func')

vllm_mlx.tool_parsers.qwen3_xml_tool_parser._ToolDef._d instance-attribute

_d = d

vllm_mlx.tool_parsers.qwen3_xml_tool_parser._ToolDef._func instance-attribute

_func = _FunctionDef(d.get('function', {}))

vllm_mlx.tool_parsers.qwen3_xml_tool_parser._ToolDef.type property

type: str

vllm_mlx.tool_parsers.qwen3_xml_tool_parser._ToolDef.function property

function: _FunctionDef

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser

StreamingXMLToolCallParser()

Streaming XML parser for Qwen 3.5 <tool_call> format.

Architecture
  1. Preprocessing (_preprocess_before_xml_parse): scans raw text for <parameter=name> tags, extracts type hints from tool schemas, and rewrites the XML into expat-parseable form.
  2. Expat parsing: an incremental xml.parsers.expat parser fires start_element / end_element / character_data callbacks.
  3. Type coercion (_coerce_param_value): converts string values to int/float/bool/object/array based on JSON Schema type hints. Complex types use a deferred ast.literal_eval + json.loads fallback.
  4. Auto-closing: if the model truncates output mid-tag, the parser synthesizes closing tags so partial tool calls are still extractable.

Streaming: update(delta) feeds incremental text. Completed tool calls are emitted via get_streaming_output() as they close. State resets between tool calls within the same response.

Source code in vllm_mlx/tool_parsers/qwen3_xml_tool_parser.py
def __init__(self):
    self.reset_streaming_state()

    # Tool configuration information
    self.tools: Union[list[ChatCompletionToolsParam], None] = None
    self.tool_call_start_token: str = "<tool_call>"
    self.tool_call_end_token: str = "</tool_call>"
    self.function_start_token: str = "<function="
    self.function_end_token: str = "</function>"
    self.parameter_start_token: str = "<parameter="
    self.parameter_end_token: str = "</parameter>"

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser.tools instance-attribute

tools: Union[list[ChatCompletionToolsParam], None] = None

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser.tool_call_start_token instance-attribute

tool_call_start_token: str = '<tool_call>'

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser.tool_call_end_token instance-attribute

tool_call_end_token: str = '</tool_call>'

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser.function_start_token instance-attribute

function_start_token: str = '<function='

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser.function_end_token instance-attribute

function_end_token: str = '</function>'

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser.parameter_start_token instance-attribute

parameter_start_token: str = '<parameter='

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser.parameter_end_token instance-attribute

parameter_end_token: str = '</parameter>'

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._TOOL_TAG_PREFIXES class-attribute instance-attribute

_TOOL_TAG_PREFIXES = ('<tool_call>', '</tool_call>', '<function=', '</function>', '<parameter=', '</parameter>')

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser.reset_streaming_state

reset_streaming_state()

Reset streaming parsing state

Source code in vllm_mlx/tool_parsers/qwen3_xml_tool_parser.py
def reset_streaming_state(self):
    """Reset streaming parsing state"""

    self.deltas = []
    # state for streaming
    self.tool_call_index = 0
    self.current_call_id = None
    self.last_completed_call_id = None
    self.current_function_name = None
    self.current_function_open = False
    # True when the current tool_call was synthesised because the model
    # emitted a bare <function=> with no <tool_call> wrapper. The matching
    # </function> closes the implicit wrapper too so the next bare
    # <function=> opens a fresh tool_call.
    self.implicit_tool_call_wrapper = False
    # Bare-function commitment delay. When auto-opening on <function=Name>,
    # we hold the function-name delta in _pending_implicit_delta until
    # something confirms a real tool call (parameter open or function
    # close). If non-whitespace character data arrives first, the
    # `<function=...>` was prose and we abandon — the original raw tag
    # text + any buffered inter-token whitespace + the prose data must
    # all be emitted as user-visible content (not swallowed).
    self._pending_implicit_delta = None
    self._pending_implicit_raw_text: str | None = None
    self._pending_implicit_text_buffer: str = ""
    # Set transiently around expat parse so _start_element can capture
    # the raw input fragment in case it ends up needing rollback.
    self._current_raw_element: str | None = None
    self.parameters = {}
    self.current_param_name = None
    self.current_param_value = ""
    self.current_param_value_converted = ""
    self.current_param_is_first = False
    self.should_emit_end_newline = False
    self.start_quote_emitted = False

    self.streaming_buffer = ""
    self.last_processed_pos = 0

    self.text_content_buffer = ""

    # state for preprocessing and deferred parsing
    self._pre_inside_parameter = False
    self._pre_param_buffer = ""
    self._pre_current_param_name = None
    self.defer_current_parameter = False
    self.deferred_param_raw_value = ""

    # recreate parser
    self.parser = ParserCreate()
    self.setup_parser()

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser.parse_single_streaming_chunks

parse_single_streaming_chunks(xml_chunk: str) -> DeltaMessage

Parse single streaming XML chunk and return Delta response This is the actual streaming interface that receives chunks one by one and maintains internal state

Parameters:

  • xml_chunk (str) –

    Single XML chunk string

Returns: DeltaMessage: Contains delta information generated by this chunk, returns empty response if no complete elements

Source code in vllm_mlx/tool_parsers/qwen3_xml_tool_parser.py
def parse_single_streaming_chunks(self, xml_chunk: str) -> DeltaMessage:
    """
    Parse single streaming XML chunk and return Delta response
    This is the actual streaming interface that receives chunks
    one by one and maintains internal state

    Args:
        xml_chunk: Single XML chunk string
    Returns:
        DeltaMessage: Contains delta information generated by this chunk,
        returns empty response if no complete elements
    """
    # Record delta count before processing
    initial_delta_count = len(self.deltas)

    self.streaming_buffer += xml_chunk

    found_elements = self._process_complete_xml_elements()

    if found_elements:
        # If complete elements found, check if end events were missed
        # some tags may not have been triggered
        try:
            new_deltas = self.deltas[initial_delta_count:]
            # If this chunk contains </function>
            # but didn't generate '}', then complete it.
            # We count every </function> literal in the chunk and compare
            # against the number of function-close deltas already emitted
            # for this chunk, regardless of which call_id they belong to.
            # When a single chunk straddles end-of-call-N and start-of-
            # call-N+1, the current_call_id at this point is N+1, so a
            # call-id-scoped check would miss the close already done for
            # call N and fire a spurious close on call N+1.
            if (
                self.current_call_id is not None
                and self.function_end_token in xml_chunk
            ):
                function_closes_in_chunk = xml_chunk.count(self.function_end_token)
                function_close_deltas_emitted = sum(
                    1
                    for td in new_deltas
                    for tc in (td.tool_calls or [])
                    if tc.function
                    and isinstance(tc.function.arguments, str)
                    and tc.function.arguments in ("}", "{}")
                )
                if function_closes_in_chunk > function_close_deltas_emitted:
                    # Close potentially unclosed element
                    if self.current_param_name:
                        self._end_element("parameter")
                    if self.current_function_name:
                        self._end_element("function")
            # If this chunk contains </tool_call>
            # but didn't generate final empty delta, then complete it
            if (
                self.current_call_id is not None
                and self.tool_call_end_token in xml_chunk
            ):
                has_toolcall_close = any(
                    (
                        td.tool_calls
                        and any(
                            (
                                tc.type == "function"
                                and tc.function
                                and tc.function.arguments == ""
                                and tc.id == self.current_call_id
                            )
                            for tc in td.tool_calls
                        )
                    )
                    for td in new_deltas
                )
                if not has_toolcall_close:
                    # Close potentially unclosed element
                    if self.current_param_name:
                        self._end_element("parameter")
                    if self.current_function_name:
                        self._end_element("function")
                    self._end_element("tool_call")
        except Exception as e:
            logger.warning("Error with fallback parsing: %s", e)
        # Merge newly generated deltas into single response
        result_delta = self._merge_new_deltas_to_single_response(
            initial_delta_count
        )
        return result_delta
    else:
        # No complete elements, check if there's unoutput text content
        if self.text_content_buffer and self.tool_call_index == 0:
            # Has text content but no tool_call yet, output text content
            text_delta = DeltaMessage(content=self.text_content_buffer)
            self._emit_delta(text_delta)
            # Clear buffer to avoid duplicate output
            self.text_content_buffer = ""
            return text_delta

        # If this chunk contains end tags but wasn't triggered by parser,
        # manually complete end events
        # Only execute when still on the same call as when entered,
        # to prevent accidentally closing new calls
        # in multi <tool_call> scenarios
        if self.current_call_id is not None and (
            self.function_end_token in xml_chunk
            or self.tool_call_end_token in xml_chunk
        ):
            # Close potentially unclosed element
            if self.current_param_name:
                self._end_element("parameter")
            if self.function_end_token in xml_chunk and self.current_function_name:
                self._end_element("function")
            if self.tool_call_end_token in xml_chunk:
                self._end_element("tool_call")
            # Return the merged delta result generated by this fallback
            result_delta = self._merge_new_deltas_to_single_response(
                initial_delta_count
            )
            return result_delta

        # No complete elements, return empty response
        return DeltaMessage(content=None)

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._escape_xml_special_chars

_escape_xml_special_chars(text: str) -> str

Escape XML special characters Args: text: Original text Returns: Escaped text

Source code in vllm_mlx/tool_parsers/qwen3_xml_tool_parser.py
def _escape_xml_special_chars(self, text: str) -> str:
    """
    Escape XML special characters
    Args:
        text: Original text
    Returns:
        Escaped text
    """
    xml_escapes = {
        "&": "&amp;",
        "<": "&lt;",
        ">": "&gt;",
        '"': "&quot;",
        "'": "&apos;",
    }

    for char, escape in xml_escapes.items():
        text = text.replace(char, escape)

    return text

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._process_complete_xml_elements

_process_complete_xml_elements() -> bool

Process complete XML elements in buffer

Returns:

  • bool ( bool ) –

    Whether complete elements were found and processed

Source code in vllm_mlx/tool_parsers/qwen3_xml_tool_parser.py
def _process_complete_xml_elements(self) -> bool:
    """
    Process complete XML elements in buffer

    Returns:
        bool: Whether complete elements were found and processed
    """
    found_any = False

    while self.last_processed_pos < len(self.streaming_buffer):
        # Find next complete xml element
        element, end_pos = self._find_next_complete_element(self.last_processed_pos)
        if element is None:
            # No complete element found, wait for more data
            break

        # Check if this element should be skipped
        if self._should_skip_element(element):
            self.last_processed_pos = end_pos
            continue

        # Found complete XML element, process it
        try:
            preprocessed_element = self._preprocess_xml_chunk(element)
            # Check if this is the first tool_call start
            if (
                (
                    preprocessed_element.strip().startswith("<tool_call>")
                    or preprocessed_element.strip().startswith("<function name=")
                )
                and self.tool_call_index == 0
            ) and self.text_content_buffer:
                # First tool_call starts,
                # output previously collected text content first
                text_delta = DeltaMessage(content=self.text_content_buffer)
                self._emit_delta(text_delta)
                # Clear buffer for potential subsequent text content
                self.text_content_buffer = ""

            # If a new tool_call starts and
            # there are already completed tool_calls
            if (
                preprocessed_element.strip().startswith("<tool_call>")
                and self.tool_call_index > 0
                and self.current_call_id
            ):
                # Reset parser state but preserve generated deltas
                if self.current_param_name:
                    self._end_element("parameter")
                if self.current_function_open or self.current_function_name:
                    self._end_element("function")
                # Output final tool_call tail delta
                final_delta = DeltaMessage(
                    role=None,
                    content=None,
                    reasoning_content=None,
                    tool_calls=[
                        DeltaToolCall(
                            index=self.tool_call_index - 1,
                            id=self.current_call_id,
                            type="function",
                            function=DeltaFunctionCall(name=None, arguments=""),
                        )
                    ],
                )
                self._emit_delta(final_delta)
                # Reset XML parser and current call state
                self._reset_xml_parser_after_tool_call()
            # Parse preprocessed element. Expose the original raw
            # element so _start_element can stash it on the pending
            # implicit delta — needed to reconstruct prose if the
            # bare-<function=> turns out not to be a tool call.
            self._current_raw_element = element
            try:
                self.parser.Parse(preprocessed_element, False)
            finally:
                self._current_raw_element = None
            found_any = True

        except Exception as e:
            logger.warning("Error when parsing XML elements: %s", e)

        # Update processed position
        self.last_processed_pos = end_pos

    return found_any

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._should_skip_element

_should_skip_element(element: str) -> bool

Determine whether an element should be skipped

Parameters:

  • element (str) –

    Element to evaluate

Returns:

  • bool ( bool ) –

    True means should skip, False means should process

Source code in vllm_mlx/tool_parsers/qwen3_xml_tool_parser.py
def _should_skip_element(self, element: str) -> bool:
    """
    Determine whether an element should be skipped

    Args:
        element: Element to evaluate

    Returns:
        bool: True means should skip, False means should process
    """

    # If it's a tool_call XML tag, don't skip
    if (
        element.startswith(self.tool_call_start_token)
        or element.startswith(self.function_start_token)
        or element.startswith(self.parameter_start_token)
    ):
        return False

    # If currently not parsing tool calls and not blank,
    # collect this text instead of skipping
    # Only process other XML elements after tool_call appears,
    # otherwise treat as plain text
    if self.current_call_id is None and element:
        # Collect text content to buffer
        self.text_content_buffer += element
        return True  # Still skip, but content has been collected

    # If currently parsing tool calls,
    # this might be parameter value, don't skip
    if self.current_call_id is not None:
        return False

    # Skip blank content
    return not element

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._looks_like_partial_tool_open

_looks_like_partial_tool_open(fragment: str) -> bool

True if fragment could complete into a tool-related XML tag.

Covers two shapes
  • fragment is a prefix of a known tag head (e.g. <funct).
  • fragment already past the = marker and accumulating the attribute name (e.g. <function=Ag) — waiting on >.
Source code in vllm_mlx/tool_parsers/qwen3_xml_tool_parser.py
def _looks_like_partial_tool_open(self, fragment: str) -> bool:
    """True if `fragment` could complete into a tool-related XML tag.

    Covers two shapes:
      * `fragment` is a prefix of a known tag head (e.g. ``<funct``).
      * `fragment` already past the ``=`` marker and accumulating the
        attribute name (e.g. ``<function=Ag``) — waiting on ``>``.
    """
    if not fragment.startswith("<"):
        return False
    for prefix in self._TOOL_TAG_PREFIXES:
        if prefix.startswith(fragment) or fragment.startswith(prefix):
            return True
    return False

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._find_next_complete_element

_find_next_complete_element(start_pos: int) -> tuple[Optional[str], int]

Find next complete XML element from specified position

Parameters:

  • start_pos (int) –

    Position to start searching

Returns:

  • Optional[str]

    (Complete element string, element end position),

  • int

    returns (None, start_pos) if no complete element found

Source code in vllm_mlx/tool_parsers/qwen3_xml_tool_parser.py
def _find_next_complete_element(self, start_pos: int) -> tuple[Optional[str], int]:
    """
    Find next complete XML element from specified position

    Args:
        start_pos: Position to start searching

    Returns:
        (Complete element string, element end position),
        returns (None, start_pos) if no complete element found
    """
    buffer = self.streaming_buffer[start_pos:]

    if not buffer:
        return None, start_pos

    if buffer.startswith("<"):
        # Need to ensure no new < appears,
        # find the nearest one between < and >
        tag_end = buffer.find("<", 1)
        tag_end2 = buffer.find(">", 1)
        if tag_end != -1 and tag_end2 != -1:
            # Next nearest is <
            if tag_end < tag_end2:
                # If the prefix is the start of a tool-related opening tag
                # awaiting its closing '>', wait for more data instead of
                # emitting it as an unclosed fragment (which would either
                # leak as text or crash expat). Required so the model can
                # drop the <tool_call> wrapper and emit a bare <function=>
                # block; without it, partial <function=Name fragments leak.
                if self._looks_like_partial_tool_open(buffer[:tag_end]):
                    return None, start_pos
                return buffer[:tag_end], start_pos + tag_end
            # Next nearest is >, means found XML element
            else:
                return buffer[: tag_end2 + 1], start_pos + tag_end2 + 1
        elif tag_end != -1:
            if self._looks_like_partial_tool_open(buffer[:tag_end]):
                return None, start_pos
            return buffer[:tag_end], start_pos + tag_end
        elif tag_end2 != -1:
            return buffer[: tag_end2 + 1], start_pos + tag_end2 + 1
        else:
            # Buffer is `<...` with neither a `>` nor a second `<` yet.
            # Wait for more data if the buffer could complete into ANY
            # known tool-related tag (not just <tool_call>) — bare
            # <function=NAME> / <parameter=NAME> may have dropped the
            # outer wrapper, and partial chunks of them must not be
            # emitted as text.
            if self._looks_like_partial_tool_open(buffer):
                return None, start_pos
            if self.current_call_id is None:
                return buffer, start_pos + len(buffer)
            # Inside a tool call, partial non-tag bytes must wait too.
            return None, start_pos
    else:
        # Find text content (until next < or buffer end)
        next_tag_pos = buffer.find("<")
        if next_tag_pos != -1:
            # Found text content
            text_content = buffer[:next_tag_pos]
            return text_content, start_pos + next_tag_pos
        else:
            # Buffer end is all text, process
            # (no longer wait for more data)
            remaining = buffer
            return remaining, start_pos + len(remaining)

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._merge_new_deltas_to_single_response

_merge_new_deltas_to_single_response(initial_count: int) -> DeltaMessage

Merge newly generated deltas from this processing into a single DeltaMessage

Parameters:

  • initial_count (int) –

    Delta count before processing

Returns:

  • DeltaMessage

    Merged DeltaMessage containing all newly generated delta information

Source code in vllm_mlx/tool_parsers/qwen3_xml_tool_parser.py
def _merge_new_deltas_to_single_response(self, initial_count: int) -> DeltaMessage:
    """
    Merge newly generated deltas from this processing
    into a single DeltaMessage

    Args:
        initial_count: Delta count before processing

    Returns:
        Merged DeltaMessage containing all newly generated delta information
    """
    if len(self.deltas) <= initial_count:
        return DeltaMessage(content=None)

    # Get newly generated deltas
    new_deltas = self.deltas[initial_count:]

    if len(new_deltas) == 1:
        # Only one new delta, return directly
        return new_deltas[0]

    # Merge multiple new deltas
    merged_tool_calls: list[DeltaToolCall] = []
    merged_content: str = ""

    for delta in new_deltas:
        if delta.content:
            merged_content += delta.content
        if delta.tool_calls:
            # For tool_calls, we need to intelligently merge arguments
            for tool_call in delta.tool_calls:
                # Find if there's already a tool_call with the same call_id
                existing_call = None
                for existing in merged_tool_calls:
                    if existing.id == tool_call.id:
                        existing_call = existing
                        break

                if existing_call and existing_call.function:
                    # Merge to existing tool_call
                    if tool_call.function and tool_call.function.name:
                        existing_call.function.name = tool_call.function.name
                    if (
                        tool_call.function
                        and tool_call.function.arguments is not None
                    ):
                        if existing_call.function.arguments is None:
                            existing_call.function.arguments = ""

                        # For streaming JSON parameters,
                        # simply concatenate in order
                        new_args = tool_call.function.arguments
                        existing_call.function.arguments += new_args
                    if tool_call.type:
                        existing_call.type = tool_call.type
                else:
                    # Add new tool_call
                    merged_tool_calls.append(tool_call)

    return DeltaMessage(
        content=merged_content if merged_content else None,
        tool_calls=merged_tool_calls,
    )

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._preprocess_xml_chunk

_preprocess_xml_chunk(chunk: str) -> str

Preprocess XML chunk, handle non-standard formats, and escape special characters

Parameters:

  • chunk (str) –

    Original XML chunk

Returns:

  • str

    Processed XML chunk

Source code in vllm_mlx/tool_parsers/qwen3_xml_tool_parser.py
def _preprocess_xml_chunk(self, chunk: str) -> str:
    """
    Preprocess XML chunk, handle non-standard formats,
    and escape special characters

    Args:
        chunk: Original XML chunk

    Returns:
        Processed XML chunk
    """

    # Check if this is a tool_call related element
    is_tool_call = False
    if chunk.startswith(self.tool_call_start_token) or chunk.startswith(
        self.tool_call_end_token
    ):
        is_tool_call = True
    if chunk.startswith(self.function_start_token) or chunk.startswith(
        self.function_end_token
    ):
        is_tool_call = True
    if chunk.startswith(self.parameter_start_token) or chunk.startswith(
        self.parameter_end_token
    ):
        is_tool_call = True
    # Handle <function=name> format -> <function name="name">
    processed = re.sub(r"<function=([^>]+)>", r'<function name="\1">', chunk)
    # Handle <parameter=name> format -> <parameter name="name">
    processed = re.sub(r"<parameter=([^>]+)>", r'<parameter name="\1">', processed)

    original_chunk = chunk
    # If in parameter value accumulation mode
    if self._pre_inside_parameter:
        # Parameter end: output accumulated raw text
        # safely then return </parameter>
        if processed.startswith("</parameter>"):
            body_text = self._pre_param_buffer
            # Trigger deferred parsing mode
            # literal_eval+json output in end_element
            self.defer_current_parameter = True
            self.deferred_param_raw_value = body_text
            # Clean up state
            self._pre_inside_parameter = False
            self._pre_param_buffer = ""
            self._pre_current_param_name = None
            safe_text = self._escape_xml_special_chars(body_text)
            return f"{safe_text}</parameter>"
        else:
            # If this is the first block of content after entering parameter
            # evaluate if deferred parsing is needed;
            # If not needed, exit accumulation mode
            # and pass through directly
            if self._pre_param_buffer == "":
                # Get current parameter type
                param_type = (
                    self._get_param_type(self._pre_current_param_name)
                    if self._pre_current_param_name
                    else "string"
                )
                # Only these types need deferred parsing to
                # handle Python literals containing single quotes
                is_object_type = param_type in ["object"]
                is_complex_type = (
                    param_type in ["array", "arr", "sequence"]
                    or param_type.startswith("dict")
                    or param_type.startswith("list")
                )

                # Only delay when contains container symbols
                # and has single quotes and is complex type
                has_container_hint = (
                    ("[" in original_chunk)
                    or ("{" in original_chunk)
                    or ("(" in original_chunk)
                )

                # Determine if deferred parsing is needed
                need_defer = False
                if is_complex_type:
                    # Complex type, always need deferred parsing
                    need_defer = True
                elif (
                    is_object_type
                    and has_container_hint
                    and ("'" in original_chunk)
                ):
                    # Object type with container symbols
                    # and single quotes, need deferred parsing
                    need_defer = True

                if not need_defer:
                    # No need for deferred parsing,
                    # exit parameter mode directly
                    self._pre_inside_parameter = False
                    return self._escape_xml_special_chars(original_chunk)
            self._pre_param_buffer += original_chunk
            return ""

    # Parameter start: enable accumulation
    if processed.startswith("<parameter name="):
        m = re.match(r'<parameter name="([^"]+)">', processed)
        if m:
            self._pre_current_param_name = m.group(1)
        self._pre_inside_parameter = True
        self._pre_param_buffer = ""
        return processed

    # If processed doesn't contain special_token, escape processed
    # This is because XML parsing encounters special characters
    # and reports errors, so escaping is needed
    if not is_tool_call:
        processed = self._escape_xml_special_chars(processed)
    return processed

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._emit_delta

_emit_delta(delta: DeltaMessage)

Emit Delta response (streaming output)

Source code in vllm_mlx/tool_parsers/qwen3_xml_tool_parser.py
def _emit_delta(self, delta: DeltaMessage):
    """Emit Delta response (streaming output)"""
    self.deltas.append(delta)

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._auto_close_open_parameter_if_needed

_auto_close_open_parameter_if_needed(incoming_tag: Optional[str] = None)

Before starting to process new elements, if there are unclosed tags from before, automatically complete their endings to the parser. - If there are unclosed parameters, it's equivalent to feeding </parameter> - When about to start a new function or tool_call, if there are unclosed functions, complete </function>. - When about to start a new tool_call, if there are unclosed tool_calls, complete </tool_call>.

Source code in vllm_mlx/tool_parsers/qwen3_xml_tool_parser.py
def _auto_close_open_parameter_if_needed(self, incoming_tag: Optional[str] = None):
    """Before starting to process new elements,
    if there are unclosed tags from before,
    automatically complete their endings to the parser.
    - If there are unclosed parameters,
    it's equivalent to feeding `</parameter>`
    - When about to start a new function or tool_call,
    if there are unclosed functions, complete `</function>`.
    - When about to start a new tool_call,
    if there are unclosed tool_calls, complete `</tool_call>`.
    """
    # First close unclosed parameters
    if self.current_param_name:
        self._end_element("parameter")

    # If about to start new function or tool_call,
    # and there are unclosed functions, close function first
    if incoming_tag in ("function", "tool_call") and self.current_function_name:
        self._end_element("function")

    # If about to start new tool_call,
    # and there are unclosed tool_calls, close tool_call first
    if incoming_tag == "tool_call" and self.current_call_id:
        self._end_element("tool_call")

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._start_element

_start_element(name: str, attrs: dict[str, str])

Handle XML start element events

Source code in vllm_mlx/tool_parsers/qwen3_xml_tool_parser.py
def _start_element(self, name: str, attrs: dict[str, str]):
    """Handle XML start element events"""

    if name == "root":
        return

    if name == "tool_call":
        # Before opening new tool_call,
        # automatically complete previous unclosed tags
        self._auto_close_open_parameter_if_needed("tool_call")

        self.parameters = {}
        self.current_call_id = self._get_next_call_id()
        self.current_param_is_first = True
        self.tool_call_index += 1
    elif name.startswith("function") or (name == "function"):
        # If missing tool_call, manually complete
        implicit_open = False
        if not self.current_call_id:
            self._start_element("tool_call", {})
            # Remember the wrapper was synthesised, so </function> can
            # also close it (the model didn't / won't emit </tool_call>).
            self.implicit_tool_call_wrapper = True
            implicit_open = True
        # Before opening new function,
        # automatically complete previous unclosed tags (parameter/function)
        self._auto_close_open_parameter_if_needed("function")
        function_name = self._extract_function_name(name, attrs)
        self.current_function_name = function_name
        self.current_function_open = True
        if function_name:
            delta = DeltaMessage(
                tool_calls=[
                    DeltaToolCall(
                        index=self.tool_call_index - 1,
                        id=self.current_call_id,
                        type="function",
                        function=DeltaFunctionCall(
                            name=function_name, arguments=""
                        ),
                    )
                ]
            )
            if implicit_open:
                # Defer until <parameter=> or </function> confirms this is
                # a real tool call, not prose mentioning `<function=...>`.
                # Stash the raw fragment so abandonment can restore it
                # as user-visible content instead of swallowing it.
                self._pending_implicit_delta = delta
                self._pending_implicit_raw_text = (
                    self._current_raw_element
                    if self._current_raw_element is not None
                    else f"<function={function_name}>"
                )
                self._pending_implicit_text_buffer = ""
            else:
                self._emit_delta(delta)
    elif name.startswith("parameter") or (name == "parameter"):
        # First <parameter=> after a deferred bare <function=> confirms
        # this really is a tool call — emit the held function delta.
        self._flush_pending_implicit_delta()
        # If previous parameter hasn't ended normally,
        # complete its end first, then start new parameter
        self._auto_close_open_parameter_if_needed("parameter")
        param_name = self._extract_parameter_name(name, attrs)
        self.current_param_name = param_name
        self.current_param_value = ""
        self.current_param_value_converted = ""
        self.start_quote_emitted = False  # Reset start quote flag

        # Only output parameter name and colon,
        # don't output quotes
        # decide after parameter value type is determined
        if param_name:
            if not self.parameters:
                # First parameter
                # start JSON, only output parameter name and colon
                json_start = f'{{"{param_name}": '
                delta = DeltaMessage(
                    tool_calls=[
                        DeltaToolCall(
                            index=self.tool_call_index - 1,
                            id=self.current_call_id,
                            type="function",
                            function=DeltaFunctionCall(
                                name=None, arguments=json_start
                            ),
                        )
                    ]
                )
                self._emit_delta(delta)
                self.current_param_is_first = True
            else:
                # Subsequent parameters
                # add comma and parameter name, no quotes
                json_continue = f', "{param_name}": '
                delta = DeltaMessage(
                    tool_calls=[
                        DeltaToolCall(
                            index=self.tool_call_index - 1,
                            id=self.current_call_id,
                            type="function",
                            function=DeltaFunctionCall(
                                name=None, arguments=json_continue
                            ),
                        )
                    ]
                )
                self._emit_delta(delta)
                self.current_param_is_first = False

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._flush_pending_implicit_delta

_flush_pending_implicit_delta() -> None

Emit a deferred bare- delta now that the call is confirmed.

Source code in vllm_mlx/tool_parsers/qwen3_xml_tool_parser.py
def _flush_pending_implicit_delta(self) -> None:
    """Emit a deferred bare-<function=> delta now that the call is confirmed."""
    if self._pending_implicit_delta is not None:
        delta = self._pending_implicit_delta
        self._pending_implicit_delta = None
        # Drop the rollback context: the call is confirmed real, so the
        # raw `<function=...>` tag is structural (not prose) and any
        # buffered inter-token whitespace belongs to the tool-call frame.
        self._pending_implicit_raw_text = None
        self._pending_implicit_text_buffer = ""
        self._emit_delta(delta)

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._abandon_pending_implicit_tool_call

_abandon_pending_implicit_tool_call() -> tuple[str, str]

Roll back a deferred bare- auto-open: prose followed.

Drops the pending function-name delta and unwinds the synthesised wrapper state so no tool_call is ever emitted for this fragment. Returns (raw_text, buffered_text) so the caller can restore the original prose (raw <function=...> tag + any whitespace held while waiting on commitment) as user-visible content.

Source code in vllm_mlx/tool_parsers/qwen3_xml_tool_parser.py
def _abandon_pending_implicit_tool_call(self) -> tuple[str, str]:
    """Roll back a deferred bare-<function=> auto-open: prose followed.

    Drops the pending function-name delta and unwinds the synthesised
    wrapper state so no tool_call is ever emitted for this fragment.
    Returns ``(raw_text, buffered_text)`` so the caller can restore the
    original prose (raw `<function=...>` tag + any whitespace held while
    waiting on commitment) as user-visible content.
    """
    raw_text = self._pending_implicit_raw_text or ""
    buffered_text = self._pending_implicit_text_buffer
    self._pending_implicit_delta = None
    self._pending_implicit_raw_text = None
    self._pending_implicit_text_buffer = ""
    # Unwind the tool_call/function we synth-opened.
    self.implicit_tool_call_wrapper = False
    self.current_function_name = None
    self.current_function_open = False
    if self.tool_call_index > 0:
        self.tool_call_index -= 1
    if self.current_call_id:
        self.last_completed_call_id = None
        self.current_call_id = None
    # Reset expat so character data that wasn't a tool call doesn't
    # accumulate inside a phantom open element.
    self._reset_xml_parser_after_tool_call()
    return raw_text, buffered_text

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._char_data

_char_data(data: str)

Handle XML character data events

Source code in vllm_mlx/tool_parsers/qwen3_xml_tool_parser.py
def _char_data(self, data: str):
    """Handle XML character data events"""
    # Bare-<function=> commitment window: until <parameter=> or </function>
    # confirms, every character data event has to be reasoned about
    # against the possibility that the `<function=...>` was prose.
    if (
        self._pending_implicit_delta is not None
        and self.current_param_name is None
        and data
    ):
        if data.strip():
            # Non-whitespace → commit was wrong, this is prose. Roll back
            # and emit (raw tag + buffered whitespace + this data) as
            # user-visible content so we don't swallow the original text.
            raw_text, buffered_text = self._abandon_pending_implicit_tool_call()
            restored = raw_text + buffered_text + data
            if restored:
                self._emit_delta(DeltaMessage(content=restored))
            return
        # Pure whitespace (newlines between <function=> and <parameter=>
        # in a real call). Buffer in case we end up abandoning so the
        # restored prose preserves it; flush will discard it on commit.
        self._pending_implicit_text_buffer += data
        return
    if data and self.current_param_name:
        # If preprocessing stage determines deferred parsing is needed,
        # only cache character data, no streaming output
        if self.defer_current_parameter:
            original_data = data
            if self.should_emit_end_newline:
                original_data = "\n" + original_data
                self.should_emit_end_newline = False
            if original_data.endswith("\n"):
                self.should_emit_end_newline = True
                original_data = original_data[:-1]
            self.current_param_value += original_data
            return

        param_type = self._get_param_type(self.current_param_name)

        # Check if this is the first time receiving data for this parameter
        # If this is the first packet of data and starts with \n, remove \n
        if not self.current_param_value and data.startswith("\n"):
            data = data[1:]

        # Output start quote for string type (if not already output)
        if (
            param_type in ["string", "str", "text", "varchar", "char", "enum"]
            and not self.start_quote_emitted
        ):
            quote_delta = DeltaMessage(
                tool_calls=[
                    DeltaToolCall(
                        index=self.tool_call_index - 1,
                        id=self.current_call_id,
                        type="function",
                        function=DeltaFunctionCall(name=None, arguments='"'),
                    )
                ]
            )
            self._emit_delta(quote_delta)
            self.start_quote_emitted = True

        if not data:
            return

        original_data = data
        # Delay output of trailing newline
        if self.should_emit_end_newline:
            original_data = "\n" + original_data
            self.should_emit_end_newline = False
        if original_data.endswith("\n"):
            self.should_emit_end_newline = True
            original_data = original_data[:-1]
        self.current_param_value += original_data

        # convert parameter value by param_type
        converted_value = self._convert_param_value(
            self.current_param_value, param_type
        )
        output_data = self._convert_for_json_streaming(converted_value, param_type)

        delta_data = output_data[len(self.current_param_value_converted) :]
        self.current_param_value_converted = output_data

        delta = DeltaMessage(
            tool_calls=[
                DeltaToolCall(
                    index=self.tool_call_index - 1,
                    id=self.current_call_id,
                    type="function",
                    function=DeltaFunctionCall(name=None, arguments=delta_data),
                )
            ]
        )
        self._emit_delta(delta)

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._end_element

_end_element(name: str)

Handle XML end element events

Source code in vllm_mlx/tool_parsers/qwen3_xml_tool_parser.py
def _end_element(self, name: str):
    """Handle XML end element events"""

    if name == "root":
        return

    # If function or tool_call ends and there are still unclosed parameters,
    # complete parameter end first
    if (
        name.startswith("function") or name == "function" or name == "tool_call"
    ) and self.current_param_name:
        self._auto_close_open_parameter_if_needed()

    if (
        name.startswith("parameter") or name == "parameter"
    ) and self.current_param_name:
        # End current parameter
        param_name = self.current_param_name
        param_value = self.current_param_value

        # If in deferred parsing mode,
        # perform overall parsing on raw content
        # accumulated in preprocessing stage and output once
        if self.defer_current_parameter:
            raw_text = (
                self.deferred_param_raw_value
                if self.deferred_param_raw_value
                else param_value
            )
            parsed_value = None
            output_arguments = None
            try:
                # If previously delayed trailing newline,
                # add it back before parsing
                if self.should_emit_end_newline:
                    raw_for_parse = raw_text + "\n"
                else:
                    raw_for_parse = raw_text
                parsed_value = ast.literal_eval(raw_for_parse)
                output_arguments = json.dumps(parsed_value, ensure_ascii=False)
            except Exception:
                # Fallback: output as string as-is
                output_arguments = json.dumps(raw_text, ensure_ascii=False)
                parsed_value = raw_text

            delta = DeltaMessage(
                tool_calls=[
                    DeltaToolCall(
                        index=self.tool_call_index - 1,
                        id=self.current_call_id,
                        type="function",
                        function=DeltaFunctionCall(
                            name=None, arguments=output_arguments
                        ),
                    )
                ]
            )
            self._emit_delta(delta)

            # Clean up and store
            self.should_emit_end_newline = False
            self.parameters[param_name] = parsed_value
            self.current_param_name = None
            self.current_param_value = ""
            self.current_param_value_converted = ""
            self.start_quote_emitted = False
            self.defer_current_parameter = False
            self.deferred_param_raw_value = ""
            return

        param_type = self._get_param_type(param_name)

        # convert complete parameter value by param_type
        converted_value = self._convert_param_value(param_value, param_type)

        # Decide whether to add end quote based on parameter type
        if param_type in ["string", "str", "text", "varchar", "char", "enum"]:
            # For empty string parameters, need special handling
            if not param_value and not self.start_quote_emitted:
                # No start quote output,
                # directly output complete empty string
                delta = DeltaMessage(
                    tool_calls=[
                        DeltaToolCall(
                            index=self.tool_call_index - 1,
                            id=self.current_call_id,
                            type="function",
                            function=DeltaFunctionCall(name=None, arguments='""'),
                        )
                    ]
                )
                self._emit_delta(delta)
            else:
                # Non-empty parameter value, output end quote
                delta = DeltaMessage(
                    tool_calls=[
                        DeltaToolCall(
                            index=self.tool_call_index - 1,
                            id=self.current_call_id,
                            type="function",
                            function=DeltaFunctionCall(name=None, arguments='"'),
                        )
                    ]
                )
                self._emit_delta(delta)

        self.should_emit_end_newline = False
        # Store converted value
        self.parameters[param_name] = converted_value
        self.current_param_name = None
        self.current_param_value = ""
        self.current_param_value_converted = ""
        self.start_quote_emitted = False

    elif name.startswith("function") or name == "function":
        # </function> after a deferred bare <function=Name> with no
        # parameters also confirms a real (parameterless) tool call.
        self._flush_pending_implicit_delta()
        # if there are parameters, close JSON object
        if self.parameters:
            delta = DeltaMessage(
                tool_calls=[
                    DeltaToolCall(
                        index=self.tool_call_index - 1,
                        id=self.current_call_id,
                        type="function",
                        function=DeltaFunctionCall(name=None, arguments="}"),
                    )
                ]
            )
            self._emit_delta(delta)
        # return empty object
        else:
            delta = DeltaMessage(
                tool_calls=[
                    DeltaToolCall(
                        index=self.tool_call_index - 1,
                        id=self.current_call_id,
                        type="function",
                        function=DeltaFunctionCall(name=None, arguments="{}"),
                    )
                ]
            )
            self._emit_delta(delta)
        self.current_function_open = False
        # If the surrounding <tool_call> was synthesised (bare <function=>
        # with no wrapper), close it now so the next <function=> opens a
        # fresh tool_call rather than reusing this id/index.
        if self.implicit_tool_call_wrapper:
            self.implicit_tool_call_wrapper = False
            self._end_element("tool_call")

    elif name == "tool_call":
        # Before ending tool_call,
        # ensure function is closed to complete missing right brace
        if self.current_function_open:
            # If there are still unclosed parameters, close them first
            if self.current_param_name:
                self._end_element("parameter")
            # Close function, ensure output '}' or '{}'
            self._end_element("function")
        # Final Delta
        delta = DeltaMessage(
            tool_calls=[
                DeltaToolCall(
                    index=self.tool_call_index - 1,
                    id=self.current_call_id,
                    type="function",
                    function=DeltaFunctionCall(name=None, arguments=""),
                )
            ]
        )
        self._emit_delta(delta)

        # Check if there's text content to output (between tool_calls)
        if self.text_content_buffer.strip():
            text_delta = DeltaMessage(content=self.text_content_buffer)
            self._emit_delta(text_delta)

        self._reset_xml_parser_after_tool_call()

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser.setup_parser

setup_parser()

Set up XML parser event handlers

Source code in vllm_mlx/tool_parsers/qwen3_xml_tool_parser.py
def setup_parser(self):
    """Set up XML parser event handlers"""
    self.parser.buffer_text = True
    self.parser.StartElementHandler = self._start_element
    self.parser.EndElementHandler = self._end_element
    self.parser.CharacterDataHandler = self._char_data

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser.set_tools

set_tools(tools: Union[list[ChatCompletionToolsParam], None])

Set tool configuration information

Source code in vllm_mlx/tool_parsers/qwen3_xml_tool_parser.py
def set_tools(self, tools: Union[list[ChatCompletionToolsParam], None]):
    """Set tool configuration information"""
    self.tools = tools

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._get_next_call_id

_get_next_call_id()

Generate unique call ID

Source code in vllm_mlx/tool_parsers/qwen3_xml_tool_parser.py
def _get_next_call_id(self):
    """Generate unique call ID"""
    return f"call_{uuid.uuid4().hex[:24]}"

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._extract_function_name

_extract_function_name(name: str, attrs: dict[str, str]) -> Optional[str]

Extract function name from various formats

Source code in vllm_mlx/tool_parsers/qwen3_xml_tool_parser.py
def _extract_function_name(self, name: str, attrs: dict[str, str]) -> Optional[str]:
    """Extract function name from various formats"""
    if attrs and "name" in attrs:
        return attrs["name"]

    if "=" in name:
        parts = name.split("=", 1)
        if len(parts) == 2 and parts[0] == "function":
            return parts[1]

    return None

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._extract_parameter_name

_extract_parameter_name(name: str, attrs: dict[str, str]) -> Optional[str]

Extract parameter name from various formats

Source code in vllm_mlx/tool_parsers/qwen3_xml_tool_parser.py
def _extract_parameter_name(
    self, name: str, attrs: dict[str, str]
) -> Optional[str]:
    """Extract parameter name from various formats"""
    if attrs and "name" in attrs:
        return attrs["name"]

    if "=" in name:
        parts = name.split("=", 1)
        if len(parts) == 2 and parts[0] == "parameter":
            return parts[1]

    return None

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._get_param_type

_get_param_type(param_name: str) -> str

Get parameter type based on tool configuration, defaults to string Args: param_name: Parameter name

Returns:

  • str

    Parameter type

Source code in vllm_mlx/tool_parsers/qwen3_xml_tool_parser.py
def _get_param_type(self, param_name: str) -> str:
    """Get parameter type based on tool configuration, defaults to string
    Args:
        param_name: Parameter name

    Returns:
        Parameter type
    """
    if not self.tools or not self.current_function_name:
        return "string"

    for tool in self.tools:
        if not hasattr(tool, "type") or not (
            hasattr(tool, "function") and hasattr(tool.function, "name")
        ):
            continue
        if (
            tool.type == "function"
            and tool.function.name == self.current_function_name
        ):
            if not hasattr(tool.function, "parameters"):
                return "string"
            params = tool.function.parameters
            if isinstance(params, dict) and "properties" in params:
                properties = params["properties"]
                if param_name in properties and isinstance(
                    properties[param_name], dict
                ):
                    return self.repair_param_type(
                        str(properties[param_name].get("type", "string"))
                    )
            elif isinstance(params, dict) and param_name in params:
                param_config = params[param_name]
                if isinstance(param_config, dict):
                    return self.repair_param_type(
                        str(param_config.get("type", "string"))
                    )
            break
    return "string"

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser.repair_param_type

repair_param_type(param_type: str) -> str

Repair unknown parameter types by treating them as string Args: param_type: Parameter type

Returns:

  • str

    Repaired parameter type

Source code in vllm_mlx/tool_parsers/qwen3_xml_tool_parser.py
def repair_param_type(self, param_type: str) -> str:
    """Repair unknown parameter types by treating them as string
    Args:
        param_type: Parameter type

    Returns:
        Repaired parameter type
    """
    if (
        param_type in ["string", "str", "text", "varchar", "char", "enum"]
        or param_type.startswith("int")
        or param_type.startswith("uint")
        or param_type.startswith("long")
        or param_type.startswith("short")
        or param_type.startswith("unsigned")
        or param_type.startswith("num")
        or param_type.startswith("float")
        or param_type in ["boolean", "bool", "binary"]
        or (
            param_type in ["object", "array", "arr", "sequence"]
            or param_type.startswith("dict")
            or param_type.startswith("list")
        )
    ):
        return param_type
    else:
        return "string"

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._convert_param_value

_convert_param_value(param_value: str, param_type: str) -> Any

Convert value based on parameter type Args: param_value: Parameter value param_type: Parameter type

Returns:

  • Any

    Converted value

Source code in vllm_mlx/tool_parsers/qwen3_xml_tool_parser.py
def _convert_param_value(self, param_value: str, param_type: str) -> Any:
    """Convert value based on parameter type
    Args:
        param_value: Parameter value
        param_type: Parameter type

    Returns:
        Converted value
    """
    if param_value.lower() == "null":
        return None

    param_type = param_type.strip().lower()
    if param_type in ["string", "str", "text", "varchar", "char", "enum"]:
        return param_value
    elif (
        param_type.startswith("int")
        or param_type.startswith("uint")
        or param_type.startswith("long")
        or param_type.startswith("short")
        or param_type.startswith("unsigned")
    ):
        try:
            return int(param_value)
        except (ValueError, TypeError):
            logger.warning(
                "Parsed value '%s' of parameter '%s' is not an integer "
                "in tool '%s', degenerating to string.",
                param_value,
                self.current_param_name,
                self.current_function_name,
            )
        return param_value
    elif param_type.startswith("num") or param_type.startswith("float"):
        try:
            float_param_value: float = float(param_value)
            return (
                float_param_value
                if float_param_value - int(float_param_value) != 0
                else int(float_param_value)
            )
        except (ValueError, TypeError):
            logger.warning(
                "Parsed value '%s' of parameter '%s' is not a float "
                "in tool '%s', degenerating to string.",
                param_value,
                self.current_param_name,
                self.current_function_name,
            )
        return param_value
    elif param_type in ["boolean", "bool", "binary"]:
        param_value = param_value.lower()
        return param_value == "true"
    else:
        return param_value

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._convert_for_json_streaming

_convert_for_json_streaming(converted_value: Any, param_type: str) -> str

Convert converted_value based on whether it's empty and if type is string Args: converted_value: Converted value param_type: Parameter type

Returns:

  • str

    Converted string for streaming output

Source code in vllm_mlx/tool_parsers/qwen3_xml_tool_parser.py
def _convert_for_json_streaming(self, converted_value: Any, param_type: str) -> str:
    """Convert converted_value based on
    whether it's empty and if type is string
    Args:
        converted_value: Converted value
        param_type: Parameter type

    Returns:
        Converted string for streaming output
    """
    # Check if value is empty, but exclude numeric 0
    if converted_value is None or converted_value == "":
        return ""

    if param_type in ["string", "str", "text", "varchar", "char", "enum"]:
        # String type, remove double quotes
        return json.dumps(converted_value, ensure_ascii=False)[1:-1]
    else:
        # Non-string type, return complete JSON string
        if not isinstance(converted_value, str):
            return json.dumps(converted_value, ensure_ascii=False)
        else:
            return converted_value

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._reset_xml_parser_after_tool_call

_reset_xml_parser_after_tool_call()

Each tool_call is treated as a separate XML document, so we need to reset the parser after each tool_call.

Source code in vllm_mlx/tool_parsers/qwen3_xml_tool_parser.py
def _reset_xml_parser_after_tool_call(self):
    """
    Each tool_call is treated as a separate XML document,
    so we need to reset the parser after each tool_call.
    """

    # recreate XML parser
    self.parser = ParserCreate()
    self.setup_parser()

    # Reset current tool_call state
    if self.current_call_id:
        self.last_completed_call_id = self.current_call_id
    self.current_call_id = None
    self.current_function_name = None
    self.current_function_open = False
    self.parameters = {}
    self.current_param_name = None
    self.current_param_value = ""
    self.current_param_value_converted = ""
    self.current_param_is_first = False
    self.should_emit_end_newline = False
    self.start_quote_emitted = False
    self.text_content_buffer = ""

    # Reset preprocessing and deferred parsing state
    self._pre_inside_parameter = False
    self._pre_param_buffer = ""
    self._pre_current_param_name = None
    self.defer_current_parameter = False
    self.deferred_param_raw_value = ""

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.Qwen3XMLToolParser

Qwen3XMLToolParser(tokenizer=None)

Bases: ToolParser

XML tool call parser for Qwen 3.5 models, adapted for vllm-mlx.

Core parsing logic from vLLM PR #25028 (Qwen API team). Uses expat-based streaming XML parser with type coercion, deferred parsing for complex types, and auto-closing of malformed XML.

Source code in vllm_mlx/tool_parsers/qwen3_xml_tool_parser.py
def __init__(self, tokenizer=None):
    super().__init__(tokenizer)
    self._xml_parser = StreamingXMLToolCallParser()
    logger.info(
        "vLLM Successfully import tool parser %s !",
        self.__class__.__name__,
    )

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.Qwen3XMLToolParser.SUPPORTS_NATIVE_TOOL_FORMAT class-attribute instance-attribute

SUPPORTS_NATIVE_TOOL_FORMAT = True

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.Qwen3XMLToolParser._xml_parser instance-attribute

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.Qwen3XMLToolParser._wrap_tools staticmethod

_wrap_tools(request: dict[str, Any] | None) -> list[_ToolDef] | None

Convert tool definition dicts to _ToolDef wrappers for attribute access.

Source code in vllm_mlx/tool_parsers/qwen3_xml_tool_parser.py
@staticmethod
def _wrap_tools(request: dict[str, Any] | None) -> list[_ToolDef] | None:
    """Convert tool definition dicts to _ToolDef wrappers for attribute access."""
    if request and request.get("tools"):
        return [_ToolDef(t) for t in request["tools"]]
    return None

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.Qwen3XMLToolParser.extract_tool_calls

extract_tool_calls(model_output: str, request: dict[str, Any] | None = None) -> ExtractedToolCallInformation

Extract tool calls from complete Qwen 3.5 output.

Source code in vllm_mlx/tool_parsers/qwen3_xml_tool_parser.py
def extract_tool_calls(
    self, model_output: str, request: dict[str, Any] | None = None
) -> ExtractedToolCallInformation:
    """Extract tool calls from complete Qwen 3.5 output."""
    # Strip think tags FIRST — safety against recursive trap.
    # (Reasoning parser should have already stripped them, but
    # this guards against non-streaming paths or missing parser.)
    cleaned = self.strip_think_tags(model_output)

    self._xml_parser.reset_streaming_state()
    tools = self._wrap_tools(request)
    if tools:
        self._xml_parser.set_tools(tools)

    result = self._xml_parser.parse_single_streaming_chunks(cleaned)

    if not result.tool_calls:
        return ExtractedToolCallInformation(
            tools_called=False,
            tool_calls=[],
            content=result.content if result.content else model_output,
        )

    tool_calls: list[dict[str, Any]] = []
    for tc in result.tool_calls:
        if tc.function and tc.function.name:
            tool_calls.append(
                {
                    "id": tc.id or f"call_{uuid.uuid4().hex[:8]}",
                    "name": tc.function.name,
                    "arguments": tc.function.arguments or "{}",
                }
            )

    return ExtractedToolCallInformation(
        tools_called=len(tool_calls) > 0,
        tool_calls=tool_calls,
        content=result.content,
    )

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.Qwen3XMLToolParser.extract_tool_calls_streaming

extract_tool_calls_streaming(previous_text: str, current_text: str, delta_text: str, previous_token_ids: Sequence[int] | None = None, current_token_ids: Sequence[int] | None = None, delta_token_ids: Sequence[int] | None = None, request: dict[str, Any] | None = None) -> dict[str, Any] | None

Extract tool calls from streaming Qwen 3.5 output.

Returns dict with 'tool_calls' and/or 'content' keys, or None to suppress the chunk.

Source code in vllm_mlx/tool_parsers/qwen3_xml_tool_parser.py
def extract_tool_calls_streaming(
    self,
    previous_text: str,
    current_text: str,
    delta_text: str,
    previous_token_ids: Sequence[int] | None = None,
    current_token_ids: Sequence[int] | None = None,
    delta_token_ids: Sequence[int] | None = None,
    request: dict[str, Any] | None = None,
) -> dict[str, Any] | None:
    """Extract tool calls from streaming Qwen 3.5 output.

    Returns dict with 'tool_calls' and/or 'content' keys,
    or None to suppress the chunk.
    """
    if not previous_text:
        self._xml_parser.reset_streaming_state()
        tools = self._wrap_tools(request)
        if tools:
            self._xml_parser.set_tools(tools)

    result = self._xml_parser.parse_single_streaming_chunks(delta_text)

    # Empty DeltaMessage (content=None, no tool_calls) → suppress
    if not result.tool_calls and not result.content:
        return None

    # Convert DeltaMessage to dict format expected by vllm-mlx
    if result.tool_calls:
        tool_calls: list[dict[str, Any]] = []
        for tc in result.tool_calls:
            entry: dict[str, Any] = {
                "index": tc.index,
                "type": tc.type or "function",
            }
            if tc.id is not None:
                entry["id"] = tc.id
            if tc.function:
                func: dict[str, Any] = {}
                if tc.function.name is not None:
                    func["name"] = tc.function.name
                if tc.function.arguments is not None:
                    func["arguments"] = tc.function.arguments
                entry["function"] = func
            tool_calls.append(entry)
        return {"tool_calls": tool_calls}

    if result.content:
        return {"content": result.content}

    return None

Complete contract reference

Expand any definition for its exact inputs, annotations, defaults, return contract, directly raised exceptions, source-grounded behavior, and immutable line link. This section includes private and nested definitions that ordinary API generators omit.

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.DeltaFunctionCall · class
vllm_mlx.tool_parsers.qwen3_xml_tool_parser.DeltaFunctionCall(name: Optional[str] = None, arguments: str = '')

Incremental function name and argument payload used by the XML parser.

Parameters

Name Type Required Default Description
name Optional[str] no None Optional constructor field; defaults to None.
arguments str no '' Optional constructor field; defaults to ''.

Returns

  • Constructs: vllm_mlx.tool_parsers.qwen3_xml_tool_parser.DeltaFunctionCall

Exceptions and behavior

Class DeltaFunctionCall declares 0 direct member(s). No direct raise statement appears in this definition.

View source #L54-L58.

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.DeltaToolCall · class
vllm_mlx.tool_parsers.qwen3_xml_tool_parser.DeltaToolCall(index: int = 0, id: Optional[str] = None, type: str = 'function', function: Optional[DeltaFunctionCall] = None)

Incremental indexed tool call produced by the XML parser shim.

Parameters

Name Type Required Default Description
index int no 0 Optional constructor field; defaults to 0.
id Optional[str] no None Optional constructor field; defaults to None.
type str no 'function' Optional constructor field; defaults to 'function'.
function Optional[DeltaFunctionCall] no None Optional constructor field; defaults to None.

Returns

  • Constructs: vllm_mlx.tool_parsers.qwen3_xml_tool_parser.DeltaToolCall

Exceptions and behavior

Class DeltaToolCall declares 0 direct member(s). No direct raise statement appears in this definition.

View source #L62-L68.

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.DeltaMessage · class
vllm_mlx.tool_parsers.qwen3_xml_tool_parser.DeltaMessage(content: Optional[str] = None, tool_calls: Optional[list[DeltaToolCall]] = None, role: Optional[str] = None, reasoning_content: Optional[str] = None)

Incremental content, reasoning, and tool calls from the parser shim.

Parameters

Name Type Required Default Description
content Optional[str] no None Optional constructor field; defaults to None.
tool_calls Optional[list[DeltaToolCall]] no None Optional constructor field; defaults to None.
role Optional[str] no None Optional constructor field; defaults to None.
reasoning_content Optional[str] no None Optional constructor field; defaults to None.

Returns

  • Constructs: vllm_mlx.tool_parsers.qwen3_xml_tool_parser.DeltaMessage

Exceptions and behavior

Class DeltaMessage declares 0 direct member(s). No direct raise statement appears in this definition.

View source #L72-L78.

vllm_mlx.tool_parsers.qwen3_xml_tool_parser._FunctionDef · class
vllm_mlx.tool_parsers.qwen3_xml_tool_parser._FunctionDef(d: dict)

Wrap a function definition dict for attribute access.

Parameters

Name Type Required Default Description
d dict yes none Required positional or keyword input.

Returns

  • Constructs: vllm_mlx.tool_parsers.qwen3_xml_tool_parser._FunctionDef

Exceptions and behavior

Class _FunctionDef declares 3 direct member(s). No direct raise statement appears in this definition.

View source #L85-L99.

vllm_mlx.tool_parsers.qwen3_xml_tool_parser._FunctionDef.__init__ · method
vllm_mlx.tool_parsers.qwen3_xml_tool_parser._FunctionDef.__init__(d: dict) -> not annotated

Method _FunctionDef.__init__ updates self._d.

Parameters

Name Type Required Default Description
d dict yes none Required positional or keyword input.

Returns

  • Type: not annotated

Exceptions and behavior

Method _FunctionDef.__init__ updates self._d. No direct raise statement appears in this definition.

View source #L90-L91.

vllm_mlx.tool_parsers.qwen3_xml_tool_parser._FunctionDef.name · method
vllm_mlx.tool_parsers.qwen3_xml_tool_parser._FunctionDef.name() -> str

Method _FunctionDef.name calls self._d.get; returns self._d.get('name', '').

Parameters

This callable has no explicit inputs.

Returns

  • Type: str
  • Direct return expressions: self._d.get('name', '')

Exceptions and behavior

Method _FunctionDef.name calls self._d.get; returns self._d.get('name', ''). No direct raise statement appears in this definition.

View source #L94-L95.

vllm_mlx.tool_parsers.qwen3_xml_tool_parser._FunctionDef.parameters · method
vllm_mlx.tool_parsers.qwen3_xml_tool_parser._FunctionDef.parameters() -> dict

Method _FunctionDef.parameters calls self._d.get; returns self._d.get('parameters', {}).

Parameters

This callable has no explicit inputs.

Returns

  • Type: dict
  • Direct return expressions: self._d.get('parameters', {})

Exceptions and behavior

Method _FunctionDef.parameters calls self._d.get; returns self._d.get('parameters', {}). No direct raise statement appears in this definition.

View source #L98-L99.

vllm_mlx.tool_parsers.qwen3_xml_tool_parser._ToolDef · class
vllm_mlx.tool_parsers.qwen3_xml_tool_parser._ToolDef(d: dict)

Wrap a tool definition dict for attribute access.

Parameters

Name Type Required Default Description
d dict yes none Required positional or keyword input.

Returns

  • Constructs: vllm_mlx.tool_parsers.qwen3_xml_tool_parser._ToolDef

Exceptions and behavior

Class _ToolDef declares 3 direct member(s). No direct raise statement appears in this definition.

View source #L102-L117.

vllm_mlx.tool_parsers.qwen3_xml_tool_parser._ToolDef.__init__ · method
vllm_mlx.tool_parsers.qwen3_xml_tool_parser._ToolDef.__init__(d: dict) -> not annotated

Method _ToolDef.__init__ updates self._d, self._func; calls _FunctionDef, d.get.

Parameters

Name Type Required Default Description
d dict yes none Required positional or keyword input.

Returns

  • Type: not annotated

Exceptions and behavior

Method _ToolDef.__init__ updates self._d, self._func; calls _FunctionDef, d.get. No direct raise statement appears in this definition.

View source #L107-L109.

vllm_mlx.tool_parsers.qwen3_xml_tool_parser._ToolDef.type · method
vllm_mlx.tool_parsers.qwen3_xml_tool_parser._ToolDef.type() -> str

Method _ToolDef.type calls self._d.get; returns self._d.get('type', 'function').

Parameters

This callable has no explicit inputs.

Returns

  • Type: str
  • Direct return expressions: self._d.get('type', 'function')

Exceptions and behavior

Method _ToolDef.type calls self._d.get; returns self._d.get('type', 'function'). No direct raise statement appears in this definition.

View source #L112-L113.

vllm_mlx.tool_parsers.qwen3_xml_tool_parser._ToolDef.function · method
vllm_mlx.tool_parsers.qwen3_xml_tool_parser._ToolDef.function() -> _FunctionDef

Method _ToolDef.function returns self._func.

Parameters

This callable has no explicit inputs.

Returns

  • Type: _FunctionDef
  • Direct return expressions: self._func

Exceptions and behavior

Method _ToolDef.function returns self._func. No direct raise statement appears in this definition.

View source #L116-L117.

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser · class
vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser()

Streaming XML parser for Qwen 3.5 <tool_call> format.

Parameters

This callable has no explicit inputs.

Returns

  • Constructs: vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser

Exceptions and behavior

Class StreamingXMLToolCallParser declares 27 direct member(s). No direct raise statement appears in this definition.

View source #L126-L1427.

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser.__init__ · method
vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser.__init__() -> not annotated

Method StreamingXMLToolCallParser.__init__ updates self.tools, self.tool_call_start_token, self.tool_call_end_token, self.function_start_token; calls self.reset_streaming_state.

Parameters

This callable has no explicit inputs.

Returns

  • Type: not annotated

Exceptions and behavior

Method StreamingXMLToolCallParser.__init__ updates self.tools, self.tool_call_start_token, self.tool_call_end_token, self.function_start_token; calls self.reset_streaming_state. No direct raise statement appears in this definition.

View source #L146-L156.

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser.reset_streaming_state · method
vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser.reset_streaming_state() -> not annotated

Reset streaming parsing state

Parameters

This callable has no explicit inputs.

Returns

  • Type: not annotated

Exceptions and behavior

Method StreamingXMLToolCallParser.reset_streaming_state updates self.deltas, self.tool_call_index, self.current_call_id, self.last_completed_call_id; calls ParserCreate, self.setup_parser. No direct raise statement appears in this definition.

View source #L158-L208.

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser.parse_single_streaming_chunks · method
vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser.parse_single_streaming_chunks(xml_chunk: str) -> DeltaMessage

Parse single streaming XML chunk and return Delta response This is the actual streaming interface that receives chunks one by one and maintains internal state Args: xml_chunk: Single XML chunk string Returns: DeltaMessage: Contains delta information generated by this chunk, returns empty response if no complete elements

Parameters

Name Type Required Default Description
xml_chunk str yes none Single XML chunk string

Returns

  • Type: DeltaMessage
  • Direct return expressions: result_delta; text_delta; DeltaMessage(content=None)

Exceptions and behavior

Method StreamingXMLToolCallParser.parse_single_streaming_chunks updates self.streaming_buffer, self.text_content_buffer; calls len, self._process_complete_xml_elements, xml_chunk.count, sum; has 3 explicit return paths. No direct raise statement appears in this definition.

View source #L210-L330.

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._escape_xml_special_chars · method
vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._escape_xml_special_chars(text: str) -> str

Escape XML special characters Args: text: Original text Returns: Escaped text

Parameters

Name Type Required Default Description
text str yes none Original text

Returns

  • Type: str
  • Direct return expressions: text

Exceptions and behavior

Method StreamingXMLToolCallParser._escape_xml_special_chars calls xml_escapes.items, text.replace; returns text. No direct raise statement appears in this definition.

View source #L332-L351.

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._process_complete_xml_elements · method
vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._process_complete_xml_elements() -> bool

Process complete XML elements in buffer Returns: bool: Whether complete elements were found and processed

Parameters

This callable has no explicit inputs.

Returns

  • Type: bool
  • Direct return expressions: found_any

Exceptions and behavior

Method StreamingXMLToolCallParser._process_complete_xml_elements updates self.last_processed_pos, self.text_content_buffer, self._current_raw_element; calls len, self._find_next_complete_element, self._should_skip_element, self._preprocess_xml_chunk; returns found_any. No direct raise statement appears in this definition.

View source #L353-L438.

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._should_skip_element · method
vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._should_skip_element(element: str) -> bool

Determine whether an element should be skipped Args: element: Element to evaluate Returns: bool: True means should skip, False means should process

Parameters

Name Type Required Default Description
element str yes none Element to evaluate

Returns

  • Type: bool
  • Direct return expressions: False; True; not element

Exceptions and behavior

Method StreamingXMLToolCallParser._should_skip_element updates self.text_content_buffer; calls element.startswith; has 3 explicit return paths. No direct raise statement appears in this definition.

View source #L440-L474.

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._looks_like_partial_tool_open · method
vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._looks_like_partial_tool_open(fragment: str) -> bool

True if fragment could complete into a tool-related XML tag.

Parameters

Name Type Required Default Description
fragment str yes none Required positional or keyword input.

Returns

  • Type: bool
  • Direct return expressions: False; True

Exceptions and behavior

Method StreamingXMLToolCallParser._looks_like_partial_tool_open calls fragment.startswith, prefix.startswith; has 2 explicit return paths. No direct raise statement appears in this definition.

View source #L488-L501.

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._find_next_complete_element · method
vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._find_next_complete_element(start_pos: int) -> tuple[Optional[str], int]

Find next complete XML element from specified position Args: start_pos: Position to start searching Returns: (Complete element string, element end position), returns (None, start_pos) if no complete element found

Parameters

Name Type Required Default Description
start_pos int yes none Position to start searching

Returns

  • Type: tuple[Optional[str], int]
  • Direct return expressions: (None, start_pos); (buffer[:tag_end], start_pos + tag_end); (buffer[:tag_end2 + 1], start_pos + tag_end2 + 1); (buffer, start_pos + len(buffer)); (text_content, start_pos + next_tag_pos); (remaining, start_pos + len(remaining))

Exceptions and behavior

Method StreamingXMLToolCallParser._find_next_complete_element calls buffer.startswith, buffer.find, self._looks_like_partial_tool_open, len; has 6 explicit return paths. No direct raise statement appears in this definition.

View source #L503-L569.

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._merge_new_deltas_to_single_response · method
vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._merge_new_deltas_to_single_response(initial_count: int) -> DeltaMessage

Merge newly generated deltas from this processing into a single DeltaMessage Args: initial_count: Delta count before processing Returns: Merged DeltaMessage containing all newly generated delta information

Parameters

Name Type Required Default Description
initial_count int yes none Delta count before processing

Returns

  • Type: DeltaMessage
  • Direct return expressions: DeltaMessage(content=None); new_deltas[0]; DeltaMessage(content=merged_content if merged_content else None, tool_calls=merged_tool_calls)

Exceptions and behavior

Method StreamingXMLToolCallParser._merge_new_deltas_to_single_response calls len, DeltaMessage, merged_tool_calls.append; has 3 explicit return paths. No direct raise statement appears in this definition.

View source #L571-L633.

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._preprocess_xml_chunk · method
vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._preprocess_xml_chunk(chunk: str) -> str

Preprocess XML chunk, handle non-standard formats, and escape special characters Args: chunk: Original XML chunk Returns: Processed XML chunk

Parameters

Name Type Required Default Description
chunk str yes none Original XML chunk

Returns

  • Type: str
  • Direct return expressions: f'{safe_text}</parameter>'; self._escape_xml_special_chars(original_chunk); ''; processed

Exceptions and behavior

Method StreamingXMLToolCallParser._preprocess_xml_chunk updates self.defer_current_parameter, self.deferred_param_raw_value, self._pre_inside_parameter, self._pre_param_buffer; calls chunk.startswith, re.sub, processed.startswith, self._escape_xml_special_chars; has 4 explicit return paths. No direct raise statement appears in this definition.

View source #L635-L748.

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._emit_delta · method
vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._emit_delta(delta: DeltaMessage) -> not annotated

Emit Delta response (streaming output)

Parameters

Name Type Required Default Description
delta DeltaMessage yes none Required positional or keyword input.

Returns

  • Type: not annotated

Exceptions and behavior

Method StreamingXMLToolCallParser._emit_delta calls self.deltas.append. No direct raise statement appears in this definition.

View source #L750-L752.

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._auto_close_open_parameter_if_needed · method
vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._auto_close_open_parameter_if_needed(incoming_tag: Optional[str] = None) -> not annotated

Before starting to process new elements, if there are unclosed tags from before, automatically complete their endings to the parser.

Parameters

Name Type Required Default Description
incoming_tag Optional[str] no None Optional positional or keyword input; defaults to None.

Returns

  • Type: not annotated

Exceptions and behavior

Method StreamingXMLToolCallParser._auto_close_open_parameter_if_needed calls self._end_element. No direct raise statement appears in this definition.

View source #L754-L777.

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._start_element · method
vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._start_element(name: str, attrs: dict[str, str]) -> not annotated

Handle XML start element events

Parameters

Name Type Required Default Description
name str yes none Required positional or keyword input.
attrs dict[str, str] yes none Required positional or keyword input.

Returns

  • Type: not annotated
  • Direct return expressions: None

Exceptions and behavior

Method StreamingXMLToolCallParser._start_element updates self.parameters, self.current_call_id, self.current_param_is_first, self.tool_call_index; calls self._auto_close_open_parameter_if_needed, self._get_next_call_id, name.startswith, self._start_element; returns None. No direct raise statement appears in this definition.

View source #L779-L888.

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._flush_pending_implicit_delta · method
vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._flush_pending_implicit_delta() -> None

Emit a deferred bare- delta now that the call is confirmed.

Parameters

This callable has no explicit inputs.

Returns

  • Type: None

Exceptions and behavior

Method StreamingXMLToolCallParser._flush_pending_implicit_delta updates self._pending_implicit_delta, self._pending_implicit_raw_text, self._pending_implicit_text_buffer; calls self._emit_delta. No direct raise statement appears in this definition.

View source #L890-L900.

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._abandon_pending_implicit_tool_call · method
vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._abandon_pending_implicit_tool_call() -> tuple[str, str]

Roll back a deferred bare- auto-open: prose followed.

Parameters

This callable has no explicit inputs.

Returns

  • Type: tuple[str, str]
  • Direct return expressions: (raw_text, buffered_text)

Exceptions and behavior

Method StreamingXMLToolCallParser._abandon_pending_implicit_tool_call updates self._pending_implicit_delta, self._pending_implicit_raw_text, self._pending_implicit_text_buffer, self.implicit_tool_call_wrapper; calls self._reset_xml_parser_after_tool_call; returns (raw_text, buffered_text). No direct raise statement appears in this definition.

View source #L902-L928.

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._char_data · method
vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._char_data(data: str) -> not annotated

Handle XML character data events

Parameters

Name Type Required Default Description
data str yes none Required positional or keyword input.

Returns

  • Type: not annotated
  • Direct return expressions: None

Exceptions and behavior

Method StreamingXMLToolCallParser._char_data updates self._pending_implicit_text_buffer, self.should_emit_end_newline, self.current_param_value, self.start_quote_emitted; calls data.strip, self._abandon_pending_implicit_tool_call, self._emit_delta, DeltaMessage; returns None. No direct raise statement appears in this definition.

View source #L930-L1025.

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._end_element · method
vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._end_element(name: str) -> not annotated

Handle XML end element events

Parameters

Name Type Required Default Description
name str yes none Required positional or keyword input.

Returns

  • Type: not annotated
  • Direct return expressions: None

Exceptions and behavior

Method StreamingXMLToolCallParser._end_element updates self.should_emit_end_newline, self.current_param_name, self.current_param_value, self.current_param_value_converted; calls name.startswith, self._auto_close_open_parameter_if_needed, ast.literal_eval, json.dumps; returns None. No direct raise statement appears in this definition.

View source #L1027-L1206.

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser.setup_parser · method
vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser.setup_parser() -> not annotated

Set up XML parser event handlers

Parameters

This callable has no explicit inputs.

Returns

  • Type: not annotated

Exceptions and behavior

Method StreamingXMLToolCallParser.setup_parser updates self.parser.buffer_text, self.parser.StartElementHandler, self.parser.EndElementHandler, self.parser.CharacterDataHandler. No direct raise statement appears in this definition.

View source #L1208-L1213.

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser.set_tools · method
vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser.set_tools(tools: Union[list[ChatCompletionToolsParam], None]) -> not annotated

Set tool configuration information

Parameters

Name Type Required Default Description
tools Union[list[ChatCompletionToolsParam], None] yes none Required positional or keyword input.

Returns

  • Type: not annotated

Exceptions and behavior

Method StreamingXMLToolCallParser.set_tools updates self.tools. No direct raise statement appears in this definition.

View source #L1215-L1217.

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._get_next_call_id · method
vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._get_next_call_id() -> not annotated

Generate unique call ID

Parameters

This callable has no explicit inputs.

Returns

  • Type: not annotated
  • Direct return expressions: f'call_{uuid.uuid4().hex[:24]}'

Exceptions and behavior

Method StreamingXMLToolCallParser._get_next_call_id calls uuid.uuid4; returns f'call_{uuid.uuid4().hex[:24]}'. No direct raise statement appears in this definition.

View source #L1219-L1221.

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._extract_function_name · method
vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._extract_function_name(name: str, attrs: dict[str, str]) -> Optional[str]

Extract function name from various formats

Parameters

Name Type Required Default Description
name str yes none Required positional or keyword input.
attrs dict[str, str] yes none Required positional or keyword input.

Returns

  • Type: Optional[str]
  • Direct return expressions: attrs['name']; parts[1]; None

Exceptions and behavior

Method StreamingXMLToolCallParser._extract_function_name calls name.split, len; has 3 explicit return paths. No direct raise statement appears in this definition.

View source #L1223-L1233.

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._extract_parameter_name · method
vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._extract_parameter_name(name: str, attrs: dict[str, str]) -> Optional[str]

Extract parameter name from various formats

Parameters

Name Type Required Default Description
name str yes none Required positional or keyword input.
attrs dict[str, str] yes none Required positional or keyword input.

Returns

  • Type: Optional[str]
  • Direct return expressions: attrs['name']; parts[1]; None

Exceptions and behavior

Method StreamingXMLToolCallParser._extract_parameter_name calls name.split, len; has 3 explicit return paths. No direct raise statement appears in this definition.

View source #L1235-L1247.

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._get_param_type · method
vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._get_param_type(param_name: str) -> str

Get parameter type based on tool configuration, defaults to string Args: param_name: Parameter name Returns: Parameter type

Parameters

Name Type Required Default Description
param_name str yes none Parameter name

Returns

  • Type: str
  • Direct return expressions: 'string'; self.repair_param_type(str(properties[param_name].get('type', 'string'))); self.repair_param_type(str(param_config.get('type', 'string')))

Exceptions and behavior

Method StreamingXMLToolCallParser._get_param_type calls hasattr, isinstance, self.repair_param_type, str; has 3 explicit return paths. No direct raise statement appears in this definition.

View source #L1249-L1287.

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser.repair_param_type · method
vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser.repair_param_type(param_type: str) -> str

Repair unknown parameter types by treating them as string Args: param_type: Parameter type Returns: Repaired parameter type

Parameters

Name Type Required Default Description
param_type str yes none Parameter type

Returns

  • Type: str
  • Direct return expressions: param_type; 'string'

Exceptions and behavior

Method StreamingXMLToolCallParser.repair_param_type calls param_type.startswith; has 2 explicit return paths. No direct raise statement appears in this definition.

View source #L1289-L1315.

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._convert_param_value · method
vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._convert_param_value(param_value: str, param_type: str) -> Any

Convert value based on parameter type Args: param_value: Parameter value param_type: Parameter type Returns: Converted value

Parameters

Name Type Required Default Description
param_value str yes none Parameter value
param_type str yes none Parameter type

Returns

  • Type: Any
  • Direct return expressions: None; param_value; int(param_value); float_param_value if float_param_value - int(float_param_value) != 0 else int(float_param_value); param_value == 'true'

Exceptions and behavior

Method StreamingXMLToolCallParser._convert_param_value calls param_value.lower, param_type.strip().lower, param_type.strip, param_type.startswith; has 5 explicit return paths. No direct raise statement appears in this definition.

View source #L1317-L1371.

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._convert_for_json_streaming · method
vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._convert_for_json_streaming(converted_value: Any, param_type: str) -> str

Convert converted_value based on whether it's empty and if type is string Args: converted_value: Converted value param_type: Parameter type Returns: Converted string for streaming output

Parameters

Name Type Required Default Description
converted_value Any yes none Converted value
param_type str yes none Parameter type

Returns

  • Type: str
  • Direct return expressions: ''; json.dumps(converted_value, ensure_ascii=False)[1:-1]; json.dumps(converted_value, ensure_ascii=False); converted_value

Exceptions and behavior

Method StreamingXMLToolCallParser._convert_for_json_streaming calls json.dumps, isinstance; has 4 explicit return paths. No direct raise statement appears in this definition.

View source #L1373-L1395.

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._reset_xml_parser_after_tool_call · method
vllm_mlx.tool_parsers.qwen3_xml_tool_parser.StreamingXMLToolCallParser._reset_xml_parser_after_tool_call() -> not annotated

Each tool_call is treated as a separate XML document, so we need to reset the parser after each tool_call.

Parameters

This callable has no explicit inputs.

Returns

  • Type: not annotated

Exceptions and behavior

Method StreamingXMLToolCallParser._reset_xml_parser_after_tool_call updates self.parser, self.last_completed_call_id, self.current_call_id, self.current_function_name; calls ParserCreate, self.setup_parser. No direct raise statement appears in this definition.

View source #L1397-L1427.

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.Qwen3XMLToolParser · class
vllm_mlx.tool_parsers.qwen3_xml_tool_parser.Qwen3XMLToolParser(tokenizer = None)

XML tool call parser for Qwen 3.5 models, adapted for vllm-mlx.

Parameters

Name Type Required Default Description
tokenizer not annotated no None Optional positional or keyword input; defaults to None.

Returns

  • Constructs: vllm_mlx.tool_parsers.qwen3_xml_tool_parser.Qwen3XMLToolParser

Exceptions and behavior

Class Qwen3XMLToolParser derives from ToolParser and declares 4 direct member(s). No direct raise statement appears in this definition.

View source #L1442-L1559.

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.Qwen3XMLToolParser.__init__ · method
vllm_mlx.tool_parsers.qwen3_xml_tool_parser.Qwen3XMLToolParser.__init__(tokenizer = None) -> not annotated

Method Qwen3XMLToolParser.__init__ updates self._xml_parser; calls super().__init__, super, StreamingXMLToolCallParser, logger.info.

Parameters

Name Type Required Default Description
tokenizer not annotated no None Optional positional or keyword input; defaults to None.

Returns

  • Type: not annotated

Exceptions and behavior

Method Qwen3XMLToolParser.__init__ updates self._xml_parser; calls super().__init__, super, StreamingXMLToolCallParser, logger.info. No direct raise statement appears in this definition.

View source #L1454-L1460.

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.Qwen3XMLToolParser._wrap_tools · method
vllm_mlx.tool_parsers.qwen3_xml_tool_parser.Qwen3XMLToolParser._wrap_tools(request: dict[str, Any] | None) -> list[_ToolDef] | None

Convert tool definition dicts to _ToolDef wrappers for attribute access.

Parameters

Name Type Required Default Description
request dict[str, Any] \| None yes none Required positional or keyword input.

Returns

  • Type: list[_ToolDef] | None
  • Direct return expressions: [_ToolDef(t) for t in request['tools']]; None

Exceptions and behavior

Method Qwen3XMLToolParser._wrap_tools calls request.get, _ToolDef; has 2 explicit return paths. No direct raise statement appears in this definition.

View source #L1463-L1467.

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.Qwen3XMLToolParser.extract_tool_calls · method
vllm_mlx.tool_parsers.qwen3_xml_tool_parser.Qwen3XMLToolParser.extract_tool_calls(model_output: str, request: dict[str, Any] | None = None) -> ExtractedToolCallInformation

Extract tool calls from complete Qwen 3.5 output.

Parameters

Name Type Required Default Description
model_output str yes none Required positional or keyword input.
request dict[str, Any] \| None no None Optional positional or keyword input; defaults to None.

Returns

  • Type: ExtractedToolCallInformation
  • Direct return expressions: ExtractedToolCallInformation(tools_called=False, tool_calls=[], content=result.content if result.content else model_out…; ExtractedToolCallInformation(tools_called=len(tool_calls) > 0, tool_calls=tool_calls, content=result.content)

Exceptions and behavior

Method Qwen3XMLToolParser.extract_tool_calls calls self.strip_think_tags, self._xml_parser.reset_streaming_state, self._wrap_tools, self._xml_parser.set_tools; has 2 explicit return paths. No direct raise statement appears in this definition.

View source #L1469-L1507.

vllm_mlx.tool_parsers.qwen3_xml_tool_parser.Qwen3XMLToolParser.extract_tool_calls_streaming · method
vllm_mlx.tool_parsers.qwen3_xml_tool_parser.Qwen3XMLToolParser.extract_tool_calls_streaming(previous_text: str, current_text: str, delta_text: str, previous_token_ids: Sequence[int] | None = None, current_token_ids: Sequence[int] | None = None, delta_token_ids: Sequence[int] | None = None, request: dict[str, Any] | None = None) -> dict[str, Any] | None

Extract tool calls from streaming Qwen 3.5 output.

Parameters

Name Type Required Default Description
previous_text str yes none Required positional or keyword input.
current_text str yes none Required positional or keyword input.
delta_text str yes none Required positional or keyword input.
previous_token_ids Sequence[int] \| None no None Optional positional or keyword input; defaults to None.
current_token_ids Sequence[int] \| None no None Optional positional or keyword input; defaults to None.
delta_token_ids Sequence[int] \| None no None Optional positional or keyword input; defaults to None.
request dict[str, Any] \| None no None Optional positional or keyword input; defaults to None.

Returns

  • Type: dict[str, Any] | None
  • Direct return expressions: None; {'tool_calls': tool_calls}; {'content': result.content}

Exceptions and behavior

Method Qwen3XMLToolParser.extract_tool_calls_streaming calls self._xml_parser.reset_streaming_state, self._wrap_tools, self._xml_parser.set_tools, self._xml_parser.parse_single_streaming_chunks; has 3 explicit return paths. No direct raise statement appears in this definition.

View source #L1509-L1559.

Complete symbol map

This map also includes private definitions and nested helpers. The signature column exposes every explicit input even when an internal helper has no dedicated parameter prose.

Symbol Kind Signature and inputs What it does Source
DeltaFunctionCall class DeltaFunctionCall(name: Optional[str] = None, arguments: str = '') Incremental function name and argument payload used by the XML parser. #L54-L58
DeltaToolCall class DeltaToolCall(index: int = 0, id: Optional[str] = None, type: str = 'function', function: Optional[DeltaFunctionCall] = None) Incremental indexed tool call produced by the XML parser shim. #L62-L68
DeltaMessage class DeltaMessage(content: Optional[str] = None, tool_calls: Optional[list[DeltaToolCall]] = None, role: Optional[str] = None, reasoning_content: Optional[str] = None) Incremental content, reasoning, and tool calls from the parser shim. #L72-L78
_FunctionDef class _FunctionDef(d: dict) Wrap a function definition dict for attribute access. #L85-L99
_FunctionDef.__init__ method _FunctionDef.__init__(d: dict) -> not annotated Method _FunctionDef.__init__ updates self._d. #L90-L91
_FunctionDef.name method _FunctionDef.name() -> str Method _FunctionDef.name calls self._d.get; returns self._d.get('name', ''). #L94-L95
_FunctionDef.parameters method _FunctionDef.parameters() -> dict Method _FunctionDef.parameters calls self._d.get; returns self._d.get('parameters', {}). #L98-L99
_ToolDef class _ToolDef(d: dict) Wrap a tool definition dict for attribute access. #L102-L117
_ToolDef.__init__ method _ToolDef.__init__(d: dict) -> not annotated Method _ToolDef.__init__ updates self._d, self._func; calls _FunctionDef, d.get. #L107-L109
_ToolDef.type method _ToolDef.type() -> str Method _ToolDef.type calls self._d.get; returns self._d.get('type', 'function'). #L112-L113
_ToolDef.function method _ToolDef.function() -> _FunctionDef Method _ToolDef.function returns self._func. #L116-L117
StreamingXMLToolCallParser class StreamingXMLToolCallParser() Streaming XML parser for Qwen 3.5 <tool_call> format. #L126-L1427
StreamingXMLToolCallParser.__init__ method StreamingXMLToolCallParser.__init__() -> not annotated Method StreamingXMLToolCallParser.__init__ updates self.tools, self.tool_call_start_token, self.tool_call_end_token, self.function_start_token; calls self.reset_streaming_state. #L146-L156
StreamingXMLToolCallParser.reset_streaming_state method StreamingXMLToolCallParser.reset_streaming_state() -> not annotated Reset streaming parsing state #L158-L208
StreamingXMLToolCallParser.parse_single_streaming_chunks method StreamingXMLToolCallParser.parse_single_streaming_chunks(xml_chunk: str) -> DeltaMessage Parse single streaming XML chunk and return Delta response This is the actual streaming interface that receives chunks one by one and maintains internal state Args: xml_chunk: Single XML chunk string Returns: DeltaMessage: Contains delta information generated by this chunk, returns empty response if no complete elements #L210-L330
StreamingXMLToolCallParser._escape_xml_special_chars method StreamingXMLToolCallParser._escape_xml_special_chars(text: str) -> str Escape XML special characters Args: text: Original text Returns: Escaped text #L332-L351
StreamingXMLToolCallParser._process_complete_xml_elements method StreamingXMLToolCallParser._process_complete_xml_elements() -> bool Process complete XML elements in buffer Returns: bool: Whether complete elements were found and processed #L353-L438
StreamingXMLToolCallParser._should_skip_element method StreamingXMLToolCallParser._should_skip_element(element: str) -> bool Determine whether an element should be skipped Args: element: Element to evaluate Returns: bool: True means should skip, False means should process #L440-L474
StreamingXMLToolCallParser._looks_like_partial_tool_open method StreamingXMLToolCallParser._looks_like_partial_tool_open(fragment: str) -> bool True if fragment could complete into a tool-related XML tag. #L488-L501
StreamingXMLToolCallParser._find_next_complete_element method StreamingXMLToolCallParser._find_next_complete_element(start_pos: int) -> tuple[Optional[str], int] Find next complete XML element from specified position Args: start_pos: Position to start searching Returns: (Complete element string, element end position), returns (None, start_pos) if no complete element found #L503-L569
StreamingXMLToolCallParser._merge_new_deltas_to_single_response method StreamingXMLToolCallParser._merge_new_deltas_to_single_response(initial_count: int) -> DeltaMessage Merge newly generated deltas from this processing into a single DeltaMessage Args: initial_count: Delta count before processing Returns: Merged DeltaMessage containing all newly generated delta information #L571-L633
StreamingXMLToolCallParser._preprocess_xml_chunk method StreamingXMLToolCallParser._preprocess_xml_chunk(chunk: str) -> str Preprocess XML chunk, handle non-standard formats, and escape special characters Args: chunk: Original XML chunk Returns: Processed XML chunk #L635-L748
StreamingXMLToolCallParser._emit_delta method StreamingXMLToolCallParser._emit_delta(delta: DeltaMessage) -> not annotated Emit Delta response (streaming output) #L750-L752
StreamingXMLToolCallParser._auto_close_open_parameter_if_needed method StreamingXMLToolCallParser._auto_close_open_parameter_if_needed(incoming_tag: Optional[str] = None) -> not annotated Before starting to process new elements, if there are unclosed tags from before, automatically complete their endings to the parser. #L754-L777
StreamingXMLToolCallParser._start_element method StreamingXMLToolCallParser._start_element(name: str, attrs: dict[str, str]) -> not annotated Handle XML start element events #L779-L888
StreamingXMLToolCallParser._flush_pending_implicit_delta method StreamingXMLToolCallParser._flush_pending_implicit_delta() -> None Emit a deferred bare- delta now that the call is confirmed. #L890-L900
StreamingXMLToolCallParser._abandon_pending_implicit_tool_call method StreamingXMLToolCallParser._abandon_pending_implicit_tool_call() -> tuple[str, str] Roll back a deferred bare- auto-open: prose followed. #L902-L928
StreamingXMLToolCallParser._char_data method StreamingXMLToolCallParser._char_data(data: str) -> not annotated Handle XML character data events #L930-L1025
StreamingXMLToolCallParser._end_element method StreamingXMLToolCallParser._end_element(name: str) -> not annotated Handle XML end element events #L1027-L1206
StreamingXMLToolCallParser.setup_parser method StreamingXMLToolCallParser.setup_parser() -> not annotated Set up XML parser event handlers #L1208-L1213
StreamingXMLToolCallParser.set_tools method StreamingXMLToolCallParser.set_tools(tools: Union[list[ChatCompletionToolsParam], None]) -> not annotated Set tool configuration information #L1215-L1217
StreamingXMLToolCallParser._get_next_call_id method StreamingXMLToolCallParser._get_next_call_id() -> not annotated Generate unique call ID #L1219-L1221
StreamingXMLToolCallParser._extract_function_name method StreamingXMLToolCallParser._extract_function_name(name: str, attrs: dict[str, str]) -> Optional[str] Extract function name from various formats #L1223-L1233
StreamingXMLToolCallParser._extract_parameter_name method StreamingXMLToolCallParser._extract_parameter_name(name: str, attrs: dict[str, str]) -> Optional[str] Extract parameter name from various formats #L1235-L1247
StreamingXMLToolCallParser._get_param_type method StreamingXMLToolCallParser._get_param_type(param_name: str) -> str Get parameter type based on tool configuration, defaults to string Args: param_name: Parameter name Returns: Parameter type #L1249-L1287
StreamingXMLToolCallParser.repair_param_type method StreamingXMLToolCallParser.repair_param_type(param_type: str) -> str Repair unknown parameter types by treating them as string Args: param_type: Parameter type Returns: Repaired parameter type #L1289-L1315
StreamingXMLToolCallParser._convert_param_value method StreamingXMLToolCallParser._convert_param_value(param_value: str, param_type: str) -> Any Convert value based on parameter type Args: param_value: Parameter value param_type: Parameter type Returns: Converted value #L1317-L1371
StreamingXMLToolCallParser._convert_for_json_streaming method StreamingXMLToolCallParser._convert_for_json_streaming(converted_value: Any, param_type: str) -> str Convert converted_value based on whether it's empty and if type is string Args: converted_value: Converted value param_type: Parameter type Returns: Converted string for streaming output #L1373-L1395
StreamingXMLToolCallParser._reset_xml_parser_after_tool_call method StreamingXMLToolCallParser._reset_xml_parser_after_tool_call() -> not annotated Each tool_call is treated as a separate XML document, so we need to reset the parser after each tool_call. #L1397-L1427
Qwen3XMLToolParser class Qwen3XMLToolParser(tokenizer = None) XML tool call parser for Qwen 3.5 models, adapted for vllm-mlx. #L1442-L1559
Qwen3XMLToolParser.__init__ method Qwen3XMLToolParser.__init__(tokenizer = None) -> not annotated Method Qwen3XMLToolParser.__init__ updates self._xml_parser; calls super().__init__, super, StreamingXMLToolCallParser, logger.info. #L1454-L1460
Qwen3XMLToolParser._wrap_tools method Qwen3XMLToolParser._wrap_tools(request: dict[str, Any] \| None) -> list[_ToolDef] \| None Convert tool definition dicts to _ToolDef wrappers for attribute access. #L1463-L1467
Qwen3XMLToolParser.extract_tool_calls method Qwen3XMLToolParser.extract_tool_calls(model_output: str, request: dict[str, Any] \| None = None) -> ExtractedToolCallInformation Extract tool calls from complete Qwen 3.5 output. #L1469-L1507
Qwen3XMLToolParser.extract_tool_calls_streaming method Qwen3XMLToolParser.extract_tool_calls_streaming(previous_text: str, current_text: str, delta_text: str, previous_token_ids: Sequence[int] \| None = None, current_token_ids: Sequence[int] \| None = None, delta_token_ids: Sequence[int] \| None = None, request: dict[str, Any] \| None = None) -> dict[str, Any] \| None Extract tool calls from streaming Qwen 3.5 output. #L1509-L1559