§
    øžyjTd ã                   ó
  — U d 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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 ddlmZ ddlZdaeed<   daeed<   d¦d
„ZddlmZ ddlmZ ddlmZ ddlZ ej        e ¦  «        Z! edd¬¦  «        Z"d	e#fd„Z$ e$¦   «         Z%dZ&ddl'Z'd	e(fd„Z)d	e(fd„Z* e*¦   «         Z+ ee+d¬¦  «        Z,d„ Z-de.d	e/fd„Z0de.d	e/fd„Z1de.d	e/fd„Z2de3d	ee.         fd„Z4 e5h d£¦  «        Z6ded ed	e/fd!„Z7d"ed#e.d	e8ee         ee.         ee.         f         fd$„Z9d%e:d	e/fd&„Z;d'd(œde.d)ed*e(d+e<d,e.d	efd-„Z=d§d/e.d)ed0e(d	efd1„Z>d"ed	e.fd2„Z?d¨d"ed3ee.         d	e.fd4„Z@d5ZAd6ZBd7ZCd8ZDd%e:d	e/fd9„ZEd"ed:e(d	e/fd;„ZF	 d¨d"ed<ed=ee<         d	e8ee         ee.         ee.         f         fd>„ZGd?ee<         d@ee<         d	ee.         fdA„ZHdeDddfd"ed3ee.         dBe(d:ee(         dCee<         d	e.fdD„ZIdEe.dFe.d	e/fdG„ZJd	e/fdH„ZK	 d¨d/e.dIe.dJe.dKe(dLee.         d	ee.ef         fdM„ZLejM        dN„ ¦   «         ZN	 	 d©d/e.dIe.dOee.         d<eeO         d	ef
dP„ZP	 	 	 dªd/e.dQe.dFe.dOee.         d<eeO         d	e.fdR„ZQd	e/fdS„ZRe dTk    �rŒ	  eSdU¦  «          eSdV¦  «          eR¦   «         ZTeTs' eSdW¦  «          eSdX¦  «          ejU        dY¦  «         n eSdZ¦  «          eSd[¦  «         e"jV        r( eSd\e"jW        › �¦  «          eSd]e"jW        › d^�¦  «         n eSd_¦  «          eSd`¦  «          eSda¦  «          eSdb¦  «          eSdc¦  «          eSdd¦  «          eSde¦  «          eSdf¦  «          eSdg¦  «          eSdh¦  «          eSdi¦  «          eSdj¦  «          eSdk¦  «          eSdl¦  «          eSdm¦  «          eSdn¦  «          eSdo¦  «          eSdp¦  «          eSdq¦  «          eSdr¦  «          eSds¦  «          eSdt¦  «          eSdu¦  «         ddvlXmYZYmZZZ dwdxdydzd{d|œdzd}d|œd~dd€id�d�d‚dƒœd„œd/dIgd…œd†œZ[d‡ee.ef         dˆed	e.fd‰„Z\ eYj]        dwdŠe[e\eRd‹dŒ¬�¦  «         dŽd�d�dŽdŽd‘d‘d’œZ^dZ_d5Z`d“ed	ee.         fd”„Zad¨d“ed3ee.         d	e.fd•„Zbd	e/fd–„Zcd—e.d	e/fd˜„Zdd™e.dOee.         d	efdš„Zed§d›e.d)ed0e(d	efdœ„Zf	 	 d©d›e.dQe.dFe.dOee.         d	e.f
d�„ZgdždŸdydzd d|œdzd¡d|œd¢œd›dIgd…œd†œZhd‡ee.ef         dˆed	ee.         fd£„Zi eYj]        džd¤eheieRd‹d¥¬�¦  «         dS )«aV  
Vision Tools Module

This module provides vision analysis tools that work with image URLs.
Uses the centralized auxiliary vision router, which can select OpenRouter,
Nous, Codex, native Anthropic, or a custom OpenAI-compatible endpoint.

Available tools:
- vision_analyze_tool: Analyze images from URLs with custom prompts

Features:
- Downloads images from URLs and converts to base64 for API compatibility
- Comprehensive image description
- Context-aware analysis based on user queries
- Automatic temporary file cleanup
- Proper error handling and validation
- Debug logging support

Usage:
    from vision_tools import vision_analyze_tool
    import asyncio
    
    # Analyze an image
    result = await vision_analyze_tool(
        image_url="https://example.com/image.jpg",
        user_prompt="What architectural style is this building?"
    )
é    N)ÚThreadPoolExecutor)ÚPath)ÚAnyÚ	AwaitableÚDictÚOptional)ÚurlparseÚasync_call_llmÚextract_content_or_reasoningÚreturnc                  ó^   — t           �t          €ddlm } m} t           €| a t          €|ad S d S d S )Nr   )r
   r   )r
   r   Úagent.auxiliary_client)Ú_aclÚ_ecrs     ú8/home/ragecks/.hermes/hermes-agent/tools/vision_tools.pyÚ_load_auxiliary_clientr   4   sf   € åÐÕ!=Ð!Eð	
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
õ Ð!Ø!ˆNÝ'Ð/Ø+/Ð(Ð(Ð(ð "FÐ!Eð 0Ð/ó    )Úget_hermes_dir)ÚDebugSession)Úcheck_website_accessÚvision_toolsÚVISION_TOOLS_DEBUG)Úenv_varc                  ó  — t          j        dd¦  «                             ¦   «         } | r 	 t          | ¦  «        S # t          $ r Y nw xY w	 ddlm}m}  |¦   «         } ||ddd¦  «        }|�t          |¦  «        S n# t          $ r Y nw xY wdS )	NÚHERMES_VISION_DOWNLOAD_TIMEOUTÚ r   ©Úcfg_getÚload_configÚ	auxiliaryÚvisionÚdownload_timeoutg      >@)	ÚosÚgetenvÚstripÚfloatÚ
ValueErrorÚhermes_cli.configr   r   Ú	Exception)Úenv_valr   r   ÚcfgÚvals        r   Ú_resolve_download_timeoutr-   M   sÒ   € ÝŒiÐ8¸"Ñ=Ô=×CÒCÑEÔE€GØð ð	Ý˜‘>”>Ð!øÝð 	ð 	ð 	ØˆDð	øøøðØ:Ð:Ð:Ð:Ð:Ð:Ð:Ð:Øˆk‰mŒmˆØˆg�c˜;¨Ð2DÑEÔEˆØˆ?Ý˜‘:”:Ðð øåð ð ð Øˆðøøøàˆ4s!   «: º
AÁAÁ0A= Á=
B
Â	B
i   c                  óÖ   — 	 t          dt          t          j        d¦  «        ¦  «        ¦  «        S # t          t
          f$ r& t          dt          j        ¦   «         pd¦  «        cY S w xY w)u  Best-effort host CPU count, honoring cgroup/affinity limits when set.

    Prefers ``os.sched_getaffinity`` (the CPUs this process may actually run
    on â€” respects container/cpuset pinning) and falls back to
    ``os.cpu_count()``. Returns at least 1.
    é   r   )ÚmaxÚlenr#   Úsched_getaffinityÚAttributeErrorÚOSErrorÚ	cpu_count© r   r   Ú_detect_host_cpusr7   …   sk   € ð+Ý�1•c�"Ô.¨qÑ1Ô1Ñ2Ô2Ñ3Ô3Ð3øÝ�GÐ$ð +ð +ð +Ý�1•b”l‘n”nÐ)¨Ñ*Ô*Ð*Ð*Ð*ð+øøøs   ‚.1 ±4A(Á'A(c                  óX  — t          j        dd¦  «                             ¦   «         } | r)	 t          | ¦  «        }|dk    r|S n# t          $ r Y nw xY w	 ddlm}m}  |¦   «         } ||ddd¦  «        }|�t          |¦  «        }|dk    r|S n# t          $ r Y nw xY wt          ¦   «         S )	uÚ  Resolve how many vision encode/resize bursts may run concurrently.

    Defaults to the host's usable core count (``_detect_host_cpus``) â€” no fixed
    ceiling, because the cap tracks the actual exhausted resource (CPU cores),
    not a magic number. The LLM call is NOT covered by this limit, so legitimate
    multi-image fan-out keeps full request concurrency; only the simultaneous
    CPU bursts are bounded so the event loop always keeps a core.

    Resolution order: HERMES_VISION_MAX_CONCURRENCY env â†’
    config.yaml auxiliary.vision.max_concurrency â†’ host core count. Any value
    that parses to < 1 is ignored in favor of the next source so the cap can
    never be disabled into an unbounded encode storm.
    ÚHERMES_VISION_MAX_CONCURRENCYr   r/   r   r   r    r!   Úmax_concurrency)
r#   r$   r%   Úintr'   r(   r   r   r)   r7   )r*   Úparsedr   r   r+   r,   s         r   Ú_resolve_vision_cpu_workersr=   ’   sö   € õ ŒiÐ7¸Ñ<Ô<×BÒBÑDÔD€GØð ð	Ý˜‘\”\ˆFØ˜Š{ˆ{Ø�ð øåð 	ð 	ð 	ØˆDð	øøøð	Ø:Ð:Ð:Ð:Ð:Ð:Ð:Ð:Øˆk‰mŒmˆØˆg�c˜;¨Ð2CÑDÔDˆØˆ?Ý˜‘X”XˆFØ˜Š{ˆ{Ø�øøÝð ð ð ØˆðøøøåÑÔÐs#   «A Á
AÁAÁ8B Â
BÂBzvision-encode)Úmax_workersÚthread_name_prefixc              �   ó”   K  — ddl }t          j        ¦   «         }|                     t           |j        | g|¢R i |¤Ž¦  «        ƒ d{V —†S )uÄ  Run a sync encode/resize callable on the bounded vision CPU executor.

    Offloads CPU-bound image work to :data:`_vision_cpu_executor` so it (a)
    never runs on the caller's event-loop thread and (b) is bounded to the
    host's usable core count process-wide. Excess encodes queue on the
    executor instead of all running at once, leaving cores free for the loop.
    The LLM call must NOT be routed through here â€” only the encode/resize.
    r   N)Ú	functoolsÚasyncioÚget_running_loopÚrun_in_executorÚ_vision_cpu_executorÚpartial)ÚfnÚargsÚkwargsrA   Úloops        r   Ú_run_encode_on_cpu_executorrK   Å   s{   è è € ð ÐÐÐÝÔ#Ñ%Ô%€DØ×%Ò%ÝÐ/˜iÔ/°ÐD°TÐDÐDÐD¸VÐDÐDñô ð ð ð ð ð ð ð r   Úurlc                 ó–   — | rt          | t          ¦  «        sdS |                      d¦  «        sdS t          | ¦  «        }|j        sdS dS )z2HTTP(S) shape check only (scheme, netloc). No DNS.F)úhttp://úhttps://T)Ú
isinstanceÚstrÚ
startswithr	   Únetloc)rL   r<   s     r   Ú_image_url_shape_okrT   Õ   s[   € àð •j ¥cÑ*Ô*ð Øˆuà�>Š>Ð1Ñ2Ô2ð Øˆuõ �c‰]Œ]€FØŒ=ð ØˆuØˆ4r   c                 óF   — t          | ¦  «        sdS ddlm}  || ¦  «        S )zHValidate image URL for sync callers and tests (SSRF via sync DNS check).Fr   )Úis_safe_url)rT   Útools.url_safetyrV   )rL   rV   s     r   Ú_validate_image_urlrX   ä   s9   € å˜sÑ#Ô#ð Øˆuà,Ð,Ð,Ð,Ð,Ð,Øˆ;�sÑÔÐr   c              ƒ   óV   K  — t          | ¦  «        sdS ddlm}  || ¦  «        ƒ d{V —†S )zAValidate remote image URL without blocking the event loop on DNS.Fr   )Úasync_is_safe_urlN)rT   rW   rZ   )rL   rZ   s     r   Ú_validate_image_url_asyncr[   í   sP   è è € å˜sÑ#Ô#ð ØˆuØ2Ð2Ð2Ð2Ð2Ð2Ø"Ð" 3Ñ'Ô'Ð'Ð'Ð'Ð'Ð'Ð'Ð'r   Údatac                 ó4  — | dd…         }|                      d¦  «        rdS |                      d¦  «        rdS |                      d¦  «        rdS |                      d	¦  «        rd
S t          |¦  «        dk    r|dd…         dk    r|dd…         dk    rdS dS )u2  Magic-byte MIME sniff on raw bytes (authoritative; no extension trust).

    Returns ``None`` for anything without a recognized image header â€” including
    SVG, which has no magic bytes. The resolver special-cases SVG (sniffs
    ``<svg``) and passes it through for rasterization at the call sites.
    Né@   s   ‰PNG

