Ë
    âß²j�3  ã                  ó®  — U d Z ddlm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mZmZ ddlmZmZmZmZmZ  ej*                  e«      Zddd	d
œZded<   dZdZdddddœdddddœdddddœdœZded<   dZd Ze	d!e
eef   f   Z d?d"„Z!d@dAd#„Z"dd$dd%d&œ	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dBd'„Z#	 d@	 	 	 	 	 	 	 dCd(„Z$	 dDdd)œ	 	 	 	 	 	 	 dEd*„Z%	 	 	 	 	 	 	 	 	 	 	 	 	 	 dFd+„Z& G d,„ d-e«      Z'd.d.d/œdGd0„Z(dHd1„Z)dId2„Z*dJd3„Z+edd4œ	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dKd5„Z,edd6œ	 	 	 	 	 	 	 	 	 	 	 dLd7„Z-dMd8„Z.e G d9„ d:«      «       Z/dd;œdNd<„Z0e.d%d=œ	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dOd>„Z1y)PzóShared helpers for the bundled ``image_gen`` provider plugins. Providers are loaded by path
(``hermes_plugins.image_gen__<name>``) and resolve this via the repo root on ``sys.path``;
not a plugin itself (the scanner only looks at directories).é    )ÚannotationsN)Ú	dataclass)ÚAnyÚCallableÚDictÚIterableÚListÚOptionalÚTuple)ÚImageGenProviderÚerror_responseÚnormalize_reference_imagesÚsave_b64_imageÚsave_url_imageÚ	1536x1024Ú	1024x1024Ú	1024x1536)Ú	landscapeÚsquareÚportraitúDict[str, str]ÚOPENAI_SIZESzgpt-image-2úgpt-image-2-mediumzGPT Image 2 (Low)z~15szFast iteration, lowest costÚlow)ÚdisplayÚspeedÚ	strengthsÚqualityzGPT Image 2 (Medium)z~40su   Balanced â€” defaultÚmediumzGPT Image 2 (High)z~2minz,Highest fidelity, strongest prompt adherenceÚhigh)zgpt-image-2-lowr   zgpt-image-2-highúDict[str, Dict[str, Any]]ÚGPT_IMAGE_2_TIERSz1Prompt is required and must be a non-empty stringz8openai Python package not installed (pip install openai).c                ó<   — t         j                  | t         d   «      S )zCOpenAI ``size`` string for a semantic aspect (square when unknown).r   )r   Úget)Úaspects    ú>/home/ubuntu/.hermes/hermes-agent/plugins/image_gen/_common.pyÚsize_forr'   2   s   € ä×Ñ˜F¤L°Ñ$:Ó;Ð;ó    c                óF  — | €dnd| › �}	 ddl m}  |«       }t        |t        «      r|j	                  d«      nd}| �#t        |t        «      r|j	                  | «      nd}t        |t        «      r|S i S # t
        $ r#}t        j                  d||«       i cY d}~S d}~ww xY w)zTRead ``image_gen`` (or ``image_gen.<sub>``) from config.yaml; ``{}`` on any failure.NÚ	image_genz
