
    (Gj                     
   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
 daeed<   defdZddlmZ ddlmZmZmZ dd	lmZ dd
lmZ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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(h d)ddd*d+d,d-dddddd.d#dddd/h d0dd1	d2d3d4d5d6d7d8d9dd#dd$d:d;h d<dd=h d>d?dd@dAdBdCdDdEdFdGddHd#ddIh dJddKh dLdMddNdOdPdQddRddSddHd#ddIh dTddUh dVdMddWdXdYdZdddddd[d%d\d]h d^dd_h d`d#ddad3dbdcddddddddih dedd1	dfdgdhdiddddddjdkd#ddldmh dnddoh dpdqddrdsdtdud6d7d8d9ddvdHih dwdd1	dxdydzd{d6d7d8d9ddvdHih dwdd1	d|Ze	ee	eef         f         ed}<   d~Z dZ!dZ"dZ#d?Z$dZ%dZ&dZ'dZ(dZ)dZ*dZ+ edd          Z,da-da. ej/                    Z0d Z1d Z2dede	eef         fdZ3de4fdZ5e!ddfdeded6ede
e6         de
e	eef                  de	eef         fdZ7e!ddfdedede8d6ede
e6         de
e	eef                  de	eef         fdZ9dedede
e	eef                  fdZ:dede;fdZ<dedz  fdZ=dededz  fdZ>dedededz  fdZ?deddfdZ@ddededz  defdZAe!dddddddfded6ede
e6         de
eB         de
e6         de
e         de
e6         de
e         de
e8         defdZCde;fdZDdefdZEde;fdZFedk    r eGd            eGd            eD            s, eGd            eGd            eGd            eHd#           eGd           	 ddla eGd           n# eI$ r  eGd            eHd#          w xY w e5            \  ZJZK eGdeKL                    deJ           deJ d            eGdeKL                    dd           deKL                    dd                       eGdeKL                    d          rdnd             eGdæ           eM                                D ]O\  ZNZOeNeJk    rdndZP eGdeNdǛdeOL                    dd          dțdeOL                    dd           eP            Pe,jQ        r eGde,jR                    ddlSmTZTmUZU ddddddМd e8e"          de!dҜdddМdddiddלd؜dgdٜdڜZVdۄ ZWd܄ ZX	 	 dded6ede
e         de
e8         fd݄ZYh dޣZZde
e         de
e         fd߄Z[de
e         de;fdZ\	 	 dded6ede
e         de
e8         de
e         f
dZ]d Z^eVd         Z_de	eef         fdZ`de	eef         fdZa eTjb        ddeVe^eFg ddea	  	         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
fal_clientreturnc                  L    t           t           S ddlm}   |             a t           S )u'  Lazily import fal_client and rebind the module global on first use.

    Idempotent. Returns the (now-loaded) ``fal_client`` module reference.
    Skips the import if the global is already truthy — this preserves the
    test pattern of monkeypatching the module global to install a mock.
    Nr   import_fal_client)r   tools.fal_commonr
   r	   s    A/home/rurouni/.hermes/hermes-agent/tools/image_generation_tool.py_load_fal_clientr   ,   s6     222222""$$J    )DebugSession)_ManagedFalSyncClient_extract_http_status_normalize_fal_queue_url_format)resolve_managed_tool_gateway)fal_key_is_configuredmanaged_nous_tools_enabled%nous_tool_gateway_unavailable_message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#   zfal-ai/flux-2/klein/9b/edit>   r$   r%   
image_urlsr"   r!   r#   	   )displayspeed	strengthsprice
size_stylesizesdefaultssupportsupscaleedit_endpointedit_supportsmax_reference_imagesz
FLUX 2 Proz~6szStudio photorealismz$0.03/MP2   g      @   5T)r!   guidance_scale
num_imagesr"   r#   safety_tolerance	sync_mode>
   r$   r%   r;   r&   r9   r"   r8   r:   r!   r#   zfal-ai/flux-2-pro/edit>
   r$   r%   r;   r'   r9   r"   r8   r:   r!   r#   zZ-Image Turboz~2szBilingual EN/CN, 6Bz	$0.005/MP   )r!   r9   r"   r#   enable_prompt_expansion>   r$   r%   r&   r9   r"   r!   r#   r=   )	r)   r*   r+   r,   r-   r.   r/   r0   r1   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)r9   r"   r:   
resolution>
   r$   r%   r;   r9   r@   r>   r"   r:   enable_web_searchlimit_generationszfal-ai/nano-banana-pro/edit>   r$   r%   r;   r'   r9   r@   r>   r"   r:   rA   rB      zGPT Image 1.5z~15szPrompt adherencez$0.034/imagegpt_literal	1536x1024	1024x1024	1024x1536medium)qualityr9   r"   >   r%   rI   r;   
backgroundr&   r9   r"   zfal-ai/gpt-image-1.5/edit>   r%   rI   r;   r&   r'   r9   r"      zGPT Image 2z~20sz3SOTA text rendering + CJK, world-aware photorealismu   $0.04–0.06/imagelandscape_4_3portrait_4_3>   r%   rI   r;   r&   r9   r"   zopenai/gpt-image-2/edit>   r%   rI   r;   r'   r9   r"   mask_image_urlzIdeogram V3z~5szBest typographyz$0.03-0.09/imageBALANCEDAUTO)rendering_speedexpand_promptstyle>   r$   rS   r%   r&   rR   rQ   zfal-ai/ideogram/v3/edit>   r$   rS   r%   r'   rR   rQ   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!   r8   r9   r"   acceleration>	   r$   r%   r;   r&   r9   rX   r"   r8   r!   zfal-ai/qwen-image-2/pro/edit>	   r$   r%   r;   r'   r9   rX   r"   r8   r!      zKrea 2 Mediumz~15-25sz9Illustration, anime, painting, expressive/artistic stylesz#$0.030 (text) / $0.035 (style refs)
creativity>   r$   r%   rZ   r>   image_style_referenceszKrea 2 Largez~25-60sz;Photorealism, raw textured looks (motion blur, grain, film)z#$0.060 (text) / $0.065 (style refs))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-imagez#fal-ai/krea/v2/medium/text-to-imagez"fal-ai/krea/v2/large/text-to-image
FAL_MODELSr\   r   zfal-ai/clarity-upscalerz"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    r   r   _resolve_managed_fal_gatewayre     s4      {'C'C t'444r   c                 @   | j                             d          | j        f}t          5  t          t
          |k    rt          cddd           S t                       t          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./N)keyqueue_run_origin)	gateway_originrstripnous_user_token_managed_fal_client_lock_managed_fal_client_managed_fal_client_configr   r   r   )managed_gatewayclient_configs     r   _get_managed_fal_clientrr     s
   
 	&--c22'M 
" # #*/I]/Z/Z&# # # # # # # # 	3/,;
 
 

 &3""# # # # # # # # # # # # # # # # # #s   B7BBBmodel	argumentsc           	         t                       dt          t          j                              i}t	                      }|t          j        | ||          S t          |          }	 |                    | ||          S # t          $ rZ}t          |          }|Cd|cxk    rdk     r6n n3d}|dv rdt          d	d
          z   }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)rt   headersi  i   >         z

managed FAL image generationT)force_freshz*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.)r   struuiduuid4re   r   submitrr   	Exceptionr   r   
ValueError)rs   rt   request_headersrp   managed_clientexcstatusgateway_messages           r   _submit_fal_requestr     s`    *C
,=,=>O244O )_UUUU,_==N$$# % 
 
 	

    
 &c**#"5"5"5"5#"5"5"5"5"5 O((;6$(     %U % %% %
 #% %   	1s   (B   