ú	image/pngs   ÿØÿú
image/jpeg)s   GIF87as   GIF89aú	image/gifs   BMú	image/bmpé   é   s   RIFFé   s   WEBPú
image/webp)rR   r1   )r\   Úheaders     r   Ú"_detect_image_mime_type_from_bytesrh   õ   s¾   € ð �#�2�#ŒY€FØ×ÒÐ-Ñ.Ô.ð Øˆ{Ø×Ò˜Ñ)Ô)ð Øˆ|Ø×ÒÐ/Ñ0Ô0ð Øˆ{Ø×Ò˜ÑÔð Øˆ{Ý
ˆ6�{„{�bÒÐ˜V B Q BœZ¨7Ò2Ð2°v¸aÀ¸d´|ÀwÒ7NÐ7NØˆ|Øˆ4r   >   ra   r_   r`   rf   Úsvg_pathÚout_pathc                 ó’  — 	 ddl }|                     t          | ¦  «        t          |¦  «        ¬¦  «         |                     ¦   «         o|                     ¦   «         j        dk    S # t          $ r Y nw xY w	 ddlm} ddl	m
}  |t          | ¦  «        ¦  «        }|�V|                     |t          |¦  «        d¬¦  «         |                     ¦   «         o|                     ¦   «         j        dk    S n# t          $ r Y nw xY wddl}ddl}dd	t          |¦  «        t          | ¦  «        gd
t          | ¦  «        dd|› �gfD ]‚}|                     |d         ¦  «        re	 |                     |ddd|j        ¬¦  «         |                     ¦   «         r |                     ¦   «         j        dk    r dS Œr# t          $ r Y Œ~w xY wŒƒdS )u�  Best-effort SVG â†’ PNG rasterization. Returns True on success.

    Tries, in order: cairosvg, svglib+reportlab, then system rasterizers
    (rsvg-convert, inkscape).  All are soft dependencies; if none is available
    we return False and the caller rejects the image with an actionable error
    rather than embedding an unsupported media_type that would wedge the
    session.
    r   N)rL   Úwrite_to)Úsvg2rlg)ÚrenderPMÚPNG)Úfmtzrsvg-convertz-oÚinkscapez--export-type=pngz--export-filename=Té   )ÚcheckÚcapture_outputÚtimeoutÚstdinF)ÚcairosvgÚsvg2pngrQ   ÚexistsÚstatÚst_sizer)   Úsvglib.svglibrm   Úreportlab.graphicsrn   Ú
drawToFileÚshutilÚ
subprocessÚwhichÚrunÚDEVNULL)	ri   rj   rw   rm   rn   ÚdrawingÚ_shutilÚ_subprocessÚcmds	            r   Ú_rasterize_svg_to_pngrˆ     s2  € ðØˆˆˆØ×Ò�S ™]œ]µS¸±]´]ÐÑCÔCÐCØ�ŠÑ Ô Ð@ X§]¢]¡_¤_Ô%<¸qÒ%@Ð@øÝð ð ð ØˆðøøøðØ)Ð)Ð)Ð)Ð)Ð)Ø/Ð/Ð/Ð/Ð/Ð/Ø�'�#˜h™-œ-Ñ(Ô(ˆØÐØ×Ò ­¨X©¬¸EÐÑBÔBÐBØ—?’?Ñ$Ô$ÐD¨¯ª©¬Ô)@À1Ò)DÐDð øõ ð ð ð Øˆðøøøð ÐÐÐØ$Ð$Ð$Ð$à	˜�s 8™}œ}­c°(©m¬mÐ<Ø	•S˜‘]”]Ð$7Ø	(˜hÐ	(Ð	(ð	*ðð ð ˆð
 �=Š=˜˜QœÑ Ô ð 		ðØ—’Ø˜t°DÀ"Ø%Ô-ð  ñ ô ð ð —?’?Ñ$Ô$ð  ¨¯ª©¬Ô)@À1Ò)DÐ)DØ˜4˜4øøÝð ð ð Ø�ðøøøð		ð ˆ5s8   ‚A%A( Á(
A5Á4A5Á9A;C6 Ã6
DÄDÅ"AF6Æ6
GÇGÚ
image_pathÚdetected_mimec                 ó’  — |t           v r| |dfS t          dd¦  «        }|                     dd¬¦  «         |dt          j        ¦   «         › d�z  }|dk    rt          | |¦  «        r|d	dfS d
S 	 ddlm} |                     | ¦  «        5 }|j	        dvr| 
                    d¦  «        }|                     |d¬¦  «         ddd¦  «         n# 1 swxY w Y   |                     ¦   «         r"|                     ¦   «         j        dk    r|d	dfS n3# t          $ r&}t                                d||¦  «         Y d}~nd}~ww xY wddd|›d�fS )uÆ  Ensure an image is in a vision-provider-supported format.

    Returns a 3-tuple ``(path, mime, error)``:
      - If ``detected_mime`` is already supported: ``(image_path, detected_mime, None)``.
      - If conversion succeeds: ``(new_png_path, "image/png", None)`` â€” the new
        path is a temp file the CALLER must clean up.
      - If conversion is impossible: ``(None, None, <error message>)``.

    SVG is rasterized to PNG (best-effort, soft deps).  Other raster formats
    Pillow can read (BMP, TIFF, etc.) are re-encoded to PNG.  This runs BEFORE
    the image is base64-embedded into conversation history, so an unsupported
    media_type can never reach the provider and wedge the session.
    Núcache/visionÚtemp_vision_imagesT©ÚparentsÚexist_okÚ
converted_ú.pngúimage/svg+xmlr_   )NNu8  This is an SVG, which vision models cannot read directly, and no SVG rasterizer is installed (tried cairosvg, svglib, rsvg-convert, inkscape). Convert the SVG to PNG first â€” e.g. open it in a browser and screenshot it, or install a rasterizer (`pip install cairosvg`) â€” then re-run vision_analyze on the PNG.r   ©ÚImage)ÚRGBÚRGBAÚLr—   ro   ©Úformatz'Failed to normalize %s image to PNG: %szImage format z– is not supported by the vision API and could not be converted to PNG (install Pillow for raster conversion). Convert it to PNG or JPEG and try again.)Ú _ANTHROPIC_SUPPORTED_MEDIA_TYPESr   ÚmkdirÚuuidÚuuid4rˆ   ÚPILr•   ÚopenÚmodeÚconvertÚsavery   rz   r{   r)   ÚloggerÚwarning)r‰   rŠ   Úout_dirrj   Ú	_PILImageÚ_imgÚ_excs          r   Ú_normalize_to_supported_imagerª   D  sþ  € ð  Õ8Ð8Ð8Ø˜=¨$Ð.Ð.å˜^Ð-AÑBÔB€GØ‡M‚M˜$¨€MÑ.Ô.Ð.ØÐ8¥d¤j¡l¤lÐ8Ð8Ð8Ñ8€Hð ˜Ò'Ð'Ý  ¨XÑ6Ô6ð 	/Ø˜[¨$Ð.Ð.ð
ð 
ð
,Ø*Ð*Ð*Ð*Ð*Ð*Ø�^Š^˜JÑ'Ô'ð 	.¨4ØŒyÐ 4Ð4Ð4Ø—|’| FÑ+Ô+�Ø�IŠI�h uˆIÑ-Ô-Ð-ð	.ð 	.ð 	.ñ 	.ô 	.ð 	.ð 	.ð 	.ð 	.ð 	.ð 	.øøøð 	.ð 	.ð 	.ð 	.ð �?Š?ÑÔð 	/ §¢¡¤Ô!8¸1Ò!<Ð!<Ø˜[¨$Ð.Ð.øøÝð ,ð ,ð ,Ý�ŠÐ@Ø$ dñ	,ô 	,ð 	,ð 	,ð 	,ð 	,ð 	,ð 	,øøøøð,øøøð 	Øð	A˜ð 	Að 	Að 	Aðð s<   Á.D Â	6CÂ?D ÃCÃD ÃCÃ8D Ä
D=ÄD8Ä8D=Úerrorc                 óÂ   — t          | t          t          f¦  «        rdS t          | t          j        ¦  «        r&| j        j        }d|cxk    rdk     rn n|dk    rdS dS dS )u‚  Return True only for transient image-download failures worth retrying.

    Non-retryable (fail-fast):
      - httpx.HTTPStatusError with a 4xx status other than 429 (404/403/410/...):
        the resource is missing or forbidden; retrying can't change that.
      - PermissionError: blocked by website policy / SSRF guard.
      - ValueError: image too large or blocked redirect â€” deterministic.

    Retryable (transient):
      - httpx 429 (rate limited) and 5xx (server-side) errors.
      - Connection/timeout/transport errors (httpx.TransportError) and any
        other unclassified exception, which may be a flaky network blip.
    Fi�  iô  i­  T)rP   ÚPermissionErrorr'   ÚhttpxÚHTTPStatusErrorÚresponseÚstatus_code)r«   Ústatuss     r   Ú_is_retryable_download_errorr³   ~  sx   € õ �%�/­:Ð6Ñ7Ô7ð ØˆuÝ�%�Ô.Ñ/Ô/ð Ø”Ô+ˆØ�&ÐÐÒÐ˜3ÒÐÐÐÐ 6¨S¢= =Ø�5ØˆtØˆ4r   r•   )Úmedia_labelÚdestinationÚ	max_bytesÚheadersr´   c             ƒ   óR  K  — ddl m} |                      d||¬¦  «        4 ƒd{V —†}|                     ¦   «          |j                             d¦  «        }|rC	 t          |¦  «        }	n# t          $ r d}	Y nw xY w|	�|	|k    rt          |› d|	› d|› d	�¦  «        ‚t          |j	        ¦  «        }
t          |
¦  «        }|rt          |d
         ¦  «        ‚|                     d|j        › dt          j        ¦   «         j        › d�¦  «        }d}	 |                     d¦  «        5 }|                     ¦   «         2 3 d{V —†}|sŒ
|t'          |¦  «        z  }||k    rt          |› d|› d|› d	�¦  «        ‚|                     |¦  «         ŒP6 	 ddd¦  «         n# 1 swxY w Y    |||¦  «         nT# t*          $ rG 	 |                     d¬¦  «         n-# t.          $ r  t0                               d|d¬¦  «         Y nw xY w‚ w xY w	 ddd¦  «        ƒd{V —† n# 1 ƒd{V —†swxY w Y   |S )u‚  Stream an HTTP download to *destination* via a temp file with a running size cap.

    Uses ``client.stream("GET", ...)`` so the response body is never fully
    buffered in memory â€” chunks are written to a temp file and the running
    byte count is checked against *max_bytes* after each chunk.  On success
    the temp file is atomically replaced onto *destination*; on failure the
    temp file is deleted.

    A ``Content-Length`` header, when present and parseable, is used for an
    early rejection before any bytes are streamed, but the streaming cap is
    the authoritative guard (servers can omit or lie about the header).
    r   )Úatomic_replaceÚGET)r·   Nzcontent-lengthz too large (z bytes, max ú)Úmessageú.z.tmpÚwbT)Ú
missing_okz%Could not delete partial download: %s©Úexc_info)Úutilsr¹   ÚstreamÚraise_for_statusr·   Úgetr;   r'   rQ   rL   r   r­   Ú	with_nameÚnamer�   rž   Úhexr    Úaiter_bytesr1   Úwriter)   Úunlinkr4   r¤   Údebug)ÚclientrL   rµ   r¶   r·   r´   r¹   r°   ÚclÚdeclared_sizeÚ	final_urlÚblockedÚtmp_destinationÚbytes_writtenÚfÚchunks                   r   Ú_stream_download_to_filerÖ   –  s¸  è è € ð* %Ð$Ð$Ð$Ð$Ð$à�}Š}˜U C°ˆ}Ñ9Ô9ð +ð +ð +ð +ð +ð +ð +¸XØ×!Ò!Ñ#Ô#Ð#ð Ô×!Ò!Ð"2Ñ3Ô3ˆØð 	ð%Ý # B¡¤��øÝð %ð %ð %Ø $���ð%øøøàÐ(¨]¸YÒ-FÐ-FÝ Ø"ÐWÐW°ÐWÐWÈ9ÐWÐWÐWñô ð õ ˜œÑ%Ô%ˆ	Ý& yÑ1Ô1ˆØð 	6Ý! '¨)Ô"4Ñ5Ô5Ð5à%×/Ò/Ø9�Ô Ð9Ð9¥4¤:¡<¤<Ô#3Ð9Ð9Ð9ñ
ô 
ˆð ˆð	Ø ×%Ò% dÑ+Ô+ð 	#¨qØ#+×#7Ò#7Ñ#9Ô#9ð #ð #ð #ð #ð #ð #ð #˜%Ø ð !Ø Ø!¥S¨¡Z¤ZÑ/�MØ$ yÒ0Ð0Ý(Ø*Ð_Ð_¸Ð_Ð_ÐS\Ð_Ð_Ð_ñô ð ð —G’G˜E‘N”N�N�Nð $:Ð#9ð	#ð 	#ð 	#ñ 	#ô 	#ð 	#ð 	#ð 	#ð 	#ð 	#ð 	#øøøð 	#ð 	#ð 	#ð 	#ð ˆN˜?¨KÑ8Ô8Ð8Ð8øÝð 	ð 	ð 	ðØ×&Ò&°$Ð&Ñ7Ô7Ð7Ð7øÝð ð ð Ý—’Ø;¸_ÐW[ð ñ ô ð ð ð ðøøøð ð	øøøð 9ðG+ð +ð +ñ +ô +ð +ð +ð +ð +ð +ð +ð +ð +ð +ð +ð +ð +ð +ð +ð +ð +ð +ð +øøøð +ð +ð +ð +ðZ Ðs£   §1HÁA)Á(HÁ)A8Á5HÁ7A8Á8BHÄF/Ä$FÄ9F	Ä?AFÆF/ÆF	ÆF/ÆF	ÆF/Æ.HÆ/
H Æ:GÇH Ç'G;Ç8H Ç:G;Ç;H È HÈ
H È#H é   Ú	image_urlÚmax_retriesc              ƒ   óz  K  — ddl }|j                             dd¬¦  «         d„ }d}t          |¦  «        D �]m}	 t	          | ¦  «        }|rt          |d         ¦  «        ‚ddlm}  |t          dd|gi¬	¦  «        4 ƒd{V —†	 }	t          |	| |t          d
ddœd¬¦  «        ƒ d{V —† ddd¦  «        ƒd{V —† n# 1 ƒd{V —†swxY w Y   |c S # t          $ rÊ}
|
}t          |
¦  «        r	||dz
  k    r7t                               d|dz   t          |
¦  «        dd…         d¬¦  «         ‚ d|dz   z  }t                               d|dz   |t          |
¦  «        dd…         ¦  «         t                               d|¦  «          |j        |¦  «        ƒ d{V —† Y d}
~
�Œgd}
~
ww xY w|�|‚t%          d|› d�¦  «        ‚)a¿  
    Download an image from a URL to a local destination (async) with retry logic.
    
    Args:
        image_url (str): The URL of the image to download
        destination (Path): The path where the image should be saved
        max_retries (int): Maximum number of retry attempts (default: 3)
        
    Returns:
        Path: The path to the downloaded image
        
    Raises:
        Exception: If download fails after all retries
    r   NTrŽ   c              “   ó~   K  — ddl m}m}  || ¦  «        }|r# ||¦  «        ƒ d{V —†st          d|› �¦  «        ‚dS dS )a*  Re-validate each redirect target to prevent redirect-based SSRF.

        Without this, an attacker can host a public URL that 302-redirects
        to http://169.254.169.254/ and bypass the pre-flight is_safe_url check.

        Must be async because httpx.AsyncClient awaits event hooks.
        r   ©rZ   Úredirect_target_from_responseNú.Blocked redirect to private/internal address: ©rW   rZ   rÝ   r'   ©r°   rZ   rÝ   Úredirect_urls       r   Ú_ssrf_redirect_guardz-_download_image.<locals>._ssrf_redirect_guardñ  s�   è è € ð 	VÐUÐUÐUÐUÐUÐUÐUØ4Ð4°XÑ>Ô>ˆØð 	Ð&7Ð&7¸Ñ&EÔ&EÐ EÐ EÐ EÐ EÐ EÐ Eð 	ÝØOÀÐOÐOñô ð ð	ð 	ð 	ð 	r   r¼   ©Úcreate_ssrf_safe_async_clientr°   ©ru   Úfollow_redirectsÚevent_hooksúoMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36zimage/*,*/*;q=0.8©z
User-AgentÚAcceptr•   ©r·   r´   r/   z-Image download failed after %s attempt(s): %séd   rÀ   é   z)Image download failed (attempt %s/%s): %sé2   zRetrying in %ss...zB_download_image exited retry loop without attempting (max_retries=r»   )rB   Úparentrœ   Úranger   r­   rW   rä   Ú_VISION_DOWNLOAD_TIMEOUTrÖ   Ú_VISION_MAX_DOWNLOAD_BYTESr)   r³   r¤   r«   rQ   r¥   ÚsleepÚRuntimeError)rØ   rµ   rÙ   rB   râ   Ú
last_errorÚattemptrÑ   rä   rÍ   ÚeÚ	wait_times               r   Ú_download_imagerù   Ý  s  è è € ð €N€N€Nð Ô×Ò T°DÐÑ9Ô9Ð9ðð ð ð €JÝ˜Ñ%Ô%ð 3+ñ 3+ˆð2	+Ý*¨9Ñ5Ô5ˆGØð :Ý% g¨iÔ&8Ñ9Ô9Ð9àFÐFÐFÐFÐFÐFð 5Ð4Ý0Ø!%Ø'Ð*>Ð)?Ð@ðñ ô ð ð ð ð ð ð ð ð ð Ý.ØØØÝ.ð 'XØ"5ðð ð !(ð
ñ 
ô 
ð 
ð 
ð 
ð 
ð 
ð 
ð 
ðð ð ñ ô ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð øøøð ð ð ð ð" ÐÐÐøÝð 	+ð 	+ð 	+ØˆJõ 0°Ñ2Ô2ð °gÀÈqÁÒ6PÐ6PÝ—’ØCØ˜a‘KÝ˜‘F”F˜4˜C˜4”LØ!ð	 ñ ô ð ð Ø˜g¨™kÑ*ˆIÝ�NŠNÐFÈÐRSÉÐU`ÕbeÐfgÑbhÔbhÐilÐjlÐilÔbmÑnÔnÐnÝ�NŠNÐ/°Ñ;Ô;Ð;Ø�'”- 	Ñ*Ô*Ð*Ð*Ð*Ð*Ð*Ð*Ð*Ð*Ð*Ð*Ð*Ñ*øøøøð'	+øøøð. ÐØÐÝ
Ø[È[Ð[Ð[Ð[ñô ð s=   »ACÂ$B:Â(CÂ:
C	ÃCÃC	ÃCÃ
F#ÃB?FÆF#c                 ót   — | j                              ¦   «         }ddddddddœ}|                     |d¦  «        S )zà
    Determine the MIME type of an image based on its file extension.
    
    Args:
        image_path (Path): Path to the image file
        
    Returns:
        str: The MIME type (defaults to image/jpeg if unknown)
    r`   r_   ra   rb   rf   r“   )z.jpgz.jpegr’   z.gifz.bmpz.webpz.svg)ÚsuffixÚlowerrÅ   )r‰   Ú	extensionÚ
mime_typess      r   Ú_determine_mime_typerÿ   ?  sO   € ð Ô!×'Ò'Ñ)Ô)€IàØØØØØØðð €Jð �>Š>˜) \Ñ2Ô2Ð2r   Ú	mime_typec                 ó®   — |                       ¦   «         }t          j        |¦  «                             d¦  «        }|pt	          | ¦  «        }d|› d|› �}|S )a0  
    Convert an image file to a base64-encoded data URL.
    
    Args:
        image_path (Path): Path to the image file
        mime_type (Optional[str]): MIME type of the image (auto-detected if None)
        
    Returns:
        str: Base64-encoded data URL (e.g., "data:image/jpeg;base64,...")
    Úasciiúdata:ú;base64,)Ú
read_bytesÚbase64Ú	b64encodeÚdecoderÿ   )r‰   r   r\   ÚencodedÚmimeÚdata_urls         r   Ú_image_to_base64_data_urlr  V  sf   € ð × Ò Ñ"Ô"€Dõ Ô˜tÑ$Ô$×+Ò+¨GÑ4Ô4€Gð Ð8Õ,¨ZÑ8Ô8€Dð /�tÐ.Ð. WÐ.Ð.€Hà€Or   i  @i  @ iÜ  i  P c                 ó|   ‡— t          | ¦  «                             ¦   «         Št          ˆfd„dD ¦   «         ¦  «        S )z;Detect if an API error is related to image or payload size.c              3   ó    •K  — | ]}|‰v V — Œ	d S ©Nr6   ©Ú.0ÚhintÚerr_strs     €r   ú	<genexpr>z'_is_image_size_error.<locals>.<genexpr>�  s7   øè è € ð ð  4ˆt�wˆð ð ð ð ð ð r   )	ú	too largeÚpayloadÚ413Úcontent_too_largeÚrequest_too_largerØ   Úinvalid_requestÚexceedsú
size limit)rQ   rü   Úany)r«   r  s    @r   Ú_is_image_size_errorr  �  sT   ø€ å�%‰jŒj×ÒÑ Ô €GÝð ð ð ð ð ,ð ñ ô ñ ô ð r   Úmax_dimensionc                 óÀ   — 	 ddl m} |                     | ¦  «        5 }t          |j        ¦  «        |k    cddd¦  «         S # 1 swxY w Y   dS # t
          $ r Y dS w xY w)uÕ  True if the image's longest side exceeds ``max_dimension`` px.

    Anthropic enforces an 8000px per-side cap independently of the 5 MB byte
    cap, so a tall small-byte screenshot can pass every byte check yet trip a
    non-retryable 400.  Returns False (don't force a resize) when Pillow is
    unavailable or the file can't be read as an image â€” the byte-based checks
    still apply, and we never want a missing soft dependency to break the
    embed path.
    r   r”   NF)rŸ   r•   r    r0   Úsizer)   )r‰   r  r§   r¨   s       r   Ú_image_exceeds_dimensionr"  —  s¾   € ðØ*Ð*Ð*Ð*Ð*Ð*Ø�^Š^˜JÑ'Ô'ð 	2¨4Ý�t”y‘>”> MÒ1ð	2ð 	2ð 	2ð 	2ñ 	2ô 	2ð 	2ð 	2ð 	2ð 	2ð 	2ð 	2øøøð 	2ð 	2ð 	2ð 	2ð 	2ð 	2øåð ð ð Øˆuˆuðøøøs3   ‚A �AµA ÁAÁA Á	AÁ
A Á
AÁAÚregionÚ
offset_outc                 óR  — 	 ddl m} n# t          $ r Y dS w xY wt          |t          t
          f¦  «        r,t          |¦  «        dk    st          d„ |D ¦   «         ¦  «        sdS 	 |                     | ¦  «        5 }|j	        \  }}d„ |D ¦   «         \  }}}	}
t          dt          ||¦  «        ¦  «        }t          dt          ||¦  «        ¦  «        }t          dt          |	|¦  «        ¦  «        }t          dt          |
|¦  «        ¦  «        }||k    s||k    r*ddd	|› d
|› d
|	› d
|
› d|› d|› d|› d
|› d�fcddd¦  «         S |                     ||||f¦  «        }|�||d<   ||d<   ||z
  |d<   ||z
  |d<   |                      | j        › dt          j        ¦   «         j        dd…         › d�¦  «        }|j        dvr|                     d¦  «        }|                     |d¬¦  «         |ddfcddd¦  «         S # 1 swxY w Y   dS # t*          $ r}ddd|› �fcY d}~S d}~ww xY w)uµ  Crop ``image_path`` to ``region`` = [x1, y1, x2, y2] (original-image pixels).

    Applied BEFORE :func:`_resize_image_for_vision` so the cropped area gets
    the full downscale resolution budget â€” a "zoom" into a detail region.
    Coordinates are clamped to the image bounds; a region that clamps to zero
    area (or is inverted/malformed) is rejected with an error naming the
    actual image dimensions so the caller can retry with sensible values.

    Ported from: QwenLM/qwen-code zoom-image.ts (Apache-2.0).

    Returns:
        (cropped_temp_path, out_mime, None) on success â€” the caller owns
        cleanup of the temp file â€” or (None, None, error_message) on failure.
    r   r”   )NNz[region cropping requires Pillow (`pip install Pillow`); retry without the region parameter.rd   c              3   óz   K  — | ]6}t          |t          t          f¦  «        ot          |t          ¦  «         V — Œ7d S r  )rP   r;   r&   Úbool©r  Úvs     r   r  z%_crop_image_region.<locals>.<genexpr>Ç  sB   è è € Ð[Ð[Èq•:˜a¥#¥u Ñ.Ô.ÐJµzÀ!ÅTÑ7JÔ7JÐ3JÐ[Ð[Ð[Ð[Ð[Ð[r   )NNzdInvalid region: expected [x1, y1, x2, y2] as four numbers (pixel coordinates in the original image).c              3   ó4   K  — | ]}t          |¦  «        V — Œd S r  )r;   r(  s     r   r  z%_crop_image_region.<locals>.<genexpr>Ñ  s(   è è € Ð5Ð5¨�c !™fœfÐ5Ð5Ð5Ð5Ð5Ð5r   NzInvalid region [ú, zG]: crops to zero area after clamping to the image bounds. The image is Úxu+    px â€” pick x1<x2 and y1<y2 inside [0, 0, z].ÚyÚwidthÚheightÚ_region_re   r’   )r–   r—   r˜   ÚLAÚPr–   ro   r™   r_   zFailed to crop region: )rŸ   r•   ÚImportErrorrP   ÚlistÚtupler1   Úallr    r!  r0   ÚminÚcroprÆ   Ústemr�   rž   rÈ   r¡   r¢   r£   r)   )r‰   r#  r$  r•   Úimgr.  r/  Úx1Úy1Úx2Úy2Úcx1Úcy1Úcx2Úcy2Úcroppedrj   Úexcs                     r   Ú_crop_image_regionrE  ©  s\  € ð&
ØÐÐÐÐÐÐøÝð 
ð 
ð 
ð
ð 
ð 
ð
øøøõ �v¥¥e˜}Ñ-Ô-ð
åˆv‰;Œ;˜!ÒÐÝÐ[Ð[ÐTZÐ[Ñ[Ô[Ñ[Ô[ð ð
ð 
ð
;Ø�ZŠZ˜
Ñ#Ô#ð 	/ sØœH‰MˆE�6Ø5Ð5¨fÐ5Ñ5Ô5‰NˆB��B˜å�a�˜R ™œÑ(Ô(ˆCÝ�a�˜R ™œÑ)Ô)ˆCÝ�a�˜R ™œÑ(Ô(ˆCÝ�a�˜R ™œÑ)Ô)ˆCØ�cŠzˆz˜S CšZ˜ZØ˜Tð2 rð 2ð 2¨Rð 2ð 2°2ð 2ð 2¸ð 2ð 2àð2ð 2à%ð2ð 2ð $ð2ð 2ð (.ð2ð 2ð 2ðð	/ð 	/ð 	/ð 	/ñ 	/ô 	/ð 	/ð 	/ð —h’h  S¨#¨sÐ3Ñ4Ô4ˆGØÐ%Ø"%�
˜3‘Ø"%�
˜3‘Ø&)¨C¡i�
˜7Ñ#Ø'*¨S¡y�
˜8Ñ$Ø!×+Ò+Ø”?ÐFÐF­D¬J©L¬LÔ,<¸R¸a¸RÔ,@ÐFÐFÐFñô ˆHð Œ|Ð#BÐBÐBØ!Ÿ/š/¨%Ñ0Ô0�Ø�LŠL˜¨%ˆLÑ0Ô0Ð0Ø˜[¨$Ð.ð7	/ð 	/ð 	/ð 	/ñ 	/ô 	/ð 	/ð 	/ð 	/ð 	/ð 	/ð 	/øøøð 	/ð 	/ð 	/ð 	/ð 	/ð 	/øõ8 ð ;ð ;ð ;Ø�TÐ:°SÐ:Ð:Ð:Ð:Ð:Ð:Ð:Ð:Ð:øøøøð;øøøs\   ‚	 ‰
–Á%H
 Á:B=G=Ä7H
 ÅB,G=Ç0H
 Ç=HÈH
 ÈHÈH
 È

H&ÈH!ÈH&È!H&Ú
scale_infoÚcrop_offsetc                 óv  — g }| rs| d         | d         }}| d         | d         }}|r||z  nd}|r||z  nd}|d›|d›k    rd|d›d�}	nd	|d›d
|d›d�}	|                      d|› d|› d|› d|› d|	› �
¦  «         |r(|                      d|d         › d|d         › d�¦  «         |sdS d                     |¦  «        S )uœ  Build a coordinate-mapping disclosure note for the analysis result.

    ``scale_info`` (from :func:`_resize_image_for_vision`) carries the
    original and downscaled pixel dimensions when a downscale actually
    happened. ``crop_offset`` (from :func:`_crop_image_region`) carries the
    clamped crop origin when a region zoom was applied. Returns ``None`` when
    neither applies â€” no note, no noise.
    Ú
orig_widthÚorig_heightÚ	new_widthÚ
new_heightg      ð?z.2fz'multiply any coordinates you report by z# to map back to the original image.z)multiply any x coordinates you report by z and any y coordinates by zImage downscaled from r,  z to z for vision; zUAnalysis was performed on a cropped region of the original image starting at offset (r+  r-  ua   ); coordinates are relative to that crop origin â€” add the offset to map back to the full image.NÚ )ÚappendÚjoin)
rF  rG  ÚpartsÚowÚohÚnwÚnhÚfxÚfyÚfactor_clauses
             r   Ú_build_scale_noterX  ï  s‰  € ð €EØð 
Ø˜LÔ)¨:°mÔ+DˆBˆØ˜KÔ(¨*°\Ô*BˆBˆØÐ#ˆR�"‰WˆW ˆØÐ#ˆR�"‰WˆW ˆØˆ;ˆ;˜R˜+˜+Ò%Ð%ð6¸"ÐCð 6ð 6ð 6ð ˆMð#¸BÐIð #ð #Ø(*ÐDð#ð #ð #ð ð
 	�Šð Rð ð ¨"ð ð °"ð ð °rð ð Øðð ñ	
ô 	
ð 	
ð ð 
Ø�ŠðHØ)4°SÔ)9ðHð Hà˜3ÔðHð Hð Hñ	
ô 	
ð 	
ð ð ØˆtØ�8Š8�E‰?Œ?Ðr   Úmax_base64_bytesÚ	scale_outc           	      ó¾	  ‡‡‡"— |                       ¦   «         j        }|dz  dz  dz   }||k    }d}‰�_	 ddlm}	 |	                     | ¦  «        5 }
t          |
j        ¦  «        ‰k    rd}ddd¦  «         n# 1 swxY w Y   n# t          $ r Y nw xY w|s)|s't          | |¬	¦  «        }t          |¦  «        |k    r|S nd}	 ddlm} ddl
}np# t          $ rc 	 dd
lm}  |dd¬¦  «         ddlm} ddl
}nA# t          $ r4 t                               d¦  «         |€t          | |¬	¦  «        }|cY cY S w xY wY nw xY wt                               d|dz  |dz  |dz  ‰¦  «         |pt!          | ¦  «        }|dk    rdnd}|dk    rdnd}	 |                     | ¦  «        }nG# t          $ r:}t                               d|¦  «         |€t          | |¬	¦  «        }|cY d}~S d}~ww xY w|dk    r|j        dv r|                     d¦  «        }|dk    rdnd}|j        |j        fŠ"|j        |j        f}d}dt*          dt*          ddfˆ"ˆfd„}dt*          dt*          dt,          fˆfd„}t/          d¦  «        D �]}|dk    �rd}t          t+          |j        |z  ¦  «        d ¦  «        }t          t+          |j        |z  ¦  «        d ¦  «        }|d k    r;|j        dk    r0d |j        z  }t          t+          |j        |z  ¦  «        d ¦  «        }n@|d k    r:|j        dk    r/d |j        z  }t          t+          |j        |z  ¦  «        d ¦  «        }||f|k    r �n0|                     ||f|j        ¦  «        }||f}t                               d!|||¦  «         |D ]í}|                     ¦   «         }d"|i} |�|| d#<    |j        |fi | ¤Ž t9          j        |                     ¦   «         ¦  «                             d$¦  «        }!d%|› d&|!› �}t          |¦  «        |k    rj ||j        |j        ¦  «        rTt                               d't          |¦  «        dz  ||j        |j        ¦  «          ||j        |j        ¦  «         |c c S Œî�Œ|�Gt                                d(|dz  t          |¦  «        dz  ¦  «          ||j        |j        ¦  «         |S |pt          | |¬	¦  «        S ))aU  Convert an image to a base64 data URL, auto-resizing if too large.

    Tries Pillow first to progressively downscale oversized images.  If Pillow
    is not installed or resizing still exceeds the limit, falls back to the raw
    bytes and lets the caller handle the size check.

    Args:
        max_dimension: If set, images whose longest side exceeds this pixel
            count are forcibly downscaled even if they're under the byte
            budget.  Anthropic enforces an 8000 px per-side cap independently
            of the 5 MB byte cap.

    Returns the base64 data URL string.
    rd   r×   rì   FNr   r”   T©r   )Úensureztool.vision)Úpromptu;   Pillow not installed â€” cannot auto-resize oversized imagezcImage file is %.1f MB (estimated base64 %.1f MB, limit %.1f MB, max_dimension=%s), auto-resizing...é   r_   ro   ÚJPEGr`   z)Pillow cannot open image for resizing: %s>   r2  r—   r–   )éU   éF   rî   r  ÚwÚhr   c                 ód   •— ‰�*| |f‰k    r$‰d         ‰d<   ‰d         ‰d<   | ‰d<   |‰d<   dS dS dS )z;Publish the downscale into ``scale_out`` when dims changed.Nr   rI  r/   rJ  rK  rL  r6   )rc  rd  Ú	orig_dimsrZ  s     €€r   Ú_record_scalez/_resize_image_for_vision.<locals>._record_scale|  s[   ø€ àÐ  a¨ V¨yÒ%8Ð%8Ø&/°¤lˆI�lÑ#Ø'0°¤|ˆI�mÑ$Ø%&ˆI�kÑ"Ø&'ˆI�lÑ#Ð#Ð#ð	 !Ð Ð%8Ð%8r   c                 ó4   •— ‰€dS t          | |¦  «        ‰k    S )z3True if both pixel dimensions are within the limit.NT)r0   )rc  rd  r  s     €r   Ú_dims_okz*_resize_image_for_vision.<locals>._dims_ok„  s!   ø€ àÐ Ø�4Ý�1�a‰yŒy˜MÒ)Ð)r   é   g      à?r^   zResized to %dx%d (attempt %d)rš   Úqualityr  r  r  z4Auto-resized image fits: %.1f MB (quality=%s, %dx%d)z=Auto-resize could not fit image under %.1f MB (best: %.1f MB))!rz   r{   rŸ   r•   r    r0   r!  r)   r  r1   Úior3  Útools.lazy_depsr]  r¤   Úinforÿ   r¡   r¢   r.  r/  r;   r'  rð   ÚresizeÚLANCZOSÚBytesIOr£   r  r  Úgetvaluer  r¥   )#r‰   r   rY  r  rZ  Ú	file_sizeÚestimated_b64Úneeds_resize_for_bytesÚneeds_resize_for_dimsÚ	_PILQuickÚ
_quick_imgr  r•   Ú_ioÚ_ensure_depr
  Ú
pil_formatÚout_mimer:  rD  Úquality_stepsÚ	prev_dimsÚ	candidaterg  ri  rö   ÚscaleÚnew_wÚnew_hÚeffective_scaleÚqÚbufÚsave_kwargsr	  rf  s#      ``                             @r   Ú_resize_image_for_visionr‡    s’  øøø€ ð( —’Ñ!Ô!Ô)€IØ ‘] qÑ(¨3Ñ.€MØ*Ð-=Ò=Ðð "ÐØÐ ð	Ø.Ð.Ð.Ð.Ð.Ð.Ø—’ 
Ñ+Ô+ð 1¨zÝ�z”Ñ'Ô'¨-Ò7Ð7Ø,0Ð)ð1ð 1ð 1ñ 1ô 1ð 1ð 1ð 1ð 1ð 1ð 1øøøð 1ð 1ð 1ð 1øøõ ð 	ð 	ð 	ØˆDð	øøøð "ð Ð*?ð å,¨ZÀ9ÐMÑMÔMˆÝˆx‰=Œ=Ð,Ò,Ð,ØˆOð -ð ˆðØÐÐÐÐÐØÐÐÐÐøÝð ð ð ð
	Ø=Ð=Ð=Ð=Ð=Ð=ð
 ˆK˜¨eÐ4Ñ4Ô4Ð4Ø!Ð!Ð!Ð!Ð!Ð!ØÐÐÐÐøÝð 	ð 	ð 	Ý�KŠKÐUÑVÔVÐVØÐÝ4°ZÈ9ÐUÑUÔU�ØˆOˆOˆOˆOˆOð		øøøð Ððøøøõ( ‡K‚KÐuØ˜[Ñ)¨=¸KÑ+HØ  KÑ0°-ñAô Að Að Ð8Õ,¨ZÑ8Ô8€Dà +Ò-Ð-��°6€JØ(¨EÒ1Ð1ˆ{ˆ{°|€HðØ�jŠj˜Ñ$Ô$ˆˆøÝð ð ð Ý�ŠÐ?ÀÑEÔEÐEØÐÝ0°ÀyÐQÑQÔQˆHØˆˆˆˆˆˆøøøøð	øøøð �VÒÐ ¤¨MÐ 9Ð 9Ø�kŠk˜%Ñ Ô ˆð %/°&Ò$8Ð$8�L�L¸g€MØ”˜CœJÐ'€IØ”˜CœJÐ'€IØ€Ið(�ð (¥ð (¨ð (ð (ð (ð (ð (ð (ð (ð*•Cð *�Cð *¥Dð *ð *ð *ð *ð *ð *õ ˜‘8”8ð #!ñ #!ˆØ�QŠ;‰;ð ˆEÝ�˜CœI¨Ñ-Ñ.Ô.°Ñ3Ô3ˆEÝ�˜CœJ¨Ñ.Ñ/Ô/°Ñ4Ô4ˆEð ˜Š{ˆ{˜sœy¨1š}˜}Ø"$ s¤y¡.�Ý�C ¤
¨_Ñ <Ñ=Ô=¸rÑBÔB��Ø˜"’� ¤¨a¢ Ø"$ s¤z¡/�Ý�C ¤	¨OÑ ;Ñ<Ô<¸bÑAÔA�à�uˆ~ Ò*Ð*Ø‘Ø—*’*˜e U˜^¨U¬]Ñ;Ô;ˆCØ ˜ˆIÝ�KŠKÐ7¸ÀÀwÑOÔOÐOàð 	!ð 	!ˆAØ—+’+‘-”-ˆCØ# ZÐ0ˆKØˆ}Ø)*�˜IÑ&ØˆCŒH�SÐ(Ð(˜KÐ(Ð(Ð(ÝÔ& s§|¢|¡~¤~Ñ6Ô6×=Ò=¸gÑFÔFˆGØ; Ð;Ð;°'Ð;Ð;ˆIÝ�9‰~Œ~Ð!1Ò1Ð1°h°h¸s¼yÈ#Ì*Ñ6UÔ6UÐ1Ý—’ÐRÝ 	™NœN¨kÑ:¸AØœI s¤zñ3ô 3ð 3ð �˜cœi¨¬Ñ4Ô4Ð4Ø Ð Ð Ð Ð Ð øñ	!ð" ÐÝ�ŠÐVØ'¨;Ñ7½¸Y¹¼È;Ñ9Wñ	Yô 	Yð 	Yàˆ�c”i ¤Ñ,Ô,Ð,ØÐð ÐQÕ0°ÀyÐQÑQÔQÐQs‰   ³B ÁA5Á)B Á5A9Á9B Á<A9Á=B Â
BÂBÂ?
C
 Ã

D7ÃC3Ã2D7Ã39D1Ä,D7Ä0D1Ä1D7Ä6D7ÆF Æ
G!Æ'/GÇG!ÇG!ÚproviderÚmodelc                 ó²  — t          | t          ¦  «        sdS |                      ¦   «                              ¦   «         }|sdS h d£}||v rdS |dv rdS |dv rdS |dv rMt          |t          ¦  «        sdS |                     ¦   «                              ¦   «         }d|v sd|v sd	|v rdS dS 	 d
dlm}  ||¦  «        }|�	|j        rdS n# t          $ r Y nw xY wdS )uÒ  Whether the given provider+model combination accepts image content
    inside a tool-result message.

    Providers covered today (per spec docs verified Apr-2026):

      * Anthropic Messages API (``anthropic`` provider, plus aggregators that
        proxy Claude â€” ``openrouter``, ``nous``, ``vertex``, ``bedrock``):
        ``tool_result`` blocks accept ``image`` content blocks.
      * OpenAI Chat Completions: tool messages accept array content with
        ``image_url`` parts.
      * OpenAI Responses (``openai-codex``): ``function_call_output.output``
        accepts an array of ``input_text``/``input_image`` items.
      * Gemini 3 (and proxied via aggregators): supports multimodal tool
        results. Older Gemini does NOT.

    For unknown / legacy providers we conservatively return False â€” the
    caller falls back to the legacy aux-LLM text path.  The check is relaxed
    when the provider's ``ProviderProfile`` declares ``supports_vision=True``.
    F>   úgoogle-vertexúanthropic-vertexÚnousÚvertexÚbedrockÚ
openrouterT>   úanthropic-directÚclaudeÚ	anthropic>   úopenai-chatúazure-openaiúopenai-codexÚopenai>   úgoogle-geminiúgoogle-vertex-geminiÚgeminiÚgooglezgemini-3zgemini-pro-3zgemini-flash-3r   )Úget_provider_profile)rP   rQ   r%   rü   Ú	providersrœ  Úsupports_visionr)   )rˆ  r‰  ÚpÚ_AGGREGATORSÚmrœ  Úprofiles          r   Ú_supports_media_in_tool_resultsr£  Ç  sT  € õ( �h¥Ñ$Ô$ð ØˆuØ�ŠÑÔ×ÒÑ Ô €AØð Øˆuðð ð €Lð 	ˆLÐÐØˆtð 	Ð7Ð7Ð7Øˆtð 	ÐEÐEÐEØˆtð 	ÐIÐIÐIÝ˜%¥Ñ%Ô%ð 	Ø�5Ø�KŠK‰MŒM×ÒÑ!Ô!ˆØ˜ˆ?ˆ?˜n°Ð1Ð1Ð5EÈÐ5JÐ5JØ�4Øˆuð
Ø2Ð2Ð2Ð2Ð2Ð2Ø&Ð& qÑ)Ô)ˆØÐ 7Ô#:ÐØ�4øøÝð ð ð Øˆðøøøð ˆ5s   Â*C Ã
CÃCc                  ó6  — 	 ddl m} m} ddlm}m} ddlm}  | ¦   «         } |¦   «         } |¦   «         } ||||¦  «        dk    rdS t          ||¦  «        p ||||¦  «        du S # t          $ r&}t                               d|¦  «         Y d	}~dS d	}~ww xY w)
aH  Whether vision tools should attach the image to the main model directly
    instead of routing through the auxiliary vision LLM.

    True when image routing resolves to ``native`` AND either the provider is
    known to accept images inside tool results, or the user explicitly declared
    the model vision-capable via the ``model.supports_vision`` config override.
    The override is the escape hatch for custom/local providers that aren't in
    the static allowlist. Best-effort: any resolution failure returns False so
    the caller falls back to the legacy aux-LLM path.
    r   )Ú_read_main_providerÚ_read_main_model)Údecide_image_input_modeÚ_lookup_supports_vision)r   ÚnativeFTz(Native vision fast-path check failed: %sN)r   r¥  r¦  Úagent.image_routingr§  r¨  r(   r   r£  r)   r¤   rÌ   )	r¥  r¦  r§  r¨  r   rˆ  r‰  r+   rD  s	            r   Ú#_should_use_native_vision_fast_pathr«    s  € ðØPÐPÐPÐPÐPÐPÐPÐPØXÐXÐXÐXÐXÐXÐXÐXØ1Ð1Ð1Ð1Ð1Ð1à&Ð&Ñ(Ô(ˆØ Ð Ñ"Ô"ˆØˆk‰mŒmˆØ"Ð" 8¨U°CÑ8Ô8¸HÒDÐDØ�5å+¨H°eÑ<Ô<ð EØ&Ð& x°¸Ñ<Ô<ÀÐDð	
