§
    ‰eîi“  ã                   ó^  — U d Z ddl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m	Z	m
Z
mZ ddlmZ ddlZddlmZ ddlmZ ddlmZmZmZ  ej        e¦  «        Zdd	d
ddddddœddddœh d£ddœ	dddddddddœdddddddd œh d!£ddœ	d"d#d$d%dddddœd&ddddd'œh d(£ddœ	d)d*d+d,d-d.d/d0dœdddd1d2œh d3£ddœ	d4d5d6d7d8d9d:d;dœd<ddd=œh d>£ddœ	d?d@dAdBddCddDdœd<ddd=œh dE£ddœ	dFdGdHdIdddddœdJddKdLœh dM£ddœ	dNd*dOdPdddddœdQdih dR£ddœ	dSdTdUdVdddddœdWdXdddYdZœh d[£ddœ	d\œ	Ze	ee	eef         f         ed]<   d^Zd_ZdZd`Z daZ!dZ"dbZ#dcZ$ddZ%deZ&dZ'dfZ( edgdh¬i¦  «        Z)da*da+ ej,        ¦   «         Z-dj„ Z.dkedlefdm„Z/ G dn„ do¦  «        Z0dp„ Z1dqedre	eef         fds„Z2dte3dle
e4         fdu„Z5dle6fdv„Z7eddfdwedxed-edye
e4         dze
e	eef                  dle	eef         fd{„Z8d|ed}edle
e	eef                  fd~„Z9edddddfdxed-ede
e4         d€e
e:         d�e
e4         d‚e
e         dye
e4         dlefdƒ„Z;dle<fd„„Z=dle<fd…„Z>ed†k    �r¡ e?d‡¦  «          e?dˆ¦  «          e=¦   «         s, e?d‰¦  «          e?dŠ¦  «          e?d‹¦  «          e@d¦  «        ‚ e?dŒ¦  «         	 ddlZ e?d�¦  «         n# eA$ r  e?dŽ¦  «          e@d¦  «        ‚w xY w e7¦   «         \  ZBZC e?d�eC D                    d�eB¦  «        › d‘eB› d’�¦  «          e?d“eC D                    d”d•¦  «        › d–eC D                    d—d•¦  «        › �¦  «          e?d˜eC D                    d™¦  «        rdšnd›› �¦  «          e?dœ¦  «         e E                    ¦   «         D ]O\  ZFZGeFeBk    rd�ndžZH e?dŸeFd ›dŸeG D                    d”d•¦  «        d¡›dŸeG D                    d—d•¦  «        › eH› �¦  «         ŒPe)jI        r e?d¢e)jJ        › �¦  «         dd£lKmLZLmMZM d¤d¥d¦d§d¨d©œd§ eNe¦  «        dªed«œd¬œdxgd­œd®œZOd¯„ ZPdxed-efd°„ZQd±„ ZR eLjS        d¤d²eOeRe>g dd³¬´¦  «         dS )µu¥  
Image Generation Tools Module

Provides image generation via FAL.ai. Multiple FAL models are supported and
selectable via ``hermes tools`` â†’ Image Generation; the active model is
persisted to ``image_gen.model`` in ``config.yaml``.

Architecture:
- ``FAL_MODELS`` is a catalog of supported models with per-model metadata
  (size-style family, defaults, ``supports`` whitelist, upscaler flag).
- ``_build_fal_payload()`` translates the agent's unified inputs (prompt +
  aspect_ratio) into the model-specific payload and filters to the
  ``supports`` whitelist so models never receive rejected keys.
- Upscaling via FAL's Clarity Upscaler is gated per-model via the ``upscale``
  flag â€” on for FLUX 2 Pro (backward-compat), off for all faster/newer models
  where upscaling would either hurt latency or add marginal quality.

Pricing shown in UI strings is as-of the initial commit; we accept drift and
update when it's noticed.
é    N)ÚAnyÚDictÚOptionalÚUnion)Ú	urlencode)ÚDebugSession)Úresolve_managed_tool_gateway)Úfal_key_is_configuredÚmanaged_nous_tools_enabledÚprefers_gatewayzFLUX 2 Klein 9Bz<1szFast, crisp textz	$0.006/MPÚimage_size_presetÚlandscape_16_9Ú	square_hdÚportrait_16_9)Ú	landscapeÚsquareÚportraité   ÚpngF)Únum_inference_stepsÚoutput_formatÚenable_safety_checker>   ÚseedÚpromptÚ
image_sizer   r   r   )	ÚdisplayÚspeedÚ	strengthsÚpriceÚ
size_styleÚsizesÚdefaultsÚsupportsÚupscalez
FLUX 2 Proz~6szStudio photorealismz$0.03/MPé2   g      @é   Ú5T)r   Úguidance_scaleÚ
num_imagesr   r   Úsafety_toleranceÚ	sync_mode>
   r   r   r+   r   r)   r   r(   r*   r   r   zZ-Image Turboz~2szBilingual EN/CN, 6Bz	$0.005/MPé   )r   r)   r   r   Úenable_prompt_expansion>   r   r   r   r)   r   r   r   r-   z$Nano Banana Pro (Gemini 3 Pro Image)z~8sz-Gemini 3 Pro, reasoning depth, text renderingz$0.15/image (1K)Úaspect_ratioz16:9z1:1z9:16Ú1K)r)   r   r*   Ú
resolution>
   r   r   r+   r)   r0   r.   r   r*   Úenable_web_searchÚlimit_generationszGPT Image 1.5z~15szPrompt adherencez$0.034/imageÚgpt_literalÚ	1536x1024Ú	1024x1024Ú	1024x1536Úmedium)Úqualityr)   r   >   r   r8   r+   Ú