C$
ACC$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 )
zResolve 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).
    rw   r   load_configrc   Nrs   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   
isinstancedictgetr}   stripr   loggerdebugosgetenvDEFAULT_MODELr]   warning)model_idr   cfgimg_cfgrawr   s         r   _resolve_fal_modelr     sf    H	L111111kmm*4S$*?*?I#''+&&&Tgt$$ 	'++g&&C#s## '99;; L L LEs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/   rw   r%      rD   r   r&   r>   zUnknown size_style: Nr$   r0   c                 0    i | ]\  }}|v s|d k    ||S )r%   rd   ).0kvr0   s      r   
<dictcomp>z&_build_fal_payload.<locals>.<dictcomp>`  s8       A==AMM 	
1)MMr   )
r]   DEFAULT_ASPECT_RATIOlowerr   r   r   r   r   intitems)r   r%   r>   r$   r   metar-   r.   aspectpayloadr   r   r0   s               @r   _build_fal_payloadr   5  si    hDl#JME2299;;AACCFU%"488J#;#;<<G2,,..GH999 %f	~	%	%"'->
>>???JtS11 OO%% 	 	DAq}
JH       r   r'   c                    t           |          }|                    d          pt                      |d         }|d         }|pt                                                                          }	|	|vrt          }	t          |                    di                     }
|pd                                |
d<   t          |          |
d<   |dv rd	v r||	         |
d	<   n|d
k    rd
v r||	         |
d
<   |t          |t                    r||
d<   |r!|
                                D ]\  }}|||
|<   ddhfd|

                                D             S )a  Build a FAL *edit* request payload (image-to-image) from unified inputs.

    Every FAL edit endpoint takes ``image_urls`` (a list of source/reference
    image URLs) plus the prompt. Size handling differs from text-to-image:
    most edit endpoints auto-infer output dimensions from the input image, so
    we only send ``image_size`` / ``aspect_ratio`` when the edit endpoint's
    ``edit_supports`` whitelist accepts it. Keys outside ``edit_supports`` are
    stripped before submission.
    r3   r-   r.   r/   rw   r%   r'   r   r&   r>   Nr$   c                 ,    i | ]\  }}|v s|v ||S rd   rd   )r   r   r   	_requiredr3   s      r   r   z+_build_fal_edit_payload.<locals>.<dictcomp>  s:       Ai 	
1!/r   )r]   r   setr   r   r   r   listr   r   r   )r   r%   r'   r>   r$   r   r   r-   r.   r   r   r   r   r   r3   s                @@r   _build_fal_edit_payloadr   f  s   " hDHH_--6Ml#JME2299;;AACCFU%"488J#;#;<<G2,,..GH ,,GL
 999lm>[>[ %f	~	%	%.M*I*I"'-JtS11 OO%% 	 	DAq}
 <(I        r   	image_urloriginal_promptc           
         	 t                               d           | t           d| t          t          t
          t          t          t          t          d	}t          t          |          }|                                }|rd|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)zUpscale 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_promptrZ   resemblancer8   r!   r#   rt   imagez$Image upscaled successfully to %sx%swidthunknownheighturlr   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          r   _upscale_imager     sr   %>??? #0EEOEE-7-/5#?%<

 

 &n@RSSS 	g''#G_NKK6""7I66""8Y77   &e,'++GQ77(,,Xq99 "1   	9:::t   0!dCCCttttts   D	D( D( (
E2EEvaluec                    | rt          | t                    sdS |                                 }|                    d          rdS t          j                            |           rdS t          |           dk    o| d         dk    o	| d         dv S )	NF)zhttp://zhttps://zdata:TrY   r6   :rC   >   rg   \)r   r}   r   
startswithr   pathisabslen)r   r   s     r   _looks_like_absolute_file_pathr     s     
5#.. uKKMME899 u	w}}U tu::?JuQx3J58{3JJr   task_idc                     	 ddl m}  || pd          S # t          $ r&}t                              d|           Y d }~d S d }~ww xY w)Nr   )get_active_envdefaultz1Could not inspect active terminal environment: %s)tools.terminal_toolr   r   r   r   )r   r   r   s      r   _active_terminal_envr     so    666666~g2333   H#NNNttttts    
AA  Aenvc                 2   | t          | dd           }t          |          rb	  |            }|r"t          |                              d          S n2# t          $ r%}t
                              d|           Y d }~nd }~ww xY wt          | dd           }|r%t          |                              d           dS | j        j        }|dv rdS t          j
        d          pd	                                                                }|d
v rdS |dk    rdS d S )Nagent_visible_cache_baserg   z.active env agent_visible_cache_base failed: %s_remote_homez/.hermes>   ModalEnvironmentDockerEnvironmentSingularityEnvironmentz/root/.hermesTERMINAL_ENVlocal>   modaldockersingularitysshz	~/.hermes)getattrcallabler}   rk   r   r   r   	__class____name__r   r   r   r   )r   explicitr   r   remote_homeenv_namebackends          r   _agent_cache_base_for_envr     sZ   

 3 :DAAH 	TT 

 2u::,,S1112 T T TMsSSSSSSSST c>488 	=+&&--c22<<<<=)ZZZ"? y((3G::<<BBDDG444%{4s   -A 
BA==B	host_pathc                     t          |           sd S t          |          }|sd S 	 ddlm}  || |          S # t          $ r%}t
                              d|           Y d }~nd }~ww xY wd S )Nr   )map_cache_path_to_container)container_basez4Could not translate image cache path for backend: %s)r   r   tools.credential_filesr   r   r   r   )r   r   
cache_baser   r   s        r   _agent_visible_cache_pathr     s    ))44 t*3//J tRFFFFFF**9ZPPPP R R RKSQQQQQQQQR4s   9 
A(A##A(c                     t          | dd           }|d S 	 |                    d           d S # t          $ r&}t                              d|           Y d }~d S d }~ww xY w)N_sync_managerTforcez1Could not force-sync generated image artifact: %s)r   syncr   r   r   )r   sync_managerr   s      r   _force_artifact_syncr    s    366LQ%%%%% Q Q QJCPPPPPPPPPQs   / 
AAAr   c                 F   	 t          | t                    rt          j        |           n| }n# t          $ r | cY S w xY wt          |t
                    r|                    d          s| S |                    d          }t          |t                    rt          |          s| S t          |          }t          ||          }|r||k    r| S |t          |           |                    d|           |                    d|           t          j        |d          S )a  Annotate successful local image results with backend-visible paths.

    ``image`` remains the host/gateway-deliverable path.  When the active
    terminal backend has a different filesystem, ``agent_visible_image`` gives
    the path the agent can use with terminal/file tools.
    successr   N
host_imageagent_visible_imageF)ensure_ascii)r   r}   jsonloadsr   r   r   r   r   r   r  
setdefaultdumps)r   r   r   r   r   
agent_paths         r   "_postprocess_image_generate_resultr  &  s9   %/S%9%9B$*S///s   


 gt$$ GKK	,B,B 
KK  EeS!! )G)N)N 

w
'
'C*5#66J u,,

S!!!|U+++,j999:gE2222s   +. ==r!   r8   r9   r"   reference_image_urlsc	                 V   t                      \  }	}
g }t          |t                    r;|                                r'|                    |                                           t          |t
          t          f          rU|D ]R}t          |t                    r;|                                r'|                    |                                           S|
                    d          }t          |          ot          |          }|rdnd}|	| |||||||t          |          d	ddddd}t          j
                                        }	 | r:t          | t                    r%t          |                                           dk    rt          d	          t                      s)t                      st          t                                |r,|s*t          d
|
                    d|	           d|	 d          |pt                                                                           }|t$          vr(t&                              d|t                      t           }i }|||d<   |||d<   |||d<   |||d<   |rt+          |
                    d          pd          }|dk    r
|d|         n|}t-          |	| ||||          }|}t&                              d|
                    d|	          |t          |          | dd                    nOt1          |	| |||          }|	}t&                              d|
                    d|	          |	| dd                    t3          ||          }|                                }t          j
                                        |z
                                  }|rd|vrt          d          |                    dg           }|st          d          t          |
                    dd                    o| }g }|D ]}t          |t6                    rd|v s|d         |                    dd          |                    d d          d!}|rZt9          |d         |                                           } | r|                    |            t&                              d"           d|d#<   |                    |           |st          d$          t;          d% |D                       }!t&                              d&t          |          ||!||           d'|r|d         d         nd|d(}"d'|d)<   t          |          |d*<   ||d+<   t<                              d,|           t<                                            tC          j"        |"d-d.          S # tF          $ r}#t          j
                                        |z
                                  }d/t          |#           }$t&          $                    d0|$d'1           ddt          |#          tK          |#          j&        d2}"|$|d3<   ||d+<   t<                              d,|           t<                                            tC          j"        |"d-d.          cY d}#~#S d}#~#ww xY w)4a-  Generate an image from a text prompt, or edit a source image, via FAL.

    Routing: when ``image_url`` (or ``reference_image_urls``) is provided AND
    the configured model declares an ``edit_endpoint``, the call routes to that
    image-to-image / edit endpoint; otherwise it's plain text-to-image.

    The agent-facing schema exposes ``prompt``, ``aspect_ratio``, ``image_url``
    and ``reference_image_urls``; the remaining kwargs are overrides for direct
    Python callers and are filtered per-model via the ``supports`` /
    ``edit_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,
    "modality": "text" | "image", "error": str, "error_type": str}``.
    r2   r   text)	r%   r>   r!   r8   r9   r"   r$   modalitysource_imagesNFr   )rs   
parametersr   r  images_generatedgeneration_timez1Prompt is required and must be a non-empty stringzModel 'r)   z' (u   ) is not capable of image-to-image / editing. Provide a text-only prompt (omit image_url), or switch to an edit-capable model via `hermes tools` → Image Generation.z-Invalid aspect_ratio '%s', defaulting to '%s'r!   r8   r9   r"   r4   r6   )r$   r   u=   Editing image with %s (%s) — %d source image(s), prompt: %sP   u,   Generating image with %s (%s) — prompt: %sr   imagesu7   Invalid response from FAL.ai API — no images returnedzNo images were generatedr1   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   r6   N)r   )r   imgs     r   	<genexpr>z&image_generate_tool.<locals>.<genexpr>  s3      RR3cggj>Q>QRQRRRRRRr   z8Generated %s image(s) in %.1fs (%s upscaled) via %s [%s]T)r  r   r  r  r  r  image_generate_toolrC   )indentr
  zError generating image: z%sr   r  r   r   
error_typer   )'r   r   r}   r   appendr   tupler   boolr   datetimenowr   r   re   _build_no_backend_setup_messager   r   VALID_ASPECT_RATIOSr   r   r   r   r   r   r   total_secondsr   r   sum_debuglog_callsaver  r  r   r   typer   )%r%   r>   r!   r8   r9   r"   r$   r   r  r   r   r  refr2   use_editr  debug_call_data
start_time	aspect_lcr   max_refsclamped_sourcesrt   endpointr   r   r  r  should_upscaleformatted_imagesr  original_imager   upscaled_countresponse_datar   	error_msgs%                                        r   r  r  F  s\   4 ())NHd M)S!! 0ioo&7&7 0Y__..///&u66 2' 	2 	2C#s## 2		 2$$SYY[[111HH_--MM"":tM':':H".wwH (#6,$*  //

 

 ! O& "&&((JIG 	RZ44 	RFLLNN8K8Kq8P8PPQQQ%'' 	@+G+I+I 	@<>>???
  	 	<$((9h77 < <H < < <   "9%9@@BBHHJJ	///NN?2   -I$&	*/BI+,%*8I&'!&0Il#$)6Io& 	488$:;;@qAAH:BQ,,mIXI66MO/&/9Y  I %HKKOH--x_9M9Mss    +&)$)  I  HKK>H--x  
 &h)DDD#,0022Z?NNPP 	X//VWWWHb)) 	97888 dhhy%8899J(l 	4 	4CsD)) esll5z!,,''(A.. N  T!/E
FLLNN!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F !!?NH	
 	
 	
 3CM%a(// 
 
 &*	".12B.C.C*+-<)*-???z-FFFF G G G#,0022Z?NNPP7s1vv77	T9t444 VVq''*	
 
 $- -<)*-???z-FFFFFFFFF#Gs    Q)V7 7
Z(CZ#Z(#Z(c                  T    t          t                      pt                                S )zDTrue if the FAL.ai API key (direct or managed gateway) is available.)r$  r   re   rd   r   r   check_fal_api_keyr>    s#    %''I+G+I+IJJJr   c                     ddg} |                      d           t                      r|                      d           n>|                      d           t          d          }|r|                      d|            |                      d           |                      d           |                      d	           t                      r|                      d
           |                      d           d                    |           S )aR  Build an actionable error string when no FAL backend is reachable.

    Used by the in-tree FAL path. Mentions:
      - FAL_KEY signup link
      - managed-gateway status (if Nous tools are enabled)
      - plugin alternative pointer (so users on a stale ``image_gen.provider``
        know the registry exists and how to inspect it)
    z4Image generation is unavailable in this environment.rw   zMissing requirements:zA  - FAL_KEY is not set and the managed FAL gateway is unreachablez+  - FAL_KEY environment variable is not setr{   z  - z&To enable image generation, do one of:z_  1. Get a free API key at https://fal.ai and set FAL_KEY=<your-key> (then restart the session)zX  2. Sign in to a Nous account that has the managed FAL gateway enabled (`hermes setup`)u     3. Configure a different image_gen provider via `hermes tools` → Image Generation (run `hermes plugins list` to see installed backends)
)r"  r   r   join)linesr   s     r   r'  r'    s6    DRHE	LL()))!## 
3O	
 	
 	
 	
 	BCCC?*
 
  	3LL111222	LL	LL9:::	LL	8   "## 
/	
 	
 	
 
LL	  
 99Ur   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>  r   ImportErroragent.image_gen_registryrD  hermes_cli.pluginsrF  is_availabler   )rD  rF  providers      r   #check_image_generation_requirementsrL  >  s   	 	
 4	    ;;;;;;AAAAAA""$$$&(( 	 	H((**  44    		     5sG   ! 
.."A> A-)A> ,A> -
A:7A> 9A::A> >
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*   ?u     ·  Price: r,   z   Upscaler: r1   onoffz
Available models:u    ← activerw   z  z<32z<6u%   
🐛 Debug mode enabled — session )registry
tool_errorimage_generateuB  Generate high-quality images from text prompts (text-to-image), or edit / transform an existing image (image-to-image) when the active model supports it. Pass `image_url` to edit that image; add `reference_image_urls` for style/composition references; omit both for text-to-image. The underlying backend (FAL, OpenAI, xAI, etc.) and model are user-configured and not selectable by the agent. Returns the result in the `image` field — either a URL or an absolute file path. To show it to the user, reference that path/URL in your response using the file-delivery convention for the current platform (your platform guidance describes how files are delivered here). When the active terminal backend has a different filesystem, successful local-file results may also include `agent_visible_image` for follow-up terminal/file operations.objectstringzThe text prompt describing the desired image (text-to-image) or the edit to apply (image-to-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.  enumrW  r   ud  Optional source image to edit/transform (image-to-image). When provided, the active backend routes to its image editing endpoint; when omitted, it generates from text alone. Pass a public URL or an absolute local file path from the conversation. Only honored by models that support editing — the description above indicates whether the active model does.arrayr.  zOptional list of additional reference image URLs / paths (style, character, or composition references) to guide an image-to-image edit. Supported only by some models and capped per-model; the description above indicates the max.)r.  r   rW  r%   r>   r   r  )r.  
propertiesrequired)namerW  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 )zBReturn the value of ``image_gen.model`` from config.yaml, or None.r   r   rc   Nrs   z"Could not read image_gen.model: %s
r   r   r   r   r   r}   r   r   r   r   r   r   sectionr   r   s        r   _read_configured_image_modelrb    s    	@111111kmm*4S$*?*?I#''+&&&Tgt$$ 	%KK((E%%% %%++-- %{{}}$ @ @ @93????????@4   B"B& &
C0CC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 )u  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 in-tree FAL fallback 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). ``"fal"``
    explicitly routes through ``plugins/image_gen/fal/`` (which delegates
    back into this module's pipeline via call-time indirection — see
    issue #26241).
    r   r   rc   NrK  z%Could not read image_gen.provider: %sr_  r`  s        r   _read_configured_image_providerre    s    	C111111kmm*4S$*?*?I#''+&&&Tgt$$ 	%KK