øõ ð ð ð Ý�ŠÐ?ÀÑEÔEÐEØˆuˆuˆuˆuˆuøøøøðøøøs   ‚AA( Á	A( Á(
BÁ2BÂBÚquestionÚimage_data_urlÚimage_size_bytesÚ
scale_notec                 óî   — d}t          |t          ¦  «        r.|                     ¦   «         r|d|                     ¦   «         › �z  }|r|d|› �z  }d|dz  d›d�}dd	|d
œdd|idœg|| dd…         |ddœdœS )a�  Build the multimodal tool-result envelope returned by the fast path.

    Shape:
      {
        "_multimodal": True,
        "content": [
          {"type": "text", "text": "<short note + the user's question>"},
          {"type": "image_url", "image_url": {"url": "data:image/png;base64,..."}}
        ],
        "text_summary": "<plain-text fallback>",
        "meta": {"image_url": ..., "size_bytes": N},
      }

    The text part exists for two reasons: (1) it gives the model an
    instruction to act on now that the pixels are in context, and
    (2) providers that don't support multimodal tool results can fall back
    to ``text_summary``.
    ul   Image loaded into your context â€” you can see it natively now. Use your built-in vision to answer the user.z

Question: z

Note: z,Image attached natively for the main model (é   ú.1fz# KB). Answer using built-in vision.TÚtext©Útyper³  rØ   rL   ©rµ  rØ   NéÈ   )rØ   Ú
size_bytesÚnative_vision)Ú_multimodalÚcontentÚtext_summaryÚmeta)rP   rQ   r%   )rØ   r¬  r­  r®  r¯  Ú	text_partÚsummarys          r   Ú _build_native_vision_tool_resultrÀ  -  sá   € ð:	7ð õ �(�CÑ Ô ð 9 X§^¢^Ñ%5Ô%5ð 9ØÐ8 h§n¢nÑ&6Ô&6Ð8Ð8Ñ8ˆ	Øð /ØÐ. *Ð.Ð.Ñ.ˆ	ð	(Ø˜tÑ#Ð/ð	(ð 	(ð 	(ð ð à YÐ/Ð/Ø °°~Ð/FÐGÐGð
ð  à" 4 C 4œØ*Ø!ð
ð 
ðð ð r   c                 ó   K  — dW V — dS )a  Deprecated no-op shim kept for backward compatibility.

    The fan-out cap was narrowed to the CPU-bound encode/resize burst only
    (see :data:`_vision_cpu_executor` / :func:`_run_encode_on_cpu_executor`).
    Holding a slot across the whole analysis serialized legitimate multi-image
    workflows behind the slow LLM call, which is exactly what we don't want.
    This context manager no longer gates anything; encode/resize is bounded
    where it actually runs. Retained only so any external caller importing it
    keeps working.
    Nr6   r6   r   r   Ú_vision_concurrency_slotrÂ  g  s   è è € ð 
€E€E€E€E€E€Er   Útask_idc           
   ƒ   ón  K  — t          | t          ¦  «        r|                      ¦   «         st          dd¬¦  «        S d}d}	 ddlm}  |¦   «         rQt          dd¬¦  «        |r=|�<	 |                     ¦   «         r|                     ¦   «          S S # t          $ r Y S w xY wS S ddl	m
}m}m}	 	  |	|  ||¬	¦  «        ¦  «        ƒ d{V —†}
np# |$ rh}t          t          |¦  «        d¬¦  «        cY d}~|r=|�<	 |                     ¦   «         r|                     ¦   «          S S # t          $ r Y S w xY wS S d}~ww xY w|
j        }t          |
j        ¦  «        }t!          d
d¦  «        }|                     dd¬¦  «         |dt%          j        ¦   «         › d�z  }t)          j        |j        |
j        ¦  «        ƒ d{V —† d}t)          j        t.          ||¦  «        ƒ d{V —†\  }}}|s|€St          |pdd¬¦  «        |r=|�<	 |                     ¦   «         r|                     ¦   «          S S # t          $ r Y S w xY wS S ||k    rY|r:|                     ¦   «         r&	 |                     ¦   «          n# t          $ r Y nw xY w|}d}|                     ¦   «         j        }i }i }|�Ùt)          j        t4          |||¬¦  «        ƒ d{V —†\  }}}|s|€St          |pdd¬¦  «        |r=|�<	 |                     ¦   «         r|                     ¦   «          S S # t          $ r Y S w xY wS S |r:|                     ¦   «         r&	 |                     ¦   «          n# t          $ r Y nw xY w|}|}d}|                     ¦   «         j        }t7          t8          ||¬¦  «        ƒ d{V —†}t          |¦  «        t:          k    }t7          t<          |t>          ¦  «        ƒ d{V —†}|s|r´t7          t@          ||t:          t>          |¬¦  «        ƒ d{V —†}t          |¦  «        tB          k    rrt          dt          |¦  «        dz  d›dtB          dz  d›d�d¬¦  «        |r=|�<	 |                     ¦   «         r|                     ¦   «          S S # t          $ r Y S w xY wS S tE          | |||tG          |pd|pd¦  «        ¬¦  «        |r=|�<	 |                     ¦   «         r|                     ¦   «          S S # t          $ r Y S w xY wS S # t          $ ry}tH           %                    d|¦  «         t          d|› �d¬¦  «        cY d}~|r=|�<	 |                     ¦   «         r|                     ¦   «          S S # t          $ r Y S w xY wS S d}~ww xY w# |r=|�<	 |                     ¦   «         r|                     ¦   «          w w # t          $ r Y w w xY ww w xY w)a.  Fast path for vision-capable main models.

    Loads the image (data: / http(s) / file:// / local path / sandbox-container
    path) via the unified resolver, base64-encodes it, and returns a multimodal
    tool-result envelope. The agent loop unwraps it; provider adapters serialize
    it into the right tool-result-with-image shape for each backend.

    Returns:
        A ``_multimodal`` envelope dict on success.
        A JSON error string on failure (matches the existing tool-result
        contract so the agent loop displays errors normally).
    zimage_url is requiredF©ÚsuccessNr   ©Úis_interruptedÚInterrupted©ÚImageResolutionErrorÚResolveContextÚresolve_image_source©rÃ  rŒ   r�   TrŽ   Útemp_image_ú.imgúImage normalization failed.©r$  úRegion crop failed.r\  )r   rY  r  rZ  ú2Image too large for vision API: base64 payload is r_  r²  ú MB (limit ú.0fúw MB) even after resizing. Install Pillow (`pip install Pillow`) for better auto-resize, or compress the image manually.)rØ   r¬  r­  r®  r¯  z"Native vision fast path failed: %szNative vision failed: )&rP   rQ   r%   Ú
