o
    پil!                     @   s   d dl Z d dlZ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
mZ d dlmZmZmZ eeZG dd deZdS )	    N)List)Tool)envs)BaseFormatDetector)StreamingParseResultToolCallItem_GetInfoFuncc                       s   e Zd ZdZ fddZedede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defddZdedeeef fddZded
e
e defddZdd ZdefddZdefddZ  ZS )PythonicDetectora  
    Detector for Llama-4 models with Pythonic tool call format.

    The Pythonic format uses Python function call syntax within square brackets,
    with arguments as Python literals rather than JSON.

    Format Structure:
    ```
    [tool1(arg1=val1, arg2=val2), tool2(arg1=val3)]
    ```

    Reference: https://huggingface.co/meta-llama/Llama-4-Scout-17B-16E-Instruct?chat_template=default
    c                    s   t    tdtj| _d S )Nz\[([a-zA-Z]+\w*\(([a-zA-Z]+\w*=.*,\s*)*([a-zA-Z]+\w*=.*\s)?\),\s*)*([a-zA-Z]+\w*\(([a-zA-Z]+\w*=.*,\s*)*([a-zA-Z]+\w*=.*\s*)?\)\s*)+\])super__init__recompileDOTALLtool_call_regexself	__class__ ^/home/ubuntu/.local/lib/python3.10/site-packages/sglang/srt/function_call/pythonic_detector.pyr   "   s
   

zPythonicDetector.__init__textreturnc                 C   s   |  dd} |  dd} | S )N<|python_start|> <|python_end|>)replace)r   r   r   r   _text_strip)   s   zPythonicDetector._text_stripc                 C   s   t | j| | S N)boolr   searchr   strip)r   r   r   r   r   has_tool_call1   s   zPythonicDetector.has_tool_calltoolsc              
   C   s  |  }| |}| j|}|d u rt|g dS | }| }|dkr+|d | nd}||| }|t|k r?||d  nd}|| }	z{t	|}
t
|
jd dd }t|tjrdtdd |jD skt|	g dW S g }| |}t|jD ]B\}}t|jtjsqw|jj}||vrtd|  tj sqwi }|jD ]}| |j||j< q|t||t j!|dd	d
 qwt|	|dW S  t"y   t#d t|	g d Y S w )N)normal_textcallsr   r   valuec                 s   s    | ]	}t |tjV  qd S r   )
isinstanceastCall).0er   r   r   	<genexpr>O   s    z4PythonicDetector.detect_and_parse.<locals>.<genexpr>z,Model attempted to call undefined function: F)ensure_ascii)
tool_indexname
parametersz$Error in pythonic tool call parsing.)$r    r   r   r   r   startendlenr'   parsegetattrbodyr&   r   allelts_get_tool_indices	enumeratefuncNameidloggerwarningr   SGLANG_FORWARD_UNKNOWN_TOOLSgetkeywords_get_parameter_valuer%   argappendr   jsondumps	Exception	exception)r   r   r"   matchtool_call_starttool_call_endnormal_text_beforetool_call_textnormal_text_afterr#   moduleparsedr$   tool_indices
call_indexcallfunction_name	argumentskeywordr   r   r   detect_and_parse4   s\   






z!PythonicDetector.detect_and_parsebufferr0   c                 C   sV   d}t |t|D ]}|| dkr|d7 }q	|| dkr(|d8 }|dkr(|  S q	dS )a^  
        Find the matching closing bracket for the opening bracket at start position.
        Properly handles nested brackets.

        Args:
            buffer: The text buffer to search in
            start: Position of the opening bracket '['

        Returns:
            Position of the matching closing bracket ']', or -1 if not found
        r   [   ])ranger2   )r   rX   r0   bracket_countir   r   r   _find_matching_bracketq   s   
z'PythonicDetector._find_matching_bracketc                 C   sj   ddg}|D ]%}|  ||}|dkr+|d|  }|| d }| |}||f  S q| |}|dfS )z
        Strip special tokens from buffer and split into safe_text and held_back_text.

        Returns:
            tuple of (safe_text_to_output, text_to_hold_in_buffer)
        r   r   r   Nr   )_ends_with_partial_tokenr   )r   rX   special_tokenstokenpartial_length	safe_text	held_backr   r   r   _strip_and_split_buffer   s   

	z(PythonicDetector._strip_and_split_buffernew_textc                 C   s   |  j |7  _ | | j \}}|d}|dkr || _ t|dS |dkr*|d| nd}| ||}|dkr]|||d  }| ||}	||d d | }
|
| _ |r[||	jpXd |	_|	S ||d | | _ |rmt|dS tddS )z
        Streaming incremental parsing for pythonic tool calls.
        Buffers input until a complete pythonic tool call (from [ to ]) is found,
        then parses and emits any detected calls.
        rY   r\   )r#   r   Nr   rZ   )_bufferrg   findr   r`   rW   r#   )r   rh   r"   stripped_bufferrf   r0   r#   r1   	call_textresultremaining_textr   r   r   parse_streaming_increment   s(   



z*PythonicDetector.parse_streaming_incrementc                    sb   t |tjr	|jS t |tjr fddt|j|jD S t |tjr- fdd|j	D S t
d)Nc                    s   i | ]\}}|j  |qS r   )r%   rB   )r)   kvr   r   r   
<dictcomp>   s    z9PythonicDetector._get_parameter_value.<locals>.<dictcomp>c                    s   g | ]}  |qS r   )rB   )r)   rq   r   r   r   
<listcomp>   s    z9PythonicDetector._get_parameter_value.<locals>.<listcomp>z$Tool call arguments must be literals)r&   r'   Constantr%   Dictzipkeysvaluesr   r7   
ValueError)r   valr   r   r   rB      s   
z%PythonicDetector._get_parameter_valuec                 C   s   dS )NFr   r   r   r   r   supports_structural_tag      z(PythonicDetector.supports_structural_tagc                 C   s   t r   )NotImplementedErrorr   r   r   r   structure_info   r|   zPythonicDetector.structure_info)__name__
__module____qualname____doc__r   staticmethodstrr   r   r!   r   r   r   rW   intr`   tuplerg   ro   rB   r{   r   r~   __classcell__r   r   r   r   r	      s&    =
0r	   )r'   rE   loggingr   typingr   &sglang.srt.entrypoints.openai.protocolr   sglang.srt.environr   -sglang.srt.function_call.base_format_detectorr   #sglang.srt.function_call.core_typesr   r   r   	getLoggerr   r=   r	   r   r   r   r   <module>   s    