++E%%% %%++-- %{{}}$ C C C<cBBBBBBBBC4rc  c                    t                      }|sdS t                      }	 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 | |d}
	 |r||
d<   t          |t                    r+|                                r|                                |
d<   d}|ddlm}  ||          }|r||
d<    |j        di |
}n?# t"          $ r}	d|
v sd|
v r\t                              dt'          |dd          |	           t          j
        d	ddt'          |dd           ddd          cY d}	~	S t                              dt'          |dd          |	           t          j
        d	ddt'          |dd           d|	 dd          cY d}	~	S d}	~	w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 ) u  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
    in-tree FAL fallback in ``image_generate_tool``.

    Dispatch fires when ``image_gen.provider`` is explicitly set — including
    ``"fal"`` itself, which now resolves to the
    ``plugins/image_gen/fal/`` plugin (the plugin re-enters this module's
    pipeline via ``_it`` indirection so behavior is identical to the
    direct call, just routed through the registry).

    ``image_url`` / ``reference_image_urls`` enable image-to-image / editing:
    they are forwarded to the provider's ``generate()`` so the backend can
    route to its edit endpoint.
    Nr   get_providerrE  z%image_gen plugin dispatch skipped: %sTr  z*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%   r>   rs   r   normalize_reference_imagesr  zQimage_gen provider '%s' rejected image-to-image kwargs (signature too narrow): %sr]  rO  z