tool_errorÚtools.interruptrÈ  ry   rË   r)   Útools.image_sourcerË  rÌ  rÍ  r
  r1   r\   r   rœ   r�   rž   rB   Ú	to_threadÚwrite_bytesrª   rz   r{   rE  rK   r  Ú_EMBED_TARGET_BYTESr"  Ú_EMBED_MAX_DIMENSIONr‡  Ú_MAX_BASE64_BYTESrÀ  rX  r¤   r¥   )rØ   r¬  rÃ  r#  Útemp_image_pathÚshould_cleanuprÈ  rË  rÌ  rÍ  ÚresolvedrD  Údetected_mime_typer®  Útemp_dirÚnormalized_pathÚ	_norm_errÚ_crop_offsetÚ_scale_infoÚcropped_pathÚcropped_mimeÚcrop_errr­  Ú_over_bytesÚ
_over_dimss                            r   Ú_vision_analyze_nativerî  v  sç  è è € õ$ �i¥Ñ%Ô%ð B¨Y¯_ª_Ñ->Ô->ð BÝÐ1¸5ÐAÑAÔAÐAà&*€OØ€NðCØ2Ð2Ð2Ð2Ð2Ð2Øˆ>ÑÔð 	<Ý˜m°UÐ;Ñ;Ô;ðv ð 	˜oÐ9ðØ"×)Ò)Ñ+Ô+ð -Ø#×*Ò*Ñ,Ô,Ð,Ð,ð-øåð ð ð Ø�ðøøøð		Ð9ðk	
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
ð	7Ø1Ð1°)¸^¸^ÐT[Ð=\Ñ=\Ô=\Ñ]Ô]Ð]Ð]Ð]Ð]Ð]Ð]ˆHˆHøØ#ð 	7ð 	7ð 	7Ý�c #™hœh°Ð6Ñ6Ô6Ð6Ð6Ð6Ð6Ð6ðX ð 	˜oÐ9ðØ"×)Ò)Ñ+Ô+ð -Ø#×*Ò*Ñ,Ô,Ð,Ð,ð-øåð ð ð Ø�ðøøøð		Ð9øøøøð[	7øøøð &œ]ÐÝ˜xœ}Ñ-Ô-ÐÝ! .Ð2FÑGÔGˆØ�Š˜t¨dˆÑ3Ô3Ð3Ø"Ð%Eµ4´:±<´<Ð%EÐ%EÐ%EÑEˆÝÔ Ô ;¸X¼]ÑKÔKÐKÐKÐKÐKÐKÐKÐKØˆõ @GÔ?PÝ)¨?Ð<Nñ@
ô @
ð :
ð :
ð :
ð :
ð :
ð :
Ñ6ˆÐ+¨Yð ð 	˜Ð/ÝØÐ:Ð:ÀEðñ ô ðr ð 	˜oÐ9ðØ"×)Ò)Ñ+Ô+ð -Ø#×*Ò*Ñ,Ô,Ð,Ð,ð-øåð ð ð Ø�ðøøøð		Ð9ðm ˜oÒ-Ð-àð  /×"8Ò"8Ñ":Ô":ð ðØ#×*Ò*Ñ,Ô,Ð,Ð,øÝ ð ð ð Ø�Dðøøøà-ˆOØ!ˆNØ.×3Ò3Ñ5Ô5Ô=Ðð  ˆØˆØÐÝ9@Ô9JÝ" O°VØ'ð:ñ :ô :ð 4ð 4ð 4ð 4ð 4ð 4Ñ0ˆL˜,¨ð ð T˜<Ð/Ý! (Ð"CÐ.CÈUÐSÑSÔSðB ð 	˜oÐ9ðØ"×)Ò)Ñ+Ô+ð -Ø#×*Ò*Ñ,Ô,Ð,Ð,ð-øåð ð ð Ø�ðøøøð		Ð9ðA ð  /×"8Ò"8Ñ":Ô":ð ðØ#×*Ò*Ñ,Ô,Ð,Ð,øÝ ð ð ð Ø�Dðøøøà*ˆOØ!-ÐØ!ˆNØ.×3Ò3Ñ5Ô5Ô=Ðå:Ý%ØÐ'9ð 
ñ  
ô  
ð 
ð 
ð 
ð 
ð 
ð 
ˆõ ˜.Ñ)Ô)Õ,?Ò?ˆÝ6Ý$ oÕ7Kñ
ô 
ð 
ð 
ð 
ð 
ð 
ð 
ˆ
ð ð 	˜*ð 	Ý#>Ý(ØÐ+=Ý!4Ý2Ø%ð$ñ $ô $ð ð ð ð ð ð ˆNõ �>Ñ"Ô"Õ%6Ò6Ð6Ý!ð7Ý˜>Ñ*Ô*¨kÑ:ÐDð7ð 7å/°;Ñ?ÐJð7ð 7ð 7ð "ðñ ô ð2 ð 	˜oÐ9ðØ"×)Ò)Ñ+Ô+ð -Ø#×*Ò*Ñ,Ô,Ð,Ð,ð-øåð ð ð Ø�ðøøøð		Ð9õ 0ØØØ)Ø-Ý(ØÐ#˜t \Ð%9°Tñô ð
ñ 
ô 
ð ð 	˜oÐ9ðØ"×)Ò)Ñ+Ô+ð -Ø#×*Ò*Ñ,Ô,Ð,Ð,ð-øåð ð ð Ø�ðøøøð		Ð9øõ ð Ið Ið IÝ�ŠÐ;¸SÑAÔAÐAÝÐ8°3Ð8Ð8À%ÐHÑHÔHÐHÐHÐHÐHÐHð ð 	˜oÐ9ðØ"×)Ò)Ñ+Ô+ð -Ø#×*Ò*Ñ,Ô,Ð,Ð,ð-øåð ð ð Ø�ðøøøð		Ð9øøøøðIøøøøð
 ð 	˜oÐ9ðØ"×)Ò)Ñ+Ô+ð -Ø#×*Ò*Ñ,Ô,Ð,Ð,ð-øåð ð ð Ø�ðøøøð		Ð9øøøse  Á S, Á'(BÂ
