
    Fij)                    &   U d Z ddlmZ ddlZddlZddlZddlZddlZddlm	Z	 ddl
mZmZmZmZmZ  ej        e          ZdZded<   d	Z G d
 dej                  Zd6dZd7dZdddd8dZddddddZdddd d9d&Zdd'd:d/Zd0d1d1d1ed2d;d5ZdS )<au  
Image Generation Provider ABC
=============================

Defines the pluggable-backend interface for image generation. Providers register
instances via ``PluginContext.register_image_gen_provider()``; the active one
(selected via ``image_gen.provider`` in ``config.yaml``) services every
``image_generate`` tool call.

Providers live in ``<repo>/plugins/image_gen/<name>/`` (built-in, auto-loaded
as ``kind: backend``) or ``~/.hermes/plugins/image_gen/<name>/`` (user, opt-in
via ``plugins.enabled``).

Response shape
--------------
All providers return a dict that :func:`success_response` / :func:`error_response`
produce. The tool wrapper JSON-serializes it. Keys:

    success        bool
    image          str | None       URL or absolute file path
    model          str              provider-specific model identifier
    prompt         str              echoed prompt
    aspect_ratio   str              "landscape" | "square" | "portrait"
    provider       str              provider name (for diagnostics)
    error          str              only when success=False
    error_type     str              only when success=False
    )annotationsN)Path)AnyDictListOptionalTuple)	landscapesquareportraitzTuple[str, ...]VALID_ASPECT_RATIOSr
   c                      e Zd ZdZeej        dd                        Zedd            ZddZ	dd	Z
ddZddZej        efdd            ZdS )ImageGenProvideru   Abstract base class for an image generation backend.

    Subclasses must implement :meth:`generate`. Everything else has sane
    defaults — override only what your provider needs.
    returnstrc                    dS )zStable short identifier used in ``image_gen.provider`` config.

        Lowercase, no spaces. Examples: ``fal``, ``openai``, ``replicate``.
        N selfs    =/home/ubuntu/.hermes/hermes-agent/agent/image_gen_provider.pynamezImageGenProvider.name:             c                4    | j                                         S )zMHuman-readable label shown in ``hermes tools``. Defaults to ``name.title()``.)r   titler   s    r   display_namezImageGenProvider.display_nameB   s     y   r   boolc                    dS )zReturn True when this provider can service calls.

        Typically checks for a required API key. Default: True
        (providers with no external dependencies are always available).
        Tr   r   s    r   is_availablezImageGenProvider.is_availableG   s	     tr   List[Dict[str, Any]]c                    g S )a  Return catalog entries for ``hermes tools`` model picker.

        Each entry::

            {
                "id": "gpt-image-1.5",               # required
                "display": "GPT Image 1.5",          # optional; defaults to id
                "speed": "~10s",                     # optional
                "strengths": "...",                  # optional
                "price": "$...",                     # optional
            }

        Default: empty list (provider has no user-selectable models).
        r   r   s    r   list_modelszImageGenProvider.list_modelsO   s	     	r   Dict[str, Any]c                    | j         ddg dS )a5  Return provider metadata for the ``hermes tools`` picker.

        Used by ``tools_config.py`` to inject this provider as a row in
        the Image Generation provider list. Shape::

            {
                "name": "OpenAI",                     # picker label
                "badge": "paid",                      # optional short tag
                "tag": "One-line description...",     # optional subtitle
                "env_vars": [                         # keys to prompt for
                    {"key": "OPENAI_API_KEY",
                     "prompt": "OpenAI API key",
                     "url": "https://platform.openai.com/api-keys"},
                ],
            }

        Default: minimal entry derived from ``display_name``. Override to
        expose API key prompts and custom badges.
         )r   badgetagenv_vars)r   r   s    r   get_setup_schemaz!ImageGenProvider.get_setup_schema`   s"    * %	
 
 	
r   Optional[str]c                h    |                                  }|r|d                             d          S dS )z7Return the default model id, or None if not applicable.r   idN)r"   get)r   modelss     r   default_modelzImageGenProvider.default_model{   s6    !!## 	'!9==&&&tr   promptaspect_ratiokwargsr   c                    dS )u'  Generate an image.

        Implementations should return the dict from :func:`success_response`
        or :func:`error_response`. ``kwargs`` may contain forward-compat
        parameters future versions of the schema will expose — implementations
        should ignore unknown keys.
        Nr   )r   r0   r1   r2   s       r   generatezImageGenProvider.generate   r   r   N)r   r   )r   r   )r   r    )r   r#   )r   r*   )r0   r   r1   r   r2   r   r   r#   )__name__
__module____qualname____doc__propertyabcabstractmethodr   r   r   r"   r)   r/   DEFAULT_ASPECT_RATIOr4   r   r   r   r   r   3   s              X ! ! ! X!      "
 
 
 
6    	 1      r   r   valuer*   r   r   c                    t          | t                    st          S |                                                                 }|t
          v r|S t          S )zClamp an aspect_ratio value to the valid set, defaulting to landscape.

    Invalid values are coerced rather than rejected so the tool surface is
    forgiving of agent mistakes.
    )
isinstancer   r<   striplowerr   )r=   vs     r   resolve_aspect_ratiorC      sL     eS!! $##Ar   r   c                 `    ddl m}   |             dz  dz  }|                    dd           |S )zBReturn ``$HERMES_HOME/cache/images/``, creating parents as needed.r   )get_hermes_homecacheimagesT)parentsexist_ok)hermes_constantsrE   mkdir)rE   paths     r   _images_cache_dirrM      sF    000000?w&1DJJtdJ+++Kr   imagepng)prefix	extensionb64_datarP   rQ   c               2   t          j        |           }t          j                                                            d          }t          j                    j        dd         }t                      | d| d| d| z  }|	                    |           |S )zDecode base64 image data and write it under ``$HERMES_HOME/cache/images/``.

    Returns the absolute :class:`Path` to the saved file.

    Filename format: ``<prefix>_<YYYYMMDD_HHMMSS>_<short-uuid>.<ext>``.
    %Y%m%d_%H%M%SN   _.)