Provider 'u   ' does not support image-to-image / editing (its generate() signature is out of date with the image_generate schema). Omit image_url for text-to-image, or pick a backend that supports editing via `hermes tools` → Image Generation.modality_unsupportedz,Image gen provider '%s' raised TypeError: %sz	' error: provider_exceptionz"Image gen provider '%s' raised: %sz#Provider returned a non-dict resultprovider_contractrd   )re  rb  rH  rh  rI  rF  r   r   r   r  r  r   r}   r   agent.image_gen_providerrk  generate	TypeErrorr   r   r   )r%   r>   r   r  
configuredconfigured_modelrh  rF  rK  r   kwargs	norm_refsrk  r   s                 r   _dispatch_to_plugin_providerrv    s}   * 122J t 455
 	:99999AAAAAA""$$$<
++   <cBBBttttt 	L '&T2222#|J//HH 	L 	L 	LLLEsKKKKKKKK	L z1z 1 1 1 4	
 	
 	 	 		 )/MMF8 	/.F7Oi%% 	4)//*;*; 	4"+//"3"3F;	+KKKKKK223GHHI 	7-6F)*"",,V,,      
 &  $:f$D$DNN-&#..  
 : Q63!?!? Q Q Q 5          	:Hfc**C	
 	
 	
 zP'(FC"@"@PP3PP.	
 
   	 	 	 	 	 	  
 
 