backgroundr   r)   r   zGPT Image 2z~20sz3SOTA text rendering + CJK, world-aware photorealismu   $0.04â€“0.06/imageÚlandscape_4_3Úportrait_4_3>   r   r8   r+   r   r)   r   zIdeogram V3z~5szBest typographyz$0.03-0.09/imageÚBALANCEDÚAUTO)Úrendering_speedÚexpand_promptÚstyle>   r   r@   r   r   r?   r>   zRecraft V4 Proz'Design, brand systems, production-readyz$0.25/imager   >   Úcolorsr   r   Úbackground_colorr   z
Qwen Imagez~12szLLM-based, complex textz$0.02/MPé   g      @Úregular)r   r(   r)   r   Úacceleration>	   r   r   r+   r   r)   rE   r   r(   r   )	úfal-ai/flux-2/klein/9bzfal-ai/flux-2-prozfal-ai/z-image/turbozfal-ai/nano-banana-prozfal-ai/gpt-image-1.5zfal-ai/gpt-image-2zfal-ai/ideogram/v3z#fal-ai/recraft/v4/pro/text-to-imagezfal-ai/qwen-imageÚ
FAL_MODELSrF   r   zfal-ai/clarity-upscaleré   z"masterpiece, best quality, highresz.(worst quality, low quality, normal quality:2)gffffffÖ?g333333ã?é   Úimage_toolsÚIMAGE_TOOLS_DEBUG)Úenv_varc                  ó^   — t          ¦   «         rt          d¦  «        sdS t          d¦  «        S )zsReturn managed fal-queue gateway config when the user prefers the gateway
    or direct FAL credentials are absent.Ú	image_genNz	fal-queue)r
   r   r	   © ó    ú=/home/div/.hermes/hermes-agent/tools/image_generation_tool.pyÚ_resolve_managed_fal_gatewayrR   B  s4   € õ ÑÔð ¥°{Ñ'CÔ'Cð ØˆtÝ'¨Ñ4Ô4Ð4rP   Úqueue_run_originÚreturnc                 óš   — t          | pd¦  «                             ¦   «                              d¦  «        }|st          d¦  «        ‚|› d�S )NÚ ú/z$Managed FAL queue origin is required)ÚstrÚstripÚrstripÚ
ValueError)rS   Únormalized_origins     rQ   Ú_normalize_fal_queue_url_formatr]   J  sW   € ÝÐ,Ð2°Ñ3Ô3×9Ò9Ñ;Ô;×BÒBÀ3ÑGÔGÐØð AÝÐ?Ñ@Ô@Ð@ØÐ"Ð"Ð"Ð"rP   c                   óº   — e Zd ZdZdedefd„Zdddddddœded	eeef         d
edee         dee         dedeeeef                  dee	e
ef                  fd„ZdS )Ú_ManagedFalSyncClientzPSmall per-instance wrapper around fal_client.SyncClient for managed queue hosts.ÚkeyrS   c                ó¼  — t          t          dd ¦  «        }|€t          d¦  «        ‚t          t          dd ¦  «        }|€t          d¦  «        ‚t          |¦  «        | _         ||¬¦  «        | _        t          | j        dd ¦  «        | _        t          |dd ¦  «        | _        t          |dd ¦  «        | _        t          |d	d ¦  «        | _	        t          |d
d ¦  «        | _
        t          |dd ¦  «        | _        t          |dd ¦  «        | _        | j        €t          d¦  «        ‚| j        �| j        €t          d¦  «        ‚| j	        €t          d¦  «        ‚d S )NÚ
SyncClientz>fal_client.SyncClient is required for managed FAL gateway modeÚclientz:fal_client.client is required for managed FAL gateway mode)r`   Ú_clientÚ_maybe_retry_requestÚ_raise_for_statusÚSyncRequestHandleÚadd_hint_headerÚadd_priority_headerÚadd_timeout_headerzFfal_client.SyncClient._client is required for managed FAL gateway modezKfal_client.client request helpers are required for managed FAL gateway modezLfal_client.client.SyncRequestHandle is required for managed FAL gateway mode)ÚgetattrÚ
fal_clientÚRuntimeErrorr]   Ú_queue_url_formatÚ_sync_clientÚ_http_clientre   rf   Ú_request_handle_classÚ_add_hint_headerÚ_add_priority_headerÚ_add_timeout_header)Úselfr`   rS   Úsync_client_classÚclient_modules        rQ   Ú__init__z_ManagedFalSyncClient.__init__T  sg  € Ý#¥J°¸dÑCÔCÐØÐ$ÝÐ_Ñ`Ô`Ð`å¥
¨H°dÑ;Ô;ˆØÐ ÝÐ[Ñ\Ô\Ð\å!@ÐAQÑ!RÔ!RˆÔØ-Ð-°#Ð6Ñ6Ô6ˆÔÝ# DÔ$5°yÀ$ÑGÔGˆÔÝ$+¨MÐ;QÐSWÑ$XÔ$XˆÔ!Ý!(¨Ð8KÈTÑ!RÔ!RˆÔÝ%,¨]Ð<OÐQUÑ%VÔ%VˆÔ"Ý '¨Ð7HÈ$Ñ OÔ OˆÔÝ$+¨MÐ;PÐRVÑ$WÔ$WˆÔ!Ý#*¨=Ð:NÐPTÑ#UÔ#UˆÔ àÔÐ$ÝÐgÑhÔhÐhØÔ$Ð,°Ô0FÐ0NÝÐlÑmÔmÐmØÔ%Ð-ÝÐmÑnÔnÐnð .Ð-rP   rV   N)ÚpathÚhintÚwebhook_urlÚpriorityÚheadersÚstart_timeoutÚapplicationÚ	argumentsry   rz   r{   r|   r}   r~   c                óÂ  — | j         |z   }	|r|	d|                     d¦  «        z   z  }	|�|	dt          d|i¦  «        z   z  }	t          |pi ¦  «        }
|�| j        �|                      ||
¦  «         |�,| j        €t          d¦  «        ‚|                      ||
¦  «         |�,| j        €t          d¦  «        ‚|                      ||
¦  «         |                      | j	        d|	|t          | j        dd¦  «        |
¬	¦  «        }|                      |¦  «         |                     ¦   «         }|                      |d
         |d         |d         |d         | j	        ¬¦  «        S )NrW   ú?Úfal_webhookzGfal_client.client.add_priority_header is required for priority requestszEfal_client.client.add_timeout_header is required for timeout requestsÚPOSTÚdefault_timeoutg      ^@)ÚjsonÚtimeoutr}   Ú
