
    .cjy                    4   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Zddlm	Z	 ddl
mZmZmZmZmZ  ej        e          Z eh d          ZdZd                    d	 eD                       Z ej        d
ez   dz   ej                  Z ej        dez   dz   ej                  Zd;dZ eh d          Z eh d          Zd<dZddd=d Z d>d!Z!d?d$Z"d@d%Z#dAd&Z$	 dBdddCd'Z%dddDd(Z&dEd+Z' eh d,          Z(dFd.Z)dBdGd1Z*dHd2Z+	 dBdId9Z,g d:Z-dS )Ju  Routing helpers for inbound user-attached images.

Two modes:

  native  — attach images as OpenAI-style ``image_url`` content parts on the
            user turn. Provider adapters (Anthropic, Gemini, Bedrock, Codex,
            OpenAI chat.completions) already translate these into their
            vendor-specific multimodal formats.

  text    — run ``vision_analyze`` on each image up-front and prepend the
            description to the user's text. The model never sees the pixels;
            it only sees a lossy text summary. This is the pre-existing
            behaviour and still the right choice for non-vision models.

The decision is made once per message turn by :func:`decide_image_input_mode`.
It reads ``agent.image_input_mode`` from config.yaml (``auto`` | ``native``
| ``text``, default ``auto``) and the active model's capability metadata.

In ``auto`` mode:
  - If the active model reports ``supports_vision=True`` (via config
    override or models.dev metadata), we attach natively — vision-capable
    main models should always see the original pixels, even when an
    auxiliary vision backend is configured. That auxiliary backend then
    acts as a *fallback* for sessions whose main model can't take images.
  - Otherwise, if the user has explicitly configured ``auxiliary.vision``
    (provider/model/base_url not ``auto``/empty), we route through the
    text pipeline so the auxiliary vision backend can describe the image
    for the text-only main model.
  - Otherwise (non-vision model, no explicit override), we fall back to
    text via the default vision_analyze flow.

This keeps ``vision_analyze`` surfaced as a tool in every session — skills
and agent flows that chain it (browser screenshots, deeper inspection of
URL-referenced images, style-gating loops) keep working. The routing only
affects *how user-attached images on the current turn* are presented to the
main model.
    )annotationsN)Path)AnyDictListOptionalTuple>   autotextnative)	.png.jpg.jpeg.gif.webp.bmpz.tiffz.tifz.heic|c              #  @   K   | ]}|                     d           V  dS ).N)lstrip).0es     5/home/ice/.hermes/hermes-agent/agent/image_routing.py	<genexpr>r   ?   s,      AAahhsmmAAAAAA    z/(?<![/:\w.])(?:~/|/)(?:[\w.\-]+/)*[\w.\-]+\.(?:z)\bzhttps?://[^\s<>\"']+?\.(?:z)(?:\?[^\s<>\"']*)?r   strreturnTuple[List[str], List[str]]c                   t          | t                    r| sg g fS g t          j        d| t          j                  D ]=}                    |                                |                                f           >t          j        d|           D ]=}                    |                                |                                f           >d
fd}g }t                      }t                              |           D ]} ||                                          r |
                    d          }t          j                            |          }	 t          j                            |          sun# t          $ r Y w xY w||v r|                    |           |                    |           g }t                      }	t"                              |           D ]y} ||                                          r |
                    d          }
|
                    d	          }
|
|	v rO|	                    |
           |                    |
           z||fS )u  Scan free-form text for image references the model should see.

    Returns ``(local_paths, urls)``:

      * ``local_paths`` — absolute (``/``) or home-relative (``~/``) paths
        whose suffix is an image extension AND whose expanded form exists
        on disk as a file. Order-preserving, deduplicated.
      * ``urls`` — ``http(s)://…`` URLs whose path ends in an image
        extension (a ``?query`` is allowed after the extension).
        Order-preserving, deduplicated.

    Matches inside fenced code blocks (``` ``` ```) and inline backticks
    (`` `…` ``) are skipped so that snippets pasted into a task body for
    reference aren't mistaken for live attachments. This mirrors the
    behaviour of ``gateway.platforms.base.BaseAdapter.extract_local_files``.

    Local paths are validated against the filesystem; URLs are not
    (the provider fetches them at request time).
    z```[^\n]*\n.*?```z	`[^`\n]+`posintr   boolc                <     t           fdD                       S )Nc              3  >   K   | ]\  }}|cxk    o|k     nc V  d S N )r   sr   r    s      r   r   z7extract_image_refs.<locals>._in_code.<locals>.<genexpr>r   s;      77DAq1<<<<a<<<<777777r   )any)r    
