
    `gjp                       U d Z ddlmZ ddlZddlZddlZddlZddlZddlZddl	m
Z
 ddlmZmZmZmZmZ ddlZddlmZmZmZ  ej,                  e      ZdZdZd	ZeZd
ZdZdZdZ dZ!dZ"h dZ#ddhZ$dZ%ddddddgddddddgddZ&de'd<   d d!hZ(dFd"Z)dGd#Z*dHd$Z+dId%Z,dJd&Z-dKd'Z.	 	 	 	 dLd(Z/dJd)Z0	 	 	 	 	 	 	 	 dMd*Z1	 	 	 	 dNd+Z2d,d-ed.	 	 	 	 	 	 	 	 	 dOd/Z3	 	 	 	 	 	 	 	 dPd0Z4d1d2	 	 	 	 	 	 	 	 	 	 	 dQd3Z5	 	 	 	 	 	 	 	 	 	 	 	 	 	 dRd4Z6 G d5 d6e      Z7dSd7Z8	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dTd8Z9dd9	 	 	 	 	 	 	 dUd:Z:ddd;	 	 	 	 	 	 	 	 	 dVd<Z;	 	 	 	 	 	 	 	 	 	 dWd=Z<	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dTd>Z=	 	 	 	 	 	 	 	 	 	 	 	 	 	 dXd?Z>	 	 	 	 	 	 	 	 dUd@Z?	 	 	 	 	 	 	 	 	 	 dVdAZ@dYdBZAddC	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dZdDZBd[dEZCy)\uR  xAI Grok-Imagine video generation backend.

Surface: text-to-video, image-to-video, and reference-to-video through the
unified video provider. xAI edit/extend are exposed through separate tools.

Originally salvaged from PR #10600 by @Jaaneek; reshaped into the
:class:`VideoGenProvider` plugin interface and trimmed to the
generate-only surface.

Authentication: xAI Grok OAuth tokens (preferred — billed against the
user's SuperGrok or X Premium+ subscription) or ``XAI_API_KEY``. Both routes are
resolved through ``tools.xai_http.resolve_xai_http_credentials`` so a
single login covers chat + TTS + image gen + video gen + transcription.
When xAI storage is enabled, the primary ``video`` / ``public_url`` fields are the
stored files-cdn HTTPS link. Pass that public MP4 URL as ``video_url`` for
edit/extend; it is sent to xAI as ``video.url``.
    )annotationsN)Path)AnyDictListOptionalTuple)VideoGenProvidererror_responsesuccess_responsezhttps://api.x.ai/v1grok-imagine-videogrok-imagine-video-1.5   16:9720p         >   1:12:33:23:44:39:16r   480p   zGrok Imagine Videoz~60-240sz.Text-to-video; legacy image-to-video fallback.z:see https://docs.x.ai/developers/models/grok-imagine-videotextimage)displayspeed	strengthsprice
modalitieszGrok Imagine Video 1.5z Latest xAI image-to-video model.z(see https://docs.x.ai/developers/pricing)r   r   zDict[str, Dict[str, Any]]_MODELSzgrok-imagine-video-1.5-previewz!grok-imagine-video-1.5-2026-05-30c                    	 ddl m}   |        xs i }t        |j                  d      xs t        j                  dd            j                         }t        |j                  d      xs t        j                  d	      xs t              j                         j                  d
      }||fS # t        $ r"}t        j	                  d|       i }Y d}~d}~ww xY w)u/  Return ``(api_key, base_url)`` from the shared xAI credential resolver.

    Order: runtime provider (xai-oauth pool entry) → singleton ``auth.json``
    OAuth tokens → ``XAI_API_KEY`` env var. ``api_key`` is empty when no
    credential source is available; callers must check before using it.
    r   )resolve_xai_http_credentialsz"xAI credential resolver failed: %sNapi_keyXAI_API_KEY base_urlXAI_BASE_URL/)tools.xai_httpr&   	ExceptionloggerdebugstrgetosgetenvstripDEFAULT_XAI_BASE_URLrstrip)r&   credsexcr'   r*   s        Q/root/.hermes/venv/lib/python3.12/site-packages/plugins/video_gen/xai/__init__.py_resolve_xai_credentialsr;   Y   s    ?,.4"
 %))I&F"))M2*FGMMOG		* 	 99^$	  egffSk	 
 H  93?s   B- -	C6CCc                 <    	 ddl m}   |        S # t        $ r Y yw xY w)Nr   hermes_xai_user_agentzhermes-agent/video_gen)r-   r>   r.   r=   s    r:   _xai_user_agentr?   q   s%    (8$&& ('(s    	c                $    d|  dt               dS )NzBearer zapplication/json)AuthorizationzContent-Typez
User-Agent)r?   )r'   s    r:   _xai_headersrB   z   s    "7),*%'     c                z    	 ddl m}  ||        y# t        $ r }t        j	                  d|       Y d}~yd}~ww xY w)ah  Refuse to read a local media path that Hermes' read deny-list blocks.

    Thin wrapper over the shared ``agent.file_safety.raise_if_read_blocked``
    chokepoint so xAI video inputs enforce the same credential-store guard as
    the image providers. Fails open if the guard machinery is unavailable
    (defense-in-depth, per the denylist's own framing).
    r   )raise_if_read_blockedz*xAI media input read guard unavailable: %sN)agent.file_safetyrE   r.   r/   r0   )refrE   r9   s      r:   _raise_if_blocked_local_inputrH      s8    ; #  A3Gs    	:5:c                   | xs dj                         }|sy|j                         }|j                  d      r|S t        |      j	                         }|j                         s|S t        |       t        j                  |j                        d   xs d}|j                  d      s|S t        j                  |j                               j                  d      }d| d| S )	z7Return a URL/data URI accepted by xAI for image inputs.r)   http://https://zdata:image/r   zapplication/octet-streamzimage/asciidata:;base64,r5   lower
startswithr   
expanduseris_filerH   	mimetypes
guess_typenamebase64	b64encode
read_bytesdecodevaluerG   rQ   pathmimeencodeds         r:   _image_ref_to_xai_urlra      s    ;B


CIIKE>?
9!D<<>
!#&		*1-K1KD??8$
t0188AG4&	**rC   c                j    t        |       }|sy |j                         }|j                  d      rd|iS y )NrJ   url)ra   rQ   rR   )r]   rG   rQ   s      r:   _image_ref_to_xai_inputrd      s9    

&CIIKE>?s|rC   c                v   t        | j                  d      t              r| j                  d      ni }|xs i }|j                  d      }t        |t              r|j	                         nd}| j                  d      }t        |t              r|j	                         nd}|xs |xs d}|r	|r||k7  r|nd}|||fS )a  Return ``(public_video_url, temporary_url, stored_public_url)``.

    ``public_video_url`` is the stored files-cdn HTTPS MP4 (``public_url``) when
    storage is enabled; otherwise xAI's temporary ``video.url``. Pass this value
    as ``video_url`` for edit/extend chaining.
    file_output
public_urlNrc   r)   )
isinstancer2   dictr1   r5   )videorf   stored_public	temporarypublic_video_urltemporary_outs         r:   _xai_video_output_urlsro      s     /9=9QSW.X%))M*^`K#KOOL1M-7s-KM'')QUM		% I%/	3%?	!TI$7	7R 9+E 	 
 ]M99rC   c                   | xs dj                         }|sy|j                         }|j                  d      r|S t        |      j	                         }|j                         s|S t        |       t        j                  |j                        d   xs d}|j                  d      s|S t        j                  |j                               j                  d      }d| d| S )	z7Return a URL/data URI accepted by xAI for video inputs.r)   )rK   rL   zdata:video/r   z	video/mp4zvideo/rM   rN   rO   rP   r\   s         r:   _video_ref_to_xai_urlrq      s    ;B