request_idÚresponse_urlÚ
status_urlÚ
cancel_url)rˆ   r‰   rŠ   r‹   rc   )rn   Úlstripr   Údictrr   rs   rm   rt   re   rp   rk   ro   rf   r†   rq   )ru   r   r€   ry   rz   r{   r|   r}   r~   ÚurlÚrequest_headersÚresponseÚdatas                rQ   Úsubmitz_ManagedFalSyncClient.submitn  s˜  € ð Ô$ {Ñ2ˆØð 	*Ø�3˜Ÿš SÑ)Ô)Ñ)Ñ)ˆCØÐ"Ø�3� M°;Ð#?Ñ@Ô@Ñ@Ñ@ˆCå˜w˜}¨"Ñ-Ô-ˆØÐ Ô 5Ð AØ×!Ò! $¨Ñ8Ô8Ð8ØÐØÔ(Ð0Ý"Ð#lÑmÔmÐmØ×%Ò% h°Ñ@Ô@Ð@ØÐ$ØÔ'Ð/Ý"Ð#jÑkÔkÐkØ×$Ò$ ]°OÑDÔDÐDà×,Ò,ØÔØØØÝ˜DÔ-Ð/@À%ÑHÔHØ#ð -ñ 
ô 
ˆð 	×Ò˜xÑ(Ô(Ð(à�}Š}‰ŒˆØ×)Ò)Ø˜LÔ)Ø˜nÔ-Ø˜LÔ)Ø˜LÔ)ØÔ$ð *ñ 
ô 
ð 	
rP   )Ú__name__Ú
__module__Ú__qualname__Ú__doc__rX   rx   r   r   r   r   ÚintÚfloatr’   rO   rP   rQ   r_   r_   Q  sí   € € € € € ØZÐZðo˜sð o°cð oð oð oð oð> Ø"Ø%)ØØ,0Ø59ð/
ð /
ð /
àð/
ð ˜˜S˜”>ð/
ð
 ð/
ð �sŒmð/
ð ˜c”]ð/
ð ð/
ð ˜$˜s C˜xœ.Ô)ð/
ð    c¨5 jÔ 1Ô2ð/
ð /
ð /
ð /
ð /
ð /
rP   r_   c                 ó  — | j                              d¦  «        | j        f}t          5  t          �t
          |k    rt          cddd¦  «         S t          | j        | j         ¬¦  «        a|at          cddd¦  «         S # 1 swxY w Y   dS )zQReuse the managed FAL client so its internal httpx.Client is not leaked per call.rW   N)r`   rS   )Úgateway_originrZ   Únous_user_tokenÚ_managed_fal_client_lockÚ_managed_fal_clientÚ_managed_fal_client_configr_   )Úmanaged_gatewayÚclient_configs     rQ   Ú_get_managed_fal_clientr¡      sû   € ð
 	Ô&×-Ò-¨cÑ2Ô2ØÔ'ð€Mõ 
"ð 	#ð 	#ÝÐ*Õ/IÈ]Ò/ZÐ/ZÝ&ð	#ð 	#ð 	#ð 	#ñ 	#ô 	#ð 	#ð 	#õ 4ØÔ/Ø,Ô;ð
ñ 
ô 
Ðð &3Ð"Ý"ð	#ð 	#ð 	#ð 	#ñ 	#ô 	#ð 	#ð 	#ð 	#ð 	#ð 	#ð 	#øøøð 	#ð 	#ð 	#ð 	#ð 	#ð 	#s   ©A?Á#A?Á?BÂBÚmodelr€   c                 óz  — dt          t          j        ¦   «         ¦  «        i}t          ¦   «         }|€t	          j        | ||¬¦  «        S t          |¦  «        }	 |                     | ||¬¦  «        S # t          $ r>}t          |¦  «        }|�'d|cxk    rdk     rn nt          d| › d|› d�¦  «        |‚‚ d}~ww xY w)	zKSubmit a FAL request using direct credentials or the managed queue gateway.zx-idempotency-keyN)r€   r}   i�  iô  z*Nous Subscription gateway rejected model 'z' (HTTP uÓ   ). This model may not yet be enabled on the Nous Portal's FAL proxy. Either:
  â€¢ Set FAL_KEY in your environment to use FAL.ai directly, or
  â€¢ Pick a different model via `hermes tools` â†’ Image Generation.)
rX   ÚuuidÚuuid4rR   rl   r’   r¡   Ú	ExceptionÚ_extract_http_statusr[   )r¢   r€   r�   rŸ   Úmanaged_clientÚexcÚstatuss          rQ   Ú_submit_fal_requestr«   ´  s  € à*­Cµ´
±´Ñ,=Ô,=Ð>€OÝ2Ñ4Ô4€OØÐÝÔ  °)À_ÐUÑUÔUÐUå,¨_Ñ=Ô=€NðØ×$Ò$ØØØ#ð %ñ 
ô 
ð 	
øõ
 ð ð ð õ
 & cÑ*Ô*ˆØÐ #¨Ð"5Ð"5Ò"5Ð"5°#Ò"5Ð"5Ð"5Ð"5Ð"5ÝðY¸Uð Yð YØðYð Yð Yñô ð ðð 	øøøøðøøøs   ÁA2 Á2
B:Á<9B5Â5B:r©   c                 óÌ   — t          | dd¦  «        }|�(t          |dd¦  «        }t          |t          ¦  «        r|S t          | dd¦  «        }t          |t          ¦  «        r|S dS )uð   Return an HTTP status code from httpx/fal exceptions, else None.

    Defensive across exception shapes â€” httpx.HTTPStatusError exposes
    ``.response.status_code`` while fal_client wrappers may expose
    ``.status_code`` directly.
    r�   NÚstatus_code)rk   Ú
isinstancer—   )r©   r�   rª   s      rQ   r§   r§   Ó  sp   € õ �s˜J¨Ñ-Ô-€HØÐÝ˜ =°$Ñ7Ô7ˆÝ�f�cÑ"Ô"ð 	ØˆMÝ�S˜-¨Ñ.Ô.€FÝ�&�#ÑÔð ØˆØˆ4rP   c                  ó¸  — d} 	 ddl m}  |¦   «         }t          |t          ¦  «        r|                     d¦  «        nd}t          |t          ¦  «        r>|                     d¦  «        }t          |t
          ¦  «        r|                     ¦   «         } n2# t          $ r%}t           	                    d|¦  «         Y d}~nd}~ww xY w| s't          j        dd¦  «                             ¦   «         } | st          t          t                   fS | t          vr:t                               d	| t          ¦  «         t          t          t                   fS | t          |          fS )
