o
    ia                     @   s   d dl Z d dlZd dlmZ d dlmZ d dlmZ d dlZ	d dl
mZ d dlmZmZ d dlmZmZmZmZmZmZ d dlmZ d d	lmZ d d
lmZ eeZG dd dZG dd deZdS )    N)Sequence)Any)ParserCreate)make_tool_call_id)ChatCompletionRequestChatCompletionToolsParam)DeltaFunctionCallDeltaMessageDeltaToolCallExtractedToolCallInformationFunctionCallToolCall)init_logger)TokenizerLike)
ToolParserc                   @   s  e Zd ZdZdd Zdd Zdedefdd	Zd
edefddZ	de
fddZdede
fddZdedeedB ef fddZdedefddZdedefddZdefddZdBdedB fd d!Zd"ed#eeef fd$d%Zd&efd'd(Zd"efd)d*Zd+d, Zd-ee dB fd.d/Zd"ed#eeef dedB fd0d1Zd"ed#eeef dedB fd2d3Zd4edefd5d6Zd7edefd8d9Zd:ed7ede fd;d<Z!d=e d7edefd>d?Z"d@dA Z#dS )CStreamingXMLToolCallParserze
    Simplified streaming XML tool call parser
    Supports streaming input, parsing, and output
    c                 C   s6   |    d | _d| _d| _d| _d| _d| _d| _d S )N<tool_call>z</tool_call>
<function=z</function>z<parameter=</parameter>)reset_streaming_statetoolstool_call_start_tokentool_call_end_tokenfunction_start_tokenfunction_end_tokenparameter_start_tokenparameter_end_tokenself r   \/home/ubuntu/vllm_env/lib/python3.10/site-packages/vllm/tool_parsers/qwen3xml_tool_parser.py__init__'   s   
z#StreamingXMLToolCallParser.__init__c                 C   s   g | _ d| _d| _d| _d| _d| _i | _d| _d| _d| _	d| _
d| _d| _d| _d| _d| _d| _d| _d| _d| _d| _t | _|   dS )zReset streaming parsing stater   NF )deltastool_call_indexcurrent_call_idlast_completed_call_idcurrent_function_namecurrent_function_open
parameterscurrent_param_namecurrent_param_valuecurrent_param_value_convertedcurrent_param_is_firstshould_emit_end_newlinestart_quote_emittedstreaming_bufferlast_processed_postext_content_buffer_pre_inside_parameter_pre_param_buffer_pre_current_param_namedefer_current_parameterdeferred_param_raw_valuer   parsersetup_parserr   r   r   r    r   3   s.   z0StreamingXMLToolCallParser.reset_streaming_state	xml_chunkreturnc           
   
      s  t  j}  j|7  _  }|rz\ j|d } jdurA j|v rAt fdd|D }|sA jr9 d  j	rA d  jdurm j
