
    Jjp                    2   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(dpd$Z)dqd&Z*drd)Z+dsd,Z,dtd.Z-dud0Z.dvd4Z/dtd5Z0dwd7Z1dxd;Z2d<d=ed>dydFZ3dzdKZ4dLdMd{dRZ5d|dVZ6 G dW dXe          Z7d}dYZ8d~d^Z9dd_ddaZ:dddbddcZ;ddeZ<d~dfZ=ddhZ>ddiZ?ddjZ@ddkZAddlddnZBddoZCdS )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-30returnTuple[str, str]c                    	 ddl m}   |             pi }n4# t          $ r'}t                              d|           i }Y d}~nd}~ww xY wt          |                    d          pt          j        dd                    	                                }t          |                    d          pt          j        d	          pt                    	                                                    d
          }||fS )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        D/home/rurouni/.hermes/hermes-agent/plugins/video_gen/xai/__init__.py_resolve_xai_credentialsr=   Y   s   ??????,,..4"   93??? %))I&&F")M2*F*FGGMMOOG		* 	 9^$$	   eggffSkk	 
 Hs    
AAAr3   c                 F    	 ddl m}   |             S # t          $ r Y dS w xY w)Nr   hermes_xai_user_agentzhermes-agent/video_gen)r/   r@   r0   r?   s    r<   _xai_user_agentrA   q   sL    (888888$$&&& ( ( ('''(s    
  r)   Dict[str, str]c                ,    d|  dt                      dS )NzBearer zapplication/json)AuthorizationzContent-Typez
User-Agent)rA   )r)   s    r<   _xai_headersrE   z   s(    ,7,,*%''      refNonec                    	 ddl m} n3# t          $ r&}t                              d|           Y d}~dS d}~ww xY w ||            dS )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_safetyrJ   r0   r1   r2   )rG   rJ   r;   s      r<   _raise_if_blocked_local_inputrL      sv    ;;;;;;;   A3GGG #s   	 
949valuec                
   | pd                                 }|sdS |                                }|                    d          r|S t          |                                          }|                                s|S t          |           t          j        |j	                  d         pd}|                    d          s|S t          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,r7   lower
startswithr   
expanduseris_filerL   	mimetypes
guess_typenamebase64	b64encode
read_bytesdecoderM   rG   rV   pathmimeencodeds         r<   _image_ref_to_xai_urlre      s    ;B



C rIIKKE>?? 
99!!D<<>> 
!#&&&	**1-K1KD??8$$ 
t001188AAG*4*****rF   Optional[Dict[str, str]]c                    t          |           }|sd S |                                }|                    d          rd|iS d S )NrO   url)re   rV   rW   )rM   rG   rV   s      r<   _image_ref_to_xai_inputri      sO    

&
&C tIIKKE>?? s|4rF   videoDict[str, Any](Tuple[str, Optional[str], Optional[str]]c                   t          |                     d          t                    r|                     d          ni }|pi }|                    d          }t          |t                    r|                                nd}|                     d          }t          |t                    r|                                nd}|p|pd}|r
|r||k    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_urlNrh   r+   )
isinstancer4   dictr3   r7   )rj   rn   stored_public	temporarypublic_video_urltemporary_outs         r<   _xai_video_output_urlsrv      s     /9=9Q9QSW.X.X`%))M***^`K#KOOL11M-7s-K-KUM'')))QUM		%  I%/	3%?%?I	!!!TI$7	7R 	&	+4+E+E 		 
 ]M99rF   c                
   | pd                                 }|sdS |                                }|                    d          r|S t          |                                          }|                                s|S t          |           t          j        |j	                  d         pd}|                    d          s|S t          j        |                                                              d          }d| d| S )	z7Return a URL/data URI accepted by xAI for video inputs.r+   )rP   rQ   zdata:video/r   z	video/mp4zvideo/rR   rS   rT   rU   ra   s         r<   _video_ref_to_xai_urlrx      s    ;B



C rIIKKE>?? 
99!!D<<>> 
!#&&&	**1-<D??8$$ 
t001188AAG*4*****rF   r,   c               0  K   | pd                                 }|sdS t          |                                          }|                                rt	          |          }|rd|indS |                                }|                    d          sdS d|iS )zHBuild xAI ``video`` input using a public HTTPS URL (``url`` field only).r+   Nrh   )rP   rQ   )r7   r   rX   rY   rx   rV   rW   )rM   r)   r,   rG   rb   data_refrV   s          r<   _video_input_from_public_urlr{      s       ;B



C t99!!D||~~ 7(--$,6x  $6IIKKE344 t3<rF   reference_image_urlsOptional[List[str]]4Tuple[Optional[List[Dict[str, str]]], Optional[str]]c                    g }| pg D ]D}|pd                                 }|st          |          }|s dS |                    |           E|r|n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))r7   ri   append)r|   refsrh   cleaned
normalizeds        r<   _normalize_reference_imagesr      s     "$D#)r 
  
 9"##%% 	,W55
 	   	J"DDdT))rF   F   )has_reference_imagesmax_secondsdefaultdurationOptional[int]r   boolr   intr   c               F    | | n|}|dk     rd}||k    r|}|r|dk    rd}|S )N   
    )r   r   r   r   rM   s        r<   _clamp_durationr     sJ     !,HH'Eqyy{ 