CIIKE>?
9!D<<>
!#&		*1-<D??8$
t0188AG4&	**rC   c                  K   | xs dj                         }|syt        |      j                         }|j                         rt	        |      }|rd|iS dS |j                         }|j                  d      syd|iS w)zHBuild xAI ``video`` input using a public HTTPS URL (``url`` field only).r)   Nrc   )rK   rL   )r5   r   rS   rT   rq   rQ   rR   )r]   r'   r*   rG   r^   data_refrQ   s          r:   _video_input_from_public_urlrt      s~      ;B


C9!D||~(-$,x 6$6IIKE343<s   A:A<c                    g }| xs g D ]9  }|xs dj                         }|st        |      }|s y|j                  |       ; |r|d fS d d fS )Nr)   )Nzwreference_image_urls must be public HTTPS URLs or data URIs (e.g. the `image`/`public_url` from a prior Imagine result))r5   rd   append)reference_image_urlsrefsrc   cleaned
normalizeds        r:   _normalize_reference_imagesr{      sn     "$D#)r 
 9"##%,W5
 	J
  DT))dT))rC   F   )has_reference_imagesmax_secondsdefaultc               @    | | n|}|dk  rd}||kD  r|}|r|dkD  rd}|S )N   
    )durationr}   r~   r   r]   s        r:   _clamp_durationr     s<     !,H'Eqy{