|v rmt fdd|D }|sm jr` d  j	rh d  d W n ty } ztd| W Y d}~nd}~ww  |}|S  jr jd	krt jd
}	 |	 d _|	S  jdurۈ j|v s j
|v rۈ jr d  j|v rʈ j	rʈ d  j
|v rԈ d  |}|S tdd
S )a  
        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
        Nc                 3   .    | ]}|j ot fd d|j D V  qdS )c                 3   s<    | ]}|j o|j jkot|j jto|j jd v V  qdS ))}{}N)functionidr%   
isinstance	argumentsstr.0tcr   r   r    	<genexpr>x   s    


UStreamingXMLToolCallParser.parse_single_streaming_chunks.<locals>.<genexpr>.<genexpr>N
tool_callsanyrE   tdr   r   r    rG   u       
zKStreamingXMLToolCallParser.parse_single_streaming_chunks.<locals>.<genexpr>	parameterr?   c                 3   r<   )c                 3   s8    | ]}|j d ko|jo|jjdko|j jkV  qdS )r?   r"   N)typer?   rB   r@   r%   rD   r   r   r    rG      s    



rH   NrI   rL   r   r   r    rG      rN   	tool_callzError with fallback parsing: %sr   contentr"   )lenr#   r0   _process_complete_xml_elementsr%   r   rK   r*   _end_elementr'   r   	Exceptionloggerwarning$_merge_new_deltas_to_single_responser2   r$   r	   _emit_delta)
r   r:   initial_delta_countfound_elements
new_deltashas_function_closehas_toolcall_closeeresult_delta
text_deltar   r   r    parse_single_streaming_chunksU   sn   


















z8StreamingXMLToolCallParser.parse_single_streaming_chunkstextc                 C   s2   dddddd}|  D ]
\}}|||}q|S )z
        Escape XML special characters
        Args:
            text: Original text
        Returns:
            Escaped text
        z&amp;z&lt;z&gt;z&quot;z&apos;)&<>"')itemsreplace)r   re   xml_escapescharescaper   r   r    _escape_xml_special_chars   s   	z4StreamingXMLToolCallParser._escape_xml_special_charsc                 C   s|  d}| j t| jk r| | j \}}|du r	 |S | |r"|| _ qzv| |}| ds6| drL| jdkrL| j	rLt
| j	d}| | d| _	| dr| jdkr| jr| jrc| d | jsi| jrn| d	 t
dddt| jd
 | jd	tddddgd}| | |   | j|d d}W n ty } ztd| W Y d}~nd}~ww || _ | j t| jk s
|S )z
        Process complete XML elements in buffer

        Returns:
            bool: Whether complete elements were found and processed
        FNr   z<function name=r   rR   r"   rO   r?      namerB   indexr@   rP   r?   )rolerS   	reasoningrJ   Tz#Error when parsing XML elements: %s)r1   rT   r0   _find_next_complete_element_should_skip_element_preprocess_xml_chunkstrip
startswithr$   r2   r	   r[   r%   r*   rV   r(   r'   r
   r   !_reset_xml_parser_after_tool_callr8   ParserW   rX   rY   )r   	found_anyelementend_pospreprocessed_elementrc   final_deltara   r   r   r    rU      sj   @








Ez9StreamingXMLToolCallParser._process_complete_xml_elementsr   c                 C   s\   | | js| | js| | jrdS | jdu r$|r$|  j|7  _dS | jdur+dS | S )z
        Determine whether an element should be skipped

        Args:
            element: Element to evaluate

        Returns:
            bool: True means should skip, False means should process
        FNT)r|   r   r   r   r%   r2   )r   r   r   r   r    ry   5  s   



z/StreamingXMLToolCallParser._should_skip_element	start_posNc                 C   sf  | j |d }|sd|fS |dr|dd}|dd}|dkrB|dkrB||k r4|d| || fS |d|d  || d fS |dkrP|d| || fS |dkrb|d|d  || d fS | jdu r|ddt| krud|fS |ds|ddt| krd|fS ||t| fS d|fS |d}|dkr|d| }||| fS |}||t| fS )a  
        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
        Nrg   rq   rh   r   r   )r0   r|   findr%   rT   )r   r   buffertag_endtag_end2next_tag_postext_content	remainingr   r   r    rx   Y  s:   


z6StreamingXMLToolCallParser._find_next_complete_elementinitial_countc           
      C   s&  t | j|krtddS | j|d }t |dkr|d S g }d}|D ]b}|jr-||j7 }|jr|jD ]Q}d}|D ]}|j|jkrE|} nq9|r|jr|jrX|jjrX|jj|j_|jrw|jjdurw|jjdu rkd|j_|jj}	|j j|	7  _|j	r~|j	|_	q3|
| q3q#t|r||dS d|dS )a  
        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
        NrR   rq   r   r"   )rS   rJ   )rT   r#   r	   rS   rJ   r@   r?   rs   rB   rP   append)
r   r   r^   merged_tool_callsmerged_contentdeltarQ   existing_callexistingnew_argsr   r   r    rZ     sP   



z?StreamingXMLToolCallParser._merge_new_deltas_to_single_responsechunkc                 C   s  d}| | js| | jrd}| | js| | jrd}| | js*| | jr,d}tdd|}tdd|}|}| j	r| dr`| j
}d| _|| _d| _	d| _
d	| _| |}| dS | j
dkr| jrn| | jnd
}|dv }|dv p| dp| d}	d|v pd|v pd|v }
d}|	rd}n
|r|
rd|v rd}|sd| _	| |S |  j
|7  _
dS | drtd|}|r|d| _d| _	d| _
|S |s| |}|S )z
        Preprocess XML chunk, handle non-standard formats,
        and escape special characters

        Args:
            chunk: Original XML chunk

        Returns:
            Processed XML chunk
        FTz<function=([^>]+)>z<function name="\1">z<parameter=([^>]+)>z<parameter name="\1">r   r"   Nstring)object)arrayarrsequencedictlist[{(rj   z<parameter name=z<parameter name="([^"]+)">rq   )r|   r   r   r   r   r   r   resubr3   r4   r6   r7   r5   rp   _get_param_typematchgroup)r   r   is_tool_call	processedoriginal_chunk	body_text	safe_text