code_spanss   `r   _in_codez$extract_image_refs.<locals>._in_codeq   s'    7777J777777r   r   z	.,;:!?)]>)r    r!   r   r"   )
isinstancer   refinditerDOTALLappendstartendset_LOCAL_IMAGE_PATH_REgroupospath
expanduserisfileOSErroradd_IMAGE_URL_RErstrip)r   mr*   local_paths
seen_pathsmatchrawexpandedurls	seen_urlsurlr)   s              @r   extract_image_refsrF   R   ss   ( dC    2v )+J[-tRY?? 0 017799aeegg.////[t,, 0 017799aeegg.////8 8 8 8 8 8  K55J%..t44 % %8EKKMM"" 	kk!nn7%%c**	7>>(++  	 	 	H	 z!!x   8$$$$D%%I''-- 
 
8EKKMM"" 	kk!nn jj%%)cCs   E66
FF>   1onyestrue>   0noofffalserA   r   Optional[bool]c                *   t          | t                    r| S t          | t                    r| dv rt          |           S dS t          | t                    r<|                                                                 }|t          v rdS |t          v rdS dS )z@Return True/False for recognised boolean values, None otherwise.)r      NTF)r+   r"   r!   r   striplower_TRUE_TOKENS_FALSE_TOKENS)rA   r'   s     r   _coerce_capability_boolrV      s    #t 
#s &==99t#s IIKK454r    requested_providercfgOptional[Dict[str, Any]]providermodelrY   c          
         t          | t                    sdS |                     d          }t          |t                    r|ni }t          |                    d                    }||S t	          |                    d          pd                                          }g }|||fD ]]}	|	s|                    |	           |	                    d          r.|	t          d          d         }
|
r|                    |
           ^|                     d          }t          |t                    r|ni }t          	                    |          D ]}|                    |          }t          |t                    r|ni }|                    d          }t          |t                    r|ni }|                    |          }t          |t                    r|ni }t          |                    d|                    d	                              }||c S |                     d
          }t          |t                    rKt          	                    |          D ]/}	|	                                                                }|D ]}t          |t                    st	          |                    d          pd                                                                          }||k    rh|                    d          }t          |t                    r|ni }|                    |          }t          |t                    r|ni }t          |                    d|                    d	                              }||c c S 1dS )u  Resolve user-declared vision capability from config.yaml.

    Resolution order, first hit wins:
      1. ``model.supports_vision`` (top-level shortcut for the active model)
      2. ``providers.<provider>.models.<model>.supports_vision``
         (named custom providers — ``provider`` may be the runtime-resolved
         value ``"custom"``, the runtime's originally requested provider,
         and/or the user-declared name under ``model.provider``; all are
         tried. For ``custom:<name>`` syntax, the stripped ``<name>`` is also
         tried as a provider key.)
      2b. ``custom_providers`` (legacy list form) ``.models.<model>``

    Under (2) and (2b), the per-model capability key may be written as
    either ``supports_vision`` or the shorter ``vision`` alias; both work.

    Returns None when no override is set, so the caller falls through to
    models.dev. Returns False explicitly only when the user wrote a
    recognised boolean false token.
    Nr]   supports_visionr\   rW   custom:	providersmodelsvisioncustom_providersname)r+   dictgetrV   r   rR   r/   
startswithlenfromkeyslistrS   )rZ   r\   r]   rY   model_cfg_raw	model_cfgtopconfig_providerprovider_candidates	candidatestripped_candidateproviders_rawproviders_cfgp	entry_rawentry
models_raw
models_cfgper_model_raw	per_modelcoercedrd   candidate_name
entry_names                           r   _supports_vision_overrider      s   4 c4   t GGG$$M1;M41P1P XVXI
!)--0A"B"B
C
CC

 )--
339r::@@BBO%'((OD ? ?	 	""9---	** 	?!*3y>>??!;! ?#**+=>>>GGK((M5?t5T5T$\MMZ\M]].//  !%%a((	-7	4-H-H P		bYYx((
3=j$3O3O%WZZUW
"u--5?t5T5T$\MMZ\	)MM+Y]]8-D-DEE
 
 NNN  ww122"D)) # ':;; 	# 	#I&__..4466N- # #	!)T22  v!6!6!<"==CCEEKKMM
//&]]844
+5j$+G+GOZZR
 *u 5 5-7t-L-LTMMRT	1MM"3Y]]85L5LMM  &"NNNNN '#  4r   c                *   	 ddl m} t           |d          pd                                          }t           |d          pd                                                                          }t          |pd                                                                          }|r
|r||k    r|S n# t
          $ r Y nw xY wt          | t                    sdS |                     d          }t          |t                    r|ni }t          |                    d          pd                                          }|r|S t          |                    d          pd                                          }	t                      }
t          d||	f          D ]}|
                    |           |                                                    d          r0|
                    |                    d	d
          d
                    n|
                    d|            |                     d          }t          |t                    rk|
D ]h}|                    |          }t          |t                    r<t          |                    d          pd                                          }|r|c S i|                     d          }t          |t                    rd |
D             }|D ]}t          |t                    st          |                    d          pd                                          }||
vr|                                |vrit          |                    d          pd                                          }|r|c S dS )z7Best-effort base URL for the active inference provider.r   _runtime_main_valuebase_urlrW   r\   r]   Nr`   :rQ   ra   rd   c                6    h | ]}|                                 S r&   )rS   )r   ns     r   	<setcomp>z._resolve_inference_base_url.<locals>.<setcomp>A  s     66617799666r   re   )agent.auxiliary_clientr   r   rR   rS   	Exceptionr+   rf   rg   r2   filterr:   rh   splitrk   )rZ   r\   r   runtimeruntime_providerrY   rl   rm   r   ro   candidate_namesru   rt   re   rw   burd   loweredrv   r~   s                       r   _resolve_inference_base_urlr     s   
	>>>>>>))*55;<<BBDD22:>>D"EEKKMMSSUU R006688>>@@ 	. 	2DHX2X2XN    c4   rGGG$$M1;M41P1P XVXI9==,,23399;;H )--