LrC   c                   | xs dj                         }|r|r|S |dk(  rt        S |t        k(  s|t        v rt        S |xs t        S )a+  Select xAI's text/video model without treating config as a prompt override.

    ``grok-imagine-video-1.5`` currently rejects text-only video
    generation, but it is the desired image-to-video backend. Explicit tool
    ``model=`` still wins for users who intentionally request another model.
    r)   r   )r5   DEFAULT_IMAGE_TO_VIDEO_MODEL _IMAGE_TO_VIDEO_COMPAT_MODEL_IDSDEFAULT_TEXT_TO_VIDEO_MODEL)modelmodalityexplicit_model	requesteds       r:   _resolve_model_for_modalityr     sR     "##%I)7++00IAa4a**333rC   generations)endpointc          	     &  K   | j                  | d| i t        |      dt        t        j                               i|d       d{   }|j                          |j                         }|j                  d      }|st        d      |S 7 Dw)zAPOST to one of xAI's async video endpoints and return request_id./videos/zx-idempotency-key<   )headersjsontimeoutN
request_idz-xAI video response did not include request_id)	postrB   r1   uuiduuid4raise_for_statusr   r2   RuntimeError)clientpayloadr'   r*   r   responsebodyr   s           r:   _submitr   2  s      [[*HXJ'Q<(Q*=s4::<?PQ	 !  H ==?D,'JJKKs   AB
BABc                 K   d}d}||k  r| j                  | d| t        |      d       d {   }|j                          |j                         }	|	j                  d      xs dj	                         }|dk(  rd|	d	S |d
v r||	d	S t        j                  |       d {    ||z  }||k  rdd|id	S 7 7 w)Ng        queuedr      )r   r   statusr)   done)r   r   >   errorfailedexpired	cancelledr   )r2   rB   r   r   rQ   asynciosleep)
r   r   r'   r*   timeout_secondspoll_intervalelapsedlast_statusr   r   s
             r:   _pollr   I  s      GK
O
#j- ) $ 
 

 	!!#}}xx)/R668& $d33EE)488mmM***= ! O
#$  (K)@AA#
 	+s(   /B?B;A2B?$B=%B?4B?=B?c            
          e Zd ZdZedd       Zedd       ZddZddZddZ	ddZ
ddZd	d	d	d	eed	d	d	d
		 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddZy	)XAIVideoGenProviderzxAI Grok Imagine video backend.c                     y)Nxair   selfs    r:   rW   zXAIVideoGenProvider.nameq      rC   c                     y)NxAIr   r   s    r:   display_namez XAIVideoGenProvider.display_nameu  r   rC   c                2    t               \  }}t        |      S Nr;   bool)r   r'   _s      r:   is_availablez XAIVideoGenProvider.is_availabley  s    -/
G}rC   c                d    t         j                         D cg c]  \  }}d|i| c}}S c c}}w )Nid)r$   items)r   midmetas      r:   list_modelszXAIVideoGenProvider.list_models}  s*    5<]]_E	Ts#d#EEEs   ,c                    t         S r   )DEFAULT_MODELr   s    r:   default_modelz!XAIVideoGenProvider.default_model  s    rC   c                j    	 ddl m}  |d      }d}|r|d| z  }dd|g d	d
S # t        $ r d}Y !w xY w)Nr   )xai_storage_notice_text	video_genr)   zgrok-imagine-video for text/reference; grok-imagine-video-1.5 for image-to-video; edit/extend: pass the stored public HTTPS MP4 (`video` / `public_url` from a prior Imagine result); uses xAI Grok OAuth or XAI_API_KEYz. zxAI Grok Imaginepaidxai_grok)rW   badgetagenv_vars
post_setup)r-   r   r.   )r   r   storage_noticer   s       r:   get_setup_schemaz$XAIVideoGenProvider.get_setup_schema  sd    	 >4[AN 	 R'((C&$
 	
  	 N	 s   $ 22c           	     X    ddgt        t              t        t              ddddt        dS )Nr   r   r|   r   F)r#   aspect_ratiosresolutionsmax_durationmin_durationsupports_audiosupports_negative_promptmax_reference_images)sortedVALID_ASPECT_RATIOSVALID_RESOLUTIONSMAX_REFERENCE_IMAGESr   s    r:   capabilitiesz XAIVideoGenProvider.capabilities  s5    !7+#$78!"34#(-$8	
 		