param_typeis_object_typeis_complex_typehas_container_hint
need_defermr   r   r    rz     s   



	


z0StreamingXMLToolCallParser._preprocess_xml_chunkr   c                 C   s   | j | dS )z&Emit Delta response (streaming output)N)r#   r   )r   r   r   r   r    r[   O  s   z&StreamingXMLToolCallParser._emit_deltaincoming_tagc                 C   sL   | j r| d |dv r| jr| d |dkr"| jr$| d dS dS dS )a  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>`.
        rO   )r?   rQ   r?   rQ   N)r*   rV   r'   r%   )r   r   r   r   r    $_auto_close_open_parameter_if_neededS  s   

z?StreamingXMLToolCallParser._auto_close_open_parameter_if_neededrs   attrsc              	   C   s  |dkrdS |dkr"|  d i | _t | _d| _|  jd7  _dS |ds+|dkrd| js4| di  |  d | ||}|| _	d| _
|rbtt| jd | jdt|ddd	gd
}| | dS dS |dsm|dkr|  d | ||}|| _d| _d| _d| _|r| jsd| d}tt| jd | jdtd|dd	gd
}| | d| _dS d| d}tt| jd | jdtd|dd	gd
}| | d| _dS dS dS )zHandle XML start element eventsrootNrQ   Trq   r?   r"   rr   rt   rJ   rO   Fz{"z": z, ")r   r)   r   r%   r-   r$   r|   _start_element_extract_function_namer'   r(   r	   r
   r   r[   _extract_parameter_namer*   r+   r,   r/   )r   rs   r   function_namer   
param_name
json_startjson_continuer   r   r    r   l  s   






z)StreamingXMLToolCallParser._start_elementdatac           	   	   C   s~  |r| j r| jr+|}| jrd| }d| _|dr"d| _|dd }|  j|7  _dS | | j }| js?|dr?|dd }|dv rb| jsbtt	| j
d | jdtdd	d
dgd}| | d| _|sfdS |}| jrrd| }d| _|drd| _|dd }|  j|7  _| | j|}| ||}|t| jd }|| _tt	| j
d | jdtd|d
dgd}| | dS dS dS )z Handle XML character data events
FTNr   rq   r   rC   re   varcharrn   enumr?   ri   rr   rt   r   )r*   r6   r.   endswithr+   r   r|   r/   r	   r
   r$   r%   r   r[   _convert_param_value_convert_for_json_streamingrT   r,   )	r   r   original_datar   quote_deltaconverted_valueoutput_data
delta_datar   r   r   r    
_char_data  sn   







