
    ^j<                    b    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
 ddddZ G d	 d
      Zy)    )annotations)Any   )STREAMABLE_PARSERSprocess_field)ResponseTemplateResponseTemplateFieldload_response_templateNprefixc               ~    t        |      }t        ||      }|j                  |        |j                         \  }}|S )zThe main function for response parsing when you don't want streaming. Takes generated output
    and the prompt prefix and parses them without streaming any events, then returns the parsed message.
    r   )r
   ResponseParserfeedfinalize)textresponse_templater   streammessage_s         z/var/www/ramen.bs-engineer-server.com/venv/lib/python3.12/site-packages/transformers/utils/chat_parsing/response_parser.pyparse_responser      s>     //@A-f=F
KK"JGQN    c                  ~    e Zd ZdZdddZddZddZddZddZddZ		 	 	 	 	 	 dd	Z
dd
ZddZddZddZddZy)r   aJ  This class implements a streaming parser with a `response_template`. If you don't need streaming and
    just want to parse a complete message, use the `parse_response` function above. Streaming parsing emits
    events indicating when regions (message fields) are opened and closed, with the model writing to the region
    that is currently open.

    Usage:
        parser = ResponseParser(response_template, prefix=chat_prompt)
        for event in parser.initial_events:
            handle(event)
        for chunk in model_text_stream:
            for event in parser.feed(chunk):
                handle(event)
        message, final_events = parser.finalize()
        for event in final_events:
            handle(event)

    Events can be either "region_open", "region_chunk", or "region_close".

    ResponseParser requires the chat `prefix` (i.e. the chat history, the prefill before the current generation).
    This is because chat templates or assistant prefills can sometimes write part of the message, and if we
    only see the model output, and not the template, then we can't reliably parse the message in those cases.
    Any events produced while consuming the prefix are exposed as `initial_events`, so renderers can show
    prefill regions before the model writes anything; closed prefill regions also land in the output dict.
    Nc                h   t        |      | _        |t        d      d| _        d| _        t        | j                  j                        | _        | j                  j                  | _	        | j                  | _
        i | _        d| _        d| _        d| _        g | _        |r| j!                  |       y y )Nah  `ResponseParser`/`parse_response` requires `prefix` (the chat prompt sent to the model before generation), because chat templates often pre-write part of the assistant message (e.g. an opening `<think>` tag) that the parser must see to parse the output correctly. If the generation already contains the complete message, pass `prefix=""` to opt out explicitly. r   F)r
   _spec
ValueError_buffer_posdictdefaults_outputimplicit_implicit_name_current	_captures_body_opened
_finalizedinitial_events_consume_prefix)selfr   r   s      r   __init__zResponseParser.__init__<   s    +,=>
>a  	'+DJJ,?,?'@*.***=*=
 %)$7$7)+
" %*,  ( r   c                    | j                   j                  |      }|sy|| _        | j                  | j                  d       y)a  Loads the prefix (the chat prefill sent to the model), right-truncates it to the start of the
        assistant message (as determined by start_anchor) and then runs the remainder through the parser.
        Events produced while processing the prefix are stashed on `initial_events` so callers can replay
        them into a renderer before feeding model output.

        Think of this as the "get the parser up to speed on the story so far" method.
        NFeos)r   truncate_past_last_anchorr   _processr*   )r,   r   	truncateds      r   r+   zResponseParser._consume_prefixV   s;     JJ88@	 d))u5r   c                    | j                   rt        d      |r| xj                  |z  c_        g }| j                  |d       |S )zFeeds more text/tokens from the model output into the tokenizer, and returns any events that result
        (regions entered or left). This is the method you want to call after each generation step. ResponseParser already finalizedFr/   )r)   RuntimeErrorr   r2   )r,   r   eventss      r   r   zResponseParser.feedd   sB     ??ABBLLD Lf%(r   c                   dd}| j                   rt        d      g }| j                  |d       | j                  j                  j                         D cg c]#  \  }}|j                  r|| j                  vs"|% }}}|rt        d|       | j                  j                  }| j                  j                         D ci c]  \  }}||v s	 ||      r|| c}}| _        d| _         | j                  |fS c c}}w c c}}w )a  Close the stream and return the final message dict together with
        any finalization events. This is necessary because some regions may only
        end at the end of the sequence, so you won't see the event telling you they're
        ready until the sequence is finalized.c                N    | d u xs  t        | t        t        t        f      xr |  S N)
