
    Pmj                       U d Z ddlmZ ddlZddlZddlZddlZddlmZ  ej	        e
          Z ej        d          ZdHd	ZdIdZdJdZdKdZdLdMdZdNdOdZdHdZdJdZdPdZdJdZdIdZg dZdQdRd$ZdSd&ZdTd(Zd) ed*d+h           e            d,d-fd. e             ed.h          d/d0fd1 e             ed2h          d3d4ffZd5ed6<   dUd8Z dVd<Z!dWd>Z"dXd?Z#dYdEZ$dZdGZ%dS )[at  Message and tool-payload sanitization helpers.

Pure functions extracted from ``run_agent.py`` so the AIAgent module can
stay focused on the conversation loop.  These walk OpenAI-format message
lists and structured payloads, repairing or stripping problematic
characters that would otherwise crash ``json.dumps`` inside the OpenAI
SDK or be rejected by upstream APIs.

All helpers are stateless and side-effect-free except for in-place
mutation of their input (where documented).  Backward-compatible
re-exports from ``run_agent`` remain in place so existing imports
``from run_agent import _sanitize_surrogates`` keep working.
    )annotationsN)Anyz[\ud800-\udfff]textstrreturnc                p    t                               |           rt                               d|           S | S )zReplace lone surrogate code points with U+FFFD (replacement character).

    Surrogates are invalid in UTF-8 and will crash ``json.dumps()`` inside the
    OpenAI SDK.  This is a fast no-op when the text contains no surrogates.
       �)_SURROGATE_REsearchsubr   s    @/home/thesage/.hermes/hermes-agent/agent/message_sanitization.py_sanitize_surrogatesr       s5     D!! 1  4000K    payloadr   boolc                0    dfd |            S )uu  Replace surrogate code points in nested dict/list payloads in-place.

    Mirror of ``_sanitize_structure_non_ascii`` but for surrogate recovery.
    Used to scrub nested structured fields (e.g. ``reasoning_details`` — an
    array of dicts with ``summary``/``text`` strings) that flat per-field
    checks don't reach.  Returns True if any surrogates were replaced.
    Fc                   t          | t                    r|                                 D ]|\  }}t          |t                    r;t                              |          r t                              d|          | |<   dUt          |t          t          f          r |           }d S t          | t                    rt          |           D ]~\  }}t          |t                    r;t                              |          r t                              d|          | |<   dUt          |t          t          f          r |           }d S d S )Nr	   T)	
isinstancedictitemsr   r
   r   r   list	enumerate)nodekeyvalueidx_walkfounds       r   r   z-_sanitize_structure_surrogates.<locals>._walk5   sQ   dD!! 	!"jjll ! !
UeS)) !$++E22 %$1$5$5h$F$FS	 $d|44 !E%LLL! ! d## 	!'oo ! !
UeS)) !$++E22 %$1$5$5h$F$FS	 $d|44 !E%LLL	! 	!! !r    r   r   r   s    @@r   _sanitize_structure_surrogatesr"   +   s=     E! ! ! ! ! !& 
E'NNNLr   messagesr   c                p   d}| D ]}t          |t                    s|                    d          }t          |t                    r;t                              |          r!t                              d|          |d<   d}nt          |t                    r~|D ]{}t          |t                    rd|                    d          }t          |t                    r:t                              |          r t                              d|          |d<   d}||                    d          }t          |t                    r:t                              |          r t                              d|          |d<   d}|                    d          }t          |t                    rs|D ]o}t          |t                    s|                    d          }	t          |	t                    r:t                              |	          r t                              d|	          |d<   d}|                    d	          }
t          |
t                    r|
                    d          }t          |t                    r:t                              |          r t                              d|          |
d<   d}|
                    d
          }t          |t                    r:t                              |          r t                              d|          |
d
<   d}q|                                D ]\  }}|dv r
t          |t                    r;t                              |          r t                              d|          ||<   d}Zt          |t          t          f          rt          |          rd}|S )a  Sanitize surrogate characters from all string content in a messages list.

    Walks message dicts in-place. Returns True if any surrogates were found
    and replaced, False otherwise. Covers content/text, name, tool call
    metadata/arguments, AND any additional string or nested structured fields
    (``reasoning``, ``reasoning_content``, ``reasoning_details``, etc.) so
    retries don't fail on a non-content field.  Byte-level reasoning models
    (xiaomi/mimo, kimi, glm) can emit lone surrogates in reasoning output
    that flow through to ``api_messages["reasoning_content"]`` on the next
    turn and crash json.dumps inside the OpenAI SDK.
    Fcontentr	   Tr   name