z%StreamingXMLToolCallParser._char_datac              	   C   s  |dkrdS | ds|dks|dkr| jr|   | ds#|dkr| jr| j}| j}| jr| jr5| jn|}d}d}z| jrD|d }n|}t|}t	j
|dd}W n tye   t	j
|dd}|}Y nw tt| jd	 | jdtd|d
dgd}| | d| _|| j|< d| _d| _d| _d| _d| _d| _dS | |}	| ||	}
|	dv r|s| jstt| jd	 | jdtddd
dgd}| | ntt| jd	 | jdtddd
dgd}| | d| _|
| j|< d| _d| _d| _d| _dS | ds|dkr?| jr!tt| jd	 | jdtddd
dgd}| | ntt| jd	 | jdtddd
dgd}| | d| _dS |dkr| jrV| jrQ| d | d tt| jd	 | jdtddd
dgd}| | | j rt| jd}| | |   dS dS )zHandle XML end element eventsr   Nr?   rQ   rO   r   Fensure_asciirq   rr   rt   r   r"   r   z""ri   r=   r>   rR   )r|   r*   r   r+   r6   r7   r.   astliteral_evaljsondumpsrW   r	   r
   r$   r%   r   r[   r)   r,   r/   r   r   r(   rV   r2   r{   r}   )r   rs   r   param_valueraw_textparsed_valueoutput_argumentsraw_for_parser   r   r   rc   r   r   r    rV     s   

























z'StreamingXMLToolCallParser._end_elementc                 C   s*   d| j _| j| j _| j| j _| j| j _dS )z Set up XML parser event handlersTN)r8   buffer_textr   StartElementHandlerrV   EndElementHandlerr   CharacterDataHandlerr   r   r   r    r9     s   

z'StreamingXMLToolCallParser.setup_parserr   c                 C   s
   || _ dS )z"Set tool configuration informationN)r   )r   r   r   r   r    	set_tools  s   
z$StreamingXMLToolCallParser.set_toolsc                 C   L   |r
d|v r
|d S d|v r$| dd}t|dkr$|d dkr$|d S dS )z*Extract function name from various formatsrs   =rq      r   r?   NsplitrT   r   rs   r   partsr   r   r    r        z1StreamingXMLToolCallParser._extract_function_namec                 C   r   )z+Extract parameter name from various formatsrs   r   rq   r   r   rO   Nr   r   r   r   r    r     r   z2StreamingXMLToolCallParser._extract_parameter_namer   c                 C   s  | j r| jsdS | j D ]x}t|drt|drt|jdsq|jdkr|jj| jkrt|jds3 dS |jj}t|trad|v ra|d }||v r^t|| tr^| 	t
|| dd  S  dS t|tr||v r|| }t|tr| 	t
|dd  S  dS qdS )zGet parameter type based on tool configuration, defaults to string
        Args:
            param_name: Parameter name

        Returns:
            Parameter type
        r   rP   r?   rs   r)   
properties)r   r'   hasattrr?   rP   rs   r)   rA   r   repair_param_typerC   get)r   r   toolparamsr   param_configr   r   r    r     sD   




	
 z*StreamingXMLToolCallParser._get_param_typer   c                 C   sz   |dv s9| ds9| ds9| ds9| ds9| ds9| ds9| ds9|d	v s9|d
v s9| ds9| dr;|S dS )zRepair unknown parameter types by treating them as string
        Args:
            param_type: Parameter type

        Returns:
            Repaired parameter type
        r   intuintlongshortunsignednumfloatbooleanboolbinary)r   r   r   r   r   r   r   )r|   )r   r   r   r   r    r     s.   	z,StreamingXMLToolCallParser.repair_param_typer   c              	   C   s  |  dkrdS |   }|dv r|S |ds-|ds-|ds-|ds-|drEzt|W S  ttfyD   td	| Y |S w |d
sO|drvzt|}|t| dkr_|W S t|W S  ttfyu   td| Y |S w |dv r|  }|dkS |S )zConvert value based on parameter type
        Args:
            param_value: Parameter value
            param_type: Parameter type

        Returns:
            Converted value
        nullNr   r   r   r   r   r   z[Parsed value '%s' of parameter '%s' is not an integer in tool '%s', degenerating to string.r   r   r   zXParsed value '%s' of parameter '%s' is not a float in tool '%s', degenerating to string.r   true)	lowerr{   r|   r   
ValueError	TypeErrorrX   rY   r   )r   r   r   float_param_valuer   r   r    r   $  sV   	
z/StreamingXMLToolCallParser._convert_param_valuer   c                 C   sN   |du s|dkr
dS |dv rt j|dddd S t|ts%t j|ddS |S )a   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
        Nr"   r   Fr   rq   r   )r   r   rA   rC   )r   r   r   r   r   r    r   X  s   