isinstancelistr    str)vs    r   	_is_emptyz*ResponseParser.finalize.<locals>._is_emptyu   s&    9LAdC/@!A!K!eLr   r5   Tr/   z>Required response_template fields missing from parsed output: )r>   r   returnbool)
r)   r6   r2   r   fieldsitemsoptionalr"   r   r!   )	r,   r?   r7   nfmissingr!   kr>   s	            r   r   zResponseParser.finalizeo   s    	M ??ABBf$'!%!2!2!8!8!:gA!**QRZ^ZfZfQf1gg]^e]fghh::&&)-););)=cAhV_`aVb1c||V## h ds   C>-C><C>DDc                |   	 | j                         }| j                  ||      \  }}||\  }}}|j                         | j                  kD  r7| j	                  || j
                  | j                  |j                                 |j                         | _        |dk(  r%| j                  |       | j                  |||       nA| j                  }	| j                  |       |	s"|j                         |j                         k(  ry |rw| j                  t        | j
                        k  rC| j	                  || j
                  | j                  d         t        | j
                        | _        | j                  |       y || j                  kD  r0| j	                  || j
                  | j                  |        || _        y )Nopen)
_watchlist_scanstartr   _accumulater   end_close_current_open_explicitr(   len)
r,   r7   r0   watchbest
hold_startkindfieldmhad_contents
             r   r2   zResponseParser._process   sj   OO%E#zz%5D*!%eQ779tyy($$VT\\$))aggi-PQEEG	6>''/''q9 #',,K''/ '1779+? 99s4<<00$$VT\\$))+-FG #DLL 1DI##F+
 DII%  dii*)MN&	r   c                   | j                   O| j                   | j                  k7  r6| j                  j                  | j                      }|j                  d|fgS g S g }| j                  j                  j                         D ]"  }|j                  |j                  d|f       $ | j                  B| j                  j                  | j                     }|j                  |j                  d|f       |S )zPatterns we care about right now: the close of the currently-open
        explicit region, or -- if we're in the implicit/null region -- every
        explicit open plus the implicit's own close (if any).closerJ   )r%   r$   r   rB   close_revaluesopen_reappend)r,   rW   rS   impls       r   rK   zResponseParser._watchlist   s     ==$$:M:M)MJJ%%dmm4E).)CWe$%KK9;ZZ&&--/ 	.E}}(fe_-	. *::$$T%8%89D}}(gt_-r   c                   d}d}t        | j                        }|D ]  \  }}|dk(  r|j                  n|j                  }|r'|j	                  | j                  | j
                        }	n(|j	                  | j                  | j
                  d      }	|	x|s:|	j                  s| j                  |||	      rt        ||	j                               }|	j                         |	j                         |	j                         z
   |dk(  rdnd|j                  f}
||
|k  s|
|||	f}} ||d   j                         |k\  rd}||fS )aH  Single pass over the watched delimiters, using the `regex` module's
        partial matching to decide -- per delimiter -- whether it can be committed
        now or must be held. Returns `(best, hold_start)`:

        * `best` is the earliest-starting delimiter we can safely commit *now*
          (longest on ties, opens before closes), or `None`.
        * `hold_start` is the leftmost buffer position occupied by a still-pending
          match: a partial (incomplete) delimiter, or a complete one ending at the
          buffer edge that more input could still grow. Bytes before it are safe to
          emit; bytes from it onward must be held. It stays `len(self._buffer)` when
          nothing is pending, letting the caller flush the whole buffer.

        A complete match is committable only if it starts strictly before
        `hold_start` -- otherwise an earlier (or co-located) pending delimiter could
        turn out to be the real one. At EOS nothing can grow, so partial matching is
        skipped and every complete match is committable.

        (The `regex` module always reports the empty string as a live prefix, so a
        partial search with no real match returns a zero-width match at the buffer
        end; that lands in the pending branch with `start == len(self._buffer)`, a
        no-op for `hold_start`.)
        NrJ   T)partialr   r      )rR   r   r^   r\   searchr   rb   	_can_growminrM   rO   name)r,   rS   r0   best_keyrT   rU   rV   rW   patternrX   keys              r   rL   zResponseParser._scan   s,   2 "&>B&
  	7KD%'+v~emm5>>GNN4<<;NN4<<DNIyAIIeQ)G QWWY7