0Hfc**C	
 	
 	
 zP'(FC"@"@PP3PP.	
 
   	 	 	 	 	 	
 fd## z:-	
 
   	 :fso   !A 
A4A//A4:B 
CB<<C)A0E 
J%AH'J	AH'!J'J4AJJJ>   krea-2-largekrea-2-mediumkrea-2-medium-turboc                 r    t          | t                    sdS |                                 }|t          v r|S dS )zIReturn the native Krea plugin model id when ``model_id`` is ``krea-2-*``.N)r   r}   r   _KREA_NATIVE_MODELS)r   	candidates     r   _normalize_krea_modelr}    s?    h$$ t  I'''4r   c                 $    t          |           duS )zATrue when ``model_id`` is a native Krea plugin id (``krea-2-*``).N)r}  )r   s    r   is_krea_modelr    s     **$66r   c                    t                      dk    rdS t          t                                }|dS 	 ddlm}  |            dS n3# t
          $ r&}t                              d|           Y d}~dS d}~ww xY w	 ddlm	} ddl
m}  |              |d          }	n3# t
          $ r&}t                              d|           Y d}~dS d}~ww xY w|	dS | ||d	}
	 t          |t                    r+|                                r|                                |
d
<   d}|ddlm}  ||          }|r||
d<    |	j        di |
}nN# t
          $ rA}t                              d|           t'          j        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 )aO  Route a native ``krea-2-*`` model to the managed Krea gateway, in managed mode.

    Returns a JSON result string when handled by the Krea managed gateway, or
    ``None`` to fall through to the normal plugin/FAL pipeline. Fires only when
    all hold:
      - the configured image model is a native ``krea-2-*`` id, AND
      - the user isn't already routed to the Krea plugin via
        ``image_gen.provider`` (that path dispatches normally), AND
      - the managed Krea gateway is resolvable (portal/managed mode).

    Direct/BYO users (no managed gateway) fall through untouched.
    kreaNr   )_resolve_managed_krea_gatewayz%Managed Krea routing probe failed: %srg  rE  z.Managed Krea routing: provider unavailable: %s)r%   r>   rs   r   rj  r  zManaged Krea routing failed: %sFzManaged Krea generation error: rm  r   z(Krea provider returned a non-dict resultrn  rd   )re  r}  rb  plugins.image_gen.krear  r   r   r   rH  rh  rI  rF  r   r}   r   ro  rk  rp  r   r  r  r   )r%   r>   r   r  