339r::@@BBO #OD8_566 / /A7799	** 	/Q 23333!....GGK((M-&& # 	 	D!%%d++E%&& :..4"55;;== IIIww122"D)) 
66o666) 	 	Ii.. Y]]6228b99??AAJ00Z5E5E5G5Gw5V5VY]]:..4"55;;==B 			 2s   B0B4 4
C Cr   r"   c                    | pd                                                                 }|dk    rdS |sdS 	 ddlm}  ||          dk    S # t          $ r Y dS w xY w)zBTrue when the active provider likely fronts a local Ollama server.rW   ollamaTFr   )detect_local_server_type)rR   rS   agent.model_metadatar   r   )r\   r   ru   r   s       r   _should_probe_ollama_visionr   O  s    	R  &&((AH}}t uAAAAAA''11X==   uus   A 
AAc                    t          | t                    sdS |                                                                 }|t          v r|S dS )z5Normalize a config value into one of the valid modes.r
   )r+   r   rR   rS   _VALID_MODES)rA   vals     r   _coerce_moder   ^  sG    c3 v
))++



C
l
6r   c                h   t          | t                    sdS |                     d          pi }t          |t                    sdS |                    d          pi }t          |t                    sdS t          |                    d          pd                                                                          }t          |                    d          pd                                          }t          |                    d          pd                                          }|dv r|s|sdS d	S )
uc  True when the user configured a specific auxiliary vision backend.

    An explicit override means the user has a dedicated vision backend
    available; it's used as a *fallback* when the main model can't take
    images natively. In ``auto`` mode, native vision on a vision-capable
    main model still wins over this fallback — see issue #29135.
    F	auxiliaryrc   r\   rW   r]   r   >   rW   r
   T)r+   rf   rg   r   rR   rS   )rZ   auxrc   r\   r]   r   s         r   _explicit_aux_vision_overrider   h  s     c4   u
''+


$"Cc4   uWWX$"Ffd## u6::j))/R006688>>@@H

7##)r**0022E6::j))/R006688H <hu4r   c               b   |s	 ddl m} t           |d          pd                                                                          }t           |d          pd                                          }t          | pd                                                                          }t          |pd                                          }||k    r2||k    r,t           |d          pd                                          }n# t
          $ r Y nw xY wt          || ||          }	|	|	S | r|sdS d}
	 dd	lm}  || |          }
n4# t
          $ r'}t          
                    d