rC   N)	r   	image_urlrw   r   aspect_ratio
resolutionnegative_promptaudioseedc       	   
     X    t        ||t        |j                  d            |||||      S )N_model_override_explicitpromptr   r   r   rw   r   r   r   )run_xai_video_generationr   r2   )r   r   r   r   rw   r   r   r   r   r   r   kwargss               r:   generatezXAIVideoGenProvider.generate  s8     (

+E FG!5%!	
 		
rC   returnr1   r   r   )r   zList[Dict[str, Any]])r   Optional[str])r   Dict[str, Any])r   r1   r   r   r   r   rw   Optional[List[str]]r   Optional[int]r   r1   r   r1   r   r   r   zOptional[bool]r   r   r   r   r   r   )__name__
__module____qualname____doc__propertyrW   r   r   r   r   r   r   DEFAULT_ASPECT_RATIODEFAULT_RESOLUTIONr   r   rC   r:   r   r   n  s    )   F
<

   $#'48"&0,)- $"

 	

 !
 2
  
 
 
 '
 
 
 
 

rC   r   c                 2    t               \  } }t        |       S r   r   )r'   r   s     r:   has_xai_video_credentialsr     s    )+JGQ=rC   c                D    t        t        | |||||||      d|| |      S )Nr   
generationoperation_labelr   r   r   )_run_xai_video_coroutine_generate_xai_video_asyncr   s           r:   r   r     s?     $!)!5%!		
 %! rC   )r   c                B    t        t        | ||      d|| t              S )Nr   	video_urlr   editr  )r  _edit_xai_video_asyncr   r  s      r:   run_xai_video_editr    s)     $VyN) rC   )r   r   c                D    t        t        | |||      d|| t              S )Nr   r  r   r   extendr  )r  _extend_xai_video_asyncr   r  s       r:   run_xai_video_extendr    s3     $		
 !) rC   c          	     6   	 t        j                         }	 |j                  |       |j                          S # |j                          w xY w# t        $ rB}t
        j                  d||d       t        d| d| dd|xs t        ||      cY d }~S d }~ww xY w)	Nz#xAI video %s unexpected failure: %sT)exc_infoz
xAI video z	 failed: 	api_errorr   )r   
error_typeproviderr   r   r   )	r   new_event_looprun_until_completecloser.   r/   warningr   r   )coror  r   r   r   loopr9   s          r:   r  r    s    
%%'	**40JJLDJJL 	
<os]ab/y>"(=%
 	
	
s1   A 8 A A

A 	B7BBBc                |  K   t               \  }}	|st        |       S | xs dj                         } d }
|xs dj                         rt        |      }
|
st	        ddd|       S |xs t
        j                         }|xs t        j                         j                         }t        |      \  }}|rt	        |dd|       S | st	        ddd|       S |r)t        |      t        kD  rt	        d	t         d
dd|       S |
r|rt	        ddd|       S |t        vrt
        }|t        vrt        }|rdn|