normalizedr  r   rh  rF  rK  rt  ru  rk  r   s                 r   _maybe_route_managed_krear    s   & '((F22t&'C'E'EFFJtHHHHHH((**24 3   <cBBBttttt999999AAAAAA""$$$<''   EsKKKttttt t $ F
i%% 	4)//*;*; 	4"+//"3"3F;	+KKKKKK223GHHI 	7-6F)*"",,V,,   8#>>>z<s<<.	
 
   	 	 	 	 	 	 fd## z?-	
 
   	 :fsN   A 
A8A33A8<!B 
C(C		CA)E 
F6FFFc                    |                      dd          }|st          d          S |                      dt                    }|                      d          }|                      d          }|                     d          }t          ||||          }|t	          ||	          S t          ||||          }|t	          ||	          S t          ||||
          }	t	          |	|	          S )Nr%   rw   z'prompt is required for image generationr>   r   r  r   )r   r  )r   rZ  )r   rS  r   rv  r  r  r  )
argskwr%   r>   r   r  r   
dispatchedkrea_routedr   s
             r   _handle_image_generater    s#   XXh##F ECDDD88N,@AAL%%I88$:;;ffYG
 .1  J
 1*gNNNN ,1  K
 1+wOOOO
!1	  C .c7CCCCr   rW  c                  :   dgdd} t                      }|r|dk    r	 ddlm} ddlm}  |              ||          }|i }	 |                                pi }n# t          $ r i }Y nw xY w|j        | d<   t                      p|	                                pd	| d
<   |
                    d          rt          |d                   | d<   |
                    d          rt          |d                   | d<   | S n# t          $ r Y nw xY w	 t                      \  }}d| d<   |
                    d|          | d