tool_callsidfunction	arguments>   r&   roler%   r'   )
r   r   getr   r
   r   r   r   r   r"   )r#   r   msgr%   partr   r&   r'   tctc_idfnfn_namefn_argsr   r   s                  r   _sanitize_messages_surrogatesr4   L   sw    E 3! 3!#t$$ 	'')$$gs## 		%(<(<W(E(E 		%*..xAAC	NEE&& 	% % %dD)) %88F++D!$,, %1E1Ed1K1K %'4'8'84'H'HV $wwvdC   	]%9%9$%?%? 	'++Hd;;CKEWW\**
j$'' 	%  % %!"d++ teS)) !m.B.B5.I.I !,005AABtH EVVJ''b$'' % ffVnnG!'3// %M4H4H4Q4Q %%2%6%6x%I%I6
 $ ff[11G!'3// %M4H4H4Q4Q %*7*;*;Hg*N*N; $ ))++ 		! 		!JC???%%% ! ''.. !,005AACH EED$<00 !1%88 ! E		! Lr   rawc                2   g }d}d}t          |           }||k     r| |         }|r|dk    rB|dz   |k     r9|                    |           |                    | |dz                       |dz  }X|dk    rd}|                    |           nmt          |          dk     r'|                    dt          |          d	           n3|                    |           n|dk    rd
}|                    |           |dz  }||k     d                    |          S )ug  Escape unescaped control chars inside JSON string values.

    Walks the raw JSON character-by-character, tracking whether we are
    inside a double-quoted string. Inside strings, replaces literal
    control characters (0x00-0x1F) that aren't already part of an escape
    sequence with their ``\uXXXX`` equivalents. Pass-through for everything
    else.

    Ported from #12093 — complements the other repair passes in
    ``_repair_tool_call_arguments`` when ``json.loads(strict=False)`` is
    not enough (e.g. llama.cpp backends that emit literal apostrophes or
    tabs alongside other malformations).
    Fr   \      "    z\u04xT )lenappendordjoin)r5   out	in_stringinchs         r   %_escape_invalid_chars_in_json_stringsrG      s+    CI	ACA
a%%V 	Tzza!eaii

2

3q1u:&&&QSyy!	

2R4

.R...////

2Syy 	JJrNNN	Q) a%%* 773<<r   ?raw_args	tool_namec                z   t          | t                    r|                                 nd}|st                              d|           dS |dk    rt                              d|           dS 	 t          j        |d          }t          j        |d	          }||k    rt                              d
|           |S # t
          j        t          t          f$ r Y nw xY w|}t          j        dd|          }|                    d          |                    d          z
  }|                    d          |                    d          z
  }|dk    r|d|z  z  }|dk    r|d|z  z  }t          d          D ]}	 t          j        |            n# t
          j        $ r |                    d          r7|                    d          |                    d          k    r|dd         }nO|                    d          r7|                    d          |                    d          k    r|dd         }nY  nY w xY w	 t          j        |           t                              d||dd         |dd                    |S # t
          j        $ r Y nw xY w	 t!          |          }	|	|k    rCt          j        |	           t                              d||dd         |	dd                    |	S n"# t
          j        t          t          f$ r Y nw xY wt                              d||dd                    dS )a  Attempt to repair malformed tool_call argument JSON.

    Models like GLM-5.1 via Ollama can produce truncated JSON, trailing
    commas, Python ``None``, etc.  The API proxy rejects these with HTTP 400
    "invalid tool call arguments".  This function applies common repairs;
    if all fail it returns ``"{}"`` so the request succeeds (better than
    crashing the session).  All repairs are logged at WARNING level.
    r=   z*Sanitized empty tool_call arguments for %sz{}Nonez0Sanitized Python-None tool_call arguments for %sF)strict),:)
