
    .`io                         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
 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 d d
lmZ  ee          Z G d de          ZdS )    N)Sequence)Any)make_tool_call_id)ChatCompletionRequest)DeltaFunctionCallDeltaMessageDeltaToolCallExtractedToolCallInformationFunctionCallToolCall)init_logger)TokenizerLike)
ToolParser)extract_intermediate_diffc                       e Zd Zdef fdZdedefdZdedefdZdedefd	Zded
e	de
fdZdeddfdZdedefdZdedefdZdedeeef         fdZdedefdZd0dZd0dZdeddfdZdefdZdedefdZdeddfdZdedefdZdedeeeef                  fdZdedej        e         defd Zded!edeedz  edz  f         fd"Z dedede!dz  fd#Z"dedede!dz  fd$Z#d%edefd&Z$d'ed%eded(e%e         d)e%e         d*e%e         d
e	de!dz  fd+Z&d%ededz  fd,Z'd%eded-ededz  fd.Z(d%ed-edefd/Z) xZ*S )1MinimaxToolParser	tokenizerc                 b   t                                          |           dg g d| _        d| _        d| _        t          j        dt
          j                  | _        d| _	        t          j        d          | _
        t          j        d          | _        d	| _        d
| _        | j        st          d          | j                            | j                  | _        | j                            | j                  | _        | j        | j        t(                              d           d S d S )Ncurrent_tool_indextool_ids
sent_toolsz<tool_calls>z</tool_calls>z/<tool_calls>(.*?)</tool_calls>|<tool_calls>(.*)<think>(.*?)</think>z"name":\s*"([^"]+)"z"arguments":\s* FzUThe model tokenizer must be passed to the ToolParser constructor during construction.zrMinimax Tool parser could not locate tool call start/end tokens in the tokenizer. Falling back to string matching.)super__init__streaming_statetool_call_start_tokentool_call_end_tokenrecompileDOTALLtool_call_regexthinking_tag_patterntool_name_patterntool_args_patternpending_bufferin_thinking_tagmodel_tokenizer
ValueErrorvocabgettool_call_start_token_idtool_call_end_token_idloggerwarning)selfr   	__class__s     y/home/jaya/work/projects/VOICE-AGENT/VIET/agent-env/lib/python3.11/site-packages/vllm/tool_parsers/minimax_tool_parser.pyr   zMinimaxToolParser.__init__!   s:   ### #%0
 0
 &4"#2 !z>	 
  
 %<!!#,B!C!C!#,>!?!? !$# 	3   )-
t7Q(R(R%&*jnnT5M&N&N#(0D4O4WNNL     5X4W    model_outputreturnc                 V    d }t          j        | j        ||t           j                  S )z
        Preprocess model output by removing tool calls from thinking tags.

        Args:
            model_output: Raw model output string

        Returns:
            Preprocessed model output with tool calls removed from thinking tags
        c                 |    |                      d          }t          j        dd|t          j                  }d| dS )N   z<tool_calls>.*?</tool_calls>r   flags<think></think>)groupr!   subr#   )matchthink_contentcleaned_contents      r4   remove_tool_calls_from_thinkzOMinimaxToolParser.preprocess_model_output.<locals>.remove_tool_calls_from_thinkT   sE    !KKNNM f/]")  O 7_6666r5   r;   )r!   r@   r%   r#   )r2   r6   rD   s      r4   preprocess_model_outputz)MinimaxToolParser.preprocess_model_outputI   s=    	7 	7 	7 v%()	
 
 
 	
r5   	args_textc                 R   |                                 }|s|S 	 t          j        |           |S # t          j        $ r Y nw xY w|                    d          rM|dd         }	 t          j        |           |S # t          j        $ r |}Y nw xY w|                    d          M|S )z
        Clean duplicate closing braces from arguments text.

        Args:
            args_text: Raw arguments text

        Returns:
            Cleaned arguments text with proper JSON formatting
        z}}Nr   )stripjsonloadsJSONDecodeErrorendswith)r2   rF   	candidates      r4   _clean_duplicate_bracesz)MinimaxToolParser._clean_duplicate_bracesb   s     OO%%	 		Jy!!!# 	 	 	D	   && 	&!#2#I&
9%%%  ' & & &%			&   && 	& s!   0 AA%A; ;BB
delta_textc                     |s|S |                                 }|rMt          d |D                       r4|                    d          }|dk    r|                    d          rdndS |S )z
        Clean delta text by removing excessive closing braces.

        Args:
            delta_text: Delta text to clean

        Returns:
            Cleaned delta text
        c              3      K   | ]}|d v V  	dS )z}
	 N ).0cs     r4   	<genexpr>z8MinimaxToolParser._clean_delta_braces.<locals>.<genexpr>   s&      !J!Ja!z/!J!J!J!J!J!Jr5   }r:   
z}
)rH   allcountrL   )r2   rO   delta_strippedbrace_counts       r4   _clean_delta_bracesz%MinimaxToolParser._clean_delta_braces   s      	#))++ 	Cc!J!J>!J!J!JJJ 	C(..s33KQ * 3 3D 9 9BuusBr5   requestc                    |                      |          }| j        |vrt          dg |          S 	 | j                            |          }g }|D ]}|d         r|d         n|d         }|                                r|                                                    d          }|D ]}	|	                                }	|	rj|	                    d          rU|	                    d          r@	 t          j
        |	          }
|                    |
           m# t          j        $ r Y ~w xY wg }|D ]]}d|v rWd	|v rS|                    t          d
t          |d         t          j        |d	         d                                         ^|                    | j                  }|dk    r|d|                                         }|rw|                    d          }t#          |          D ]O}	|	                                }	|	r7|                    |	          }|dk    r|d|t%          |	          z            } nPd}nd}n|}t          t%          |          dk    ||                                r|                                nd          S # t&          $ r/ t(                              d           t          dg |          cY S w xY w)a  
        Extract tool calls from model output for non-streaming mode.

        Args:
            model_output: Complete model output
            request: Chat completion request

        Returns:
            ExtractedToolCallInformation containing tool calls and content
        F)tools_called
tool_callscontentr   r:   rW   {rV   name	argumentsfunction)ensure_ascii)rc   rd   )typere   r   Nr   z9An unexpected error occurred during tool call extraction.)rE   r   r
   r$   findallrH   split
startswithrL   rI   rJ   appendrK   r   r   dumpsfindreversedlen	Exceptionr0   	exception)r2   r6   r]   processed_outputfunction_call_tuplesraw_function_callsrA   tool_call_contentlineslineparsed_callr`   function_callprocessed_posprocessed_contentposra   s                    r4   extract_tool_callsz$MinimaxToolParser.extract_tool_calls   sh     77EE%-===/"r<   @	#'#7#?#?@P#Q#Q !#- ) )05a$FE!HHeAh!$**,, 	)-3355;;DAAE % ) )#zz|| )DOOC$8$8 )T]]3=O=O )).2j.>.> 2 9 9+ F F F F#'#7 ) ) ) () J!3  ]**{m/K/K%% !+%1%26%:*.*$1+$>U+" +" +"& & &  
 
 
 -11$2LMMM""$4^m^$D$J$J$L$L!$ !-33D99E ( % %#zz|| &"."3"3D"9"9C"byy*67Hs4yy7H*I %"$ GG&/ __q0%+2==??D     	 	 	K   0"r<     		s7   B7J *)DJ D&#J %D&&E+J 6K
KtextNc                     |                     d          }|                     d          }||k    p||k    o|                    d          | _        dS )z
        Update the thinking tag state based on text content.

        Args:
            text: Text to analyze for thinking tags
        r=   r>   N)rY   rL   r)   )r2   r~   