LrF   modelOptional[str]modalityexplicit_modelc                   | pd                                 }|r|r|S |dk    rt          S |t          k    s	|t          v rt          S |p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   )r7   DEFAULT_IMAGE_TO_VIDEO_MODEL _IMAGE_TO_VIDEO_COMPAT_MODEL_IDSDEFAULT_TEXT_TO_VIDEO_MODEL)r   r   r   	requesteds       r<   _resolve_model_for_modalityr     sh     "##%%I ) 7++000IAa4a4a**333rF   generations)endpointclienthttpx.AsyncClientpayloadr   c          	     P  K   |                      | d| i t          |          dt          t          j                              i|d           d{V }|                                 |                                }|                    d          }|st          d          |S )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)	postrE   r3   uuiduuid4raise_for_statusr   r4   RuntimeError)r   r   r)   r,   r   responsebodyr   s           r<   _submitr   2  s       [[''X''Q<((Q*=s4:<<?P?PQQ	 !        H ==??D,''J LJKKKrF   r   timeout_secondspoll_intervalc                 K   d}d}||k     r|                      | d| t          |          d           d {V }|                                 |                                }	|	                     d          pd                                }|dk    rd|	d	S |d
v r||	d	S t          j        |           d {V  ||z  }||k     dd|id	S )Ng        queuedr      )r   r   statusr+   done)r   r   >   errorfailedexpired	cancelledr   )r4   rE   r   r   rV   asynciosleep)
r   r   r)   r,   r   r   elapsedlast_statusr   r   s
             r<   _pollr   I  s6      GK
O
#
#---- )) $ 
 
 
 
 
 
 
 

 	!!###}}xx))/R6688&  $d333EEE)4888mM*********= ! O
#
#$  (K)@AAArF   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dS )'XAIVideoGenProviderzxAI Grok Imagine video backend.r%   r3   c                    dS )Nxair   selfs    r<   r\   zXAIVideoGenProvider.nameq      urF   c                    dS )NxAIr   r   s    r<   display_namez XAIVideoGenProvider.display_nameu  r   rF   r   c                B    t                      \  }}t          |          S Nr=   r   )r   r)   _s      r<   is_availablez XAIVideoGenProvider.is_availabley  s    -//
G}}rF   List[Dict[str, Any]]c                H    d t                                           D             S )Nc                     g | ]\  }}d |i|S )idr   ).0midmetas      r<   
<listcomp>z3XAIVideoGenProvider.list_models.<locals>.<listcomp>~  s&    EEE	Ts#d#EEErF   )r$   itemsr   s    r<   list_modelszXAIVideoGenProvider.list_models}  s    EEW]]__EEEErF   r   c                    t           S r   )DEFAULT_MODELr   s    r<   default_modelz!XAIVideoGenProvider.default_model  s    rF   rk   c                t    	 ddl m}  |d          }n# t          $ r d}Y nw xY wd}|r|d| z  }dd|g d	d
S )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)r\   badgetagenv_vars
post_setup)r/   r   r0   )r   r   storage_noticer   s       r<   get_setup_schemaz$XAIVideoGenProvider.get_setup_schema  s    	 >>>>>>44[AANN 	  	  	 NNN	  	  	)((((C&$
 
 	
s    ##c           	     n    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  s@    !7+#$788!"344#(-$8	
 	
 		
rF   N)	r   	image_urlr|   r   aspect_ratio
resolutionnegative_promptaudioseedpromptr   r   r|   r}   r   r   r   r   r   r   Optional[bool]r   kwargsr   c       	   
     p    t          ||t          |                    d                    |||||          S )N_model_override_explicitr   r   r   r   r|   r   r   r   )run_xai_video_generationr   r4   )r   r   r   r   r|   r   r   r   r   r   r   r   s               r<   generatezXAIVideoGenProvider.generate  sH     (

+E F FGG!5%!	
 	
 	
 		
rF   r%   r3   r%   r   )r%   r   )r%   r   )r%   rk   )r   r3   r   r   r   r   r|   r}   r   r   r   r3   r   r3   r   r   r   r   r   r   r   r   r%   rk   )__name__
__module____qualname____doc__propertyr\   r   r   r   r   r   r   DEFAULT_ASPECT_RATIODEFAULT_RESOLUTIONr  r   rF   r<   r   r   n  s        ))   X    X   F F F F   
 
 
 