image_gen.r   )Úload_configzCould not load %s config: %s)Úhermes_cli.configr+   Ú
isinstanceÚdictr$   Ú	ExceptionÚloggerÚdebug)ÚsubÚlabelr+   ÚcfgÚsectionÚexcs         r&   Úload_image_gen_configr7   7   s”   € à˜;‰K¨j¸¸Ð,>€Eð
Ý1á‹mˆÜ*4°S¼$Ô*?�#—'‘'˜+Ô&ÀTˆØˆ?Ü*4°W¼dÔ*C�g—k‘k #Ô&ÈˆGÜ$ W¬dÔ3ˆwÐ;¸Ð;øÜò Ü�‰Ð3°U¸CÔ@Ø�	ûðús$   ‹A&A4 Á2A4 Á4	B Á=BÂB ÂB TF)ÚexplicitÚinclude_top_levelÚconfigÚpassthroughc               óN  — t        |t        «      r5|j                  «       | v r#|j                  «       | |j                  «          fS t        j                  j                  |«      }|r|| v r|| |   fS |€
t        «       n|}	|	j                  |«      }
t        |
t        «      r|
j                  d«      ndg}|r |j                  |	j                  d«      «       |D ]   }t        |t        «      sŒ|| v sŒ|| |   fc S  |r"t        d„ ||d   fD «       d«      }|r|||ddœfS || |   fS )uB  ``(model_id, meta)`` from a fixed catalog; first *known* id wins (unknown ids fall through):
    explicit â†’ ``env_var`` â†’ ``image_gen.<config_key>.model`` â†’ ``image_gen.model`` â†’ ``default``.

    ``passthrough``: an unknown id from ``env_var`` or the provider-scoped ``image_gen.<config_key>.model``
    is sent verbatim as the API model with no ``quality`` (OpenAI-compatible gateways serve their own
    image model names and reject unknown enum values, #97928). The shared top-level ``image_gen.model``
    never passes through â€” it may hold another provider's id.NÚmodelc              3  ó€   K  — | ]6  }t        |t        «      sŒ|j                  «       sŒ%|j                  «       –— Œ8 y ­w©N)r-   ÚstrÚstrip)Ú.0Úcs     r&   ú	<genexpr>z'resolve_static_model.<locals>.<genexpr>a   s-   è ø€ Òl QÌ:ÐVWÔY\ÕK]Ðbc×biÑbiÕbk�q—w‘w—yÑlùs   ‚>˜>©>r   Ú )r   Ú	api_modelr   )
r-   r@   rA   ÚosÚenvironr$   r7   r.   ÚappendÚnext)ÚmodelsÚdefaultÚenv_varÚ
config_keyr8   r9   r:   r;   Úenv_overrider4   ÚscopedÚ
candidatesÚ	candidateÚcustoms                 r&   Úresolve_static_modelrT   G   s.  € ô �(œCÔ  X§^¡^Ó%5¸Ñ%?Ø�~‰~Ó ¨¯©Ó(8Ñ!9Ð9Ð9Ü—:‘:—>‘> 'Ó*€LÙ˜¨Ñ.Ø˜V LÑ1Ð1Ð1Ø%+ ^Ô
Ô
!¸€CØ�W‰W�ZÓ €FÜ)3°F¼DÔ)A�&—*‘*˜WÔ%ÀtÐL€JÙØ×Ñ˜#Ÿ'™' 'Ó*Ô+Øò 0ˆ	Ü�i¤Õ%¨)°vÒ*=Ø˜f YÑ/Ð/Ò/ð0ñ ÜÑl¨<¸ÀA¹Ð*GÔlÐnpÓqˆÙØ v¸FÈtÑTÐTÐTØ�F˜7‘OÐ#Ð#r(   c                óÒ   — g }t        | t        «      r/| j                  «       r|j                  | j                  «       «       |j	                  t        |«      xs g «       |�|d| S |S )zNPrimary ``image_url`` first, then normalized references, clamped to ``limit``.N)r-   r@   rA   rI   Úextendr   )Ú	image_urlÚreference_image_urlsÚlimitÚsourcess       r&   Úcollect_source_imagesr[   g   s]   € ð €GÜ�)œSÔ! i§o¡oÔ&7Ø�‰�y—‘Ó(Ô)Ø‡N�NÔ-Ð.BÓCÒIÀrÔJØ#Ð/ˆ7�6�Eˆ?Ð<°WÐ<r(   ©Úpricec               ó¸   — g }| j                  «       D ]D  \  }}d|i}|D ]  }|j                  ||dk(  r|nd«      ||<   Œ  |�||d<   |j                  |«       ŒF |S )u`   Picker rows: ``id`` + ``fields`` (missing ``display`` â†’ id, else ``""``); ``price`` overrides.Úidr   rE   r]   )Úitemsr$   rI   )rK   Úfieldsr]   ÚrowsÚmodel_idÚmetaÚrowÚfields           r&   Úcatalog_rowsrg   r   s|   € ð €DØ Ÿ,™,›.ò ‰ˆ�$Ø# XÐ.ˆØò 	QˆEØŸ™ %°U¸iÒ5G©ÈRÓPˆC�ŠJð	QàÐØ ˆC�‰LØ�‰�CÕðð €Kr(   c               ó   — | |||||dœgdœS )zH``get_setup_schema()`` dict for a provider authenticated by one env var.)ÚkeyÚpromptÚurl)ÚnameÚbadgeÚtagÚenv_vars© )rl   rm   rn   ri   rj   rk   s         r&   Úapi_key_setup_schemarq   ƒ   s#   € ð
 ˜u¨SÀsÐV\ÐehÑ?iÐ>jñð r(   c                  ó¨   — e Zd ZU dZded<   ded<   i Zded<   dZded	<   dZded
<   dZded<   i Z	ded<   e
dd„«       Ze
dd„«       Zdd„Zdd„Zdd„Zy)ÚStaticImageGenProvidera  Identity + picker surface from class attributes: ``provider_id``/``label``; fixed catalog via
    ``models`` (+ ``default_model_id``, ``price``, ``catalog_fields``); single-env-var auth via
    ``setup`` (kwargs for :func:`api_key_setup_schema`). Dynamic providers override methods.r@   Úprovider_idr3   r!   rK   NúOptional[str]Údefault_model_idr]   ©r   r   r   r]   zTuple[str, ...]Úcatalog_fieldsúDict[str, Any]Úsetupc                ó   — | j                   S r?   )rt   ©Úselfs    r&   rl   zStaticImageGenProvider.name™   s   € à×ÑÐr(   c                ó   — | j                   S r?   )r3   r|   s    r&   Údisplay_namez#StaticImageGenProvider.display_name�   s   € à�z‰zÐr(   c                óZ   — t        | j                  | j                  | j                  ¬«      S )Nr\   )rg   rK   rx   r]   r|   s    r&   Úlist_modelsz"StaticImageGenProvider.list_models¡   s   € Ü˜DŸK™K¨×)<Ñ)<ÀDÇJÁJÔOÐOr(   c                ó   — | j                   S r?   )rv   r|   s    r&   Údefault_modelz$StaticImageGenProvider.default_model¤   s   € Ø×$Ñ$Ð$r(   c                ó,   — t        di | j                  ¤ŽS )Nrp   )rq   rz   r|   s    r&   Úget_setup_schemaz'StaticImageGenProvider.get_setup_schema§   s   € Ü#Ñ1 d§j¡jÑ1Ð1r(   )Úreturnr@   )r†   úList[Dict[str, Any]])r†   ru   )r†   ry   )Ú__name__Ú