zÊResolve the active FAL model from config.yaml (primary) or default.

    Returns (model_id, metadata_dict). Falls back to DEFAULT_MODEL if the
    configured model is unknown (logged as a warning).
    rV   r   ©Úload_configrN   Nr¢   z.Could not load image_gen.model from config: %sÚFAL_IMAGE_MODELz4Unknown FAL model '%s' in config; falling back to %s)Úhermes_cli.configr±   r®   r�   ÚgetrX   rY   r¦   ÚloggerÚdebugÚosÚgetenvÚDEFAULT_MODELrG   Úwarning)Úmodel_idr±   ÚcfgÚimg_cfgÚrawr©   s         rQ   Ú_resolve_fal_modelr¿   è  sf  € ð €Hð	LØ1Ð1Ð1Ð1Ð1Ð1Øˆk‰mŒmˆÝ*4°S½$Ñ*?Ô*?ÐI�#—'’'˜+Ñ&Ô&Ð&ÀTˆÝ�g�tÑ$Ô$ð 	'Ø—+’+˜gÑ&Ô&ˆCÝ˜#�sÑ#Ô#ð 'ØŸ9š9™;œ;�øøÝð Lð Lð LÝ�ŠÐEÀsÑKÔKÐKÐKÐKÐKÐKÐKøøøøðLøøøð ð <Ý”9Ð.°Ñ3Ô3×9Ò9Ñ;Ô;ˆàð 8Ý�j­Ô7Ð7Ð7à•zÐ!Ð!Ý�ŠØBØ•mñ	
ô 	
ð 	
õ �j­Ô7Ð7Ð7à•Z Ô)Ð)Ð)s   „BB Â
CÂB>Â>Cr»   r   r   Ú	overridesc                 ó\  ‡— t           |          }|d         }|d         }|pt                               ¦   «                              ¦   «         }||vrt          }t	          |                     di ¦  «        ¦  «        }	|pd                     ¦   «         |	d<   |dv r||         |	d<   n$|dk    r||         |	d<   nt          d	|›�¦  «        ‚|�t          |t          ¦  «        r||	d<   |r!| 	                    ¦   «         D ]\  }
}|�||	|
<   Œ|d         Šˆfd„|	 	                    ¦   «         D ¦   «         S )a)  Build a FAL request payload for `model_id` from unified inputs.

    Translates aspect_ratio into the model's native size spec (preset enum,
    aspect-ratio enum, or GPT literal string), merges model defaults, applies
    caller overrides, then filters to the model's ``supports`` whitelist.
    r    r!   r"   rV   r   )r   r3   r   r.   zUnknown size_style: Nr   r#   c                 ó$   •— i | ]\  }}|‰v ¯	||“ŒS rO   rO   )Ú.0ÚkÚvr#   s      €rQ   ú
<dictcomp>z&_build_fal_payload.<locals>.<dictcomp>3  s$   ø€ Ð>Ð>Ð>‘T�Q˜°°X°°ˆAˆq°°°rP   )
rG   ÚDEFAULT_ASPECT_RATIOÚlowerrY   r�   r´   r[   r®   r—   Úitems)r»   r   r.   r   rÀ   Úmetar    r!   ÚaspectÚpayloadrÄ   rÅ   r#   s               @rQ   Ú_build_fal_payloadrÍ     sX  ø€ õ �hÔ€DØ�lÔ#€JØ�ŒM€EàÐ2Õ2×9Ò9Ñ;Ô;×AÒAÑCÔC€FØ�UÐÐÝ%ˆå" 4§8¢8¨J¸Ñ#;Ô#;Ñ<Ô<€GØ˜ 2×,Ò,Ñ.Ô.€GˆHÑàÐ9Ð9Ð9Ø % f¤ˆ�ÑÐØ	�~Ò	%Ð	%Ø"'¨¤-ˆ�ÑÐåÐ>°
Ð>Ð>Ñ?Ô?Ð?àÐ�J t­SÑ1Ô1ÐØˆ�‰àð Ø—O’OÑ%Ô%ð 	ð 	‰DˆAˆqØˆ}Ø�˜‘
øà�JÔ€HØ>Ð>Ð>Ð>˜WŸ]š]™_œ_Ð>Ñ>Ô>Ð>rP   Ú	image_urlÚoriginal_promptc           
      óº  — 	 t                                d¦  «         | t          › d|› �t          t          t
          t          t          t          t          dœ	}t          t          |¬¦  «        }|                     ¦   «         }|r‹d|v r‡|d         }t                                d|                     dd¦  «        |                     d	d¦  «        ¦  «         |d
         |                     dd¦  «        |                     d	d¦  «        dt          dœS t                                d¦  «         dS # t          $ r(}t                                d|d¬¦  «         Y d}~dS d}~ww xY w)zšUpscale an image using FAL.ai's Clarity Upscaler.

    Returns upscaled image dict, or None on failure (caller falls back to
    the original image).
    z(Upscaling image with Clarity Upscaler...z, )	rÎ   r   Úupscale_factorÚnegative_promptÚ
creativityÚresemblancer(   r   r   ©r€   Úimagez$Image upscaled successfully to %sx%sÚwidthÚunknownÚheightrŽ   r   T)rŽ   r×   rÙ   ÚupscaledrÑ   z"Upscaler returned invalid responseNzError upscaling image: %s©Úexc_info)rµ   ÚinfoÚUPSCALER_DEFAULT_PROMPTÚUPSCALER_FACTORÚUPSCALER_NEGATIVE_PROMPTÚUPSCALER_CREATIVITYÚUPSCALER_RESEMBLANCEÚUPSCALER_GUIDANCE_SCALEÚUPSCALER_NUM_INFERENCE_STEPSÚUPSCALER_SAFETY_CHECKERr«   ÚUPSCALER_MODELr´   Úerrorr¦   )rÎ   rÏ   Úupscaler_argumentsÚhandlerÚresultÚupscaled_imageÚes          rQ   Ú_upscale_imagerí   9  sr  € ð%Ý�ŠÐ>Ñ?Ô?Ð?ð #Ý0ÐEÐE°OÐEÐEÝ-Ý7Ý-Ý/Ý5Ý#?Ý%<ð

ð 