<   |
                    d          r/ddg| d<   t          |
                    d          pd          | d<   ndg| d<   d| d<   n# t          $ r Y nw xY w| S )a  Best-effort: return the active backend/model's image capabilities.

    Resolution order mirrors the runtime dispatch:
    1. If ``image_gen.provider`` is set, ask that plugin provider.
    2. Otherwise inspect the in-tree FAL model catalog for the active model.

    Returns a dict like ``{"modalities": [...], "max_reference_images": N,
    "model": "...", "provider": "..."}``. Never raises.
    r  r   )
modalitiesr4   falrg  rE  NrK  rw   rs   r  r4   zFAL.air)   r2   r   r6   )re  rH  rh  rI  rF  capabilitiesr   display_namerb  default_modelr   r   r   r   )r   configured_providerrh  rF  rK  capsr   r   s           r   _active_image_capabilitiesr  "  s@    ,2(ANND9;; 2e;;	======EEEEEE&&(((#|$788H##00228bDD    DDD#+#8Z  < > > b8CYCYC[C[Ca_aW88L)) B)-d<.@)A)AD&88233 U36t<R7S3T3TD/0 $  	 	 	D	+--$#ZH55W88O$$ 	-"('!2D+.txx8N/O/O/TST+U+UD'(("(D+,D'(    KsI   %C; A C; A*'C; )A**BC; ;
DDA>F 
FFc                  ~   t           g} 	 t                      }n# t          $ r dt           icY S w xY w|                    d          }|                    d          }t	          |                    d          pdg          }d}|r|d| z  }|r|d| z  }|                     |           d	|v rEd|v rA|                    d
          pd}|r|dk    rd| dnd}|                     d| d           n3d	|v rd|vr|                     d           n|                     d           dd                    |           iS )zIBuild a description reflecting whether the active model supports editing.rW  rK  rs   r  r  z
