o
    پi/                     @   s   d dl Z d dlZd dlmZmZmZmZ d dlmZ d dl	m
Z
 d dlmZmZmZmZ d dlmZ eeZG dd de
ZdS )	    N)AnyListOptionalTuple)Tool)BaseFormatDetector)StreamingParseResultStructureInfoToolCallItem_GetInfoFunc)_is_complete_jsonc                       s   e Zd ZdZ fddZdedefddZdedee	 de
fd	d
Zdedee	 de
f fddZdedeeeeef  fddZdededeee ef fddZdedefddZdefddZ  ZS )MistralDetectora  
    Detector for Mistral tool/function call formats.

    Supported formats:

    1) JSON-array format:
       `[TOOL_CALLS] [{"name": "...", "arguments": {...}}, ...]`

    2) Compact format (common in newer templates/models, especially in streaming):
       `[TOOL_CALLS]tool_name[ARGS]{...}`
       (also tolerates missing delimiters like `]` after `[TOOL_CALLS` and/or `[ARGS]` while streaming)

    Reference: https://huggingface.co/mistralai/Mistral-7B-Instruct-v0.3?chat_template=default
    c                    s&   t    d| _d| _d| _d| _dS )z&Initialize tokens and streaming state.z[TOOL_CALLS] [z[TOOL_CALLS]z, N)super__init__	bot_token_tool_calls_marker	eot_tokentool_call_separatorself	__class__ ]/home/ubuntu/.local/lib/python3.10/site-packages/sglang/srt/function_call/mistral_detector.pyr   "   s
   

zMistralDetector.__init__textreturnc                 C   s
   | j |v S )zCReturn True if the text contains either supported tool-call marker.)r   )r   r   r   r   r   has_tool_call,   s   
zMistralDetector.has_tool_calltoolsc              
   C   s  | | j}|dkrt|g dS |d|  }||d }| j|v r| |}|s0t|g dS g }zt|}t|t	s@|g}| 
||}W n  tjyg }	 ztd| dt|	  W Y d}	~	nd}	~	ww |ro| |nd}
|
dkr||
t| d  nd}|r|d |  n|}t||dS | |}|st|g dS |\}}}| 
||d|}||d  }|r|d |  n|}t||dS )	a$  
        One-time parsing: Detects and parses tool calls in the provided text.

        :param text: The complete text to parse.
        :param tools: List of available tools.
        :return: ParseResult indicating success or failure, consumed text, leftover text, and parsed calls.
        normal_textcallsNzFailed to parse JSON part: z, JSON parse error:   name	arguments)findr   r   stripr   _extract_json_arrayjsonloads
isinstancelistparse_base_jsonJSONDecodeErrorloggerwarningstrlen_try_parse_compact_args_format)r   r   r   
marker_idxr!   	tool_partjson_array_strr"   function_call_arrejson_postrailing_textcombined_normalparsed	func_nameargs_objconsumedr   r   r   detect_and_parse0   sT   





z MistralDetector.detect_and_parsenew_textc                    s  |  j |7  _ | j }| j|vr1| | j | js.| j }d| _ | j|v r)|| jd}t|dS t S || j}|dkrM|d| }||d | _ t|dS t| dsX| || _	| 
|}|r|\}}}	|| j	vrt| j }d| _ t|dS | jdkrd| _g | _g | _tj|dd}
| j}t| j|kr| ji  t| j|kst| j|kr| jd t| j|ks||d	| j|< |
| j|< t||dd
t|d|
d
g}||	d | _ |  jd7  _d| _td|dS | j|v rt jd|dS t S )a.  
        Streaming parsing for both JSON-array and compact formats.

        For the compact format, this buffers until the JSON arguments payload is complete,
        then emits two items: tool name (with empty parameters) and a full arguments JSON
        chunk (OpenAI streaming semantics).
        r#   )r!   r   N_tool_indicesr   F)ensure_asciir%   )