Ðõ &¥nÐ@RÐSÑSÔSˆØ—’‘”ˆàð 	�g Ð'Ð'Ø# Gœ_ˆNÝ�KŠKØ6Ø×"Ò" 7¨IÑ6Ô6Ø×"Ò" 8¨YÑ7Ô7ñô ð ð & eÔ,Ø'×+Ò+¨G°QÑ7Ô7Ø(×,Ò,¨X°qÑ9Ô9Ø Ý"1ðð ð õ 	�ŠÐ9Ñ:Ô:Ð:Øˆtøåð ð ð Ý�ŠÐ0°!¸dˆÑCÔCÐCØˆtˆtˆtˆtˆtøøøøðøøøs   ‚D	D( ÄD( Ä(
EÄ2EÅEr   r(   r)   r   c           	      óº	  — t          ¦   «         \  }}|| ||||||dœdddddœ}	t          j                             ¦   «         }
	 | r:t          | t          ¦  «        r%t          |                      ¦   «         ¦  «        dk    rt          d¦  «        ‚t          ¦   «         s2t          ¦   «         s$d}t          ¦   «         r|dz  }t          |¦  «        ‚|pt                               ¦   «                              ¦   «         }|t          vr(t                               d	|t          ¦  «         t          }i }|�||d
<   |�||d<   |�||d<   |�||d<   t!          || |||¬¦  «        }t                               d|                     d|¦  «        || dd…         ¦  «         t'          ||¬¦  «        }|                     ¦   «         }t          j                             ¦   «         |
z
                       ¦   «         }|rd|vrt          d¦  «        ‚|                     dg ¦  «        }|st          d¦  «        ‚t+          |                     dd¦  «        ¦  «        }g }|D ]Æ}t          |t,          ¦  «        rd|v sŒ|d         |                     dd¦  «        |                     dd¦  «        dœ}|rZt/          |d         |                      ¦   «         ¦  «        }|r|                     |¦  «         Œ’t                               d¦  «         d|d<   |                     |¦  «         ŒÇ|st          d¦  «        ‚t3          d„ |D ¦   «         ¦  «        }t                               dt          |¦  «        |||¦  «         d |r|d         d         ndd!œ}d |	d"<   t          |¦  «        |	d#<   ||	d$<   t4                               d%|	¦  «         t4                               ¦   «          t;          j        |d&d¬'¦  «        S # t>          $ rç}t          j                             ¦   «         |
z
                       ¦   «         }d(t	          |¦  «        › �}t                                d)|d ¬*¦  «         ddt	          |¦  «        tC          |¦  «        j"        d+œ}||	d,<   ||	d$<   t4                               d%|	¦  «         t4                               ¦   «          t;          j        |d&d¬'¦  «        cY d}~S d}~ww xY w)-aê  Generate an image from a text prompt using the configured FAL model.

    The agent-facing schema exposes only ``prompt`` and ``aspect_ratio``; the
    remaining kwargs are overrides for direct Python callers and are filtered
    per-model via the ``supports`` whitelist (unsupported overrides are
    silently dropped so legacy callers don't break when switching models).

    Returns a JSON string with ``{"success": bool, "image": url | None,
    "error": str, "error_type": str}``.
    )r   r.   r   r(   r)   r   r   NFr   )r¢   Ú
parametersrç   ÚsuccessÚimages_generatedÚgeneration_timez1Prompt is required and must be a non-empty stringz$FAL_KEY environment variable not setz' and managed FAL gateway is unavailablez-Invalid aspect_ratio '%s', defaulting to '%s'r   r(   r)   r   )r   rÀ   u,   Generating image with %s (%s) â€” prompt: %sr   éP   rÕ   Úimagesu7   Invalid response from FAL.ai API â€” no images returnedzNo images were generatedr$   rŽ   r×   rÙ   )rŽ   r×   rÙ   z1Using original image as fallback (upscale failed)rÚ   z%No valid image URLs returned from APIc              3   óD   K  — | ]}|                      d ¦  «        ¯dV — ŒdS )rÚ   r&   N)r´   )rÃ   Úimgs     rQ   ú	<genexpr>z&image_generate_tool.<locals>.<genexpr>Ü  s3   è è € ÐRÐR 3¸c¿gºgÀjÑ>QÔ>QÐR˜QÐRÐRÐRÐRÐRÐRrP   z3Generated %s image(s) in %.1fs (%s upscaled) via %sT)rð   rÖ   rð   rñ   rò   Úimage_generate_toolrH   )ÚindentÚensure_asciizError generating image: z%srÛ   ©rð   rÖ   rç   Ú
error_typerç   )#r¿   ÚdatetimeÚnowr®   rX   ÚlenrY   r[   r
   rR   r   rÇ   rÈ   ÚVALID_ASPECT_RATIOSrµ   rº   rÍ   rÝ   r´   r«   Útotal_secondsÚboolr�   rí   ÚappendÚsumÚ_debugÚlog_callÚsaver†   Údumpsr¦   rç   Útyper“   )r   r.   r   r(   r)   r   r   r»   rÊ   Údebug_call_dataÚ
start_timeÚmessageÚ	aspect_lcrÀ   r€   ré   rê   rò   rô   Úshould_upscaleÚformatted_imagesrö   Úoriginal_imagerë   Úupscaled_countÚresponse_datarì   Ú	error_msgs                               rQ   rø   rø   j  sc  € õ& (Ñ)Ô)�N€Hˆdð àØ(Ø#6Ø,Ø$Ø*Øð
ð 
ð ØØØðð €Oõ" Ô"×&Ò&Ñ(Ô(€JðnGØð 	R�Z¨µÑ4Ô4ð 	R½¸F¿LºL¹N¼NÑ8KÔ8KÈqÒ8PÐ8PÝÐPÑQÔQÐQå%Ñ'Ô'ð 	&Õ+GÑ+IÔ+Ið 	&Ø<ˆGÝ)Ñ+Ô+ð EØÐDÑD�Ý˜WÑ%Ô%Ð%à!Ð9Õ%9×@Ò@ÑBÔB×HÒHÑJÔJˆ	ØÕ/Ð/Ð/Ý�NŠNØ?ØÕ2ñô ð õ -ˆIà$&ˆ	ØÐ*Ø/BˆIÐ+Ñ,ØÐ%Ø*8ˆIÐ&Ñ'ØÐ!Ø&0ˆI�lÑ#ØÐ$Ø)6ˆI�oÑ&å&Ø�f˜i¨d¸ið
ñ 
ô 
ˆ	õ 	�ŠØ:Ø�HŠH�Y Ñ)Ô)¨8°V¸C¸R¸C´[ñ	
ô 	
ð 	
õ
 & h¸)ÐDÑDÔDˆØ—’‘”ˆå#Ô,×0Ò0Ñ2Ô2°ZÑ?×NÒNÑPÔPˆàð 	X˜¨Ð/Ð/ÝÐVÑWÔWÐWà—’˜H bÑ)Ô)ˆØð 	9ÝÐ7Ñ8Ô8Ð8å˜dŸhšh y°%Ñ8Ô8Ñ9Ô9ˆàÐØð 	4ð 	4ˆCÝ˜s¥DÑ)Ô)ð ¨e°s¨l¨lØà˜5”zØŸš ¨!Ñ,Ô,ØŸ'š' (¨AÑ.Ô.ðð ˆNð ð TÝ!/°°E´