rdnd}t        |||      }|r+|t        k7  r"|rt	        dt         d| dd||       S t        }t        |t!        |            }|| |||d}|
r|
|d<   |r||d<   t#        ||	d|| ||||d|       d {   S 7 w)Nr)   zlimage_url must be a public HTTPS URL or data URI (e.g. the `image`/`public_url` from a prior Imagine result)invalid_image_urlr   r   r  r  r   invalid_reference_image_urlsz+prompt is required for xAI video generationmissing_promptz&reference_image_urls supports at most z images on xAItoo_many_referencesz<image_url and reference_image_urls cannot be combined on xAIconflicting_inputs	referencer   r   r   r   z xAI reference-to-video requires z; got unsupported_modelr   r  r  r   r   )r}   )r   r   r   r   r   reference_imagesr   r   )r'   r*   r   r   r   resolved_modelr   r   r   	operationr   )r;   _auth_required_responser5   rd   r   r   r   rQ   r{   lenr   r   r   r   r   r   r   _submit_xai_video_payload)r   r   r   r   rw   r   r   r   r'   r*   image_inputnormalized_aspect_rationormalized_resolutionrx   
refs_errormodality_usedr(  clamped_durationr   s                      r:   r  r  (  sK     12GX&v..l!!#FKR -i8!R /   ,C/CJJL'=+=DDFLLN23GHD*5	
 	
 ?'6
 	

 D	00:;O:PP^_,6
 	

 tP+6
 	
 &99"6$55 2#'Kg&M0%N
 "==!6236.9IK /$	 	 5&xd4jQ$/+G &&*"#*%,!(   s   F3F<5F:6F<c                v  K   t               \  }}|st        |       S | xs dj                         } t        |xs d||       d{   }| st	        ddd|       S |st	        dd	d|       S t        |d
t        |            }	|	| |d}
|dk(  r||
d<   t        ||||
| |	|t        ||
       d{   S 7 n7 w)zNEdit or extend using a public HTTPS ``video_url`` input (``url`` on the wire).r)   )r'   r*   Nz,prompt is required for xAI video edit/extendr   r   r  z_video_url must be a public HTTPS MP4 URL (the `video`/`public_url` from a prior Imagine result)missing_videor   r$  )r   r   rj   
extensionsr   )
r'   r*   r   r   r   r(  r   r   r   r)  )	r;   r*  r5   rt   r   r   r   r,  r   )r   r  r   r   r)  r   r'   r*   video_inputr(  r   s              r:   _run_xai_video_mutationr7    s     12GX&v..l!!#F4R K
 @'	
 	
 I '
 	
 1E{N  G
 <&
*%)  IHs%   AB9B5A)B90B71B97B9c                H   K   t        | ||ddt               d {   S 7 w)Neditsr	  r   r  r   r   r)  r   )r7  DEFAULT_DURATIONr  s      r:   r
  r
    s1      )!   s   " "c                d   K   t        |dt              }t        | ||dd|       d {   S 7 w)Nr   )r~   r   r5  r  r:  )r   DEFAULT_EXTEND_DURATIONr7  )r   r  r   r   r2  s        r:   r  r    sG      ''
 )!   s   '0.0c                     t        ddd|       S )NzNo xAI credentials found. Sign in via `hermes auth add xai-oauth` (SuperGrok / Premium+) or set XAI_API_KEY from https://console.x.ai/.auth_requiredr   r  )r   )r   s    r:   r*  r*    s    % #v rC   )r   c           
     (  K   	 ddl m}m}m}  |ddd      } |d      } |d      }|||d	<   t        j                         4 d {   }	 t        ||| ||
       d {   }t        ||| |t        t               d {   }d d d       d {    d   }|d   }|dk(  r5|j!                  d      xs i }t#        |t$              si }t#        |j!                  d      t$              r|j!                  d      ni }|xs i }t'        |      \  }}}|s#t        ddd|j!                  d      xs ||      S |	t)        |j!                  d            d}|
r|
|d<   |r||d<   |r||d<   |r||d<   |rd D ]  }||v s||   ||<    |j!                  d!      r|d!   |d!<   t+        ||j!                  d      xs |||||j!                  d"      xs |d|#      S |d$k(  rt        d%t         d&d$d||      S |j!                  d'i       xs i j!                  d(      xs |j!                  d(      xs d)| d*}t        |d+| d||      S # t        $ r d }d }ddi}Y 7w xY w7 7 # t
        j                  $ rv}d}	 |j                  j                  d d }n# t        $ r Y nw xY wt        d|j                  j                   d|xs | dd||      cY d }~cd d d       d {  7   S d }~ww xY w7 t7 g# 1 d {  7  sw Y   xxY ww),Nr   )build_xai_storage_options"maybe_mark_xai_storage_notice_seenread_xai_imagine_storage_configr   zhermes-xai-videomp4)filename_prefix	extensionenabledFstorage_options)r'   r*   r   r)   i  zxAI submit failed (z): r  r   r&  )r'   r*   r   r   r   r   r   rj   rf   z/xAI video request completed without a video URLempty_responser   )r   r)  storage_enabledr   r   rg   temporary_url)filename