__module__Ú__qualname__Ú__doc__Ú__annotations__rK   rv   r]   rx   rz   Úpropertyrl   r   r�   rƒ   r…   rp   r(   r&   rs   rs   Œ   s   … ñ`ð ÓØƒJØ(*€FÐ%Ó*Ø&*Ð�mÓ*Ø€Eˆ=ÓØ&P€N�OÓPØ€Eˆ>Óàò ó ð ð òó ðóPó%ô2r(   rs   rE   ©r=   rj   c               ó    ‡ ‡‡‡— dˆˆˆˆ fd„}|S )zPReturn ``fail(error, error_type, **override)`` pre-bound to this call's context.c                ó`   •— t        ‰‰‰‰¬«      }|j                  |«       t        d| |dœ|¤ŽS )N)Úproviderr=   rj   Úaspect_ratio)ÚerrorÚ
error_typerp   )r.   Úupdater   )r“   r”   ÚoverrideÚkwargsr%   r=   rj   r‘   s       €€€€r&   Úfailzerror_factory.<locals>.fail®   s5   ø€ Ü˜x¨u¸VÐRXÔYˆØ�‰�hÔÜÐK E°jÑKÀFÑKÐKr(   )r“   r@   r”   r@   r–   r   r†   ry   rp   )r‘   r%   r=   rj   r˜   s   ```` r&   Úerror_factoryr™   «   s   û€ ÷Lð Lð
 €Kr(   c                ó0   —  t        | |«      t        d«      S )NÚinvalid_argument)r™   ÚPROMPT_REQUIRED)r‘   r%   s     r&   Úprompt_required_errorr�   ¶   s   € Ø*Œ=˜ 6Ó*¬?Ð<NÓOÐOr(   c                 ó$   — t        dd«      d   d uS )NrE   r   )Úimport_openairp   r(   r&   Úopenai_importabler    º   s   € Ü˜˜RÓ  Ñ#¨4Ð/Ð/r(   c                ód   — 	 ddl }|dfS # t        $ r d t        | |«      t        d«      fcY S w xY w)zIReturn ``(openai_module, None)`` or ``(None, missing_dependency error)``.r   NÚmissing_dependency)ÚopenaiÚImportErrorr™   ÚOPENAI_MISSING)r‘   r%   r£   s      r&   rŸ   rŸ   ¾   sG   € ð[Ûð �4ˆ<Ðøô ò [ØÐ4”] 8¨VÓ4´^ÐEYÓZÐZÒZð[ús   ‚
 Š"/®/)ÚlogÚon_url_failc               óâ   — t        ||||¬«      }
| r	 t        t        | |¬«      «      dfS |rt	        |||||	¬«      dfS d |
|› d�d«      fS # t        $ r}d |
d|› �d«      fcY d}~S d}~ww xY w)	uÃ   ``(image_ref, None)`` or ``(None, error)`` for a ``(b64_json, url)`` pair. Base64 is always
    cached (write failure â†’ ``io_error``); a URL is cached best-effort, falling back to the bare URL.rŽ   ©ÚprefixNzCould not save image to cache: Úio_error)rª   r3   r¦   Úon_failz, response contained neither b64_json nor URLÚempty_response)r™   r@   r   r/   Úcache_url_best_effort)Úb64rk   rª   r3   r‘   r=   rj   r%   r¦   r§   r˜   r6   s               r&   Úmaterialize_imager°   Ç   sœ   € ô ˜ 6°¸vÔF€DÙ
