o
    :/i7                     @   sN  d dl Z d dlZd dlmZmZ d dlmZ d dlZd dlmZm	Z	 d dl
mZ d dlmZ d dlmZmZ d dlmZmZmZmZ d d	lmZ eeZd
ededefddZd
ededefddZdededefddZdededeeef fddZ dede!fddZ"dededefddZ#deeB deee$eef dB f fd d!Z%deeB de$fd"d#Z&d$e'eeB  de$fd%d&Z(d$e'eeB  de$fd'd(Z)d)ee	B eB d$e'eeB  dB dee$B dB fd*d+Z*G d,d- d-e+Z,dd.d/d0Z-d1e j.defd2d3Z/d4e j0defd5d6Z1d7edeeef dB fd8d9Z2d:ed;ed<ed=ededB f
d>d?Z3dS )@    N)JSONDecodeErrorJSONDecoder)Any)FunctionToolToolChoiceFunction)Tool)Allow)"ChatCompletionNamedToolChoiceParamChatCompletionToolsParam)DeltaFunctionCallDeltaToolCallFunctionCallToolCall)init_loggers1s2returnc                 C   sL   d}t t| t|}td|D ]}| | || kr!|| | 7 }q |S |S )a  
    Finds a common prefix that is shared between two strings, if there is one.
    Order of arguments is NOT important.

    This function is provided as a UTILITY for extracting information from JSON
    generated by partial_json_parser, to help in ensuring that the right tokens
    are returned in streaming, so that close-quotes, close-brackets and
    close-braces are not returned prematurely.

    e.g. find_common_prefix('{"fruit": "ap"}', '{"fruit": "apple"}') ->
    '{"fruit": "ap'
     r   )minlenrange)r   r   prefix
min_lengthi r   d/lsinfo/ai/hellotax_ai/llm_service/venv_vllm/lib/python3.10/site-packages/vllm/tool_parsers/utils.pyfind_common_prefix    s   r   c                 C   sd   d}t t| t|}td|d D ]}| |  ||  kr-| |   s-| |  | }q |S |S )a  
    Finds a common suffix shared between two strings, if there is one. Order of
    arguments is NOT important.
    Stops when the suffix ends OR it hits an alphanumeric character

    e.g. find_common_suffix('{"fruit": "ap"}', '{"fruit": "apple"}') -> '"}'
    r      )r   r   r   isalnum)r   r   suffixr   r   r   r   r   find_common_suffix7   s   "r    curroldc                 C   s   t | |}|ddd |ddd ddddd }t| |}| }t|r<|ddd |ddd ddddd }t|rG||dd}|S )a  
    Given two strings, extract the difference in the middle between two strings
    that are known to have a common prefix and/or suffix.

    This function is provided as a UTILITY for extracting information from JSON
    generated by partial_json_parser, to help in ensuring that the right tokens
    are returned in streaming, so that close-quotes, close-brackets and
    close-braces are not returned prematurely. The order of arguments IS
    important - the new version of the partially-parsed JSON must be the first
    argument, and the secnod argument must be from the previous generation.

    What it returns, is tokens that should be streamed to the client.

    e.g. extract_intermediate_diff('{"fruit": "apple"}', '{"fruit": "ap"}')
        -> 'ple'

    Nr   r   )r    replacer   r   )r!   r"   r   r   diffr   r   r   extract_intermediate_diffI   s   
,
,r&   	input_strflagsc              
   C   sX   zt | |t| fW S  ty+ } zd|jv r&t }|| W  Y d }~S  d }~ww )Nz
Extra data)partial_json_parserloadsr   r   msgr   
raw_decode)r'   r(   edecr   r   r   partial_json_loadsl   s   
r/   c                 C   s&   zt |  W dS  ty   Y dS w )NTF)jsonr*   r   )r'   r   r   r   is_complete_jsonv   s   
r1   r   sc                 C   s<   | t |k r||   r| d7 } | t |k r||   s| S )Nr   )r   isspace)r   r2   r   r   r   consume_space~   s   r4   toolc                 C   sB   t | tr| j| jfS t | tr| jj| jjfS tdt|  )NzUnsupported tool type: )
isinstancer   name
parametersr
   function	TypeErrortype)r5   r   r   r   _extract_tool_info   s
   

r<   c                 C   s:   t | \}}|r
|ndi d}d|gd|dddgdS )	Nobject)r;   
propertiesstring)r;   enum)r7   r8   r7   r8   )r>   required)r<   )r5   r7   paramsr   r   r   _get_tool_schema_from_tool   s   
rC   toolsc                 C   sr   i }| D ]2}t |\}}|d u rq|di }| D ]\}}||v r1|| |kr1td| d|||< qq|S )N$defszTool definition 'z/' has multiple schemas, which is not supported.)r<   popitems
ValueError)rD   all_defsr5   _rB   defsdef_name
def_schemar   r   r   _get_tool_schema_defs   s   

rN   c                 C   s4   ddddd | D dd}t | }|r||d< |S )	Narrayr   r=   c                 S      g | ]}t |qS r   )rC   .0r5   r   r   r   
<listcomp>       z/_get_json_schema_from_tools.<locals>.<listcomp>)r;   anyOf)r;   minItemsrG   rE   )rN   )rD   json_schemajson_schema_defsr   r   r   _get_json_schema_from_tools   s   rY   tool_choicec                 C   s   | dv s|d u r
d S t | ts/t | tr/| j}dd |D }||vr*td| d|| jS t | tsVt | trV| jj}dd |D }||vrPtd| d|| jjS | dkr^t|S d S )N)noneNc                 S   s   i | ]}t |tr|j|qS r   )r6   r   r7   rQ   r   r   r   
<dictcomp>   s    z.get_json_schema_from_tools.<locals>.<dictcomp>zTool 'z!' has not been passed in `tools`.c                 S   s    i | ]}t |tr|jj|qS r   )r6   r
   r9   r7   rQ   r   r   r   r\      s    rA   )	r6   strr   r7   rH   r8   r	   r9   rY   )rZ   rD   	tool_nametool_mapr   r   r   get_json_schema_from_tools   s.   