tool_indexr&   
parameters   r    )rC   r   )_bufferr   _ends_with_partial_tokenr   replacer   r(   hasattr_get_tool_indicesrD   r5   current_tool_idprev_tool_call_arrstreamed_args_for_toolr+   dumpsr4   appendr
   current_tool_name_sentr   r   parse_streaming_increment)r   rC   r   current_textr!   
marker_poscompactr?   r@   rA   	args_jsontool_idr"   r   r   r   rT   k   sd   












z)MistralDetector.parse_streaming_incrementc                 C   s  | | j}|dkrdS |t| j }|t|k r#|| dkr#|d7 }|t|k r?||  r?|d7 }|t|k r?||  s/d}| ||}|dkrMdS |||  }|sYdS |t| }|t|k ro|| dkro|d7 }|t|k r||  r|d7 }|t|k r||  s{|t|ks|| dvrdS | ||\}}	|sdS t|sdS zt|}
W n tj	y   Y dS w ||
|	fS )av  
        Parse the compact tool call format:
            `[TOOL_CALLS]tool_name[ARGS]{...}`

        Tolerates common streaming variants where delimiters are missing:
            `[TOOL_CALLStool_name[ARGS{...}`

        Returns:
            (tool_name, arguments_obj, consumed_end_index) if a complete JSON arguments
            payload is present; otherwise None.
        r   Nr   rH   z[ARGS{[)
r(   r   r4   isspacer)   _extract_json_valuer   r+   r,   r0   )r   r   startiargs_markerargs_posr?   jjson_strend_idxr@   r   r   r   r5      sF   
z.MistralDetector._try_parse_compact_args_format
json_startc           
      C   s   |t |ks|| dvrd|fS || }|dkrdnd}d}d}d}t|t |D ]B}|| }	|r4d}q)|	dkr;d	}q)|	d
krE|sE| }q)|rHq)|	|krQ|d7 }q)|	|krk|d8 }|dkrk|||d  |d f  S q)d|fS )z
        Extract a JSON value (object or array) starting at json_start using bracket counting,
        robust to nested braces/brackets inside strings.

        Returns:
            (json_str_or_None, end_index_exclusive)
        rZ   N{}r   r   F\T"rH   )r4   range)
r   r   rd   openingclosingdepth	in_stringescape_nextkchr   r   r   r\      s8   

z#MistralDetector._extract_json_valuec           	      C   s   | | j}|dkrdS |t| j d }d}d}d}t|t|D ]=}|| }|r-d}q"|dkr4d}q"|dkr>|s>| }q"|s_|d	krI|d7 }q"|d
kr_|d8 }|dkr_|||d    S q"dS )z
        Extract the JSON array part using bracket counting to handle nested brackets.

        :param text: The complete text containing [TOOL_CALLS] [...]
        :return: The JSON array string or None if not found
        r   NrH   r   Frg   Trh   [r   )r(   r   r4   ri   )	r   r   	start_idxrd   bracket_countrm   rn   r^   charr   r   r   r*     s8   
z#MistralDetector._extract_json_arrayc                 C   s   dd S )Nc                 S   s   t d|  d dddS )Nz[TOOL_CALLS] [{"name":"z", "arguments":z}]z[TOOL_CALLS])beginendtrigger)r	   )r&   r   r   r   <lambda>K  s
    
z0MistralDetector.structure_info.<locals>.<lambda>r   r   r   r   r   structure_infoJ  s   zMistralDetector.structure_info)__name__
__module____qualname____doc__r   r3   boolr   r   r   r   rB   rT   r   r   r   intr5   r\   r*   r   ry   __classcell__r   r   r   r   r      s4    
;S
7
),r   )r+   loggingtypingr   r   r   r   &sglang.srt.entrypoints.openai.protocolr   -sglang.srt.function_call.base_format_detectorr   #sglang.srt.function_call.core_typesr   r	   r
   r   sglang.srt.function_call.utilsr   	getLoggerrz   r1   r   r   r   r   r   <module>   s    