open_countclose_counts       r4   _update_thinking_statez(MinimaxToolParser._update_thinking_state   sZ     ZZ	**
jj,,)K7  
+%C$--
*C*C 	r5   c                     | j         | j        fD ]Zt          fdt          dt	          t                    dz   t                                        D                       r dS [dS )z
        Check if text might be the start of a tool call tag.

        Args:
            text: Text to check

        Returns:
            True if text could be the start of a tool call tag
        c              3   T   K   | ]"}                     | d                    V  #d S N)rj   )rS   itagr~   s     r4   rU   z<MinimaxToolParser._is_potential_tag_start.<locals>.<genexpr>  sM         tQBCCy))     r5   r:   TF)r   r    anyrangeminro   )r2   r~   r   s    `@r4   _is_potential_tag_startz)MinimaxToolParser._is_potential_tag_start   s     .0HI 	 	C     q#c$ii!mSXX">">??      tt	
 ur5   c                     | j         rdS t          | j        p&| j        |v p| j        |v p|                    d                    S )z
        Determine if content should be buffered for later processing.

        Args:
            delta_text: Delta text to check

        Returns:
            True if content should be buffered
        F<)r)   boolr(   r   r    rj   )r2   rO   s     r4   _should_buffer_contentz(MinimaxToolParser._should_buffer_content  sd      	5 *)Z7*':5* $$S))	
 
 	