ð	SÜ”~ c°&Ô9Ó:¸DÐ@Ð@ñ Ü$ S°¸uÈ#ÐWbÔcÐeiÐiÐiØ‘˜˜ÐKÐLÐN^Ó_Ð_Ð_øô	 ò 	SØ™Ð ?À¸uÐEÀzÓRÐRÕRûð	Sús   “A Á	A.ÁA)Á#A.Á)A.)r¦   r¬   c               óž   — 	 t        t        | |¬«      «      S # t        $ r+}|�	 ||«       n|j                  d|| |«       | cY d}~S d}~ww xY w)zUCache ``url`` locally; on failure warn (or call ``on_fail``) and return the bare URL.r©   NzC%s image URL %s could not be cached (%s); falling back to bare URL.)r@   r   r/   Úwarning)rk   rª   r3   r¦   r¬   r6   s         r&   r®   r®   Ù   sQ   € ð
Ü”> #¨fÔ5Ó6Ð6øÜò ØÐÙ�C�Là�K‰KÐ]Ð_dÐfiÐknÔoØ�
ûðús   ‚ ˜	A¡ AÁAÁAc                óØ   — 	 | j                  «       j                  di «      j                  d| j                  dd «      S # t        $ r! | �| j                  dd cY S t	        |«      cY S w xY w)zJ``error.message`` from an HTTP error body, else the first 300 chars of it.r“   ÚmessageNi,  )Újsonr$   Útextr/   r@   )Úresponser6   s     r&   Úrequests_error_messager¸   è   si   € ðIØ�}‰}‹×"Ñ" 7¨BÓ/×3Ñ3°I¸x¿}¹}ÈTÈcÐ?RÓSÐSøÜò IØ&.Ð&:ˆx�}‰}˜T˜cÐ"ÒHÄÀCÃÒHðIús   ‚<? ¿A)ÁA)Á(A)c                  óZ   — e Zd ZU dZded<   ded<   ded<   dZded<   d	Zded
<   dZded<   y)ÚHttpFailureuë   One failed ``post_json`` attempt as ``(error, error_type)``. ``kind`` âˆˆ http / timeout /
    connection / request / invalid_json; ``message`` = HTTP error text or decode error;
    ``status`` / ``response`` are set for ``http`` only.r@   Úkindr“   r”   r   ÚintÚstatusrE   r´   Nr   r·   )rˆ   r‰   rŠ   r‹   rŒ   r½   r´   r·   rp   r(   r&   rº   rº   ð   s5   … ñ<ð ƒIØƒJØƒOØ€FˆCƒOØ€GˆSÓØ€HˆcÔr(   rº   )Úbase_urlc               óJ   — | syddl m} ddlm}  | ||| ¬«      d||¬«       y)a?  Record a token-billed image call against the ambient session as task ``image_generation``.

    Token-metered image models (OpenRouter chat-image and Image API models, OpenAI ``gpt-image``)
    bill exactly like a chat completion, so they get a ``session_model_usage`` row through the
    same chokepoint as auxiliary calls. ``usage`` is the response's usage block (dict or SDK
    object). Per-image backends (FAL, xAI, Krea, ...) return no token usage and never call this;
    a body without tokens is a no-op inside ``record_aux_usage``, as is running outside a turn.
    Nr   )ÚSimpleNamespace)Úrecord_aux_usage)r=   ÚusageÚimage_generation)r‘   r¾   )ÚtypesrÀ   Úagent.aux_accountingrÁ   )rÂ   r=   r‘   r¾   rÀ   rÁ   s         r&   Úrecord_token_usagerÆ   þ   s,   € ñ ØÝ%å5áÙ˜e¨5Ô1Ð3EÐPXÐckömr(   )Úerror_messageÚcatch_request_exceptionc          
     óÚ  — ddl }t        |t        «      r|d   n|}	 |j                  | |||¬«      }	|	j	                  «        	 |	j                  «       dfS # |j
                  $ rJ}