BÂBÂ#
S, Â.C Ã
S, ÃD8ÃD3Ã-D8Ã.S, Ã7(D!Ä!
D.Ä-D.Ä3D8Ä8CS, Ç>(H(È(
H5È4H5È:S, ÉI, É+S, É,
I9É6S, É8I9É9A#S, Ë!(LÌ
LÌLÌS, Ì4M	 ÍS, Í	
MÍS, ÍMÍC/S, Ñ
(Q4Ñ4
RÒ RÒ%S, Ò0(SÓ
S'Ó&S'Ó,
U/Ó6.U*Ô$U/Ô%U2 Ô.(UÕ
U%Õ$U%Õ*U/Õ/U2 Õ2V4Õ8(V"Ö V4Ö"
V/Ö,V4Ö.V/Ö/V4Úuser_promptc              ƒ   ó¤  ‡/K  — t          |t          ¦  «        s|�t          |¦  «        nd}| t          |¦  «        dk    r|dd…         dz   n||dœddd|ddœ}d}d	}d}	 dd
lm}	  |	¦   «         r�t          dd¬¦  «        |rz|ry|                     ¦   «         rf	 |                     ¦   «          t           	                    d¦  «         S # t          $ r'}
t                               d|
d	¬¦  «         Y d}
~
S d}
~
ww xY wS S S t                               d| dd…         ¦  «         t                               d|dd…         ¦  «         ddlm}m}m} 	  ||  ||¬¦  «        ¦  «        ƒ d{V —†}n)# |$ r!}t#          t          |¦  «        ¦  «        ‚d}~ww xY w|j        }t'          dd¦  «        }|                     d	d	¬¦  «         |dt+          j        ¦   «         › d�z  }t/          j        |j        |j        ¦  «        ƒ d{V —† d	}t          |j        ¦  «        }|dz  }t                               d|¦  «         t/          j        t6          ||¦  «        ƒ d{V —†\  }}}|s|€t#          |pd¦  «        ‚||k    r@|r:|                     ¦   «         r&	 |                     ¦   «          n# t          $ r Y nw xY w|}d	}i }i }|�~t/          j        t8          |||¬¦  «        ƒ d{V —†\  }}}|s|€t#          |pd¦  «        ‚|r:|                     ¦   «         r&	 |                     ¦   «          n# t          $ r Y nw xY w|}|}d	}t                               d ¦  «         t;          t<          ||¬!¦  «        ƒ d{V —†}t          |¦  «        dz  }t                               d"|¦  «         t          |¦  «        t>          k    rft;          t@          |||¬#¦  «        ƒ d{V —†}t          |¦  «        t>          k    r0t#          d$t          |¦  «        d%z  d&›d't>          d%z  d(›d)�¦  «        ‚||d*<   |}d+d,|d-œd.d/|id0œgd1œg}t                               d2¦  «         d3}d4}	 dd5l!m"} m#}!  |!¦   «         }" | |"d6d7i ¬8¦  «        }#|# $                    d9¦  «        }$|$�tK          |$¦  «        }|# $                    d:¦  «        }%|%�tK          |%¦  «        }n# t          $ r Y nw xY wd7||d;|d<œ}&|r||&d=<   tM          ¦   «          	 tO          dZi |&¤Žƒ d{V —†}'nÁ# t          $ r´}(tQ          |(¦  «        ršt          |¦  «        tR          k    r‚t                               d>t          |¦  «        d%z  tR          d%z  ¦  «         t;          t@          |||¬#¦  «        ƒ d{V —†}||d         d?         d@         d.         d/<   tO          dZi |&¤Žƒ d{V —†}'n‚ Y d}(~(nd}(~(ww xY wtU          |'¦  «        })|)s;t                               dA¦  «         tO          dZi |&¤Žƒ d{V —†}'tU          |'¦  «        })t          |)¦  «        }*t                               dB|*¦  «         |)pdC})tW          |pd|pd¦  «        }+d	|+rdD|+› dE|)› �n|)dFœ},|+r|+|,dG<   d	|dH<   |*|dI<   tX           -                    dJ|¦  «         tX           .                    ¦   «          t_          j0        |,dKd¬L¦  «        |rz|ry|                     ¦   «         rf	 |                     ¦   «          t           	                    d¦  «         S # t          $ r'}
t                               d|
d	¬¦  «         Y d}
~
S d}
~
ww xY wS S S # t          $ �r…}-dMt          |-¦  «        › �}.t           1                    dN|.d	¬¦  «         t          |-¦  «         2                    ¦   «         Š/tg          ˆ/fdO„dPD ¦   «         ¦  «        rdQ|-› �})n6tg          ˆ/fdR„dSD ¦   «         ¦  «        r|› dT|-› �})ndU‰/v sd.‰/v rdV|-› �})ndW|-› �})d|.|)dXœ},|.|dY<   tX           -                    dJ|¦  «         tX           .                    ¦   «          t_          j0        |,dKd¬L¦  «        cY d}-~-|rz|ry|                     ¦   «         rf	 |                     ¦   «          t           	                    d¦  «         S # t          $ r'}
t                               d|
d	¬¦  «         Y d}
~
S d}
~
ww xY wS S S d}-~-ww xY w# |rz|ry|                     ¦   «         rf	 |                     ¦   «          t           	                    d¦  «         w # t          $ r'}
t                               d|
d	¬¦  «         Y d}
~
w d}
~
ww xY ww w w xY w)[a“  
    Analyze an image from a URL or local file path using vision AI.
    
    This tool accepts either an HTTP/HTTPS URL or a local file path. For URLs,
    it downloads the image first. In both cases, the image is converted to base64
    and processed using Gemini 3 Flash Preview via OpenRouter API.
    
    The user_prompt parameter is expected to be pre-formatted by the calling
    function (typically model_tools.py) to include both full description
    requests and specific questions.
    
    Args:
        image_url (str): The URL or local file path of the image to analyze.
                         Accepts http://, https:// URLs or absolute/relative file paths.
        user_prompt (str): The pre-formatted prompt for the vision model
        model (str): The vision model to use (default: google/gemini-3-flash-preview)
    
    Returns:
        str: JSON string containing the analysis results with the following structure:
             {
                 "success": bool,
                 "analysis": str (defaults to error message if None)
             }
    
    Raises:
        Exception: If download fails, analysis fails, or API key is not set
        
    Note:
        - For URLs, temporary images are stored under $HERMES_HOME/cache/vision/ and cleaned up
        - For local file paths, the file is used directly and NOT deleted
        - Supports common image formats (JPEG, PNG, GIF, WebP, etc.)
    Nr   r·  ú...)rØ   rï  r‰  Fr   )Ú
parametersr«   rÆ  Úanalysis_lengthÚ
model_usedr®  TrÇ  rÉ  rÅ  zCleaned up temporary image fileú#Could not delete temporary file: %srÀ   zAnalyzing image: %sé<   úUser prompt: %srì   rÊ  rÎ  rŒ   r�   rŽ   rÏ  rÐ  r±  zImage ready (%.1f KB)rÑ  rÒ  rÓ  zConverting image to base64...r\  z#Image converted to base64 (%.1f KB))r   rZ  rÔ  r_  r²  rÕ  rÖ  r×  r®  Úuserr³  r´  rØ   rL   r¶  ©Úroler»  z%Processing image with vision model...g      ^@çš™™™™™¹?r   r    r!   ©Údefaultru   ÚtemperatureiÐ  ©ÚtaskÚmessagesrþ  Ú
max_tokensru   r‰  zYAPI rejected image (%.1f MB, likely too large); auto-resizing to ~%.0f MB and retrying...r»  r/   z0Vision LLM returned empty content, retrying oncez(Image analysis completed (%s characters)zIThere was a problem with the request and the image could not be analyzed.Ú[z] ©rÆ  Úanalysisr¯  rÆ  ró  Úvision_analyze_toolrí   ©ÚindentÚensure_asciizError analyzing image: ú%sc              3   ó    •K  — | ]}|‰v V — Œ	d S r  r6   r  s     €r   r  z&vision_analyze_tool.<locals>.<genexpr>$  ó7   øè è € ð ð  4ˆt�wˆð ð ð ð ð ð r   ©Ú402Úinsufficientzpayment requiredÚcreditsÚbillingúhInsufficient credits or payment required. Please top up your API provider account and try again. Error: c              3   ó    •K  — | ]}|‰v V — Œ	d S r  r6   r  s     €r   r  z&vision_analyze_tool.<locals>.<genexpr>+  s7   øè è € ð ð  T�˜�ð ð ð ð ð ð r   )údoes not supportznot support imageÚcontent_policyÚ
multimodalúunrecognized request argumentzimage inputzO does not support vision or our request was not accepted by the server. Error: r  z¹The vision API rejected the image. This can happen when the image is in an unsupported format, corrupted, or still too large after auto-resize. Try a smaller JPEG/PNG and retry. Error: zQThere was a problem with the request and the image could not be analyzed. Error: ©rÆ  r«   r  r«   r6   )4rP   rQ   r1   rÙ  rÈ  rØ  ry   rË   r¤   rÌ   r)   r¥   rn  rÚ  rË  rÌ  rÍ  r'   r
  r   rœ   r�   rž   rB   rÛ  rÜ  r\   rª   rE  rK   r  rß  r‡  r(   r   r   rÅ   r&   r   r
   r  Ú_RESIZE_TARGET_BYTESr   rX  Ú_debugÚlog_callr£   ÚjsonÚdumpsr«   rü   r  )0rØ   rï  r‰  rÃ  r#  Údebug_call_datarà  rá  rã  rÈ  Úcleanup_errorrË  rÌ  rÍ  râ  rD  rä  r®  Úimage_size_kbrå  ræ  rç  rè  ré  rê  rë  r­  Údata_size_kbÚcomprehensive_promptr  Úvision_timeoutÚvision_temperaturer   r   Ú_cfgÚ_vision_cfgÚ_vtÚ_vtempÚcall_kwargsr°   Ú_api_errr  ró  r¯  Úresultr÷   Ú	error_msgr  s0                                                  @r   r  r    sà  øè è € õN �k¥3Ñ'Ô'ð JØ*5Ð*A•c˜+Ñ&Ô&Ð&Àrˆð #Ý8;¸KÑ8HÔ8HÈ3Ò8NÐ8N˜; t¨ tÔ,¨uÑ4Ð4ÐT_Øð