r5   c                 6   | j         r|dfS | j        | j        fD ]|}t          dt	          |                    D ]\}|d|         }|                    |          }|dk    r5|                    ||d                   r|d|         ||d         fc c S ]}|dfS )z
        Split delta text into safe content and potential tag content.

        Args:
            delta_text: Delta text to split

        Returns:
            Tuple of (safe_content, potential_tag_content)
        r   r:   Nr   )r)   r   r    r   ro   rfindrj   )r2   rO   r   r   
tag_prefixr|   s         r4   _split_content_for_bufferingz.MinimaxToolParser._split_content_for_buffering   s      	"r>!.0HI 	> 	>C1c#hh'' > > !W
 &&z22"99
3440@!A!A9%dsd+Z-=======	>
 2~r5   new_contentc                 (   | xj         |z  c_         d}| j        r| j         }d| _         |S | j         r| j                             | j                  }| j                             | j                  }|dk    r#|dk    s||k     r|t          | j                  }}nJ|dk    r|t          | j                  }}n-|                     | j                   rnB|| j         z  }d| _         n0|| j         d|         z  }| j         ||z   d         | _         | j         |S )z
        Process buffered content and return output content.

        Args:
            new_content: New content to add to buffer

        Returns:
            Processed output content
        r   r   N)r(   r)   rm   r   r    ro   r   )r2   r   output_content	start_posend_postag_postag_lens          r4   _process_bufferz!MinimaxToolParser._process_buffer5  sG    	{* 	"!0N"$D!!! 	K+001KLLI)..t/GHHGBGrMMY5H5H#,c$2L.M.MB#*C0H,I,I//0CDD $"55&(#d1(7(;;N"&"5g6G6I6I"JD! ! 	K$ r5   c                     dg g d| _         dS )z,Reset the streaming state to initial values.r   r   Nr   r2   s    r4   _reset_streaming_statez(MinimaxToolParser._reset_streaming_state[  s"     #% 
  
r5   c                 P    t          | j        d                   dz   | j        d<   dS )z3Advance to the next tool in the streaming sequence.r   r:   Nintr   r   s    r4   _advance_to_next_toolz'MinimaxToolParser._advance_to_next_toolc  s1     $%9:;;a? 	1222r5   indexc                     || j         d<   dS )za
        Set the current tool index.

        Args:
            index: Tool index to set
        r   Nr   )r2   r   s     r4   _set_current_tool_indexz)MinimaxToolParser._set_current_tool_indexi  s     6;1222r5   c                 6    t          | j        d                   S )z^
        Get the current tool index.

        Returns:
            Current tool index
        r   r   r   s    r4   _get_current_tool_indexz)MinimaxToolParser._get_current_tool_indexr  s     4'(<=>>>r5   
tool_countc                     t          | j        d                   }t          |          D ]+}|t          |          k     r||         d         s|c S (|c S dS )z
        Get the index of the next unsent tool.

        Args:
            tool_count: Total number of tools

        Returns:
            Index of next unsent tool, or -1 if all tools sent
        r   	sent_namer   )listr   r   ro   )r2   r   r   r   s       r4   _get_next_unsent_tool_indexz-MinimaxToolParser._get_next_unsent_tool_index{  sp     $.|<==
z"" 	 	A3z??""!!}[1 HHH rr5   c                    t          | j        d                   }t          | j        d                   }t          |          |k     r8|                    ddt	                      d           t          |          |k     8t          |          |k     r(|                    d           t          |          |k     (|| j        d<   || j        d<   dS )z
        Ensure state arrays have sufficient capacity for tool_count tools.

        Args:
            tool_count: Number of tools to prepare for
        r   r   Fr   )r   sent_argumentsidN)r   r   ro   rk   r   )r2   r   r   r   s       r4   _ensure_state_arraysz&MinimaxToolParser._ensure_state_arrays  s     $.|<==