separatorsz>Repaired unescaped control chars in tool_call arguments for %sz,\s*([}\]])z\1{}[]r   2   Nu8   Repaired malformed tool_call arguments for %s: %s → %sP   uA   Repaired control-char-laced tool_call arguments for %s: %s → %suP   Unrepairable tool_call arguments for %s — replaced with empty object (was: %s))r   r   striploggerwarningjsonloadsdumpsJSONDecodeError	TypeError
ValueErrorrer   countrangeendswithrG   )
rI   rJ   raw_strippedparsedreserialisedfixed
open_curlyopen_bracket_escapeds
             r   _repair_tool_call_argumentsrm      s    (2(C'@'@H8>>###bL  CYOOOt vI9UUUt
L777z&Z@@@<''NNP    )Z8    EF>5%00ES!!EKK$4$44J;;s##ekk#&6&66LA~~z!!a|##2YY 
 
		JuE# 	 	 	~~c"" u{{3'7'7%++c:J:J'J'Jcrc
$$ S)9)9EKK<L<L)L)Lcrc
	
5F|CRC(%*	
 	
 	
    
7>>eJwNNS<,gcrcl   N   )Z8   
 NN	/<$  
 4sL   /AB> >CC<FB'H?>H?AJ JJAK5 5LLfinal_responsec                   | sdS | d         }t          |t                    r|                    d          dk    rdS t          |t                    r|nd}|                     d|                                pdd           d	S )
u"  Append a synthetic assistant turn when an interrupted tail is a tool result.

    A turn cut short by ``/stop`` can leave the transcript ending on a raw
    ``tool`` message (a tool finished, or its execution was cancelled, but the
    model never streamed a closing assistant turn). Persisting that tail means
    the next user message lands as ``… tool → user`` — a role-alternation
    violation that strict providers (Gemini, Claude) react to by hallucinating
    a continuation of the user's message and ignoring prior context, which
    reads to the user as "lost context" (#48879).

    ``finalize_turn`` closes this on the happy interrupt path, but the
    retry/backoff/error interrupt aborts in ``conversation_loop`` ``return``
    early and never reach it — this shared helper closes the sequence on all of
    them. ``final_response`` is usually empty on an interrupt, so an explicit
    placeholder is used rather than an empty-content assistant turn.

    Mutates ``messages`` in place. Returns True if a closing turn was appended.
    FrV   r+   toolr=   	assistantzOperation interrupted.)r+   r%   T)r   r   r,   r   r?   rX   )r#   rn   lastr   s       r   close_interrupted_tool_sequencers     s    &  uB<DdD!! TXXf%5%5%?%?u'<<D>>"DOO::<<;#;     4r   c                V    |                      dd                              d          S )zRemove non-ASCII characters, replacing with closest ASCII equivalent or removing.

    Used as a last resort when the system encoding is ASCII and can't handle
    any non-ASCII characters (e.g. LANG=C on Chromebooks).
    asciiignoreerrors)encodedecoder   s    r   _strip_non_asciir{   ;  s(     ;;wx;0077@@@r   c                R   d}| D ] }t          |t                    s|                    d          }t          |t                    rt	          |          }||k    r||d<   d}nut          |t
                    r`|D ]]}t          |t                    rF|                    d          }t          |t                    rt	          |          }||k    r||d<   d}^|                    d          }t          |t                    rt	          |          }||k    r||d<   d}|                    d          }t          |t
                    r|D ]}	t          |	t                    rq|	                    di           }
t          |
t                    rF|
                    d          }t          |t                    rt	          |          }||k    r||
d<   d}|                                D ];\  }}|d	v r
t          |t                    rt	          |          }||k    r|||<   d}<"|S )
a  Strip non-ASCII characters from all string content in a messages list.

    This is a last-resort recovery for systems with ASCII-only encoding
    (LANG=C, Chromebooks, minimal containers).  Returns True if any
    non-ASCII content was found and sanitized.
    Fr%   Tr   r&   r'   r)   r*   >   r&   r+   r%   r'   )r   r   r,   r   r{   r   r   )r#   r   r-   r%   	sanitizedr.   r   r&   r'   r/   r1   r3   r   r   s                 r   _sanitize_messages_non_asciir~   D  s]    E /! /!#t$$ 	'')$$gs## 	)(11IG##!*I&& 	) ) )dD)) )88F++D!$,, )$4T$:$:	$,,+4DL$(EwwvdC   	(..ID  'FWW\**
j$'' 
	-  	- 	-b$'' -
B//B!"d++ -"$&&"5"5%gs33 -(8(A(AI(G332;;(,))++ 	! 	!JC???%%% !,U33	%%(CH E	! Lr   toolsc                     t          |           S )z7Strip non-ASCII characters from tool payloads in-place.)_sanitize_structure_non_ascii)r   s    r   _sanitize_tools_non_asciir     s    (///r   c                2   d}g }t          |           D ]\  }}t          |t                    s|                    d          }t          |t                    sFg }|D ]F}t          |t                    r|                    d          dv rd}1|                    |           Gt          |          t          |          k     r<|r||d<   |                    d          dk    rd|d<   |                    |           t          |          D ]}| |= |S )	uB  Remove image_url content parts from all messages in-place.

    Called when a server signals it does not support images (e.g.
    "Only 'text' content type is supported.").  Mutates messages so the
    next API call sends text only.

    Preserves message alternation invariants:
      * ``tool``-role messages whose content was entirely images are replaced
        with a plaintext placeholder, NOT deleted — deleting them would leave
        the paired ``tool_call_id`` on the prior assistant message unmatched,
        which providers reject with HTTP 400.
      * Non-tool messages whose content becomes empty are dropped.  In
        practice this only hits synthetic image-only user messages appended
        for attachment delivery; real user turns always include text.

    Returns True if any image parts were removed.
    Fr%   type>   image	image_urlinput_imageTr+   rp   u:   [image content removed — server does not support images])r   r   r   r,   r   r?   r>   reversed)r#   r   	to_deleterD   r-   r%   	new_partsr.   s           r   _strip_images_from_messagesr     sH   $ EIH%% $ $3#t$$ 	'')$$'4(( 		 	' 	'D$%% '$((6*:*:>c*c*c  &&&&y>>CLL(( 	$!*IF** "^I   ###i    QKKLr   c                0    dfd |            S )zCStrip non-ASCII characters from nested dict/list payloads in-place.Fc                &   t          | t                    ru|                                 D ]^\  }}t          |t                    rt	          |          }||k    r|| |<   d7t          |t          t
          f          r |           _d S t          | t
                    rnt          |           D ]`\  }}t          |t                    rt	          |          }||k    r|| |<   d7t          |t          t
          f          r |           _d S d S )NT)r   r   r   r   r{   r   r   )r   r   r   r}   r   r   r   s        r   r   z,_sanitize_structure_non_ascii.<locals>._walk  s5   dD!! 	!"jjll ! !