| ||           Y d}~nd}~ww xY w|
t          |
j                  S t          ||           }|s.| pd                                                                dk    rd}t          | |          rL	 ddlm}  |||          }||S n4# t
          $ r'}t          
                    d| ||           Y d}~nd}~ww xY wdS )a  Return True/False if we can resolve caps, None if unknown.

    Consults the user's ``supports_vision`` override in config.yaml first
    (so custom/local models declared as vision-capable don't fall through to
    text routing in ``auto`` mode), then falls back to models.dev.
    r   r   r\   rW   r]   rY   rX   N)get_model_capabilitiesu2   image_routing: caps lookup failed for %s:%s — %sr   zhttp://localhost:11434/v1)query_ollama_supports_visionu:   image_routing: ollama vision probe failed for %s:%s — %s)r   r   r   rR   rS   r   r   agent.models_devr   loggerdebugr"   r_   r   r   r   r   )r\   r]   rZ   rY   r   r   runtime_modellookup_providerlookup_modeloverridecapsr   excr   r   ollama_visions                   r   _lookup_supports_visionr     s   $  	BBBBBB"##J//52   eggeegg    3 3G < < BCCIIKKM!(.b117799??AAOu{++1133L?22}7T7T%(''(<==C& &%'' #  	 	 	D	 )-	  H  5 tDa;;;;;;%%h66 a a aI8UZ\_````````aD()))*399H /R..006688HDD."8X66 	IIIIII88IIM($$ ) 	 	 	LLL	       	 4sB   D D 
DD6E	 	
E:E55E:$G; ;
H,H''H,c               l   d}t          |t                    rN|                    d          pi }t          |t                    r"t          |                    d                    }|dk    rdS |dk    rdS |rt	          | |||          }nt	          | ||          }|du rdS t          |          rdS dS )a~  Return ``"native"`` or ``"text"`` for the given turn.

    Args:
      provider: active inference provider ID (e.g. ``"anthropic"``, ``"openrouter"``).
      model:    active model slug as it would be sent to the provider.
      cfg:      loaded config.yaml dict, or None. When None, behaves as auto.
      requested_provider: provider identity before runtime canonicalization.
    r
   agentimage_input_moder   r   rX   T)r+   rf   rg   r   r   r   )r\   r]   rZ   rY   mode_cfg	agent_cfgsupportss          r   decide_image_input_moder     s     H#t GGGG$$*	i&& 	G#IMM2D$E$EFFH8x6v  
A*1	
 
 
 +8UC@@4x$S)) v6r   bytesOptional[str]c                |   | sdS |                      d          rdS |                      d          rdS | dd         dv rdS t          |           d	k    r| dd
         dk    r| dd	         dk    rdS |                      d          rdS t          |           d	k    r$| d
d         dk    r| dd	         }|dv rdS |dv rdS | dd
         dv rdS | dd
         dk    rdS | dd                                                                         }|                     d          s|                     d          rd|v rdS dS )a  Detect image MIME from magic bytes. Returns None if unrecognised.

    Filename-based detection (``mimetypes.guess_type``) is unreliable when
    upstream platforms lie about content-type. Discord, for example, can
    serve a PNG with ``content_type=image/webp`` for proxied/animated
    stickers, custom emoji previews, or images uploaded via certain bots.
    Anthropic strictly validates that declared media_type matches the
    actual bytes and returns HTTP 400 on mismatch, so we sniff to be safe.
    Ns   PNG

	image/pngs   