<

 

 

 

   $#'48"&0,)- $"
 
 
 
 
 
 
 
rF   r   c                 B    t                      \  } }t          |           S r   r   )r)   r   s     r<   has_xai_video_credentialsr    s    )++JGQ==rF   r   r   r   r   c                T    t          t          | |||||||          d|| |          S )Nr   
generationoperation_labelr   r   r   )_run_xai_video_coroutine_generate_xai_video_asyncr   s           r<   r   r     sS     $!)!5%!		
 		
 		
 %!   rF   )r   	video_urlc                T    t          t          | ||          d|| t                    S )Nr   r  r   editr  )r  _edit_xai_video_asyncr	  r  s      r<   run_xai_video_editr    s9     $VyNNN)   rF   )r   r   c                V    t          t          | |||          d|| t                    S )Nr   r  r   r   extendr  )r  _extend_xai_video_asyncr	  r  s       r<   run_xai_video_extendr    sG     $		
 	
 	
 !)   rF   r  c          	     ^   	 t          j                    }	 |                    |           |                                 S # |                                 w xY w# t          $ rJ}t
                              d||d           t          d| d| dd|p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closer0   r1   warningr   r   )coror  r   r   r   loopr;   s          r<   r  r    s    
%''	**400JJLLLLDJJLLLL 	
 	
 	
<os]abbb>>>>>"(=%
 
 
 	
 	
 	
 	
 	
 	
	
s1   A ? A AA 
B,"?B'!B,'B,c                  K   t                      \  }}	|st          |           S | pd                                } d }
|pd                                r$t          |          }
|
st	          ddd|           S |pt
                                          }|pt                                                                          }t          |          \  }}|rt	          |dd|           S | st	          ddd|           S |r4t          |          t          k    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          |||          }|r3|t          k    r(|rt	          dt           d| dd||           S t          }t          |t!          |                    }|| |||d}|
r|
|d<   |r||d<   t#          ||	d|| ||||d|           d {V S )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_responser7   ri   r   r	  r
  rV   r   lenr   r   r   r   r   r   r   _submit_xai_video_payload)r   r   r   r   r|   r   r   r   r)   r,   image_inputnormalized_aspect_rationormalized_resolutionr   
refs_errormodality_usedr5  clamped_durationr   s                      r<   r  r  (  s9      122GX /&v...l!!##FKR   -i88 		!R /     ,C/CJJLL'=+=DDFFLLNN23GHHD* 
5	
 
 
 	
  
?'6
 
 
 	

  
D		000_;O___,6
 
 
 	

  
t 
P+6
 
 
 	
 &999"6$555 2#'QKK.Pgg&M0%  N
  5"=== 
	!K2K K:HK K /$	 	 	 	 5&xd4jjQQQ$/+ G  '& +&*"#*%,!(         rF   r6  c                  K   t                      \  }}|st          |           S | pd                                } t          |pd||           d{V }| st	          ddd|           S |st	          dd	d|           S t          |d
t          |                    }	|	| |d}
|dk    r||
d<   t          ||||
| |	|t          ||
  
         d{V S )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   r1  )r   r   rj   
extensionsr   )
r)   r,   r   r   r   r5  r   r   r   r6  )	r=   r7  r7   r{   r   r   r   r9  r	  )r   r  r   r   r6  r   r)   r,   video_inputr5  r   s              r<   _run_xai_video_mutationrD    s      122GX /&v...l!!##F4R        K
  
@'	
 
 
 	
  	
I '
 
 
 	
 1E{{  N   G
 <&
*%)         rF   c                F   K   t          | ||ddt                     d {V S )Neditsr  r   r  r   r   r6  r   )rD  DEFAULT_DURATIONr  s      r<   r  r    sO       )!         rF   c                j   K   t          |dt                    }t          | ||dd|           d {V S )Nr   )r   r   rB  r  rG  )r   DEFAULT_EXTEND_DURATIONrD  )r   r  r   r   r?  s        r<   r  r    so       ''  
 )!         rF   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<   r7  r7    s(    % #v   rF   )r   r5  c           
       K   	 ddl m}m}m}  |ddd          } |d          } |d          }n# t          $ r d }d }ddi}Y nw xY w|||d	<   t          j                    4 d {V }	 t          ||| ||
           d {V }n|# t
          j        $ rj}d}	 |j	        j
        d d         }n# t          $ r Y nw xY wt          d|j	        j         d|p| dd||          cY d }~cd d d           d {V  S d }~ww xY wt          ||| |t          t                     d {V }d d d           d {V  n# 1 d {V swxY w Y   |d         }|d         }|dk    rh|                    d          pi }t#          |t$                    si }t#          |                    d          t$                    r|                    d          ni }|pi }t'          |          \  }}}|s)t          ddd|                    d          p||          S ||	t)          |                    d                    d}|