expires_atpublic_url_expires_atpublic_url_errorstorage_errorusager   )rj   r   r   r   r   r   r  extrar   z.Timed out waiting for xAI video request after sr   messagez%xAI video request ended with status ''xai_)r-   rA  rB  rC  r.   httpxAsyncClientr   HTTPStatusErrorr   r   r   status_coder   DEFAULT_TIMEOUT_SECONDSDEFAULT_POLL_INTERVAL_SECONDSr2   rh   ri   ro   r   r   ) r'   r*   r   r   r   r(  r   r   r   r)  r   rA  rB  rC  rH  r   storage_cfgr   r   r9   detailpoll_resultr   r   rj   rf   rm   rK  stored_public_urlrR  keyrT  s                                    r:   r,  r,    s    )	
 	
 4.

 <KH5kB
 "%4!"  " 
 
f	&8!  J$ "Jh37	
 
)
 
6 "FvD!'R%&E2<UYY}=UW[2\eii.bd!'R=STY=Z:-):!G+hhw'9>  %"#KOOI$>?!

 ",E,&4E"#"3E,%2E/" 2 +%!,S!1E#J2 88G!']E'N"((7#5~%YYz*6h	
 		
 BCZB[[\]  
 	
 
'2		$")))4 	=88I	=26(!< 
 &? M  ) %()
 $$ 	F**4C0 !+CLL,D,D+ESSVX&$ 
 
 
	
)
 
 
 
s   L%I LI$	LK<I*!I'"I*&K<K6K<LK9C,L CLI!L I!!L'I**K3=K. JK.	J&#K.%J&&0K.K3K<L'K*(L.K33K<9L<LLL
Lc                6    | j                  t                      y)uF   Plugin entry point — wire ``XAIVideoGenProvider`` into the registry.N)register_video_gen_providerr   )ctxs    r:   registerre    s    ##$7$9:rC   )r   zTuple[str, str]r   )r'   r1   r   zDict[str, str])rG   r1   r   None)r]   r1   r   r1   )r]   r1   r   Optional[Dict[str, str]])rj   r   r   z(Tuple[str, Optional[str], Optional[str]])r]   r1   r'   r1   r*   r1   r   rg  )rw   r   r   z4Tuple[Optional[List[Dict[str, str]]], Optional[str]])
r   r   r}   r   r~   intr   rh  r   rh  )r   r   r   r1   r   r   r   r1   )r   httpx.AsyncClientr   r   r'   r1   r*   r1   r   r1   r   r1   )r   ri  r   r1   r'   r1   r*   r1   r   rh  r   rh  r   r   r   )r   r1   r   r   r   r   r   r   rw   r   r   r   r   r1   r   r1   r   r   )r   r1   r  r1   r   r   r   r   )
r   r1   r  r1   r   r   r   r   r   r   )
r  r1   r   r   r   r1   r   r1   r   r   )r   r1   r  r1   r   r   r   r1   r)  r1   r   rh  r   r   )r   r1   r   r   )r'   r1   r*   r1   r   r1   r   r   r   r1   r(  r1   r   r1   r   r1   r   rh  r)  r1   r   r   r   r   )r   rf  )Dr   
__future__r   r   rX   loggingrU   r3   r   pathlibr   typingr   r   r   r   r	   rW  agent.video_gen_providerr
   r   r   	getLoggerr   r/   r6   r   r   r   r;  r   r   r[  r\  r=  r   r   r   r$   __annotations__r   r;   r?   rB   rH   ra   rd   ro   rq   rt   r{   r   r   r   r   r   r   r   r  r  r  r  r7  r
  r  r*  r,  re  r   rC   r:   <module>rq     s'  $ #     	   3 3   
		8	$ - 2 7 +     !  I V$  
 (EMw' ,7;i&	" $ %'$  0( +.::-:0+.  	
 .*-*9** "'#  	
  	"44 4 	4
 	48 " 	
   	.BBB 	B
 B B B BJW
* W
t
  	
  .    B  	  	
 & #  	
  *
 
 	

 
 
 
4mm m 	m
 m .m m m m m`>> > 	>
 > > > >B  	
    	
  ,	0 !%HH H 	H
 H H H H H H H H H`;rC   