r`   c                   @   s   e Zd ZdZdS )UnexpectedAstErrorzXRaised when the AST structure does not match the expected
    pythonic tool call format.N)__name__
__module____qualname____doc__r   r   r   r   ra      s    ra   TF)nulltruefalsevalc                 C   s   t | tjr	| jS t | tjr2tdd | jD s&tdt	|  t
ddd t| j| jD S t | tjr@dd | jD S t | tjrP| jtv rPt| j S td	t	|  t
d
)aB  Extract a Python literal value from an AST expression node.

    Handles constants, dicts, lists, and JSON-style name literals
    (null, true, false) that some models produce instead of Python
    literals (None, True, False).

    Raises:
        UnexpectedAstError: If the AST node is not a supported literal type.
    c                 s   s    | ]	}t |tjV  qd S )N)r6   astConstant)rR   kr   r   r   	<genexpr>  s    z&get_parameter_value.<locals>.<genexpr>z+Dict argument keys are not all literals: %sz/Dict tool call arguments must have literal keysc                 S   s   i | ]
\}}|j t|qS r   )valueget_parameter_value)rR   rl   vr   r   r   r\   
  s    
z'get_parameter_value.<locals>.<dictcomp>c                 S   rP   r   )ro   )rR   rp   r   r   r   rS     rT   z'get_parameter_value.<locals>.<listcomp>z4Unsupported AST node type in tool call arguments: %sz$Tool call arguments must be literals)r6   rj   rk   rn   Dictallkeysloggerwarningdumpra   zipvaluesListeltsNameid_JSON_NAME_LITERALS)ri   r   r   r   ro      s*   

ro   callc                 C   sp   t | jtjstdt| j td| jj}i }| j	D ]
}t
|j||j< qtdt|tj|ddddS )zConvert a single AST function call node into a ToolCall object.

    Raises:
        UnexpectedAstError: If the call node does not have a simple
            function name (e.g. it's an attribute access or subscript).
    z*Tool call has non-simple function name: %szInvalid tool call namer9   F)ensure_asciir7   	arguments)r;   r9   )r6   funcrj   r{   rt   ru   rv   ra   r|   keywordsro   rn   argr   r   r0   dumps)r~   function_namer   keywordr   r   r   handle_single_tool  s"   

r   textc                 C   s  g }t | D ]m\}}|dv r|| q|dkr%|r | dkr$tdq|dkr6|r1| dkr5tdq|dkrG|rB| d	krFtd
q|dv rs|re|d |kre|dkr`| |d  dkr`q|  q|rn|d dv rnq|| q|  } | ds| drdS |r|d d	kr| d| d	 }|d}|d}||krdS |r|d dkr| d| d }|d}|d}	||	krdS | dr| dd } |r|d dkr| ds| dsdS dddddd}
d}t|D ]}||
| 7 }q| | |fS )a  Attempt to close all open brackets/quotes to make partial Python valid.

    Used during streaming to parse incomplete tool call expressions by
    appending the necessary closing characters.

    Returns:
        A tuple of (completed_text, added_suffix) if the text can be
        made valid, or None if the text is too incomplete to complete
        meaningfully (e.g. mid-parameter-name or mid-dict-key).

    Raises:
        UnexpectedAstError: If mismatched brackets or parentheses
            are detected.
    >   {([]r   zMismatched square brackets)r   zMismatched parentheses}r   zMismatched curly braces>   "'r#   r   r   \=:N,r   r   )r   r   r   r   r   r   )		enumerateappendrF   ra   rstripendswithrfindcountreversed)r   bracket_stackindexchartrailing_dict_textnum_keys
num_valuestrailing_params_textnum_full_param_namesnum_full_param_values_CLOSING
added_textr   r   r   make_valid_python4  sn   






r   previously_sent_argsnew_callr   withheld_suffixc                 C   s   |j j}|r&||sd| d| d}t| t||dt|  }| s7t|jd|t	|j j
|ddS |t| d }|rKtd|t	|dd	S dS )
a  Compute the incremental delta between previously streamed arguments
    and the current tool call state.

    Returns:
        A DeltaToolCall with only the new argument characters, or None
        if there is no difference from what was previously sent.
    zTool call arguments 'z,' do not end with expected withheld suffix 'r   Nr9   r   )r|   r;   r   r9   )r   )r|   r   r9   )r9   r   r   rt   errorrH   r   r   r|   r   r7   )r   r   r   r   new_call_argsr+   arg_diffr   r   r   compute_tool_delta|  s<   


r   )4rj   r0   r   r   typingr   r)   openai.types.responsesr   r   openai.types.responses.toolr    partial_json_parser.core.optionsr   0vllm.entrypoints.openai.chat_completion.protocolr	   r
   'vllm.entrypoints.openai.engine.protocolr   r   r   r   vllm.loggerr   rb   rt   r]   r   r    r&   tupleintr/   boolr1   r4   dictr<   rC   listrN   rY   r`   	Exceptionra   r}   exprro   Callr   r   r   r   r   r   r   <module>   sx   #








*#H