base64	b64decodedatetimenowstrftimeuuiduuid4hexrM   write_bytes)rR   rP   rQ   rawtsshortrL   s          r   save_b64_imagerd      s     
8
$
$C					 	 	)	)/	:	:BJLLRaR EF!E!ER!E!E%!E!E)!E!EEDSKr   jpgwebpgif)z	image/pngz
image/jpegz	image/jpgz
image/webpz	image/gifg      N@i  )rP   timeout	max_bytesurlrh   floatri   intc          	        ddl }|                    | |d          }|                                 |j                            d          pd                    dd          d                                                                         }t                              |          }|W|                     d	d          d                                         }d
D ]&}	|                    d|	           r|	dk    rdn|	} n'|d}t          j	        
                                                    d          }
t          j                    j        dd         }t                      | d|
 d| d| z  }d}|                    d          5 }|                    d          D ]}|s|t%          |          z  }||k    rS|                                 	 |                                 n# t*          $ r Y nw xY wt-          d|  d|dz   d          |                    |           	 ddd           n# 1 swxY w Y   |dk    r9	 |                                 n# t*          $ r Y nw xY wt-          d|  d          |S )u  Download an image URL and write it under ``$HERMES_HOME/cache/images/``.

    Used by providers (xAI, fallback OpenAI) whose API returns an *ephemeral*
    URL instead of inline base64 — those URLs frequently expire before a
    downstream consumer (Telegram ``send_photo``, browser fetch) can resolve
    them, so we materialise the bytes locally at tool-completion time.
    Mirrors :func:`save_b64_image`'s shape so providers can swap in one line.

    Returns the absolute :class:`Path` to the saved file.  Raises on any
    network / HTTP / oversize / non-image-content-type error so callers can
    fall back to returning the bare URL with a clear error message.
    r   NT)rh   streamzContent-Typer%   ;   ?)rO   re   jpegrf   rg   rW   rr   re   rO   rT   rU   rV   wbi   )
chunk_sizez	Image at z	 exceeds i   zMB cap; refusing to cache.z% returned 0 bytes; refusing to cache.)requestsr-   raise_for_statusheaderssplitr@   rA   _URL_IMAGE_CONTENT_TYPESendswithrZ   r[   r\   r]   r^   r_   rM   openiter_contentlencloseunlinkOSError
ValueErrorwrite)rj   rP   rh   ri   ru   responsecontent_typerQ   url_pathextrb   rc   rL   bytes_writtenfhchunks                   r   save_url_imager      s
   & OOO||C|>>H
 $((88>BEEc1MMaPVVXX^^``L(,,\::I99S!$$Q'--//8 	 	C  S++ %(F]]EE	 						 	 	)	)/	:	:BJLLRaR EF!E!ER!E!E%!E!E)!E!EEDM	4 B**i*@@ 	 	E SZZ'My((


KKMMMM   D dddiK.Hddd   HHUOOOO	                	KKMMMM 	 	 	D	OSOOOPPPKsI   AH0G$#H0$
G1.H00G112H00H47H4I 
I$#I$)extramodelr0   r1   providerr   Optional[Dict[str, Any]]r#   c                |    d| ||||d}|r0|                                 D ]\  }}|                    ||           |S )zBuild a uniform success response dict.

    ``image`` may be an HTTP URL or an absolute filesystem path (for b64
    providers like OpenAI). Callers that need to pass through additional
    backend-specific fields can supply ``extra``.
    T)successrN   r   r0   r1   r   )items
setdefault)	rN   r   r0   r1   r   r   payloadkrB   s	            r   success_responser     se      $ G  %KKMM 	% 	%DAqq!$$$$Nr   provider_errorr%   )
error_typer   r   r0   r1   errorr   c           	         dd| |||||dS )z$Build a uniform error response dict.FN)r   rN   r   r   r   r0   r1   r   r   )r   r   r   r   r0   r1   s         r   error_responser   1  s+      $	 	 	r   )r=   r*   r   r   )r   r   )rR   r   rP   r   rQ   r   r   r   )
rj   r   rP   r   rh   rk   ri   rl   r   r   )rN   r   r   r   r0   r   r1   r   r   r   r   r   r   r#   )r   r   r   r   r   r   r   r   r0   r   r1   r   r   r#   )r8   
__future__r   r:   rX   rZ   loggingr]   pathlibr   typingr   r   r   r   r	   	getLoggerr5   loggerr   __annotations__r<   ABCr   rC   rM   rd   ry   r   r   r   r   r   r   <module>r      s    8 # " " " " " 



           3 3 3 3 3 3 3 3 3 3 3 3 3 3		8	$	$ (K  J J J J" \ \ \ \ \sw \ \ \H           	     2    %B B B B B BX '+     @ ',       r   