Active backendz: u    · model: r   r4   r   r6   z; up to z, reference image(s) via reference_image_urlsrw   z\- supports both text-to-image (omit image_url) and image-to-image / editing (pass image_url)u    — routes automaticallyuD   - this model is image-to-image / edit only — image_url is REQUIREDu   - this model is text-to-image only — it is NOT capable of image-to-image / editing; do not pass image_url or reference_image_urls (they will be rejected). Provide a text-only prompt.r@  )_GENERIC_IMAGE_DESCRIPTIONr  r   r   r   r"  rA  )partsr   rK  rs   r  liner4  ref_notes           r   _build_dynamic_image_schemar  W  s   '(E;)++ ; ; ;9::::; xx
##HHHWETXXl++7x88JD  X &%e%%%	LL*:!5!5882338q $qLL NxMMMM 	
 	#8@# # #	
 	
 	
 	

 
J		6#;#;R	
 	
 	
 	
 	 	
 	
 	
 499U++,,s    //rc   u   🎨)	r]  toolsetschemar   check_fnrequires_envis_asyncemojidynamic_schema_overrides)N)NN)c__doc__r  loggingr   r%  	threadingr~   typingr   r   r   r   __annotations__r   tools.debug_helpersr   r   r   r   r   tools.managed_tool_gatewayr   tools.tool_backend_helpersr   r   r   r   	getLoggerr   r   r]   r}   r   r   r(  r   r   r   r   r   r   r   r   r   r+  rn   ro   Lockrm   re   rr   r   r#  r   r   r   r   r   r   r$  r   r   r   r   r  r  floatr  r>  r'  rL  print
SystemExitrG  r   r   r   r   midmmarkeractive
session_idtools.registryrR  rS  IMAGE_GENERATE_SCHEMArb  re  rv  r{  r}  r  r  r  r  r  r  registerrd   r   r   <module>r     s    *   				       & & & & & & & & & & 
C   #     - , , , , ,         
 D C C C C C            
	8	$	$4 %'))!'
 
 $%"%*
 


 
 
  7
 
 
 !"9 >  *))!'
 
 $&!"%* #
 

 
 

 1
 
 

 !"C" "H #*))!'
 
 $%"%*',
 

 
 

 / 4 :D#$
 
 " # 
 

 
 

  7
 
 

 !"C" "H #'#$!#
 
  "
 

 
 
 4
 
 
 !#; @ !J% *(!&
 
  "
 

 
 
  3
 
 
 !#O( (T !&#))!'
 
  *!
 


 
 
 2
 
 
 !"7 < $>))!'
 
 $U

 
 
 ', ,,  .))!'
 
 $&!"%
 

 
 
  8
 
 
 !"= H #P6$
 
 (

 
 
 %, ,* "R6$
 
 (

 
 
 %+ +_	C) C)
Dd38n$% C C CL
 )" 9  + > K    !  
m-@	A	A	A ! )9>++ 5 5 5# # #0(s (tCH~ ( ( ( (\ *E  *  *  *  *L -*.. ... . 3-	.
 S#X'. 
#s(^. . . .j -*.6 666 6 	6
 3-6 S#X'6 
#s(^6 6 6 6x+c +C +HT#s(^<T + + + +bK# K$ K K K K#*     3  3:        F 3 3:    "Qc Qd Q Q Q Q3 3C 3#* 3PS 3 3 3 3D -)-&* $#'#+/GG GGGGGG "#GG UO	GG
 GG C=GG 3-GG }GG #4.GG 	GG GG GG GGTK4 K K K K
& & & & &R'T ' ' ' 'Z z	E
FGGG	E(OOO 8999=>>>-...jmm	E
$%%%01111   KLLLjmm ('))NHd	E
LH = =
L
L
L
L
LMMM	E
Ttxx--
T
TDHHWc<R<R
T
TUUU	E
B$((9"5"5@$$5
B
BCCC	E
   ""$$ W WQ"%//rU3UUUaeeGS11UUUgs8K8KUVUUVVVV} LJv7HJJKKK 0 / / / / / / / 	.  !'  !011  N/	  !-    (+Q		% 	%7%
 %
L JQ) )-@ @ F    6  $+/	  } #4.	   V ONN HSM hsm    7HSM 7d 7 7 7 7  $+/	O OOO }O #4.	O
 c]O O O Od'D 'D 'Dl 3=A 2DcN 2 2 2 2j,-T#s(^ ,- ,- ,- ,-^  	 "0
8
 
 
 
 
 
s   M# #M?