,Z899*oo
**!&&(+--    *oo
** (mmj((OOD!!! (mmj(( .8\*+3Z(((r5   c                 T    | j                             |          }t          |          S )z
        Detect the number of tools in text by counting name patterns.

        Args:
            text: Text to analyze

        Returns:
            Number of tools detected
        )r&   rh   ro   )r2   r~   matchess      r4   _detect_tools_in_textz'MinimaxToolParser._detect_tools_in_text  s&     (00667||r5   c                    g }d}|t          |          k     r||         dk    r|}d}d}d}|t          |          k     r||         dk    r|dz  }nF||         dk    r:|dz  }|dk    r/|dz   }|||         }	d|	v rd|	v r|                    ||f           n>|sd|||dz            v rd}|sd|||dz            v rd}|dz  }|t          |          k     |dk    r|r|                    ||f           n|dz  }|t          |          k     |S )	z
        Find the boundaries of tool calls in text.

        Args:
            text: Text to analyze

        Returns:
            List of (start, end) positions for tool calls
        r   rb   Fr:   rV   z"name"z"arguments"T)ro   rk   )
r2   r~   
boundariesr   startdepthhas_namehas_argumentsendsegments
             r4   _find_tool_boundariesz'MinimaxToolParser._find_tool_boundaries  sv    
#d))mmAw#~~  %#d))mmAw#~~
aC
 A::"#a%C&*59oG'722}7O7O * 1 15#, ? ? ?!# (DQ4G(G(G#'( -]d51q5=>Q-Q-Q(,FA# #d))mm& 1999%%uaj111Q; #d))mm< r5   tool_content
args_matchc                    |                                 }||d         }|                                                    d          rDd}t          |          D ]1\  }}|dk    r|dz  }|dk    r|dz  }|dk    r|d|dz            c S 2n7|                    d          }|dk    r|d|                                         S |                    d                                          S )z
        Extract tool arguments from tool content.

        Args:
            tool_content: Tool call content
            args_match: Regex match for arguments pattern

        Returns:
            Extracted arguments as string
        Nrb   r   r:   rV   )r   rH   rj   	enumeraterm   rstrip)	r2   r   r   args_start_posremaining_contentr   r   charargs_ends	            r4   _extract_tool_argsz$MinimaxToolParser._extract_tool_args  s    $))(9""$$//44 	<E$%677 : :43;;QJEES[[QJEzz01q59999: )--c22H!||((399;;; '',,22444r5   
tool_indexc                 
   |                      |          }|t          |          k    rdS ||         \  }}|||         }| j                            |          }|r|                    d          nd}| j                            |          }	|	rr	 |                     ||	          }
||
fS # t          $ rJ ||	                                d         }|	                    d          
                                }
||
fcY S w xY w|dfS )a  
        Get the content of a specific tool by index.

        Args:
            text: Text containing tool calls
            tool_index: Index of tool to extract

        Returns:
            Tuple of (tool_name, tool_arguments) or (None, None) if not found
        )NNr:   NrV   )r   ro   r&   searchr?   r'   r   rp   r   r   rH   )r2   r~   r   r   r   r   r   