779!'') 3446>aqRWR\R\]C3>!$tUA&6$	7$ Q: =DZr   c                   |j                         t        | j                        k7  ry|j                         |j                         k(  ry|dk(  r|j                  |j
                  fn|j                  |j                  f\  }}|du xs |S )a1  Whether a *complete* match ending at the current buffer edge could still
        change as more input arrives -- in which case we defer rather than commit. A
        match ending before the edge has already seen its terminating byte and is
        final. At the edge: zero-width matches (`$` / `\Z`) are only real at true
        EOS; a fully-present literal that no other literal in its set extends cannot
        grow (the fast path that keeps literal delimiters zero-latency); anything
        else (regex delimiters, prefix-overlapping literal lists) might.FTrJ   N)rO   rR   r   rM   open_literalsopen_literal_can_extendclose_literalsclose_literal_can_extend)r,   rV   rW   rX   literals
can_extends         r   re   zResponseParser._can_grow   s     557c$,,''779 v~   %"?"?@&&(F(FG 	*
 4-:-r   c                X   |r| j                   y| j                  j                  | j                      }| j                  s%|j	                  d| j                   d       d| _        | xj
                  |z  c_        |j                  t        v}|j	                  d| j                   ||d       y)a  Route `text` into the currently active region. When the current
        region is the null sink (no implicit declared, no explicit open), we
        silently discard. Every routed chunk emits a `region_chunk` event so
        consumers can render live; `dirty=True` flags chunks from structured
        parsers (json, xml-inline, kv-lines) whose raw bytes will only be
        parsed into the final value on close.Nregion_opentyperW   Tregion_chunk)ru   rW   r   dirty)r%   r   rB   r(   r_   r'   contentr   )r,   r7   r   rW   rw   s        r   rN   zResponseParser._accumulate  s     t}},

!!$--0||MM=4==IJDL

d
%77~t^cder   c                    |j                   | _        |j                         j                         D ci c]  \  }}|	|| c}}| _        d| _        d| _        |j                  d|j                   d       y c c}}w )Nr   Trs   rt   )rg   r%   	groupdictrC   r&   r'   r(   r_   )r,   r7   rW   rX   rH   r>   s         r   rQ   zResponseParser._open_explicit  sc    

+,;;=+>+>+@R41aAM!Q$R
}uzzBC Ss
   
A9A9c                   | j                   | j                  s| j                          y| j                  j                  | j                      }t        | j                  || j                        }|j                  r6| j                  j                  | j                   g       j                  |       n|| j                  | j                   <   |j                  d| j                   |d       | j                          y)zClose the current region and reset to the implicit/null region.
        Skipped (aside from the reset) when the current region never opened --
        avoids vacuous open/close pairs at every explicit boundary.Nregion_close)ru   rW   value)r%   r(   _reset_to_implicitr   rB   r   r'   r&   repeatsr"   
setdefaultr_   )r,   r7   rW   r}   s       r   rP   zResponseParser._close_current  s     == ##%

!!$--0djj%@==LL##DMM26==eD*/DLL'~PUVW!r   c                P    | j                   | _        i | _        d| _        d| _        y )Nr   F)r$   r%   r&   r'   r(   )r,   s    r   r~   z!ResponseParser._reset_to_implicit)  s$    ++
r   r:   )r   dict | ResponseTemplater   
str | None)r   r=   r@   None)r   r=   r@   
list[dict])r@   ztuple[dict, list[dict]])r7   r   r0   rA   r@   r   )r@   'list[tuple[str, ResponseTemplateField]])rS   r   r0   rA   r@   z9tuple[tuple[str, ResponseTemplateField, Any] | None, int])rV   r=   rW   r	   rX   r   r@   rA   )r7   r   r   r=   r@   r   )r7   r   rW   r	   rX   r   r@   r   )r7   r   r@   r   )r@   r   )__name__
__module____qualname____doc__r-   r+   r   r   r2   rK   rL   re   rN   rQ   rP   r~    r   r   r   r   "   s_    2)46	$*%N"0 <0 CG0 	B0 d.&f"D" r   r   )r   r=   r   r   r   r   r@   r    )
__future__r   typingr   content_parsersr   r   response_templatesr   r	   r
   r   r   r   r   r   <module>r      s.    #  > _ _ cg K Kr   