o
    
۾iP                     @   s   d Z ddlZddl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eZdS )a  
GLM-4 Tool Call Parser with incremental string streaming support.

This parser fixes the streaming issue reported in Issue #32829 where long string
parameters (e.g., file content with 4000+ characters of code) are buffered until
complete, causing multi-second delays before the user sees any content.

The fix streams string values incrementally as they arrive, providing a true
streaming experience for long content.
    N)Sequence)Any)make_tool_call_id)ChatCompletionRequestChatCompletionToolsParam)DeltaFunctionCallDeltaMessageDeltaToolCallExtractedToolCallInformationFunctionCallToolCall)init_logger)TokenizerLike)
ToolParserc                       sl  e Zd ZdZdef fddZededefddZ	ed	edefd
dZ
edededee dB defddZededefddZde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d5d"d#Zd5d$d%Zd5d&d'Zd5d(d)Zdedefd*d+Zd,edefd-d.Zd/ed0ededB fd1d2ZdedB fd3d4Z  Z S )6Glm4MoeModelToolParsera  Tool parser for GLM-4 models with incremental string streaming.

    This parser emits tool-call deltas incrementally as arguments arrive.
    For string-type parameters, content is streamed character-by-character
    rather than waiting for the complete </arg_value> tag.
    	tokenizerc                    s   t  | d| _g | _d| _g | _d| _d| _d| _d| _	d| _
d| _| j| _td	tj| _td
tj| _tdtj| _| jsGtd| j| j| _| j| j| _d| _d| _d | _d | _d| _g | _g | _g | _ g | _!d S )NFz<tool_call>z</tool_call>z	<arg_key>z
</arg_key>z<arg_value>z</arg_value>z<tool_call>.*?</tool_call>z%<tool_call>([^\n]*)\n(.*)</tool_call>z7<arg_key>(.*?)</arg_key>\s*<arg_value>(.*?)</arg_value>zUThe model tokenizer must be passed to the ToolParser constructor during construction. )"super__init__current_tool_name_sentprev_tool_call_arrcurrent_tool_idstreamed_args_for_tooltool_call_start_tokentool_call_end_tokenarg_key_startarg_key_endarg_val_startarg_val_endtool_calls_start_tokenrecompileDOTALLfunc_call_regexfunc_detail_regexfunc_arg_regexmodel_tokenizer
ValueErrorvocabgettool_call_start_token_idtool_call_end_token_id_buffer_in_tool_call_current_tool_name_pending_key_streaming_string_value_tool_call_ids_args_started_args_closed
_seen_keys)selfr   	__class__ Z/home/ubuntu/.local/lib/python3.10/site-packages/vllm/tool_parsers/glm4_moe_tool_parser.pyr   3   sD   
zGlm4MoeModelToolParser.__init__valuereturnc              	   C   sH   zt | W S  t jy   Y nw zt| W S  ttfy#   Y | S w )N)jsonloadsJSONDecodeErrorastliteral_evalr(   SyntaxError)r;   r9   r9   r:   _deserialize`   s   z#Glm4MoeModelToolParser._deserializesc                 C   s   | sdS t j| dddd S )zJSON-escape string content for incremental streaming.

        This escapes the content that goes INSIDE a JSON string (between quotes),
        not including the surrounding quotes themselves.
        r   Fensure_ascii   r   )r=   dumps)rD   r9   r9   r:   _json_escape_string_contentn   s   z2Glm4MoeModelToolParser._json_escape_string_content	tool_namearg_nametoolsNc                 C   sp   |d u rdS |D ]'}|j j| krq|j jd u r dS |j jdi |i dd }|dk  S td|  dS )NF