¸F¿LºL¹N¼NÑ!KÔ!K�Ø!ð Ø$×+Ò+¨NÑ;Ô;Ð;ØÝ—’ÐRÑSÔSÐSà).ˆN˜:Ñ&Ø×#Ò# NÑ3Ô3Ð3Ð3àð 	FÝÐDÑEÔEÐEåÐRÐRÐ*:ÐRÑRÔRÑRÔRˆÝ�ŠØAÝÐ Ñ!Ô! ?°NÀHñ	
ô 	
ð 	
ð Ø3CÐMÐ% aÔ(¨Ô/Ð/Èð
ð 
ˆð
 &*ˆ˜	Ñ"Ý.1Ð2BÑ.CÔ.CˆÐ*Ñ+Ø-<ˆÐ)Ñ*Ý�ŠÐ-¨Ñ?Ô?Ð?Ý�Š‰ŒˆåŒz˜-°ÀÐFÑFÔFÐFøåð Gð Gð GÝ#Ô,×0Ò0Ñ2Ô2°ZÑ?×NÒNÑPÔPˆØ7­s°1©v¬vÐ7Ð7ˆ	Ý�Š�T˜9¨tˆÑ4Ô4Ð4ð ØÝ˜‘V”VÝ˜q™'œ'Ô*ð	
ð 
ˆð $-ˆ˜Ñ Ø-<ˆÐ)Ñ*Ý�ŠÐ-¨Ñ?Ô?Ð?Ý�Š‰ŒˆåŒz˜-°ÀÐFÑFÔFÐFÐFÐFÐFÐFÐFøøøøð#Gøøøs    ÁN&O) Ï)
SÏ3CSÓSÓSc                  óT   — t          t          ¦   «         pt          ¦   «         ¦  «        S )zDTrue if the FAL.ai API key (direct or managed gateway) is available.)r  r
   rR   rO   rP   rQ   Úcheck_fal_api_keyr    s#   € åÕ%Ñ'Ô'ÐIÕ+GÑ+IÔ+IÑJÔJÐJrP   c                  ó  — 	 t          ¦   «         r	t           dS n# t          $ r Y nw xY w	 ddlm}  ddlm}  |¦   «           | ¦   «         D ]*}	 |                     ¦   «         r dS Œ# t          $ r Y Œ'w xY wn# t          $ r Y nw xY wdS )aü  True if any image gen backend is available.

    Providers are considered in this order:

    1. The in-tree FAL backend (FAL_KEY or managed gateway).
    2. Any plugin-registered provider whose ``is_available()`` returns True.

    Plugins win only when the in-tree FAL path is NOT ready, which matches
    the historical behavior: shipping hermes with a FAL key configured
    should still expose the tool. The active selection among ready
    providers is resolved per-call by ``image_gen.provider``.
    Tr   )Úlist_providers©Ú_ensure_plugins_discoveredF)	r  rl   ÚImportErrorÚagent.image_gen_registryr  Úhermes_cli.pluginsr  Úis_availabler¦   )r  r  Úproviders      rQ   Ú#check_image_generation_requirementsr    s  € ðÝÑÔð 	ÝˆJØ�4ð	øõ ð ð ð ØˆðøøøðØ;Ð;Ð;Ð;Ð;Ð;ØAÐAÐAÐAÐAÐAà"Ð"Ñ$Ô$Ð$Ø&˜Ñ(Ô(ð 	ð 	ˆHðØ×(Ò(Ñ*Ô*ð  Ø˜4˜4ð øåð ð ð Ø�ðøøøð		øõ ð ð ð Øˆðøøøð ˆ5sG   ‚ š
'¦'«"A7 ÁA&Á"A7 Á%A7 Á&
A3Á0A7 Á2A3Á3A7 Á7
BÂBÚ__main__u:   ðŸŽ¨ Image Generation Tools â€” FAL.ai multi-model supportz<============================================================u(   â�Œ FAL_KEY environment variable not setz-   Set it via: export FAL_KEY='your-key-here'z   Get a key: https://fal.ai/u   âœ… FAL.ai API key foundu    âœ… fal_client library availableu;   â�Œ fal_client library not found â€” pip install fal-clientu   ðŸ¤– Active model: r   z (ú)z
   Speed: r   r‚   u     Â·  Price: r   z   Upscaler: r$   ÚonÚoffz
Available models:u    â†� activerV   z  z<32z<6u%   
ðŸ�› Debug mode enabled â€” session )ÚregistryÚ
tool_errorÚimage_generatea3  Generate high-quality images from text prompts. The underlying backend (FAL, OpenAI, etc.) and model are user-configured and not selectable by the agent. Returns either a URL or an absolute file path in the `image` field; display it with markdown ![description](url-or-path) and the gateway will deliver it.ÚobjectÚstringzJThe text prompt describing the desired image. Be detailed and descriptive.)r	  ÚdescriptionzlThe aspect ratio of the generated image. 'landscape' is 16:9 wide, 'portrait' is 16:9 tall, 'square' is 1:1.)r	  Úenumr)  Údefault©r   r.   )r	  Ú
propertiesÚrequired)Únamer)  rï   c                  ó´  — 	 ddl m}   | ¦   «         }t          |t          ¦  «        r|                     d¦  «        nd}t          |t          ¦  «        rR|                     d¦  «        }t          |t
          ¦  «        r(|                     ¦   «         r|                     ¦   «         S n2# t          $ r%}t           	                    d|¦  «         Y d}~nd}~ww xY wdS )ui  Return the value of ``image_gen.provider`` from config.yaml, or None.

    We only consult the plugin registry when this is explicitly set â€” an
    unset value keeps users on the legacy in-tree FAL path even when other
    providers happen to be registered (e.g. a user has OPENAI_API_KEY set
    for other features but never asked for OpenAI image gen).
    r   r°   rN   Nr  z%Could not read image_gen.provider: %s)