ð 
ð
 ØØØØðð €Oð €Oð €NØÐðHØ2Ð2Ð2Ð2Ð2Ð2Øˆ>ÑÔð 	<Ý˜m°UÐ;Ñ;Ô;ð| ð 	˜oð 	°/×2HÒ2HÑ2JÔ2Jð 	ðØ×&Ò&Ñ(Ô(Ð(Ý—’Ð>Ñ?Ô?Ð?Ð?øÝð ð ð Ý—’Ø9¸=ÐSWð ñ ô ð ð ð ð ð ð øøøøðøøøð		ð 	ð 	õy 	�ŠÐ)¨9°S°b°S¬>Ñ:Ô:Ð:Ý�ŠÐ% {°4°C°4Ô'8Ñ9Ô9Ð9ð	
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
ð	'Ø1Ð1°)¸^¸^ÐT[Ð=\Ñ=\Ô=\Ñ]Ô]Ð]Ð]Ð]Ð]Ð]Ð]ˆHˆHøØ#ð 	'ð 	'ð 	'Ý�S ™XœXÑ&Ô&Ð&øøøøð	'øøøð &œ]ÐÝ! .Ð2FÑGÔGˆØ�Š˜t¨dˆÑ3Ô3Ð3Ø"Ð%Eµ4´:±<´<Ð%EÐ%EÐ%EÑEˆÝÔ Ô ;¸X¼]ÑKÔKÐKÐKÐKÐKÐKÐKÐKØˆõ ˜xœ}Ñ-Ô-ÐØ(¨4Ñ/ˆÝ�ŠÐ+¨]Ñ;Ô;Ð;õ @GÔ?PÝ)¨?Ð<Nñ@
ô @
ð :
ð :
ð :
ð :
ð :
ð :
Ñ6ˆÐ+¨Yð ð 	I˜Ð/Ý˜YÐGÐ*GÑHÔHÐHØ˜oÒ-Ð-Øð  /×"8Ò"8Ñ":Ô":ð ðØ#×*Ò*Ñ,Ô,Ð,Ð,øÝ ð ð ð Ø�Dðøøøà-ˆOØ!ˆNð  ˆØˆØÐÝ9@Ô9JÝ" O°VØ'ð:ñ :ô :ð 4ð 4ð 4ð 4ð 4ð 4Ñ0ˆL˜,¨ð ð D˜<Ð/Ý  Ð!BÐ-BÑCÔCÐCØð  /×"8Ò"8Ñ":Ô":ð ðØ#×*Ò*Ñ,Ô,Ð,Ð,øÝ ð ð ð Ø�Dðøøøà*ˆOØ!-ÐØ!ˆNõ 	�ŠÐ3Ñ4Ô4Ð4Ý:Ý% ÐBTð Vñ  Vô  Vð Vð Vð Vð Vð Vð Vˆå˜>Ñ*Ô*¨TÑ1ˆÝ�ŠÐ9¸<ÑHÔHÐHõ ˆ~ÑÔÕ!2Ò2Ð2å#>Ý(ØÐ+=Ø%ð$'ñ $'ô $'ð 'ð 'ð 'ð 'ð 'ð 'ˆNõ �>Ñ"Ô"Õ%6Ò6Ð6Ý ð7Ý˜>Ñ*Ô*¨kÑ:ÐDð7ð 7å/°;Ñ?ÐJð7ð 7ð 7ñô ð ð /?ˆÐ*Ñ+ð  +Ðð
 ð !'Ø 4ðð ð
 !,à! >ð&ðð ððð ð
ˆõ$ 	�ŠÐ;Ñ<Ô<Ð<ð
 ˆØ Ðð	Ø>Ð>Ð>Ð>Ð>Ð>Ð>Ð>Ø�;‘=”=ˆDØ!˜' $¨°XÀrÐJÑJÔJˆKØ—/’/ )Ñ,Ô,ˆCØˆÝ!& s¡¤�Ø —_’_ ]Ñ3Ô3ˆFØÐ!Ý%*¨6¡]¤]Ð"øøÝð 	ð 	ð 	ØˆDð	øøøð Ø Ø-ØØ%ð
ð 
ˆð ð 	)Ø#(ˆK˜Ñ ÝÑ Ô Ð ð	Ý+Ð:Ð:¨kÐ:Ð:Ð:Ð:Ð:Ð:Ð:Ð:ˆHˆHøÝð 	ð 	ð 	Ý$ XÑ.Ô.ð Ý˜NÑ+Ô+Õ.BÒBÐBÝ—’ð@å˜Ñ'Ô'¨;Ñ7Ý(¨KÑ8ñ	ô ð õ (CÝ,Ø#Ð/AØ)ð(+ñ (+ô (+ð "+ð "+ð "+ð "+ð "+ð "+�ð AO�˜”˜IÔ& qÔ)¨+Ô6°uÑ=Ý!/Ð!>Ð!>°+Ð!>Ð!>Ð>Ð>Ð>Ð>Ð>Ð>��àð ����øøøøð	øøøõ& 0°Ñ9Ô9ˆð ð 	>Ý�NŠNÐMÑNÔNÐNÝ+Ð:Ð:¨kÐ:Ð:Ð:Ð:Ð:Ð:Ð:Ð:ˆHÝ3°HÑ=Ô=ˆHå˜h™-œ-ˆå�ŠÐ>ÀÑPÔPÐPð ÐjÐjˆÝ&ØÐ˜4 Ð!5°ñ
ô 
ˆ
ð Ø8BÐPÐ4˜JÐ4Ð4¨(Ð4Ð4Ð4Èð
ð 
ˆð ð 	.Ø#-ˆF�<Ñ à%)ˆ˜	Ñ"Ø-<ˆÐ)Ñ*õ 	�ŠÐ-¨Ñ?Ô?Ð?Ý�Š‰ŒˆåŒz˜&¨¸Ð?Ñ?Ô?ðj ð 	˜oð 	°/×2HÒ2HÑ2JÔ2Jð 	ðØ×&Ò&Ñ(Ô(Ð(Ý—’Ð>Ñ?Ô?Ð?Ð?øÝð ð ð Ý—’Ø9¸=ÐSWð ñ ô ð ð ð ð ð ð øøøøðøøøð		ð 	ð 	øõg ð /@ñ /@ð /@Ø6­c°!©f¬fÐ6Ð6ˆ	Ý�Š�T˜9¨tˆÑ4Ô4Ð4õ �a‘&”&—,’,‘.”.ˆÝð ð ð ð ð ,
ð ñ ô ñ ô ð 	ðBØ>?ðBð Bð ˆHõ ð ð ð ð ð .
ð ñ ô ñ ô ð 	ð ð 6ð 6Ø23ð6ð 6ð ˆHð  'Ð)Ð)¨[¸GÐ-CÐ-Cðð ðð ð ˆHð+Ø'(ð+ð +ð ð ØØ ð
ð 
ˆð $-ˆ˜Ñ Ý�ŠÐ-¨Ñ?Ô?Ð?Ý�Š‰ŒˆåŒz˜&¨¸Ð?Ñ?Ô?Ð?Ð?Ð?Ð?Ð?ð ð 	˜oð 	°/×2HÒ2HÑ2JÔ2Jð 	ðØ×&Ò&Ñ(Ô(Ð(Ý—’Ð>Ñ?Ô?Ð?Ð?øÝð ð ð Ý—’Ø9¸=ÐSWð ñ ô ð ð ð ð ð ð øøøøðøøøð		ð 	ð 	øøøøðg/@øøøøðf ð 	˜oð 	°/×2HÒ2HÑ2JÔ2Jð 	ðØ×&Ò&Ñ(Ô(Ð(Ý—’Ð>Ñ?Ô?Ð?Ð?øÝð ð ð Ý—’Ø9¸=ÐSWð ñ ô ð ð ð ð ð ð øøøøðøøøð		ð 	ð 	øøøsj  Á  \< Â.CÃ
C9ÃC4Ã4C9Ã?A\< ÅE- Å,\< Å-FÅ2FÆFÆC<\< ÊJ% Ê$\< Ê%
J2Ê/\< Ê1J2Ê2A\< ÌL' Ì&\< Ì'
L4Ì1\< Ì3L4Ì4D \< ÑA-S Ó\< Ó
SÓ\< ÓSÓ \< Ó1T Ô\< Ô
WÔB*V=Ö8\< Ö=W×C;\< Û.\Ü
\6Ü\1Ü1\6Ü<cÝC<cácác á!.bâ
câb<â<cãcãc ãeã).däeä
e	ä"eä?eåe	å	ec                  ó¤   — 	 ddl m}  n# t          $ r Y dS w xY w	  | ¦   «         \  }}}|�dS  | d¬¦  «        \  }}}|duS # t          $ r Y dS w xY w)u  Check if the configured runtime vision path can resolve a client.

    Mirrors the fallback chain that ``call_llm(task="vision")`` actually uses
    at runtime: first the explicit ``auxiliary.vision.provider`` (if any),
    and if that fails, the auto chain (main provider â†’ openrouter â†’ nous).
    Without the auto-fallback step the tool would disappear from the model's
    tool list whenever the explicit provider name was unresolvable, even
    when the auto chain would have served the request (issue #31179).
    r   )Úresolve_vision_provider_clientFNTÚauto)rˆ  )r   r.  r3  r)   )r.  Ú	_providerrÍ   Ú_models       r   Úcheck_vision_requirementsr2  Z  s­   € ðØIÐIÐIÐIÐIÐIÐIøÝð ð ð Øˆuˆuðøøøð	Ø$BÐ$BÑ$DÔ$DÑ!ˆ	�6˜6ØÐØ�4ð %CÐ$BÈFÐ$SÑ$SÔ$SÑ!ˆ	�6˜6Ø˜TÐ!Ð!øÝð ð ð Øˆuˆuðøøøs"   ‚	 ‰
–›A ­A Á
AÁAÚ__main__u   ðŸ‘�ï¸� Vision Tools Modulez(========================================u'   â�Œ No auxiliary vision model availablezvConfigure a supported multimodal backend (OpenRouter, Nous, Codex, Anthropic, or a custom OpenAI-compatible endpoint).r/   u   âœ… Vision model availableu#   ðŸ› ï¸� Vision tools ready for use!u&   ðŸ�› Debug mode ENABLED - Session ID: z:   Debug logs will be saved to: ./logs/vision_tools_debug_z.jsonu@   ðŸ�› Debug mode disabled (set VISION_TOOLS_DEBUG=true to enable)z
Basic usage:z.  from vision_tools import vision_analyze_toolz  import asyncior   z  async def main():z)      result = await vision_analyze_tool(z4          image_url='https://example.com/image.jpg',z6          user_prompt='What do you see in this image?'z      )z      print(result)z  asyncio.run(main())z
Example prompts:z0  - 'What architectural style is this building?'z2  - 'Describe the emotions and mood in this image'z+  - 'What text can you read in this image?'z)  - 'Identify any safety hazards visible'z(  - 'What products or brands are shown?'z
Debug mode:z  # Enable debug loggingz   export VISION_TOOLS_DEBUG=truez<  # Debug logs capture all vision analysis calls and resultsz6  # Logs saved to: ./logs/vision_tools_debug_UUID.json)ÚregistryrØ  Úvision_analyzeuÖ  Load an image into the conversation so you can see it. Accepts a URL, local file path, or data URL. When your active model has native vision, the image is attached to your context directly and you read the pixels yourself on the next turn â€” call this any time the user references an image (filepath in their message, URL in tool output, screenshot from the browser, etc.). For non-vision models, falls back to an auxiliary vision model that returns a text description.ÚobjectÚstringz>Image URL (http/https), local file path, or data: URL to load.)rµ  Údescriptionz{Your specific question or request about the image. Optional context the model uses on the next turn after seeing the image.Úarrayrµ  Úintegerrd   aF  Optional [x1, y1, x2, y2] crop region in pixel coordinates of the ORIGINAL image, applied before any downscaling so the region keeps full resolution. Intended flow: load the full image first, then call again with a region to zoom into a detail (small text, UI element, fine print). Coordinates are clamped to the image bounds.)rµ  ÚitemsÚminItemsÚmaxItemsr8  )rØ   r¬  r#  )rµ  Ú
propertiesÚrequired)rÇ   r8  rò  rH   Úkwc              ‹   óz  K  — |                       dd¦  «        }|                       dd¦  «        }|                       d¦  «        }|                      d¦  «        }t          ¦   «         r3t                               d¦  «         t	          ||||¬¦  «        ƒ d {V —†S d|› �}d }	 d	d
lm}m}	  |	¦   «         }
 ||
ddd¦  «        }|r#t          |¦  «         	                    ¦   «         pd }n# t          $ r Y nw xY w|s)t          j        dd¦  «         	                    ¦   «         pd }t          |||||¬¦  «        ƒ d {V —†S )NrØ   r   r¬  r#  rÃ  z vision_analyze: native fast path)rÃ  r#  z]Fully describe and explain everything about this image, then answer the following question:

r   r   r    r!   r‰  ÚAUXILIARY_VISION_MODEL)rÅ   r«  r¤   rn  rî  r(   r   r   rQ   r%   r)   r#   r$   r  )rH   r@  rØ   r¬  r#  rÃ  Úfull_promptr‰  r   r   r%  Ú_vmodels               r   Ú_handle_vision_analyzerE  Ù  s”  è è € Ø—’˜ bÑ)Ô)€IØ�xŠx˜
 BÑ'Ô'€HØ�XŠX�hÑÔ€FØ�fŠf�YÑÔ€Gõ +Ñ,Ô,ð aÝ�ŠÐ6Ñ7Ô7Ð7Ý+¨I°xÈÐY_Ð`Ñ`Ô`Ð`Ð`Ð`Ð`Ð`Ð`Ð`ð	-Ø"*ð	-ð 	-ð ð
 €EðØ:Ð:Ð:Ð:Ð:Ð:Ð:Ð:Øˆ{‰}Œ}ˆØ�'˜$ ¨X°wÑ?Ô?ˆØð 	1Ý˜‘L”L×&Ò&Ñ(Ô(Ð0¨DˆEøøÝð ð ð Øˆðøøøàð HÝ”	Ð2°BÑ7Ô7×=Ò=Ñ?Ô?ÐGÀ4ˆÝ$ Y°¸UÈGÐ\bÐcÑcÔcÐcÐcÐcÐcÐcÐcÐcs   Â"AC( Ã(