name_matchrc   r   rF   r   s               r4   _get_current_tool_contentz+MinimaxToolParser._get_current_tool_content  s.    //55
Z((:
+
sE#I+22<@@
&0:z"""d+22<@@
 	'' 33L*MM	Y& ' ' '$01A1A1C1C$D!-44S99??AA	Y&&&'
 Tzs   B* *AC>=C>c           
      H   |                      |          }|dk    rdS |                     |          }|t          |          k    rdS |                     ||          \  }}|sdS |                     |           t          | j        d                   }t          | j        d                   }||         d         }	|	||<   d||         d<   || j        d<   || j        d<   t          t          |d|	t          |	          
                    d
                    g          S )z
        Handle streaming of tool names.

        Args:
            tool_content: Content containing tool calls
            tool_count: Total number of tools

        Returns:
            DeltaMessage with tool name or None if no tool to stream
        r   Nr   r   r   Tr   re   )rc   exclude_none)r   rg   r   re   r`   )r   r   ro   r   r   r   r   r   r	   r   
model_dump)
r2   r   r   next_idxr   	tool_name_r   r   tool_ids
             r4   _handle_tool_name_streamingz-MinimaxToolParser._handle_tool_name_streaming"  sR    33J??r>>4//==
s:&&455lHMM	1 	4$$X...$.|<==
,Z899X&t,$,0
8[)-7\*+3Z("#.I>>>II%) J  	  	
 
 
 	
r5   c                    |                                  }|dk     s||k    rdS |                     ||          \  }}|r|dS t          | j        d                   }||         d         sdS |                     |          }||         d         }||k    rX|r|                    |          rt          ||          }	|	r|                     |	          }	|||         d<   || j        d<   |                    d          r| 	                                 t          t          |t          |	                              d	          
          g          S n|s|r|                     |          }
|||         d<   || j        d<   |                    d          r| 	                                 t          t          |t          |
                              d	          
          g          S dS )a  
        Handle streaming of tool arguments.

        Args:
            tool_content: Content containing tool calls
            tool_count: Total number of tools

        Returns:
            DeltaMessage with tool arguments or None if no arguments to stream
        r   Nr   r   r   rV   )rd   Tr   )r   re   r   )r   r   r   r   rN   rj   r   r\   rL   r   r   r	   r   r   )r2   r   r   current_idxr   	tool_argsr   
clean_args	sent_args
args_deltaclean_args_deltas              r4   _handle_tool_args_streamingz-MinimaxToolParser._handle_tool_args_streamingT  s_    2244??kZ774#==lKXX	9 	I-4$.|<==
+&{3 	411)<<
{+,<=	"" %Z229== %6z9MM
 !%!9!9*!E!EJ@JJ{+,<=9CD(6!**3// 522444')&1):.8*" *" *"",*$*"?"?	  $	 	 	 	$  : #'#;#;J#G#G <F
;'(895?$\2&&s++ 1..000#%"-%6*:& & &(jdj;;	   	 	 	 	 tr5   current_textc                    | j         |vrdS g }d}	 |                    | j         |          dk    rn|                               dz   }=g }t          j        | j        |t          j                  D ]=}|                    |                                |                                f           >|D ]"t          fd|D                       }|s dS #dS )NFr   Tr   r:   r;   c              3   6   K   | ]\  }}|k    o|k     V  d S r   rR   rS   t_startt_endr|   s      r4   rU   z7MinimaxToolParser._is_end_tool_calls.<locals>.<genexpr>  E        3A7Ew.3;     r5   )
r    rm   rk   r!   finditerr%   r#   r   r   r   )r2   r   end_token_positionssearch_startthink_regionsrA   in_thinkr|   s          @r4   _is_end_tool_callsz$MinimaxToolParser._is_end_tool_calls  s.   #<775 	###D$<lKKCbyy&&s+++7L	# [%|29
 
 
 	? 	?E   %++--!=>>>>& 	 	C    ER    H  tt ur5   previous_textprevious_token_idscurrent_token_idsdelta_token_idsc                 8   |                      |           | j        rt          |          S |                     |          r)|                     |          }|rt          |          nd S |                     |          rt          |          S |                     |          \  }	}
|
r$| xj        |
z  c_        |	rt          |	          nd S |                     |          }| j	        |vr|| j
        |v r| j	        |v rd S |                                dk    r| j	        |v rd S |                                 dk    r| j
        |v r|                                  t          |          S | j        | j        |v rt          |          dk    rd S |                     |          }|d S |                     |||          }|rt          |          S 	 |                     ||          }|                     |          }|dk    rd S |                                 dk    r|                                  |                     |           |                     ||          p|                     ||          S # t.          $ r t0                              dd           Y d S w xY w)N)ra   r   r   r:   r   zAn unexpected error occurred z$during streaming tool call handling.)r   r)   r   r   r   r   r   r(   rE   r   r    rH   r   r   r.   ro   !_find_tool_start_outside_thinking_extract_content_before_tools_extract_tool_contentr   r   r   r   rp   r0   rq   )r2   r   r   rO   r   r   r   r]   buffered_outputsafe_contentpotential_tagprocessed_current_textoriginal_tool_startcontent_before_toolsr   current_tools_counts                   r4   extract_tool_calls_streamingz.MinimaxToolParser.extract_tool_calls_streaming  s*    	##L111 	4
3333&&z22 	V"22:>>O<KU<8888QUU""<00 	4
3333&*&G&G
&S&S#m 	P=09EO<55554O!%!=!=l!K!K%-CCC(J66.,>>t!!R''D,F,,V,Vt,,.."44,<<++---
3333 )5-@@O$$))4"DD\RR&4#AA*&9 
  
   	>(<====	55lDWXXL"&"<"<\"J"J"a''t++--33++---%%&9:::331  U11,@STTU  	 	 	/1W   44		s   1I0 A,I0 0%JJc                     d}	 |                     | j        |          dk    rdS d t          j        d|t          j                  D             }t          fd|D                       }|sS d	z   }s)
z
        Find the start position of tool calls outside of thinking tags.

        Args:
            current_text: Current text to search

        Returns:
            Position of tool call start or None if not found
        r   Tr   Nc                 ^    g | ]*}|                                 |                                f+S rR   )r   r   )rS   ms     r4   
<listcomp>zGMinimaxToolParser._find_tool_start_outside_thinking.<locals>.<listcomp>  s=        AEEGG$  r5   r   r;   c              3   6   K   | ]\  }}|k    o|k     V  d S r   rR   r   s      r4   rU   zFMinimaxToolParser._find_tool_start_outside_thinking.<locals>.<genexpr>!  r   r5   r:   )rm   r   r!   r   r#   r   )r2   r   r   r   r   r|   s        @r4   r   z3MinimaxToolParser._find_tool_start_outside_thinking  s     	###D$>MMCbyyt +\    M     ER    H  
7L%	#r5   
tool_startc                     |dk    rPt          |          t          |          z
  }||k     r+|}|t          |          z   |k    r|d||z
           }|r|ndS dS )a  
        Extract content that appears before tool calls.

        Args:
            current_text: Current text
            delta_text: Delta text
            tool_start: Start position of tools

        Returns:
            Content before tools or None
        r   N)ro   )r2   r   rO   r  delta_start_poscontent_parts         r4   r   z/MinimaxToolParser._extract_content_before_tools*  st     >>!,//#j//AO++)"S__4zAA#-.L
_0L.L#ML'3=||=tr5   c                     |t          | j                  z   }||d         }|                    | j                  }|dk    r
|d|         }|S )z
        Extract tool content from current text starting at tool_start.

        Args:
            current_text: Current text
            tool_start: Start position of tool calls

        Returns:
            Extracted tool content
        Nr   )ro   r   rm   r    )r2   r   r  tool_content_startr   r   s         r4   r   z'MinimaxToolParser._extract_tool_contentA  s^     (#d.H*I*II#$6$7$78##D$<==b=='1Lr5   )r7   N)+__name__
__module____qualname__r   r   strrE   rN   r\   r   r
   r}   r   r   r   r   tupler   r   r   r   r   r   r   r   r   r   r   r   r!   Matchr   r   r   r   r   r   r   r   r   r   r   __classcell__)r3   s   @r4   r   r       s       &- & & & & & &P
C 
C 
 
 
 
2     <c c    ,VV 'V 
&	V V V Vp
3 
4 
 
 
 
C D    $
 
 
 
 
 
&s uS#X    *$3 $3 $ $ $ $L
 
 
 

 
 
 
;S ;T ; ; ; ;? ? ? ? ?c c    &4s 4t 4 4 4 42# #    *# *$uS#X2G * * * *X5s 5 5RU 5 5 5 5<""%("	sTz3:%	&" " " "H0
0
-00
		0
 0
 0
 0
dFF-0F		F F F FPs t    8QQ Q 	Q
 %SMQ $C=Q "#Q 'Q 
	Q Q Q Qf#c #cDj # # # #>-0>A	t   .# 3 3        r5   r   )rI   collections.abcr   typingr   regexr!   vllm.entrypoints.chat_utilsr   0vllm.entrypoints.openai.chat_completion.protocolr   'vllm.entrypoints.openai.engine.protocolr   r   r	   r
   r   r   vllm.loggerr   vllm.tokenizersr   &vllm.tool_parsers.abstract_tool_parserr   vllm.tool_parsers.utilsr   r  r0   r   rR   r5   r4   <module>r     se    $ $ $ $ $ $           9 9 9 9 9 9                     $ # # # # # ) ) ) ) ) )      > = = = = =	X		s s s s s
 s s s s sr5   