UeS)) ! 0 7 7I E))$-S	 $d|44 !E%LLL! ! d## 	!'oo ! !
UeS)) ! 0 7 7I E))$-S	 $d|44 !E%LLL	! 	!! !r   r    r!   s    @@r   r   r     s;    E! ! ! ! ! !* 
E'NNNLr   )r
   rs   r   r"   r4   rG   rm   r{   r~   r   r   r   deterministic_call_idcoalesce_tool_call_iduniquify_tool_call_idsreasoning_echo_familymatches_reasoning_echo_familyneeds_reasoning_echoapply_reasoning_content_policyreapply_reasoning_echor2   r*   indexintc                    |  d| d| }t          j        |                    dd                                                    dd         }d| S )u  Generate a deterministic call_id from tool call content.

    Used as a fallback when the API doesn't provide a call_id.
    Deterministic IDs prevent cache invalidation — random UUIDs would
    make every API call's prefix unique, breaking OpenAI's prompt cache.
    rO   zutf-8replacerw   N   call_)hashlibsha256ry   	hexdigest)r2   r*   r   seeddigests        r   r   r      sd     ++	++E++D^DKK	KBBCCMMOOPSQSPSTF6r   r/   c                   t          | t                    r@|                     dd          p|                     dd          pd                                S t	          | dd          pt	          | dd          pd                                S )a'  Extract the effective call ID from a tool_call entry (dict or object).

    Single owner for the ``call_id or id`` coalescing rule: Codex Responses
    tool calls carry ``call_id`` (authoritative pairing key), Chat
    Completions ones carry ``id`` only. Returns ``""`` when neither is set.
    call_idr=   r(   )r   r   r,   rX   getattr)r/   s    r   r   r     s     "d Iy"%%?b)9)9?RFFHHHB	2&&E'"dB*?*?E2LLNNNr   r'   c                  	 t                      }| pg D ]`}t          |t                    r-|                    d          p|                    d          pd}n$t	          |dd          pt	          |dd          pd}t          |t
                    r|                                nd}|s|                    dd          d         }|s||vr|                    |           d}| d	| 		|v r|dz  }| d	| 		|v |                    	           	fd
}	 t          |t                    rJ|                    d          r ||d                   |d<   n	|d<   |                    d          r	|d<   n7 |t	          |dd                    |_	        t	          |dd          r	|_
        n,# t          $ r t                              d|           Y w xY wt          |t                    r|                    d          nt	          |dd          }t          |t                    r|                    d          nt	          |dd          pd}t                              d|	|           b| S )u  Ensure every tool call in a single assistant turn has a distinct id.

    Some models/providers reuse one call id across different calls in a
    single batch (observed with native Kimi Responses replays, Ollama-
    compatible endpoints, and degraded models at long context; same bug
    class as openclaw/openclaw#110518 / #110956). Duplicate ids are lossy
    downstream: the pre-API sanitizer keeps only the first call/result
    pair per id (#58327), so the later call's result silently vanishes
    from every replayed payload, and strict providers (Anthropic
    tool_use, DeepSeek) reject duplicate ids outright.

    The first occurrence keeps its id; later collisions get a
    deterministic ``<id>_d<n>`` suffix — never a random UUID, which would
    break prompt-cache prefix stability across replays. Mutates the
    entries in place (SDK models / SimpleNamespace / dicts) and returns
    the same list. Blank/missing ids are left for the deterministic
    fallback in ``build_assistant_message``.
    r   r(   r=   N|r8   r   r9   _dc                |    t          | t                    r%d| v r! d|                     dd          d          S S )Nr   r8   )r   r   split)r   new_ids    r   _renamedz(uniquify_tool_call_ids.<locals>._renamedE  sK     %%% <#,, ;;5;;sA#6#6q#9;;;Mr   z,Could not uniquify duplicate tool call id %sr)   r&   rH   zyModel reused tool call id %s within one turn; renamed the duplicate to %s (tool=%s) to keep call/result pairing lossless.)setr   r   r,   r   r   rX   r   addr(   r   	ExceptionrY   rZ   )
r'   seenr/   r5   cidrE   r   _fn_fn_namer   s
            @r   r   r     s   & DB 7
 7
 b$ 	P&&##9rvvd||9rCC"i..O'"dD2I2IORC'S119ciikkkr 	 iiQ" 	d??HHSMMM1nnFA]]q]]F nn 		 	 	 	 		"d## 
(66$<< &'x411BtHH%BtH66)$$ +$*ByM T4!8!8992y$// (!'BJ 	 	 	NN>   H		
 %/r4$8$8[bffZ   gb*VZ>[>['1#t'<'<\CGGFOOO'#vW[B\B\dadfh	
 	
 	
 	

 s   BF33%GGkimizkimi-codingzkimi-coding-cnr    )zapi.kimi.comzmoonshot.aizmoonshot.cndeepseek)r   )zapi.deepseek.commimoxiaomi)r   )zapi.xiaomimimo.comzxiaomimimo.comtuple_REASONING_ECHO_RULESfamilyc                T    t           D ]}|d         | k    r|c S t          |           )Nr   )r   KeyError)r   rules     r   _family_ruler     s<    %  7fKKK 
6

r   providermodelbase_urlc                  
 ddl m
 t          |           \  }}}}}|pd                                }	|pd                                ||v s|	|v rdS t	          fd|D                       rdS t	          
fd|D                       S )a  True when (provider, model, base_url) matches one echo-back family.

    Families can overlap (e.g. a deepseek-named model pointed at a kimi
    host); this membership test is independent per family so per-family
    predicates keep their original semantics.
    r   )base_url_host_matchesr=   Tc              3      K   | ]}|v V  	d S Nr    ).0r   model_lowers     r   	<genexpr>z0matches_reasoning_echo_family.<locals>.<genexpr>  s(      
4
4#3+
4
4
4
4
4
4r   c              3  0   K   | ]} |          V  d S r   r    )r   hostr   r   s     r   r   z0matches_reasoning_echo_family.<locals>.<genexpr>  s1      GG$$Xt44GGGGGGr   )utilsr   r   lowerany)r   r   r   r   rk   raw_providerslowered_providers
model_subshostsprovider_lowerr   r   s      `      @@r   r   r     s     ,+++++=I&=Q=Q:A}'Un"++--N;B%%''K=  N6G$G$Gt