|
j                  }|�|j                  nd} |||
«      }dt        d|› d|› d|› �d|||¬	«      fcY d}
~
S d}
~
w|j                  $ r! dt        d
|› dt        |«      › d�d
«      fcY S |j                  $ r}
dt        d|› d|
› �d«      fcY d}
~
S d}
~
w|j                  $ r!}
|s‚ dt        d|› d|
› �d«      fcY d}
~
S d}
~
ww xY w# t        $ r)}
dt        d|› d|
› �dt        |
«      ¬«      fcY d}
~
S d}
~
ww xY w)uê   POST ``payload`` â†’ ``(json_body, None)`` or ``(None, failure)``. ``timeout`` goes to ``requests``
    verbatim (message reports the read component); ``error_message(response, exc)`` extracts the
    backend-specific HTTP error text.r   Né   )Úheadersrµ   ÚtimeoutÚhttpz image generation failed (z): Ú	api_error)r½   r´   r·   rÌ   z image generation timed out (zs)Ú
connectionz connection error: Úconnection_errorÚrequestz request failed: Úinvalid_jsonz returned invalid JSON: Úinvalid_response)r´   )Úrequestsr-   ÚtupleÚpostÚraise_for_statusÚ	HTTPErrorr·   Ústatus_coderº   ÚTimeoutr¼   ÚConnectionErrorÚRequestExceptionrµ   r/   r@   )rk   rË   ÚpayloadrÌ   r3   rÇ   rÈ   rÔ   Úread_timeoutr·   r6   Úrespr½   r´   s                 r&   Ú	post_jsonrà     s»  € ó ä!+¨G´UÔ!;�7˜1’:À€Lð[Ø—=‘= ¨g¸GÈW�=ÓUˆØ×!Ñ!Ô#ð"
Ø�}‰}‹ Ð$Ð$øð# ×Ñò ;Ø�|‰|ˆØ%)Ð%5�×!Ò!¸1ˆÙ  cÓ*ˆØ”[Ø�u�gÐ7¸°x¸sÀ7À)ÐLÈkØ 7°Tô;ð ;õ 	;ûð ×Ñò `Ø”[Ø˜%˜Ð =¼cÀ,Ó>OÐ=PÐPRÐSÐU^ó`ð `ò 	`à×#Ñ#ò gØ”[ °%°Ð8KÈCÈ5Ð/QÐSeÓfÐfÕfûØ×$Ñ$ò [Ù&ØØ”[ ¨u¨gÐ5FÀsÀeÐ,LÈkÓZÐZÕZûð[ûô ò 
Ø”[Ø˜u˜gÐ%=¸c¸UÐCÐEWÔadÐehÓaiô
ð 
õ 	
ûð
úse   �%A ÁD8 ÁD5Á$?B)Â#D5Â)0D5ÃD5Ã)DÃ<D5ÄD5ÄD0Ä*D5Ä0D5Ä8	E*ÅE%ÅE*Å%E*)r%   r@   r†   r@   r?   )r2   ru   r†   ry   )rK   r!   rL   r@   rM   r@   rN   r@   r8   ru   r9   Úboolr:   zOptional[Dict[str, Any]]r;   rá   r†   zTuple[str, Dict[str, Any]])rW   ru   rX   zOptional[List[str]]rY   zOptional[int]r†   z	List[str])rw   )rK   r!   ra   zIterable[str]r]   ru   r†   r‡   )rl   r@   rm   r@   rn   r@   ri   r@   rj   r@   rk   r@   r†   ry   )
r‘   r@   r%   r@   r=   r@   rj   r@   r†   ÚErrorFn)r‘   r@   r%   r@   r†   ry   )r†   rá   )r‘   r@   r%   r@   r†   z$Tuple[Any, Optional[Dict[str, Any]]])r¯   ru   rk   ru   rª   r@   r3   r@   r‘   r@   r=   r@   rj   r@   r%   r@   r¦   úlogging.Loggerr§   ú%Optional[Callable[[Exception], None]]r†   z.Tuple[Optional[str], Optional[Dict[str, Any]]])rk   r@   rª   r@   r3   r@   r¦   rã   r¬   rä   r†   r@   )r·   r   r6   r/   r†   r@   )
rÂ   r   r=   r@   r‘   r@   r¾   ru   r†   ÚNone)rk   r@   rË   r   rÝ   ry   rÌ   r   r3   r@   rÇ   zCallable[[Any, Exception], str]rÈ   rá   r†   z+Tuple[Optional[Any], Optional[HttpFailure]])2r‹   Ú
__future__r   ÚloggingrG   Údataclassesr   Útypingr   r   r   r   r	   r
   r   Úagent.image_gen_providerr   r   r   r   r   Ú	getLoggerrˆ   r0   r   rŒ   ÚGPT_IMAGE_2_API_MODELÚGPT_IMAGE_2_DEFAULTr"   rœ   r¥   r@   râ   r'   r7   rT   r[   rg   rq   rs   r™   r�   r    rŸ   r°   r®   r¸   rº   rÆ   rà   rp   r(   r&   ú<module>rî      s<  ðò@õ #ã Û 	Ý !ß G× GÑ G÷bõ bð 