C5Ã4C5r!   Tu   ðŸ‘�ï¸�)rÇ   ÚtoolsetÚschemaÚhandlerÚcheck_fnÚis_asyncÚemojiú	video/mp4z
video/webmz	video/movz
video/mpeg)ú.mp4z.webmz.movz.aviz.mkvz.mpegz.mpgÚ
video_pathc                 óh   — | j                              ¦   «         }t                               |¦  «        S )zIReturn a video MIME type based on file extension, or None if unsupported.)rû   rü   Ú_VIDEO_MIME_TYPESrÅ   )rN  Úexts     r   Ú_detect_video_mime_typerR     s*   € à
Ô
×
!Ò
!Ñ
#Ô
#€CÝ× Ò  Ñ%Ô%Ð%r   c                 óð   — |                       ¦   «         }t          j        |¦  «                             d¦  «        }|p1t                               | j                             ¦   «         d¦  «        }d|› d|› �S )z2Convert a video file to a base64-encoded data URL.r  rL  r  r  )r  r  r  r  rP  rÅ   rû   rü   )rN  r   r\   r	  r
  s        r   Ú_video_to_base64_data_urlrT  &  sq   € à× Ò Ñ"Ô"€DÝÔ˜tÑ$Ô$×+Ò+¨GÑ4Ô4€GØÐUÕ)×-Ò-¨jÔ.?×.EÒ.EÑ.GÔ.GÈÑUÔU€DØ*�4Ð*Ð* Ð*Ð*Ð*r   c                  ó|   — t          j        dd¦  «                             ¦   «                              ¦   «         } | dv S )NÚTERMINAL_ENVÚlocal)r   rW  )r#   r$   r%   rü   )Úbackends    r   Ú_terminal_backend_is_localrY  .  s6   € ÝŒi˜¨Ñ0Ô0×6Ò6Ñ8Ô8×>Ò>Ñ@Ô@€GØ�mÐ#Ð#r   Úvaluec                 ó†   — | pd                      ¦   «                              ¦   «         }|sdS |                     d¦  «         S )Nr   F)rN   rO   r  )r%   rü   rR   )rZ  Úlowereds     r   Ú_is_path_like_video_sourcer]  3  sH   € Øˆ{˜×!Ò!Ñ#Ô#×)Ò)Ñ+Ô+€GØð ØˆuØ×!Ò!Ð"BÑCÔCÐCÐCr   Úvideo_sourcec              ƒ   ó¨  K  — ddl m}m}m} | }|                     d¦  «        r|t          d¦  «        d…         }t          |¦  «        j                             ¦   «         }|t          vrLt          d|› dd                     t          t                               ¦   «         ¦  «        ¦  «        › �¦  «        ‚	  ||  ||¬¦  «        d	¬
¦  «        ƒ d{V —†}n # |$ r}t          d|› �¦  «        |‚d}~ww xY wt          dd¦  «        }	|	                     dd¬¦  «         |	dt!          j        ¦   «         › |› �z  }
|
                     |j        ¦  «         |
S )u6  Read a path via the shared media resolver into a local temp video file.

    Routes through :func:`tools.image_source.resolve_image_source` with
    ``permitted=("video",)`` so terminal-backend video reads get the exact
    pipeline vision_analyze uses: media-cache host reads (gateway-downloaded
    videos live on the host, not in the sandbox), bounded in-sandbox exec-read
    (``head -c`` cap â€” no unbounded base64 stream, no python3 dependency in
    the sandbox image), lazy env bring-up (#62825), the credential-read
    guard, and the 50MB ingest cap.
    r   rÊ  úfile://NúUnsupported video format: 'ú'. Supported: r+  rÎ  )Úvideo)Ú	permittedz,Could not read video from terminal backend: úcache/videoÚtemp_video_filesTrŽ   Úterminal_video_)rÚ  rË  rÌ  rÍ  rR   r1   r   rû   rü   rP  r'   rO  ÚsortedÚkeysr   rœ   r�   rž   rÜ  r\   )r^  rÃ  rË  rÌ  rÍ  Úsourcerû   râ  rD  rä  Ú	temp_paths              r   Ú(_materialize_video_from_terminal_backendrl  :  sÄ  è è € ð ^Ð]Ð]Ð]Ð]Ð]Ð]Ð]Ð]Ð]à€FØ×Ò˜Ñ#Ô#ð )Ø�˜I™œ˜˜Ô(ˆÝ�&‰\Œ\Ô ×&Ò&Ñ(Ô(€FØÕ&Ð&Ð&ÝðH¨&ð Hð HØŸ)š)¥FÕ+<×+AÒ+AÑ+CÔ+CÑ$DÔ$DÑEÔEðHð Hñ
ô 
ð 	
ð
XØ-Ð-Ø˜.˜.°Ð9Ñ9Ô9ÀZð
ñ 
ô 
ð 
ð 
ð 
ð 
ð 
ð 
ˆˆøð  ð Xð Xð XÝÐMÈÐMÐMÑNÔNÐTWÐWøøøøðXøøøõ ˜mÐ-?Ñ@Ô@€HØ‡N‚N˜4¨$€NÑ/Ô/Ð/ØÐC­T¬Z©\¬\ÐC¸6ÐCÐCÑC€IØ×Ò˜(œ-Ñ(Ô(Ð(ØÐs   Â7C ÃC3ÃC.Ã.C3Ú	video_urlc              ƒ   ó  K  — ddl }|j                             dd¬¦  «         d„ }d}t          |¦  «        D �];}	 t	          | ¦  «        }|rt          |d         ¦  «        ‚ddlm}  |ddd	|gi¬
¦  «        4 ƒd{V —†	 }	t          |	| |t          dddœd¬¦  «        ƒ d{V —† ddd¦  «        ƒd{V —† n# 1 ƒd{V —†swxY w Y   |c S # t          $ r�}
|
}||dz
  k     rTd|dz   z  }t                               d|dz   |t          |
¦  «        dd…         ¦  «          |j        |¦  «        ƒ d{V —† n3t                               d|t          |
¦  «        dd…         d¬¦  «         Y d}
~
�Œ5d}
~
ww xY w|€t!          d|› d�¦  «        ‚|‚)z7Download video from URL with SSRF protection and retry.r   NTrŽ   c              “   ó~   K  — ddl m}m}  || ¦  «        }|r# ||¦  «        ƒ d {V —†st          d|› �¦  «        ‚d S d S )Nr   rÜ   rÞ   rß   rà   s       r   râ   z-_download_video.<locals>._ssrf_redirect_guarde  sŒ   è è € ØUÐUÐUÐUÐUÐUÐUÐUØ4Ð4°XÑ>Ô>ˆØð 	Ð&7Ð&7¸Ñ&EÔ&EÐ EÐ EÐ EÐ EÐ EÐ Eð 	ÝØOÀÐOÐOñô ð ð	ð 	ð 	ð 	r   r¼   rã   g      N@r°   rå   rè   zvideo/*,*/*;q=0.8ré   ÚVideorë   r/   rí   z)Video download failed (attempt %s/%s): %srî   z+Video download failed after %s attempts: %srì   rÀ   zB_download_video exited retry loop without attempting (max_retries=r»   )rB   rï   rœ   rð   r   r­   rW   rä   rÖ   Ú_MAX_VIDEO_BASE64_BYTESr)   r¤   r¥   rQ   ró   r«   rô   )rm  rµ   rÙ   rB   râ   rõ   rö   rÑ   rä   rÍ   r÷   rø   s               r   Ú_download_videorr  _  sâ  è è € à€N€N€NàÔ×Ò T°DÐÑ9Ô9Ð9ðð ð ð €JÝ˜Ñ%Ô%ð $ñ $ˆð#	Ý*¨9Ñ5Ô5ˆGØð :Ý% g¨iÔ&8Ñ9Ô9Ð9àFÐFÐFÐFÐFÐFà4Ð4ØØ!%Ø'Ð*>Ð)?Ð@ðñ ô ð ð ð ð ð ð ð ð ð Ý.ØØØÝ+ð 'XØ"5ðð ð !(ð
ñ 
ô 
ð 
ð 
ð 
ð 
ð 
ð 
ð 
ðð ð ñ ô ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð øøøð ð ð ð ð" ÐÐÐøÝð 
	ð 
	ð 
	ØˆJØ˜ q™Ò(Ð(Ø '¨A¡+Ñ.�	Ý—’ÐJÈGÐVWÉKÐYdÕfiÐjkÑflÔflÐmpÐnpÐmpÔfqÑrÔrÐrØ#�g”m IÑ.Ô.Ð.Ð.Ð.Ð.Ð.Ð.Ð.Ð.å—’ØAØ¥ Q¡¤¨¨¨¤¸ð ñ ô ð øøøøùøøøøð
	øøøð ÐÝØ_ÐQ\Ð_Ð_Ð_ñ
ô 
ð 	
ð Ðs=   »AC
Á?$B5Â#C
Â5
B?	Â?C
ÃB?	ÃC
Ã

E1ÃBE,Å,E1c              ƒ   ór  ‡#K  — t          |t          ¦  «        s|�t          |¦  «        nd}| t          |¦  «        dk    r|dd…         dz   n||dœddd|ddœ}d}d	}	 dd
lm}  |¦   «         r�t          dd¬¦  «        |rz|ry|                     ¦   «         rf	 |                     ¦   «          t           	                    d¦  «         S # t          $ r'}t                               d|d	¬¦  «         Y d}~S d}~ww xY wS S S t                               d| dd…         ¦  «         t                               d|dd…         ¦  «         | }	|	                     d¦  «        r|	t          d¦  «        d…         }	t          t          j                             |	¦  «        ¦  «        }
t%          ¦   «         sCt'          | ¦  «        r4t                               d| ¦  «         t)          | |¦  «        ƒ d{V —†}d	}nß|
                     ¦   «         r>ddlm}  |t          |
¦  «        ¦  «         t                               d| ¦  «         |
}d}n�t1          | ¦  «        ƒ d{V —†rit3          | ¦  «        }|rt5          |d         ¦  «        ‚t7          dd¦  «        }|dt9          j        ¦   «         › d�z  }t=          | |¦  «        ƒ d{V —† d	}nt?          d¦  «        ‚|                      ¦   «         j!        }|dz  }t                               d|¦  «         tE          |¦  «        }|sQt?          d |j#        › d!d" $                    tK          tL           '                    ¦   «         ¦  «        ¦  «        › �¦  «        ‚|tP          k    rt                               d#|¦  «         tS          ||¬$¦  «        }t          |¦  «        dz  }t          |¦  «        tT          k    r t?          d%|d&›d'tT          dz  d(›d)�¦  «        ‚||d*<   d+d,|d-œd.d/|id0œgd1œg}d2}d3}	 dd4l+m,}m-}  |¦   «         } ||d5d6i ¬7¦  «        }| .                    d8¦  «        }|�t_          ta          |¦  «        d2¦  «        }| .                    d9¦  «        }|�ta          |¦  «        }n# t          $ r Y nw xY wd6||d:|d;œ}|r||d<<   tc          ¦   «          te          dTi |¤Žƒ d{V —†}tg          |¦  «        }|s;t                               d=¦  «         te          dTi |¤Žƒ d{V —†}tg          |¦  «        }|rt          |¦  «        nd}t                               d>|¦  «         d	|pd?d@œ} d	|dA<   ||dB<   th           5                    dC|¦  «         th           6                    ¦   «          to          j8        | dDd¬E¦  «        |rz|ry|                     ¦   «         rf	 |                     ¦   «          t           	                    d¦  «         S # t          $ r'}t                               d|d	¬¦  «         Y d}~S d}~ww xY wS S S # t          $ �r–}!dFt          |!¦  «        › �}"t           9                    dG|"d	¬¦  «         t          |!¦  «         :                    ¦   «         Š#tw          ˆ#fdH„dID ¦   «         ¦  «        rdJ|!› �}nGtw          ˆ#fdK„dLD ¦   «         ¦  «        rdM|!› �}n&tw          ˆ#fdN„dOD ¦   «         ¦  «        rdP|!› �}ndQ|!› �}d|"|dRœ} |"|dS<   th           5                    dC|¦  «         th           6                    ¦   «          to          j8        | dDd¬E¦  «        cY d}!~!|rz|ry|                     ¦   «         rf	 |                     ¦   «          t           	                    d¦  «         S # t          $ r'}t                               d|d	¬¦  «         Y d}~S d}~ww xY wS S S d}!~!ww xY w# |rz|ry|                     ¦   «         rf	 |                     ¦   «          t           	                    d¦  «         w # t          $ r'}t                               d|d	¬¦  «         Y d}~w d}~ww xY ww w w xY w)UzEAnalyze a video via multimodal LLM. Returns JSON {success, analysis}.Nr   r·  rñ  )rm  rï  r‰  Fr   )rò  r«   rÆ  ró  rô  Úvideo_size_bytesTrÇ  rÉ  rÅ  zCleaned up temporary video filerõ  rÀ   zAnalyzing video: %srö  r÷  rì   r`  z-Reading video source via terminal backend: %s)Úraise_if_read_blockedzUsing local video file: %sr¼   re  rf  Útemp_video_rM  zKInvalid video source. Provide an HTTP/HTTPS URL or a valid local file path.r_  zVideo ready (%.1f MB)ra  rb  r+  u,   Video is %.1f MB â€” may be slow or rejectedr\  z+Video too large for API: base64 payload is r²  rÕ  rÖ  z+ MB). Compress or trim the video and retry.rt  rø  r³  r´  rm  rL   )rµ  rm  rù  g     €f@rû  r   r    r!   rü  ru   rþ  i   rÿ  r‰  z#Empty video response, retrying oncez(Video analysis completed (%s characters)zIThere was a problem with the request and the video could not be analyzed.r  rÆ  ró  Úvideo_analyze_toolrí   r  zError analyzing video: r
  c              3   ó    •K  — | ]}|‰v V — Œ	d S r  r6   r  s     €r   r  z%video_analyze_tool.<locals>.<genexpr>;  r  r   r  r  c              3   ó    •K  — | ]}|‰v V — Œ	d S r  r6   r  s     €r   r  z%video_analyze_tool.<locals>.<genexpr>B  s7   øè è € ð ð  T�˜�ð ð ð ð ð ð r   )r  znot support videor  r  r  zvideo inputrm  z˜The model does not support video analysis or the request was rejected. Ensure you're using a video-capable model (e.g. google/gemini-2.5-flash). Error: c              3   ó    •K  — | ]}|‰v V — Œ	d S r  r6   r  s     €r   r  z%video_analyze_tool.<locals>.<genexpr>M  s7   øè è € ð ð  T�˜�ð ð ð ð ð ð r   )r  r  r  r  r  r  r  z_The video is too large for the API. Try compressing or trimming the video (max ~50 MB). Error: zQThere was a problem with the request and the video could not be analyzed. Error: r  r«   r6   )<rP   rQ   r1   rÙ  rÈ  rØ  ry   rË   r¤   rÌ   r)   r¥   rn  rR   r   r#   ÚpathÚ
expanduserrY  r]  rl  Úis_fileÚagent.file_safetyru  r[   r   r­   r   r�   rž   rr  r'   rz   r{   rR  rû   rO  rh  rP  ri  Ú_VIDEO_SIZE_WARN_BYTESrT  rq  r(   r   r   rÅ   r0   r&   r   r
   r   r  r  r£   r  r  r«   rü   r  )$rm  rï  r‰  rÃ  r  Útemp_video_pathrá  rÈ  r  Úresolved_urlÚ