r³   r±   r®   r�   r´   rX   rY   r¦   rµ   r¶   )r±   r¼   ÚsectionÚvaluer©   s        rQ   Ú_read_configured_image_providerr3  r  sé   € ð	CØ1Ð1Ð1Ð1Ð1Ð1Øˆk‰mŒmˆÝ*4°S½$Ñ*?Ô*?ÐI�#—'’'˜+Ñ&Ô&Ð&ÀTˆÝ�g�tÑ$Ô$ð 	%Ø—K’K 
Ñ+Ô+ˆEÝ˜%¥Ñ%Ô%ð %¨%¯+ª+©-¬-ð %Ø—{’{‘}”}Ð$øøÝð Cð Cð CÝ�ŠÐ<¸cÑBÔBÐBÐBÐBÐBÐBÐBøøøøðCøøøàˆ4s   ‚B"B& Â&
CÂ0CÃCc                 óL  — t          ¦   «         }|r|dk    rdS 	 ddlm} ddlm}  |¦   «           ||¦  «        }n3# t
          $ r&}t                               d|¦  «         Y d}~dS d}~ww xY w|€K	  |d¬¦  «          ||¦  «        }n2# t
          $ r%}t                               d	|¦  «         Y d}~nd}~ww xY w|€t          j	        d
dd|› d�ddœ¦  «        S 	 | 
                    | |¬¦  «        }np# t
          $ rc}t                               dt          |dd¦  «        |¦  «         t          j	        d
ddt          |dd¦  «        › d|› �ddœ¦  «        cY d}~S d}~ww xY wt          |t          ¦  «        st          j	        d
ddddœ¦  «        S t          j	        |¦  «        S )aº  Route the call to a plugin-registered provider when one is selected.

    Returns a JSON string on dispatch, or ``None`` to fall through to the
    built-in FAL path.

    Dispatch only fires when ``image_gen.provider`` is explicitly set AND
    it does not point to ``fal`` (FAL still lives in-tree in this PR;
    a later PR ports it into ``plugins/image_gen/fal/``). Any other value
    that matches a registered plugin provider wins.
    ÚfalNr   )Úget_providerr  z%image_gen plugin dispatch skipped: %sT)Úforcez*image_gen plugin force-refresh skipped: %sFzimage_gen.provider='zk' is set but no plugin registered that name. Run `hermes plugins list` to see available image gen backends.Úprovider_not_registeredrû   r,  z"Image gen provider '%s' raised: %sr/  r‚   z
Provider 'z	' error: Úprovider_exceptionz#Provider returned a non-dict resultÚprovider_contract)r3  r  r6  r  r  r¦   rµ   r¶   r†   r  Úgeneraterº   rk   r®   r�   )r   r.   Ú
configuredr6  r  r  r©   rê   s           rQ   Ú_dispatch_to_plugin_providerr=  ‡  sŽ  € õ 1Ñ2Ô2€JØð ˜ uÒ,Ð,Øˆtð
ð 	:Ð9Ð9Ð9Ð9Ð9ØAÐAÐAÐAÐAÐAà"Ð"Ñ$Ô$Ð$Ø�< 
Ñ+Ô+ˆˆøÝð ð ð Ý�ŠÐ<¸cÑBÔBÐBØˆtˆtˆtˆtˆtøøøøðøøøð Ðð	Lð 'Ð&¨TÐ2Ñ2Ô2Ð2Ø#�| JÑ/Ô/ˆHˆHøÝð 	Lð 	Lð 	LÝ�LŠLÐEÀsÑKÔKÐKÐKÐKÐKÐKÐKøøøøð	Løøøð ÐÝŒzØØð1 zð 1ð 1ð 1ð 4ð	
ð 	
ñ 	ô 	ð 		ðØ×"Ò"¨&¸|Ð"ÑLÔLˆˆøÝð 
ð 
ð 
Ý�ŠØ0Ý�H˜f cÑ*Ô*¨Cñ	
ô 	
ð 	
õ ŒzØØØP¥'¨(°F¸CÑ"@Ô"@ÐPÐPÈ3ÐPÐPØ.ð	
ð 
ñ ô ð 	ð 	ð 	ð 	ð 	ð 	øøøøð
øøøõ �f�dÑ#Ô#ð ÝŒzØØØ:Ø-ð	
ð 
ñ ô ð 	õ Œ:�fÑÔÐsL   š!< ¼
A,ÁA'Á'A,Á2B
 Â

B9ÂB4Â4B9ÃC4 Ã4
E!Ã>AEÅE!ÅE!c                 óÐ   — |                       dd¦  «        }|st          d¦  «        S |                       dt          ¦  «        }t          ||¦  «        }|�|S t	          ||¬¦  «        S )Nr   rV   z'prompt is required for image generationr.   r,  )r´   r%  rÇ   r=  rø   )ÚargsÚkwr   r.   Ú
dispatcheds        rQ   Ú_handle_image_generaterB  Ï  s{   € Ø�XŠX�h Ñ#Ô#€FØð EÝÐCÑDÔDÐDØ—8’8˜NÕ,@ÑAÔA€Lõ .¨f°lÑCÔC€JØÐØÐåØØ!ðñ ô ð rP   rN   u   ðŸŽ¨)r/  ÚtoolsetÚschemaré   Úcheck_fnÚrequires_envÚis_asyncÚemoji)Tr–   r†   Úloggingr·   rý   Ú	threadingr¤   Útypingr   r   r   r   Úurllib.parser   rl   Útools.debug_helpersr   Útools.managed_tool_gatewayr	   Útools.tool_backend_helpersr
   r   r   Ú	getLoggerr“   rµ   rG   rX   Ú__annotations__r¹   rÇ   r   ræ   rß   rå   rÞ   rà   rá   râ   rã   rä   r  r�   rž   ÚLockrœ   rR   r]   r_   r¡   r«   ÚBaseExceptionr—   r§   Útupler¿   rÍ   rí   r˜   rø   r  r  r  ÚprintÚ