image/jpeg   >      GIF87a   GIF89a	image/gif      s   RIFF   s   WEBP
image/webps   BM	image/bmps   ftyp>      avif   avisz
image/avif>      heic   heim   heis   heix   hevc   hevx   mif1   msf1z
image/heic>      II*    MM *z
image/tiffs      zimage/x-iconi   s   <?xmls   <svgzimage/svg+xml)rh   ri   r   rS   )rA   brandheads      r   _sniff_mime_from_bytesr     s     t
~~*++ {
~~o&& |
2A2w((({
3xx2~~#bqb'W,,QrTg1E1E|
~~e {
3xx2~~#ac(g--AbD	&&&< 
 
 
  <
2A2w***|
2A2w%%%~tt9##%%Dx   #DOOG$<$< #d??"?4r   >   r   r   r   r   Optional[bytes]c                t   	 ddl m} n+# t          $ r t                              d           Y dS w xY w	 ddl}|                                 n# t          $ r Y nw xY w	 ddl}n# t          $ r Y nw xY w	 ddl	m
} |                     ||                     5 }|j        dvr|                    d          } |            }|                    |dd	
           |                                cddd           S # 1 swxY w Y   dS # t          $ r&}t                              d|           Y d}~dS d}~ww xY w)a  Decode arbitrary image bytes with Pillow and re-encode as PNG.

    Returns None if Pillow isn't installed or can't decode the input
    (rare formats, corrupted bytes, missing optional decoder plugin for
    HEIC/AVIF, or vector formats like SVG). Caller falls back to skipping
    the image so the rest of the turn still works.

    HEIC/HEIF and AVIF need optional Pillow plugins; we try to register
    them on demand and swallow ImportError so a missing plugin just
    looks like 'Pillow can't decode this' rather than crashing.
    r   )Imagezimage_routing: Pillow not installed; cannot transcode non-standard image format to PNG. Install with `pip install Pillow` (and `pillow-heif` / `pillow-avif-plugin` for those formats).N)BytesIO>   LPLARGBRGBAr   PNGF)formatoptimizez<image_routing: Pillow could not transcode image to PNG -- %s)PILr   ImportErrorr   infopillow_heifregister_heif_openerr   pillow_avifior   openmodeconvertsavegetvalue)rA   r   r   r   r   imbufr   s           r   _transcode_to_pngr   O  s      L	
 	
 	

 tt((****      ZZ%% 	" w===ZZ'''))CGGCG666<<>>	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	"    JC	
 	
 	
 ttttt	sl   	 $11A 
AAA$ $
A10A15$D AC:-D :C>>D C>D 
D7D22D7r6   r   c                   |t          |          }|r|S t          j        t          |                     \  }}|r|                    d          r|S | j                                        }ddddddd                    |d          S )	zReturn image MIME type for *path*.

    If *raw* bytes are provided, magic-byte sniffing wins (authoritative).
    Otherwise we fall back to ``mimetypes`` then suffix-based defaults.
    Nzimage/r   r   r   r   r   )r   r   r   r   r   r   )r   	mimetypes
guess_typer   rh   suffixrS   rg   )r6   rA   sniffedmime_r   s         r   _guess_mimer     s     (-- 	N"3t99--GD! ))  [  F  
c&, r   c                   	 ddl m}  |t          |                      n?# t          $ r'}t                              d| |           Y d}~dS d}~wt          $ r Y nw xY w	 |                                 }n4# t          $ r'}t                              d| |           Y d}~dS d}~ww xY wt          | |          }|t          vrTt          |          }|t                              d| |           dS t                              d| j        |           |}d	}t          j        |                              d
          }d| d| S )uW  Encode a local image as a base64 data URL at its native size.

    Size limits are NOT enforced here — the agent retry loop
    (``run_agent._try_shrink_image_parts_in_messages``) shrinks on the
    provider's first rejection. Keeping this simple means providers that
    accept large images (OpenAI 49 MB+, Gemini 100 MB) don't pay a silent
    quality tax just because one other provider is stricter.

    Format compatibility IS handled here: if the sniffed MIME isn't one
    of ``_UNIVERSALLY_SUPPORTED_MIMES`` (i.e. it's something like AVIF,
    HEIC, BMP, TIFF, or ICO that some providers reject outright), we
    transcode to PNG with Pillow before declaring media_type. This fixes
    the user-visible "Could not process image" HTTP 400 from Anthropic on
    Discord-attached AVIF/HEIC/BMP files.

    Returns None if the file can't be read OR if the format isn't
    universally supported AND Pillow can't transcode it (Pillow missing,
    HEIC/AVIF plugin missing, vector format like SVG, corrupt bytes). The
    caller reports those paths in ``skipped`` and the rest of the turn
    proceeds.
    r   )raise_if_read_blockedz6image_routing: blocked local image attachment %s -- %sNu'   image_routing: failed to read %s — %s)rA   zimage_routing: %s is %s which is not accepted by all major vision providers and could not be transcoded to PNG; skipping this attachment.zIimage_routing: transcoded %s (%s) -> image/png for provider compatibilityr   asciizdata:z;base64,)agent.file_safetyr   r   
ValueErrorr   warningr   
read_bytesr   _UNIVERSALLY_SUPPORTED_MIMESr   r   re   base64	b64encodedecode)r6   r   r   rA   r   
transcodedb64s          r   _file_to_data_urlr    s   ,	;;;;;;c$ii((((   OQUWZ[[[ttttt   oo   @$LLLttttt t%%%D///&s++
NN, d	   4WIt	
 	
 	
 

3


&
&w
/
/C&4&&&&&s2   ! 
AAAA!A6 6
B' B""B'	user_textimage_paths	List[str]
image_urlsOptional[List[str]]&Tuple[List[Dict[str, Any]], List[str]]c                   g }g }g }g }|D ]}t          |          }|                                r|                                s#|                    t	          |                     \t          |          }	|	s#|                    t	          |                     |                    dd|	id           |                    t	          |                     |pg D ]J}
|
pd                                }
|
s|                    dd|
id           |                    |
           K| pd                                }|s|r~|pd}g }|                    d |D                        |                    d |D                        | dd	                    |          z   }d