local_pathru  rÑ   rä  rt  Úvideo_size_mbrŠ   Úvideo_data_urlÚdata_size_mbr  r#  r$  r   r   r%  r&  r'  r(  r)  r°   r  ró  r+  r÷   r,  r  s$                                      @r   rw  rw  ›  s:  øè è € õ �k¥3Ñ'Ô'ð JØ*5Ð*A•c˜+Ñ&Ô&Ð&Àrˆð #Ý8;¸KÑ8HÔ8HÈ3Ò8NÐ8N˜; t¨ tÔ,¨uÑ4Ð4ÐT_Øð
ð 
ð
 ØØØØðð €Oð €OØ€Nð{Ø2Ð2Ð2Ð2Ð2Ð2Øˆ>ÑÔð 	<Ý˜m°UÐ;Ñ;Ô;ðb ð 	˜oð 	°/×2HÒ2HÑ2JÔ2Jð 	ðØ×&Ò&Ñ(Ô(Ð(Ý—’Ð>Ñ?Ô?Ð?Ð?øÝð ð ð Ý—’Ø9¸=ÐSWð ñ ô ð ð ð ð ð ð øøøøðøøøð		ð 	ð 	õ_ 	�ŠÐ)¨9°S°b°S¬>Ñ:Ô:Ð:Ý�ŠÐ% {°4°C°4Ô'8Ñ9Ô9Ð9ð !ˆØ×"Ò" 9Ñ-Ô-ð 	9Ø'­¨I©¬¨¨Ô8ˆLÝ�"œ'×,Ò,¨\Ñ:Ô:Ñ;Ô;ˆ
å)Ñ+Ô+ð 	Õ0JÈ9Ñ0UÔ0Uð 	Ý�KŠKÐGÈÑSÔSÐSÝ$LÈYÐX_Ñ$`Ô$`Ð`Ð`Ð`Ð`Ð`Ð`ˆOØ!ˆNˆNØ×ÒÑ!Ô!ð 	Ø?Ð?Ð?Ð?Ð?Ð?Ø!Ð!¥# j¡/¤/Ñ2Ô2Ð2Ý�KŠKÐ4°iÑ@Ô@Ð@Ø(ˆOØ"ˆNˆNÝ,¨YÑ7Ô7Ð7Ð7Ð7Ð7Ð7Ð7ð 	Ý*¨9Ñ5Ô5ˆGØð :Ý% g¨iÔ&8Ñ9Ô9Ð9Ý% mÐ5GÑHÔHˆHØ&Ð)Iµt´z±|´|Ð)IÐ)IÐ)IÑIˆOÝ! )¨_Ñ=Ô=Ð=Ð=Ð=Ð=Ð=Ð=Ð=Ø!ˆNˆNåØ]ñô ð ð +×/Ò/Ñ1Ô1Ô9ÐØ(¨KÑ8ˆÝ�ŠÐ+¨]Ñ;Ô;Ð;å/°Ñ@Ô@ˆØð 	ÝðL¨oÔ.Dð Lð LØ"Ÿiši­Õ/@×/EÒ/EÑ/GÔ/GÑ(HÔ(HÑIÔIðLð Lñô ð ð
 Õ4Ò4Ð4Ý�NŠNÐIÈ=ÑYÔYÐYå2°?ÈmÐ\Ñ\Ô\ˆÝ˜>Ñ*Ô*¨kÑ:ˆåˆ~ÑÔÕ!8Ò8Ð8Ýð9¸lÐTð 9ð 9Ý1°[ÑAÐMð9ð 9ð 9ñô ð ð /?ˆÐ*Ñ+ð ð !'Ø +ðð ð
 !,à! >ð&ðð ððð ð
ˆð$ ˆØ Ðð	Ø>Ð>Ð>Ð>Ð>Ð>Ð>Ð>Ø�;‘=”=ˆDØ!˜' $¨°XÀrÐJÑJÔJˆKØ—/’/ )Ñ,Ô,ˆCØˆÝ!$¥U¨3¡Z¤Z°Ñ!7Ô!7�Ø —_’_ ]Ñ3Ô3ˆFØÐ!Ý%*¨6¡]¤]Ð"øøÝð 	ð 	ð 	ØˆDð	øøøð Ø Ø-ØØ%ð
ð 
ˆð ð 	)Ø#(ˆK˜Ñ åÑ Ô Ð Ý'Ð6Ð6¨+Ð6Ð6Ð6Ð6Ð6Ð6Ð6Ð6ˆÝ/°Ñ9Ô9ˆàð 	>Ý�NŠNÐ@ÑAÔAÐAÝ+Ð:Ð:¨kÐ:Ð:Ð:Ð:Ð:Ð:Ð:Ð:ˆHÝ3°HÑ=Ô=ˆHà+3Ð:�#˜h™-œ-˜-¸ˆÝ�ŠÐ>ÀÑPÔPÐPð Ø ÐoÐ$oð
ð 
ˆð
 &*ˆ˜	Ñ"Ø-<ˆÐ)Ñ*Ý�ŠÐ,¨oÑ>Ô>Ð>Ý�Š‰ŒˆåŒz˜&¨¸Ð?Ñ?Ô?ðh ð 	˜oð 	°/×2HÒ2HÑ2JÔ2Jð 	ðØ×&Ò&Ñ(Ô(Ð(Ý—’Ð>Ñ?Ô?Ð?Ð?øÝð ð ð Ý—’Ø9¸=ÐSWð ñ ô ð ð ð ð ð ð øøøøðøøøð		ð 	ð 	øõe ð /@ñ /@ð /@Ø6­c°!©f¬fÐ6Ð6ˆ	Ý�Š�T˜9¨tˆÑ4Ô4Ð4å�a‘&”&—,’,‘.”.ˆÝð ð ð ð ð ,
ð ñ ô ñ ô ð 	ðBØ>?ðBð Bð ˆHõ ð ð ð ð ð .
ð ñ ô ñ ô ð 	ð>à:;ð>ð >ð ˆHõ
 ð ð ð ð ð .
ð ñ ô ñ ô ð 	ð
6Ø23ð6ð 6ð ˆHð+Ø'(ð+ð +ð ð ØØ ð
ð 
ˆð $-ˆ˜Ñ Ý�ŠÐ,¨oÑ>Ô>Ð>Ý�Š‰ŒˆåŒz˜&¨¸Ð?Ñ?Ô?Ð?Ð?Ð?Ð?Ð?ð ð 	˜oð 	°/×2HÒ2HÑ2JÔ2Jð 	ðØ×&Ò&Ñ(Ô(Ð(Ý—’Ð>Ñ?Ô?Ð?Ð?øÝð ð ð Ý—’Ø9¸=ÐSWð ñ ô ð ð ð ð ð ð øøøøðøøøð		ð 	ð 	øøøøðe/@øøøøðd ð 	˜oð 	°/×2HÒ2HÑ2JÔ2Jð 	ðØ×&Ò&Ñ(Ô(Ð(Ý—’Ð>Ñ?Ô?Ð?Ð?øÝð ð ð Ý—’Ø9¸=ÐSWð ñ ô ð ð ð ð ð ð øøøøðøøøð		ð 	ð 	øøøsÐ   Á X Â.CÃ
C7ÃC2Ã2C7Ã=LX ÐA;Q? Ñ>X Ñ?
RÒ	X ÒRÒDX Ö,.W×
X×%XØXØ^3ØD^.Ü*^3Ü+^6 Ý.]7Ý7
^(Þ^#Þ#^(Þ.^3Þ3^6 Þ6`6ß._?ß>`6ß?
`0à	`+à&`6à+`0à0`6Úvideo_analyzeu?  Analyze a video from a URL or local file path using a multimodal AI model. Sends the video to a video-capable model (e.g. Gemini) for understanding. Use this for video files â€” for images, use vision_analyze instead. Supports mp4, webm, mov, avi, mkv, mpeg formats. Note: large videos (>20 MB) may be slow; max ~50 MB.z5Video URL (http/https) or local file path to analyze.zpYour specific question about the video. The AI will describe what happens in the video and answer your question.)rm  r¬  c                 ó"  — |                       dd¦  «        }|                       dd¦  «        }d|› �}d }	 ddlm}m}  |¦   «         } ||ddd	¦  «        p ||dd
d	¦  «        }	|	r#t	          |	¦  «                             ¦   «         pd }n# t          $ r Y nw xY w|sPt          j        dd¦  «                             ¦   «         p(t          j        dd¦  «                             ¦   «         pd }t          ||||                      d¦  «        ¬¦  «        S )Nrm  r   r¬  z¸Fully describe and explain everything happening in this video, including visual content, motion, audio cues, text overlays, and scene transitions. Then answer the following question:

r   r   r    rc  r‰  r!   ÚAUXILIARY_VIDEO_MODELrB  rÃ  rÎ  )
rÅ   r(   r   r   rQ   r%   r)   r#   r$   rw  )
rH   r@  rm  r¬  rC  r‰  r   r   r%  rD  s
             r   Ú_handle_video_analyzer‰  Œ  sR  € Ø—’˜ bÑ)Ô)€IØ�xŠx˜
 BÑ'Ô'€Hð	Jà?Gð	Jð 	Jð ð €EðØ:Ð:Ð:Ð:Ð:Ð:Ð:Ð:Øˆ{‰}Œ}ˆØ�'˜$ ¨W°gÑ>Ô>ÐoÀ'À'È$ÐP[Ð]eÐgnÑBoÔBoˆØð 	1Ý˜‘L”L×&Ò&Ñ(Ô(Ð0¨DˆEøøÝð ð ð Øˆðøøøàð zÝ”	Ð1°2Ñ6Ô6×<Ò<Ñ>Ô>ÐyÅ"Ä)ÐLdÐfhÑBiÔBi×BoÒBoÑBqÔBqÐyÐuyˆÝ˜i¨°eÀRÇVÂVÈIÑEVÔEVÐWÑWÔWÐWs   µAB	 Â	
BÂBrc  u   ðŸŽ¬)r   N)r×   r  )NN)NNN)jÚ__doc__r  Ú
contextlibrB   r  Úconcurrent.futuresr   Úloggingr#   r�   Úpathlibr   Útypingr   r   r   r   Úurllib.parser	   r®   r
   Ú__annotations__r   r   Úhermes_constantsr   Útools.debug_helpersr   Útools.website_policyr   ÚsysÚ	getLoggerÚ__name__r¤   r  r&   r-   rñ   rò   Ú	threadingr;   r7   r=   Ú_VISION_CPU_WORKERSrE   rK   rQ   r'  rT   rX   r[   Úbytesrh   Ú	frozensetr›   rˆ   r5  rª   r)   r³   ÚdictrÖ   rù   rÿ   r  rß  rÝ  rÞ  r  r  r"  rE  rX  r‡  r£  r«  rÀ  ÚasynccontextmanagerrÂ  r4  rî  r  r2  ÚprintÚapi_availableÚexitÚactiveÚ
session_idÚtools.registryr4  rØ  ÚVISION_ANALYZE_SCHEMArE  ÚregisterrP  rq  r  rR  rT  rY  r]  rl  rr  rw  ÚVIDEO_ANALYZE_SCHEMAr‰  r6   r   r   ú<module>r§     s†  ððð ð ð: €€€Ø Ð Ð Ð Ø €€€Ø €€€Ø 1Ð 1Ð 1Ð 1Ð 1Ð 1Ø €€€Ø 	€	€	€	Ø €€€Ø Ð Ð Ð Ð Ð Ø 1Ð 1Ð 1Ð 1Ð 1Ð 1Ð 1Ð 1Ð 1Ð 1Ð 1Ð 1Ø !Ð !Ð !Ð !Ð !Ð !Ø €€€ð €�Ð Ð Ñ Ø$(Ð ˜cÐ (Ð (Ñ (ð
0ð 
0ð 
0ð 
0ð ,Ð +Ð +Ð +Ð +Ð +Ø ,Ð ,Ð ,Ð ,Ð ,Ð ,Ø 5Ð 5Ð 5Ð 5Ð 5Ð 5Ø 
€
€
€
à	ˆÔ	˜8Ñ	$Ô	$€à	ˆ�nÐ.BÐ	CÑ	CÔ	C€ð
 5ð ð ð ð ð" 5Ð4Ñ6Ô6Ð ð .Ð ð@ Ð Ð Ð ð
+˜3ð 
+ð 
+ð 
+ð 
+ð  Sð  ð  ð  ð  ðF 2Ð1Ñ3Ô3Ð ð *Ð)Ø#Ø&ðñ ô Ð ðð ð ð ˜Sð  Tð ð ð ð ð˜Sð  Tð ð ð ð ð(¨ð (°ð (ð (ð (ð (ð¨Uð °xÀ´}ð ð ð ð ð6 $- 9Ø:Ð:Ð:ñ$ô $Ð  ð
, Dð ,°Dð ,¸Tð ,ð ,ð ,ð ,ð^7Øð7Ø%(ð7à
ˆ8�DŒ>˜8 Cœ=¨(°3¬-Ð7Ô8ð7ð 7ð 7ð 7ðt¨	ð °dð ð ð ð ð> ðDð Dð Dà	ðDð ðDð ð	Dð ðDð ðDð 
ðDð Dð Dð DðN_ð _ Sð _°tð _È#ð _ÐVZð _ð _ð _ð _ðD3 Tð 3¨cð 3ð 3ð 3ð 3ð.ð ¨$ð ¸8ÀC¼=ð ÐTWð ð ð ð ð8 %Ð ð &Ð ð Ð ð 'Ð ð 	ð ¨dð ð ð ð ð¨ð ¸cð Àdð ð ð ð ð* "&ðC;ð C;ØðC;àðC;ð ˜”ðC;ð ˆ8�DŒ>˜8 Cœ=¨(°3¬-Ð7Ô8ð	C;ð C;ð C;ð C;ðL*Ø˜”ð*à˜$”ð*ð ˆc„]ð*ð *ð *ð *ðZ KOØ6JØ=AØ:>ð\Rð \R¨ð \R¸(À3¼-ð \RØ03ð\Rà-5°c¬]ð\Rð *2°$¬ð\Rð DGð\Rð \Rð \Rð \RðVE¨cð E¸#ð EÀ$ð Eð Eð Eð EðP¨Tð ð ð ð ðF !%ð7ð 7Øð7àð7ð ð7ð ð	7ð
 ˜”ð7ð 
ˆ#ˆsˆ(„^ð7ð 7ð 7ð 7ðt Ôð
ð 
ñ  Ôð
ð" "Ø!ð	Zð ZØðZàðZð �cŒ]ðZð �TŒNð	Zð
 	ðZð Zð Zð Zð@ Ø!Ø!ðDð DØðDàðDð ðDð �cŒ]ð	Dð
 �TŒNðDð 	ðDð Dð Dð DðN
 4ð ð ð ð ð6 ˆzÒÑðð 
€EÐ
'Ñ(Ô(Ð(Ø	€Eˆ(�O„O€Oð .Ð-Ñ/Ô/€Màð ,ØˆÐ7Ñ8Ô8Ð8Øˆð  Gñ  	Hô  	Hð  	HØˆŒ�‰ŒˆˆàˆÐ*Ñ+Ô+Ð+à	€EÐ
/Ñ0Ô0Ð0ð „}ð RØˆÐJ°vÔ7HÐJÐJÑKÔKÐKØˆÐcÈ6ÔK\ÐcÐcÐcÑdÔdÐdÐdàˆÐPÑQÔQÐQà	€EÐ
ÑÔÐØ	€EÐ
:Ñ;Ô;Ð;Ø	€EÐ
ÑÔÐØ	€Eˆ"�I„I€IØ	€EÐ
Ñ Ô Ð Ø	€EÐ
5Ñ6Ô6Ð6Ø	€EÐ
@ÑAÔAÐAØ	€EÐ
BÑCÔCÐCØ	€Eˆ)ÑÔÐØ	€EÐ
Ñ Ô Ð Ø	€EÐ
!Ñ"Ô"Ð"à	€EÐ
ÑÔÐØ	€EÐ
<Ñ=Ô=Ð=Ø	€EÐ
>Ñ?Ô?Ð?Ø	€EÐ
7Ñ8Ô8Ð8Ø	€EÐ
5Ñ6Ô6Ð6Ø	€EÐ
4Ñ5Ô5Ð5à	€Eˆ/ÑÔÐØ	€EÐ
$Ñ%Ô%Ð%Ø	€EÐ
,Ñ-Ô-Ð-Ø	€EÐ
HÑIÔIÐIØ	€EÐ
BÑCÔCÐCð 0Ð /Ð /Ð /Ð /Ð /Ð /Ð /ð ð	&ð ð !Ø_ðð ð
 !ð  ]ðð ð
  Ø  )Ð,ØØðCðð ð
ð 
ð0 ! *Ð-ð5ð ð(ð (Ð ðV&d t¨C°¨H¤~ð &d¸Sð &dÀSð &dð &dð &dð &dðR €Ô Ø	ØØ Ø"Ø&ØØ
ðñ ô ð ð" ØØØØØØðð Ð ð +Ð Ø)Ð ð&¨ð &°¸#´ð &ð &ð &ð &ð+ð +¨$ð +¸8ÀC¼=ð +ÐTWð +ð +ð +ð +ð$ Dð $ð $ð $ð $ð
D cð D¨dð Dð Dð Dð Dð"Àð "ÈxÐX[Ì}ð "Ðaeð "ð "ð "ð "ðJ9ð 9 Sð 9°tð 9È#ð 9ÐVZð 9ð 9ð 9ð 9ð~ Ø!ð	Tð TØðTàðTð ðTð �cŒ]ð	Tð
 	ðTð Tð Tð Tðp ð	?ð ð !ØVðð ð
 !ð  Rðð ð	
ð 	
ð ! *Ð-ðð ðð Ð ð4X  S¨# X¤ð X°cð X¸iÈ¼nð Xð Xð Xð Xð0 €Ô Ø	ØØØ!Ø&ØØ
ðñ ô ð ð ð r   