r|
|d<   |r||d<   |r||d<   |r||d<   |rd D ]}||v r||         ||<   |                    d!          r|d!         |d!<   t+          ||                    d          p|||||                    d"          p|d|#          S |d$k    rt          d%t           d&d$d||          S |                    d'i           pi                     d(          p|                    d(          pd)| d*}t          |d+| d||          S ),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   r3  )r)   r,   r   r   r   r   r   rj   rn   z/xAI video request completed without a video URLempty_responser   )r   r6  storage_enabledr   r   ro   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/   rN  rO  rP  r0   httpxAsyncClientr   HTTPStatusErrorr   r   r   status_coder   DEFAULT_TIMEOUT_SECONDSDEFAULT_POLL_INTERVAL_SECONDSr4   rp   rq   rv   r   r   ) r)   r,   r   r   r   r5  r   r   r   r6  r   rN  rO  rP  rU  r   storage_cfgr   r   r;   detailpoll_resultr   r   rj   rn   rt   rX  stored_public_urlr_  keyra  s                                    r<   r9  r9    s     )	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 43.
 
 

 <;KHH55kBB ) ) ) %() "%4!" "" 
 
 
 
 
 
 
f	&8!           JJ $ 	 	 	F*4C40   !XCL,DXXSVXX&$       
 
 
 
 
 
 
 
 
 
 
 
 
 
	 "Jh37	
 
 
 
 
 
 
 
 
)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6 "FvD!!'R%&& 	E2<UYY}=U=UW[2\2\deii...bd!'R=STY=Z=Z:-): 	!G+hhw''9>    %"#KOOI$>$>??!
 !

  	-",E, 	5&4E"# 	4"3E, 	3%2E/" 		2 2 2 +%%!,S!1E#J88G 	+!']E'N"((7##5~%YYz**6h	
 	
 	
 		
 ]CZ]]]  
 
 
 	
 
'2			$")))44 	=88I	=<6<<< 
 "&??   su   .3 AA+D;-BD;DC<B/.C</
B<9C<;B<<(C<$D%D;<D(D;;
EEc                H    |                      t                                 dS )uF   Plugin entry point — wire ``XAIVideoGenProvider`` into the registry.N)register_video_gen_providerr   )ctxs    r<   registerrr    s#    ##$7$9$9:::::rF   )r%   r&   r  )r)   r3   r%   rB   )rG   r3   r%   rH   )rM   r3   r%   r3   )rM   r3   r%   rf   )rj   rk   r%   rl   )rM   r3   r)   r3   r,   r3   r%   rf   )r|   r}   r%   r~   )
r   r   r   r   r   r   r   r   r%   r   )r   r   r   r3   r   r   r%   r3   )r   r   r   rk   r)   r3   r,   r3   r   r3   r%   r3   )r   r   r   r3   r)   r3   r,   r3   r   r   r   r   r%   rk   r  )r   r3   r   r   r   r   r   r   r|   r}   r   r   r   r3   r   r3   r%   rk   )r   r3   r  r3   r   r   r%   rk   )
r   r3   r  r3   r   r   r   r   r%   rk   )
r  r3   r   r   r   r3   r   r3   r%   rk   )r   r3   r  r3   r   r   r   r3   r6  r3   r   r   r%   rk   )r   r3   r%   rk   )r)   r3   r,   r3   r   r3   r   rk   r   r3   r5  r3   r   r3   r   r3   r   r   r6  r3   r   r   r%   rk   )r%   rH   )Dr  
__future__r   r   r]   loggingrZ   r5   r   pathlibr   typingr   r   r   r   r	   rd  agent.video_gen_providerr
   r   r   	getLoggerr  r1   r8   r   r   r   rH  r	  r
  rh  ri  rJ  r   r   r   r$   __annotations__r   r=   rA   rE   rL   re   ri   rv   rx   r{   r   r   r   r   r   r   r  r   r  r  r  r  rD  r  r  r7  r9  rr  r   rF   r<   <module>rz     s`    $ # " " " " "        				        3 3 3 3 3 3 3 3 3 3 3 3 3 3           
	8	$	$ - 2 7 +     !  III V$  
 (EMw'  ,7;i & &    $ %'$     0( ( ( (       + + + +.   : : : :0+ + + +.   .* * * ** "'#     "4 4 4 48 "     .B B B BJW
 W
 W
 W
 W
* W
 W
 W
t   
   B  	     & #     *
 
 
 
4m m m m`> > > >B       ,	 	 	 	0 !%H H H H H H`; ; ; ; ; ;rF   