SystemExitr  r»   rÊ   r´   rÉ   ÚmidÚmÚmarkerÚactiveÚ
session_idÚtools.registryr$  r%  ÚlistÚIMAGE_GENERATE_SCHEMAr3  r=  rB  ÚregisterrO   rP   rQ   ú<module>r`     s­
  ððð ð ð* €€€Ø €€€Ø 	€	€	€	Ø €€€Ø Ð Ð Ð Ø €€€Ø -Ð -Ð -Ð -Ð -Ð -Ð -Ð -Ð -Ð -Ð -Ð -Ø "Ð "Ð "Ð "Ð "Ð "à Ð Ð Ð à ,Ð ,Ð ,Ð ,Ð ,Ð ,Ø CÐ CÐ CÐ CÐ CÐ Cðð ð ð ð ð ð ð ð ð ð 
ˆÔ	˜8Ñ	$Ô	$€ð4 %ØØ'ØØ)à)Ø!Ø'ð
ð 
ð $%Ø"Ø%*ð
ð 
ð

ð 
ð 
ð ð)ð ð.  ØØ*ØØ)à)Ø!Ø'ð
ð 
ð $&Ø!ØØ"Ø%*Ø #Øð
ð 
ð
ð 
ð 
ð
 ð3ð ð8 #ØØ*ØØ)à)Ø!Ø'ð
ð 
ð $%ØØ"Ø%*Ø',ð
ð 
ð
ð 
ð 
ð
 ð/ð ð4 :ØØDØ#Ø$àØØð
ð 
ð Ø"Ø #ð ð
ð 
ð
ð 
ð 
ð
 ð1ð ð6 #ØØ'ØØ#à$Ø!Ø#ð
ð 
ð  ØØ"ð
ð 
ð
ð 
ð 
ð ð-ð ð2 !ØØJØ%ð *à(Ø!Ø&ð
ð 
ð  ØØ"ð
ð 
ð
ð 
ð 
ð ð=ð ðB !ØØ&Ø#Ø)à)Ø!Ø'ð
ð 
ð  *Ø!Øð
ð 
ð

ð 
ð 
ð ð)ð ð. $ØØ>ØØ)à)Ø!Ø'ð
ð 
ð $ Uð
ð
ð 
ð 
ð ð',ð ,ð,  ØØ.ØØ)à)Ø!Ø'ð
ð 
ð $&Ø!ØØ"Ø%ð
ð 
ð
ð 
ð 
ð ð-ð ðQ`)ð `)€
ˆD��d˜3 ˜8”nÐ$Ô%ð `ð `ñ `ðF )€à"Ð Ø9Ð ð +€Ø€ØÐ Ø>Ð ØKÐ ØÐ ØÐ ØÐ Ø!Ð ð 
ˆ�mÐ-@Ð	AÑ	AÔ	A€ØÐ Ø!Ð Ø)˜9œ>Ñ+Ô+Ð ð5ð 5ð 5ð#°cð #¸cð #ð #ð #ð #ðL
ð L
ð L
ð L
ð L
ñ L
ô L
ð L
ð^#ð #ð #ð(˜sð ¨t°C¸°H¬~ð ð ð ð ð>˜mð °¸´ð ð ð ð ð* *˜Eð  *ð  *ð  *ð  *ðL -ØØ*.ð(?ð (?Øð(?àð(?ð ð(?ð �3Œ-ð	(?ð
 ˜˜S #˜XœÔ'ð(?ð 
ˆ#ˆsˆ(„^ð(?ð (?ð (?ð (?ð\+˜cð +°Cð +¸HÀTÈ#ÈsÈ(Ä^Ô<Tð +ð +ð +ð +ðf -Ø)-Ø&*Ø $Ø#'ØðVGð VGØðVGàðVGð " #œðVGð ˜U”Oð	VGð
 ˜”ðVGð ˜C”=ðVGð �3Œ-ðVGð 	ðVGð VGð VGð VGðrK˜4ð Kð Kð Kð Kð
#¨Tð #ð #ð #ð #ðR ˆzÒÑØ	€EÐ
FÑGÔGÐGØ	€Eˆ(�O„O€OàÐÑÔð ØˆÐ8Ñ9Ô9Ð9ØˆÐ=Ñ>Ô>Ð>ØˆÐ-Ñ.Ô.Ð.Øˆj˜‰mŒmÐØ	€EÐ
$Ñ%Ô%Ð%ðØÐÐÐØˆÐ0Ñ1Ô1Ð1Ð1øØð ð ð ØˆÐKÑLÔLÐLØˆj˜‰mŒmÐðøøøð (Ð'Ñ)Ô)�N€HˆdØ	€EÐ
L §¢¨°HÑ =Ô =Ð
LÐ
LÀÐ
LÐ
LÐ
LÑMÔMÐMØ	€EÐ
T�t—x’x ¨Ñ-Ô-Ð
TÐ
T¸D¿HºHÀWÈcÑ<RÔ<RÐ
TÐ
TÑUÔUÐUØ	€EÐ
B $§(¢(¨9Ñ"5Ô"5Ð@˜$˜$¸5Ð
BÐ
BÑCÔCÐCà	€EÐ
Ñ Ô Ð Ø×"Ò"Ñ$Ô$ð Wð W‰ˆˆQØ"%¨¢/ /��°rˆØˆÐU�3ÐUÐUÐU˜aŸeše G¨SÑ1Ô1ÐUÐUÐU¸¿º¸gÀsÑ8KÔ8KÐUÈVÐUÐUÑVÔVÐVÐVà„}ð LØˆÐJ°vÔ7HÐJÐJÑKÔKÐKð 0Ð /Ð /Ð /Ð /Ð /Ð /Ð /ð ð	Gð ð !Økðð ð
 !Ø˜Ð0Ñ1Ô1ð  NØ/ð	ð ð
ð 
ð �Jðð ðð Ð ð8ð ð ð*E¨ð E¸Cð Eð Eð Eð EðPð ð ð$ €Ô Ø	ØØ Ø"Ø0ØØØ
ð	ñ 	ô 	ð 	ð 	ð 	s   ÊJ' Ê'K