|dg}|                    |           ||fS g }|r|                    d
|d           ||fS )u  Build an OpenAI-style ``content`` list for a user turn.

    Shape:
      [{"type": "text", "text": "...\n\n[Image attached at: /local/path]"},
       {"type": "image_url", "image_url": {"url": "data:image/png;base64,..."}},
       {"type": "image_url", "image_url": {"url": "https://example.com/a.png"}},
       ...]

    Local paths are read from disk and embedded as base64 ``data:`` URLs.
    Remote URLs (``http(s)://``) are passed through verbatim — the provider
    fetches them server-side. The model still sees the pixels either way.

    For each successfully attached image, a hint is appended to the text
    part:

      * local path → ``[Image attached at: <path>]``
      * URL        → ``[Image attached: <url>]``

    The hint gives the model a string handle so MCP/skill tools that take
    an image path or URL argument can be invoked on the same image without
    an extra round-trip. This parallels the text-mode hint produced by
    ``Runner._enrich_message_with_vision`` (``vision_analyze using image_url:
    <path>``) so behaviour is consistent across both image input modes.

    Images are attached at their native size. If a provider rejects the
    request because an image is too large (e.g. Anthropic's 5 MB per-image
    ceiling), the agent's retry loop transparently shrinks and retries
    once — see ``run_agent._try_shrink_image_parts_in_messages``.

    Returns (content_parts, skipped). Skipped entries are local paths
    that couldn't be read from disk; URLs are never skipped (they're
    not validated here).
    	image_urlrE   )typer	  rW   zWhat do you see in this image?c              3  "   K   | ]
}d | dV  dS )z[Image attached at: ]Nr&   )r   ru   s     r   r   z-build_native_content_parts.<locals>.<genexpr>#  s.      NN!5555NNNNNNr   c              3  "   K   | ]
}d | dV  dS )z[Image attached: r  Nr&   )r   us     r   r   z-build_native_content_parts.<locals>.<genexpr>$  s.      JJq2a222JJJJJJr   z


r   )r
  r   )	r   existsis_filer/   r   r  rR   extendjoin)r  r  r  skippedimage_partsattached_pathsattached_urlsraw_pathru   data_urlrE   r   	base_text
hint_linescombined_textpartss                   r   build_native_content_partsr    s\   L G(*K "N!M - -NNxxzz 	 	NN3x==)))$Q'' 	NN3x==)))*
 
 	 	 	 	c(mm,,,,R " "yb!! 	
 
 	 	 	 	S!!!!O""$$D   <<	 "
NN~NNNNNNJJMJJJJJJ$***TYYz-B-BB06'N'N&O[!!!g~ E 5fd33444'>r   )r   r  rF   )r   r   r   r   )rA   r   r   rO   )
rZ   r[   r\   r   r]   r   rY   r   r   rO   )rZ   r[   r\   r   r   r   )r\   r   r   r   r   r"   )rA   r   r   r   )rZ   r[   r   r"   r%   )
r\   r   r]   r   rZ   r[   rY   r   r   rO   )
r\   r   r]   r   rZ   r[   rY   r   r   r   )rA   r   r   r   )rA   r   r   r   )r6   r   rA   r   r   r   )r6   r   r   r   )r  r   r  r  r  r  r   r  ).__doc__
__future__r   r   loggingr   r5   r,   pathlibr   typingr   r   r   r   r	   	getLogger__name__r   	frozensetr   _IMAGE_EXTSr  _IMAGE_EXT_PATTERNcompile
IGNORECASEr3   r;   rF   rT   rU   rV   r   r   r   r   r   r   r   r   r   r   r   r  r  __all__r&   r   r   <module>r,     s  $ $L # " " " " "       				 				       3 3 3 3 3 3 3 3 3 3 3 3 3 3		8	$	$ y33344 XXAA[AAAAA 
 "rz69KKfTM   
!$669OOM B B B BZ y33344	55566   , !] ] ] ] ] ]@8 8 8 8v         < %)G
 !G G G G G G^ !- - - - - -~0 0 0 0|  )y * * *     
1 1 1 1h         48' 8' 8' 8'| '+V V V V Vr  r   