4
4
4
4
4
4
444 tGGGGGGGGGGGr   'str | None'c                ^    t           D ]$}t          |d         | ||          r
|d         c S %dS )uE  Classify the provider direction for the reasoning_content echo policy.

    Returns ``"kimi"``, ``"deepseek"``, or ``"mimo"`` (first match in table
    order) when the target endpoint enforces reasoning_content echo-back on
    assistant turns, else ``None`` (strict/indifferent side — the field must
    be stripped).
    r   N)r   r   )r   r   r   r   s       r   r   r     sD     &  (a(E8LL 	7NNN	4r   c                (    t          | ||          duS )z<True when the endpoint requires reasoning_content echo-back.N)r   )r   r   r   s      r   r   r     s     5(;;4GGr   
source_msgr   api_msgneeds_thinking_padrL   c                (   |                      d          dk    rdS |                      d          }t          |t                    r,|s|                    dd           n|dk    rd|d<   n||d<   dS |                      d          }|r3|                      d          rt          |t                    r	|rd|d<   dS t          |t                    r"|r |r||d<   n|                    dd           dS |rd|d<   dS |                    dd           dS )	zCopy provider-facing reasoning fields onto an API replay message.

    ``needs_thinking_pad`` is the require-side flag (see
    ``needs_reasoning_echo`` / the agent's cached
    ``_needs_thinking_reasoning_pad``). Mutates ``api_msg`` in place.
    r+   rq   Nreasoning_contentr=    	reasoningr'   )r,   r   r   pop)r   r   r   existingnormalized_reasonings        r   r   r     sr    ~~f,,( ~~122H(C   ! 	4KK+T2222^^+.G'((+3G'( &>>+66NN<(( +S11 !	 (+#$ &,, 1E  	3+?G'((KK+T222  '*#$ KK#T*****r   api_messagesc                   d}| D ]}|                     d          dk    r|rB|                     d          r4t          |||           |                     d          r|dz  }`d|v r|                    dd           |dz  }|S )u0  Re-pad (or strip) assistant turns' reasoning_content for the active provider.

    ``api_messages`` is built once, before the retry loop, while the *primary*
    provider is active.  A mid-conversation fallback can then switch providers,
    so the reasoning fields baked into ``api_messages`` are shaped for the
    *prior* provider and must be reconciled against the *current* one:

    * Switching TO a require-side provider (DeepSeek / Kimi / MiMo thinking
      mode): assistant turns built when the prior provider did NOT need the
      echo-back go out without ``reasoning_content`` and the new provider
      rejects them with HTTP 400 ("The reasoning_content in the thinking mode
      must be passed back").  Re-apply the pad.

    * Switching TO a strict provider that rejects the field (Mistral,
      Cerebras, Groq, SambaNova, …): assistant turns built under a reasoning
      primary carry a ``reasoning_content`` pad (often a single space ``" "``),
      and the strict provider rejects it with HTTP 400/422 ("Extra inputs are
      not permitted").  Strip the field.  This is the exact cross-provider
      fallback bug from #45655 — a DeepSeek primary pads history with ``" "``,
      the request falls back to Mistral, and Mistral 422s on the stale pad.

    Calling this immediately before building the request kwargs reconciles the
    fields against the *current* provider.  It is idempotent and safe to call
    every iteration; it covers every fallback path.

    Returns the number of assistant turns whose reasoning_content was added or
    removed.
    r   r+   rq   r   r8   N)r,   r   r   )r   r   changedr   s       r   r   r     s    : G  ;;v+-- 	{{.// *7G=OPPP{{.// 1
 #g--/6661Nr   )r   r   r   r   )r   r   r   r   )r#   r   r   r   )r5   r   r   r   )rH   )rI   r   rJ   r   r   r   r   )r#   r   rn   r   r   r   )r   r   r   r   )r   )r2   r   r*   r   r   r   r   r   )r/   r   r   r   )r'   r   r   r   )r   r   r   r   )
r   r   r   r   r   r   r   r   r   r   )r   r   r   r   r   r   r   r   )r   r   r   r   r   r   r   r   )r   r   r   r   r   r   r   rL   )r   r   r   r   r   r   )&__doc__
__future__r   r   r[   loggingra   typingr   	getLogger__name__rY   compiler
   r   r"   r4   rG   rm   rs   r{   r~   r   r   r   __all__r   r   r   	frozensetr   __annotations__r   r   r   r   r   r   r    r   r   <module>r      s     # " " " " "    				      		8	$	$ 
-..      BA A A AH' ' ' 'T^ ^ ^ ^ ^B    @A A A A8 8 8 8v0 0 0 0
- - - -`   :  ^	 	 	 	 		O 	O 	O 	OL L L LV YY'789999;;35ii55}YY[[))XJ//-/	   	 	 	 	   H H H H*   H H H H
X+ X+ X+ X+v. . . . . .r   