z6StreamingXMLToolCallParser._convert_for_json_streamingc                 C   s   t  | _|   | jr| j| _d| _d| _d| _i | _d| _d| _	d| _
d| _d| _d| _d| _d| _d| _d| _d| _d| _dS )z
        Each tool_call is treated as a separate XML document,
        so we need to reset the parser after each tool_call.
        NFr"   )r   r8   r9   r%   r&   r'   r(   r)   r*   r+   r,   r-   r.   r/   r2   r3   r4   r5   r6   r7   r   r   r   r    r}   p  s(   
z<StreamingXMLToolCallParser._reset_xml_parser_after_tool_call)N)$__name__
__module____qualname____doc__r!   r   rC   r	   rd   rp   r   rU   ry   r   tuplerx   rZ   rz   r[   r   r   r   r   rV   r9   r   r   r   r   r   r   r   r   r   r   r}   r   r   r   r    r   !   s4    "{P$C@sZK -""(4r   c                       sr   e Zd Zdef fddZdededefddZd	ed
edede	e
 de	e
 de	e
 dededB fddZ  ZS )Qwen3XMLToolParser	tokenizerc                    s4   t  | t | _g | _g | _td| jj	 d S )Nz)vLLM Successfully import tool parser %s !)
superr!   r   r8   prev_tool_call_arrstreamed_args_for_toolrX   info	__class__r   )r   r   r  r   r    r!     s   zQwen3XMLToolParser.__init__model_outputrequestr;   c              
   C   sV  | j   g | _g | _|r| j |j | j |}|js%tg d|j	dS g }|jD ]t}|j
r|j
jr|t|j|jt|j
j|j
jdd |jd urO|jnt| jd }t| j|krm| jddd t| j|ks]t| j|kr| jd t| j|kst|j
j| j| d< |j
j| j| d< |j
jr|j
j| j|< q*t|t|d	k|j	dS )
NF)rJ   tools_calledrS   rr   )r@   rP   r?   rq   r"   rs   rB   r   )r8   r   r  r  r   r   rd   rJ   r   rS   r?   rs   r   r   r@   rP   r   rB   ru   rT   )r   r  r  resultrJ   rQ   
tool_indexr   r   r    extract_tool_calls  s`   



z%Qwen3XMLToolParser.extract_tool_callsprevious_textcurrent_text
delta_textprevious_token_idscurrent_token_idsdelta_token_idsNc                 C   st  |s| j   g | _g | _|r| j |j |s?|r?|| j j|| j j }|dkr2| j j	dks8| j j	s=|r=t
ddS d S | j |}	|	r|	jr|	jD ]j}
|
jr|
jd urZ|
jnt| jd }t| j|krx| jddd t| j|ksht| j|kr| jd t| j|ks|
jjr|
jj| j| d< |
jjd ur| j| d  |
jj7  < | j|  |
jj7  < qM|	S )Nr   r"   rR   rq   rr   rs   rB   )r8   r   r  r  r   r   countr   r   r$   r	   rd   rJ   r?   ru   rT   r   rs   rB   )r   r  r  r  r  r  r  r  
open_callsr
  rQ   r  r   r   r    extract_tool_calls_streaming  s\   






z/Qwen3XMLToolParser.extract_tool_calls_streaming)r   r   r   r   r!   rC   r   r   r  r   r   r	   r  __classcell__r   r   r  r    r     s4    
B	r   ) r   r   collections.abcr   typingr   xml.parsers.expatr   regexr   vllm.entrypoints.chat_utilsr   0vllm.entrypoints.openai.chat_completion.protocolr   r   'vllm.entrypoints.openai.engine.protocolr   r	   r
   r   r   r   vllm.loggerr   vllm.tokenizersr   &vllm.tool_parsers.abstract_tool_parserr   r   rX   r   r   r   r   r   r    <module>   s.            x