propertiestypestringzNo tool named '%s'.)functionname
parametersr*   loggerdebug)rJ   rK   rL   toolarg_typer9   r9   r:   _is_string_typey   s   z&Glm4MoeModelToolParser._is_string_typerequestc                 C   sJ   zt | dd}t | dd}t|o|dkW S  ty$   td Y dS w )z?Return whether tool parsing should be applied for this request.rL   Ntool_choicenonez)Failed to determine if tools are enabled.F)getattrbool	ExceptionrS   	exception)rX   rL   rY   r9   r9   r:   _tools_enabled   s   
z%Glm4MoeModelToolParser._tools_enabledc                    s&   t  |}|jr|jdkrd|_|S )z7Adjust request parameters for tool call token handling.rZ   F)r   adjust_requestrL   rY   skip_special_tokens)r6   rX   r7   r9   r:   r`      s   z%Glm4MoeModelToolParser.adjust_requestmodel_outputc                 C   sR  | j |}td| zig }|D ]b}| j|}|s"td| q|d }|d}|r6| j	|ng }	i }
|	D ]$\}}| }| }| 
|||jsU| |}td|| ||
|< q<|tdt|tj|
ddd	d
 qW n ty   td tdg |d Y S w t|dkr|d || j }td||dS tdg |dS )Nzmodel_output: %sz*Failed to parse tool call details from: %srG      zarg_key = %s, arg_val = %srP   FrE   rQ   	arguments)rN   rP   z Failed to extract tool call spec)tools_called
tool_callscontentr   T)r$   findallrS   rT   r%   searchwarninggroupstripr&   rW   rL   rC   appendr   r   r=   rH   r]   r^   r
   lenfindr    )r6   rb   rX   matched_tool_callsrg   match	tc_detailtc_nametc_argspairsarg_dctkeyr;   arg_keyarg_valrh   r9   r9   r:   extract_tool_calls   s^   




z)Glm4MoeModelToolParser.extract_tool_callsprevious_textcurrent_text
delta_textprevious_token_idscurrent_token_idsdelta_token_idsc           "   
   C   s2  |  |s|rt|dS d S |  j|7  _	 | js| j| j}|dkrgtdt| jD ]+}	| j| jd |	 rW| jd |	  }
| j|	 d  | _|
rSt|
d  S d   S q,| j}
d| _|
ret|
dS d S |dkr| jd | }
| j|d  | _|
rt|
dS d S | jt| jd  | _| 	  q| j
s| jd}| j| j}| j| j}dd	 |||fD }|sd S t|}| jd |  }|dkr||kr| j|t| j d  | _|   |   q||kr| j|d d  | _n| j|d  | _|| _d| _
| |S | jd usJ | jr| j| j}|dkrP| jd | }| j|t| j d  | _d
| _d | _| |}|d }| j| j  |7  < | |S t| j}tdt| jD ]}	| j| jd |	 rtt| j|	 } nq]|dkr| jd | }| j|d  | _| |}|r| j| j  |7  < | |S d S | jd urf| j| j}|dkrd S |dkr| j|d  | _| jpd }| | j||j}|r,| jt| jd  | _|| j| j v rd | _q| j| j | tj |d
d}| j!| j sd| d }d| j!| j< nd| d }| j| j  |7  < d| _| |S | j| j}|dkr:d S | jt| j|  }| j|t| j d  | _d | _| j"||d}|re| |S q| j| j}| j| j}|dkr|dks||k r| j|t| j d  | _| # }| jrz| j| j }t$|}| j|d| j%| j< W n tj&t'fy }  zt()d| j|  W Y d } ~ nd } ~ ww |   |r| |S d S |dkrd S |dkr| j|d  | _| j| j*}!|!dkrd S | jt| j|! }| j|!t| j* d  | _|| _q)N)rh   Tr   rG   r   r   
c                 S   s   g | ]}|d kr|qS )r   r9   ).0ir9   r9   r:   
<listcomp>  s    zGGlm4MoeModelToolParser.extract_tool_calls_streaming.<locals>.<listcomp>F"rE   {z:",)rx   raw_valrd   z2Failed to finalize tool call state for tool %d: %s)+r_   r   r-   r.   rp   r   rangero   endswith_begin_tool_callr   r   r   minrm   _finish_tool_call_revert_last_tool_call_stater/   _emit_tool_name_deltar1   r   r0   rI   r   r   _emit_tool_args_deltar   rW   rL   r5   addr=   rH   r3   _append_arg_fragment_close_args_if_neededr>   r   r?   
IndexErrorrS   rk   r   )"r6   r|   r}   r~   r   r   r   rX   	start_idxr   outnlakend
candidatescutrJ   val_endraw_contentescapedfragsafe_lento_emitval_posrx   	is_stringkey_jsonr   end_poskey_posfull_args_str	args_dictekey_endr9   r9   r:   extract_tool_calls_streaming   s  



















z3Glm4MoeModelToolParser.extract_tool_calls_streamingc                 C   s  t | j| jkr| jtdd d d t | j| jkst | j| jkr1| jd t | j| jks#t | j| jkrG| ji  t | j| jks9t | j| jkr]| jd t | j| jksOt | j| jkrs| jd t | j| jkset | j	| jkr| j	t
  t | j	| jks{d S d S )Nrandom)id_type	func_nameidxr   F)ro   r2   r   rn   r   r   r   r3   r4   r5   setr6   r9   r9   r:   _ensure_tool_state  s(   z)Glm4MoeModelToolParser._ensure_tool_statec                 C   sJ   | j dkr	d| _ n|  j d7  _ |   d| _d | _d | _d| _d| _d S )Nr   r   rG   FT)r   r   r   r/   r0   r1   r.   r   r9   r9   r:   r     s   

z'Glm4MoeModelToolParser._begin_tool_callc                 C   s   d| _ d | _d | _d| _d S )NF)r.   r/   r0   r1   r   r9   r9   r:   r     s   
z(Glm4MoeModelToolParser._finish_tool_callc                 C   s\   | j dk rdS | j  | j  | j  | j  | j  | j  |  j d8  _ dS )z3Revert the state allocation for the last tool call.r   NrG   )r   r2   popr   r   r3   r4   r5   r   r9   r9   r:   r     s   






z3Glm4MoeModelToolParser._revert_last_tool_call_statec              	   C   s2   t t| j| j| j dt|ddjdddgdS )NrP   r   rd   Texclude_none)indexidrN   rP   rg   )r   r	   r   r2   r   
model_dump)r6   rJ   r9   r9   r:   r     s   
z,Glm4MoeModelToolParser._emit_tool_name_deltafragmentc                 C   s$   t t| jt|djdddgdS )N)re   Tr   )r   rP   r   )r   r	   r   r   r   r6   r   r9   r9   r:   r     s   
z,Glm4MoeModelToolParser._emit_tool_args_deltarx   r   c                C   s   |  }|sd S || j| j v rd S | |}tj|dd}tj|dd}| j| j s:d| d | }d| j| j< nd| d | }| j| j | | j| j  |7  < |S )NFrE   r   :Tr   )	rm   r5   r   rC   r=   rH   r3   r   r   )r6   rx   r   val_objr   val_jsonr   r9   r9   r:   r     s   
z+Glm4MoeModelToolParser._append_arg_fragmentc                 C   sX   | j | j rd S d| j | j< | j| j sd}|| j| j< |S d}| j| j  |7  < |S )NTz{}})r4   r   r3   r   r   r9   r9   r:   r     s   z,Glm4MoeModelToolParser._close_args_if_needed)r<   N)!__name__
__module____qualname____doc__r   r   staticmethodstrr   rC   rI   listr   r\   rW   r   r_   r`   r
   r{   r   intr   r   r   r   r   r   r   r   r   r   __classcell__r9   r9   r7   r:   r   +   sv    -



4	
 
<



r   )r   r@   r=   collections.abcr   typingr   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   rS   r   r9   r9   r9   r:   <module>   s    