ˆ×	Ñ	˜8Ó	$€ð .9ÀKÐ]hÑi€ˆnÓ ið &Ð Ø*Ð ð 'ØØ2Øñ	ð *ØØ+Øñ	ð (ØØCØñ	ñ0Ð Ð,ó ð* F€ØK€à
�3˜˜S #˜X™Ð&Ñ
'€ó<ô
ð$ #¸dØ'+Àñ$Ø%ð$Ø03ð$ØADð$ØRUð$àð$à7;ð$ð %ð$ð ;?ð$ð  ó	$ðB aeð=Øð=Ø4Gð=ØP]ð=àó=ð Gðð  ñØ%ðàðð ðð ó	ð"Ø
ðØðØ #ðØ-0ðØ:=ðØDGðàóô2Ð-ô 2ð> ?AÐPRõ óPó0óð 5;Ø9=ñ`Ø	ð`Ø*ð`Ø7:ð`ØCFð`ØRUð`Ø^að`àð`àð`à#1ð`ð 7ð`ð 4ó	`ð& AGØ59ñØ	ðØðØ%(ðØ/=ðà2ðð 	óóIð ÷
ð 
ó ð
ð \`õ mð* 6LØ$)ñ#
Ø	ð#
Ø(ð#
Ø3Að#
ØLOð#
ØX[ð#
à2ð#
ð "ð#
ð 1ô	#
r(   