
    iz              	          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mZ ddlm	Z	m
Z
mZmZ ddlmZ ddlZddlmZmZ ddlmZ ddlmZ dd	lmZ  ej        e          Z ed
d          ZdefdZ e            ZdZ de!de"fdZ#dedee!         fdZ$d[de!dede%defdZ&dede!fdZ'd\dedee!         de!fdZ(dZ)dZ*de+de"fdZ,de*fdedee!         d e%de!fd!Z-	 d\de!d"e!d#e!de!fd$Z.de"fd%Z/ed&k    r	  e0d'            e0d(            e/            Z1e1s" e0d)            e0d*            e2d+           n e0d,            e0d-           ej3        r( e0d.ej4                     e0d/ej4         d0           n e0d1            e0d2            e0d3            e0d4            e0d5            e0d6            e0d7            e0d8            e0d9            e0d:            e0d;            e0d<            e0d=            e0d>            e0d?            e0d@            e0dA            e0dB            e0dC            e0dD            e0dE            e0dF            e0dG           ddHl5m6Z6m7Z7 dIdJdKdLdMdNdLdOdNdPddQgdRdSZ8dTee!e	f         dUe	de
e!         fdVZ9 e6j:        dIdWe8e9e/dXdYZ           dS )]aV  
Vision Tools Module

This module provides vision analysis tools that work with image URLs.
Uses the centralized auxiliary vision router, which can select OpenRouter,
Nous, Codex, native Anthropic, or a custom OpenAI-compatible endpoint.

Available tools:
- vision_analyze_tool: Analyze images from URLs with custom prompts

Features:
- Downloads images from URLs and converts to base64 for API compatibility
- Comprehensive image description
- Context-aware analysis based on user queries
- Automatic temporary file cleanup
- Proper error handling and validation
- Debug logging support

Usage:
    from vision_tools import vision_analyze_tool
    import asyncio
    
    # Analyze an image
    result = await vision_analyze_tool(
        image_url="https://example.com/image.jpg",
        user_prompt="What architectural style is this building?"
    )
    N)Path)Any	AwaitableDictOptional)urlparse)async_call_llmextract_content_or_reasoning)get_hermes_dir)DebugSession)check_website_accessvision_toolsVISION_TOOLS_DEBUG)env_varreturnc                     t          j        dd                                          } | r 	 t          |           S # t          $ r Y nw xY w	 ddlm}m}  |            } ||ddd          }|t          |          S n# t          $ r Y nw xY wdS )	NHERMES_VISION_DOWNLOAD_TIMEOUT r   cfg_getload_config	auxiliaryvisiondownload_timeoutg      >@)	osgetenvstripfloat
ValueErrorhermes_cli.configr   r   	Exception)env_valr   r   cfgvals        7/home/ubuntu/.hermes/hermes-agent/tools/vision_tools.py_resolve_download_timeoutr&   4   s    i8"==CCEEG 	>>! 	 	 	D	::::::::kmmgc;2DEE?::    4s!   : 
AA0A= =
B
	B
i   urlc                     | rt          | t                    sdS |                     d          sdS t          |           }|j        sdS ddlm}  ||           sdS dS )z
    Basic validation of image URL format.
    
    Args:
        url (str): The URL to validate
        
    Returns:
        bool: True if URL appears to be valid, False otherwise
    F)zhttp://zhttps://r   is_safe_urlT)
isinstancestr
startswithr   netloctools.url_safetyr*   )r'   parsedr*   s      r%   _validate_image_urlr1   L   s      jc** u >>122 u c]]F= u -,,,,,;s u4    
image_pathc                 L   |                      d          5 }|                    d          }ddd           n# 1 swxY w Y   |                    d          rdS |                    d          rdS |                    d          rd	S |                    d
          rdS t          |          dk    r|dd         dk    r|dd         dk    rdS | j                                        dk    r7|                     dd          dd                                         }d|v rdS dS )z>Return a MIME type when the file looks like a supported image.rb@   Ns   PNG

	image/pngs   
image/jpeg)s   GIF87as   GIF89a	image/gifs   BM	image/bmp      s   RIFF   s   WEBP
image/webp.svgzutf-8ignore)encodingerrorsi   z<svgimage/svg+xml)openreadr-   lensuffixlower	read_text)r3   fheaderheads       r%   _detect_image_mime_typerM   k   sy   			 !               -.. {)) |/00 { {
6{{bVBQBZ722vad|w7N7N|  F**##WX#FFuuMSSUUT>>"?4s   8<<   	image_urldestinationmax_retriesc           
      l  K   ddl }|j                            dd           d }d}t          |          D ]f}	 t	          |           }|rt          |d                   t          j        t          dd|gi          4 d{V 	 }|	                    | d	d
d           d{V }	|	
                                 |	j        	                    d          }
|
r@t          |
          t          k    r(t          dt          |
           dt           d          t          |	j                  }t	          |          }|rt          |d                   |	j        }t%          |          t          k    r(t          dt%          |           dt           d          |                    |           ddd          d{V  n# 1 d{V swxY w Y   |c S # t(          $ r}|}||dz
  k     rtd|dz   z  }t*                              d|dz   |t          |          dd                    t*                              d|           |                    |           d{V  n3t*                              d|t          |          dd         d           Y d}~`d}~ww xY w|t3          d| d          |)a  
    Download an image from a URL to a local destination (async) with retry logic.
    
    Args:
        image_url (str): The URL of the image to download
        destination (Path): The path where the image should be saved
        max_retries (int): Maximum number of retry attempts (default: 3)
        
    Returns:
        Path: The path to the downloaded image
        
    Raises:
        Exception: If download fails after all retries
    r   NT)parentsexist_okc                    K   | j         rC| j        r>t          | j        j                  }ddlm}  ||          st          d|           dS dS dS )a*  Re-validate each redirect target to prevent redirect-based SSRF.

        Without this, an attacker can host a public URL that 302-redirects
        to http://169.254.169.254/ and bypass the pre-flight is_safe_url check.

        Must be async because httpx.AsyncClient awaits event hooks.
        r   r)   z.Blocked redirect to private/internal address: N)is_redirectnext_requestr,   r'   r/   r*   r   )responseredirect_urlr*   s      r%   _ssrf_redirect_guardz-_download_image.<locals>._ssrf_redirect_guard   s        	H$9 	x4899L444444;|,,  S\SS  		 	 	 	 r2   messagerX   )timeoutfollow_redirectsevent_hookszoMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36zimage/*,*/*;q=0.8)z
User-AgentAccept)headerszcontent-lengthzImage too large (z bytes, max )      z)Image download failed (attempt %s/%s): %s2   zRetrying in %ss...z+Image download failed after %s attempts: %sd   exc_infozB_download_image exited retry loop without attempting (max_retries=)asyncioparentmkdirranger   PermissionErrorhttpxAsyncClient_VISION_DOWNLOAD_TIMEOUTgetraise_for_statusr`   int_VISION_MAX_DOWNLOAD_BYTESr   r,   r'   contentrF   write_bytesr!   loggerwarningsleeperrorRuntimeError)rO   rP   rQ   rh   rZ   
last_errorattemptblockedclientrX   cl	final_urlbodye	wait_times                  r%   _download_imager      s      NNN TD999    J%% 9 98	*955G :%gi&8999
 (0!%'*>)?@    .  .  .  .  .  .  .  . !' 'X"5  ", " "       ))+++ %))*:;; #b''$>>>$^CGG^^A[^^^    --	.y99 >)')*<===  't99999$`CII``C]```   ''---A .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .D  	 	 	Jq(('A+.	JGVWKYdfijkflflmpnpmpfqrrr3Y???mmI..........AFF4C4L!	    	 _Q\___
 
 	
 s>   AGD'G .G 
G
	
GG
	G
JB2JJc                 t    | j                                         }dddddddd}|                    |d          S )z
    Determine the MIME type of an image based on its file extension.
    
    Args:
        image_path (Path): Path to the image file
        
    Returns:
        str: The MIME type (defaults to image/jpeg if unknown)
    r8   r7   r9   r:   r>   rC   ).jpgz.jpegz.pngz.gifz.bmpz.webpr?   )rG   rH   rp   )r3   	extension
mime_typess      r%   _determine_mime_typer      sO     !''))I J >>)\222r2   	mime_typec                     |                                  }t          j        |                              d          }|pt	          |           }d| d| }|S )a0  
    Convert an image file to a base64-encoded data URL.
    
    Args:
        image_path (Path): Path to the image file
        mime_type (Optional[str]): MIME type of the image (auto-detected if None)
        
    Returns:
        str: Base64-encoded data URL (e.g., "data:image/jpeg;base64,...")
    asciidata:;base64,)
read_bytesbase64	b64encodedecoder   )r3   r   dataencodedmimedata_urls         r%   _image_to_base64_data_urlr      sf       ""D t$$++G44G 8,Z88D /t..W..HOr2   i  @i  P ry   c                 |    t          |                                           t          fddD                       S )z;Detect if an API error is related to image or payload size.c              3       K   | ]}|v V  	d S N .0hinterr_strs     r%   	<genexpr>z'_is_image_size_error.<locals>.<genexpr>%  s7        4tw      r2   )	z	too largepayload413content_too_largerequest_too_largerO   invalid_requestexceedsz
size limit)r,   rH   any)ry   r   s    @r%   _is_image_size_errorr   "  sT    %jj  G     ,      r2   max_base64_bytesc           	         |                                  j        }|dz  dz  dz   }||k    r't          | |          }t          |          |k    r|S nd}	 ddlm} ddl}n?# t          $ r2 t          	                    d           |t          | |          }|cY S w xY wt          	                    d	|d
z  |d
z  |d
z             |pt          |           }|dk    rdnd}	|	dk    rdnd}
	 |                    |           }nG# t          $ r:}t          	                    d|           |t          | |          }|cY d}~S d}~ww xY w|	dk    r|j        dv r|                    d          }|	dk    rdnd}|j        |j        f}d}t#          d          D ]}|dk    rd}t%          t'          |j        |z            d          }t%          t'          |j        |z            d          }|dk    r;|j        dk    r0d|j        z  }t%          t'          |j        |z            d          }n@|dk    r:|j        dk    r/d|j        z  }t%          t'          |j        |z            d          }||f|k    r n|                    ||f|j                  }||f}t          	                    d|||           |D ]}|                                }d|	i}|||d<    |j        |fi | t1          j        |                                                              d          }d|
 d| }t          |          |k    r>t          	                    dt          |          d
z  ||j        |j                   |c c S |1t                              d|d
z  t          |          d
z             |S |pt          | |          S )aE  Convert an image to a base64 data URL, auto-resizing if too large.

    Tries Pillow first to progressively downscale oversized images.  If Pillow
    is not installed or resizing still exceeds the limit, falls back to the raw
    bytes and lets the caller handle the size check.

    Returns the base64 data URL string.
    r<   rN   re   r   Nr   )Imageu;   Pillow not installed — cannot auto-resize oversized imagezQImage file is %.1f MB (estimated base64 %.1f MB, limit %.1f MB), auto-resizing...   r7   PNGJPEGr8   z)Pillow cannot open image for resizing: %s)RGBAPRGB)U   F   rd   r      g      ?r6   zResized to %dx%d (attempt %d)formatqualityr   r   r   z4Auto-resized image fits: %.1f MB (quality=%s, %dx%d)z=Auto-resize could not fit image under %.1f MB (best: %.1f MB))statst_sizer   rF   PILr   ioImportErrorrv   infor   rD   r!   modeconvertwidthheightrk   maxrr   resizeLANCZOSBytesIOsaver   r   getvaluer   rw   )r3   r   r   	file_sizeestimated_b64r   r   _ior   
pil_formatout_mimeimgexcquality_steps	prev_dims	candidater|   scalenew_wnew_heffective_scaleqbufsave_kwargsr   s                            r%   _resize_image_for_visionr   ,  s    !!)I]q(3.M(((,Z9MMMx==,,,O -    QRRR0yQQQH	 KKc[)=K+H K02 2 2 8,Z88D+--6J(E11{{|Hjj$$   ?EEE0yQQQH	 VM 9 9kk%  
 %/&$8$8LLgMCJ'II88 "! "!Q;; ECI-..33ECJ.//44E {{sy1}}"$sy.C
_ <==rBB"a"$sz/C	O ;<<bAAu~****eU^U];;CIKK7wOOO 	! 	!A++--C#Z0K})*I&CHS((K(((&s||~~66==gFFG;;;';;I9~~!111R	NNk:AIsz3 3 3 !     	 2	!  V';7Y;9W	Y 	Y 	Y Q0yQQQQs0   
A   9BB+D 
E/E :E Euser_promptmodelc                   #K   | t          |          dk    r|dd         dz   n||dddd|dd}d}d}d}	 dd	lm}  |            rt          d
d          |rz|ry|                                rf	 |                                 t                              d           S # t          $ r'}t          	                    d|d           Y d}~S d}~ww xY wS S S t          
                    d| dd                    t          
                    d|dd                    | }	|	                    d          r|	t          d          d         }	t          t          j                            |	                    }
|
                                r t          
                    d|            |
}d}nt#          |           rt%          |           }|rt'          |d                   t          
                    d           t)          dd          }|dt+          j                     dz  }t/          | |           d{V  d}nt1          d          |                                j        }|dz  }t          
                    d|           t7          |          }|st1          d          t          
                    d           t9          ||           }t          |          dz  }t          
                    d!|           t          |          t:          k    rYt=          ||           }t          |          t:          k    r0t1          d"t          |          d#z  d$d%t:          d#z  d&d'          ||d(<   |}d)d*|d+d,d-|id.gd/g}t          
                    d0           d1}d2}	 dd3lm }m!}  |            } ||d4d5i 6          }|"                    d7          }|tG          |          }|"                    d8          }|tG          |          }n# t          $ r Y nw xY wd5||d9|d:}|r||d;<   	 tI          dUi | d{V }n# t          $ r}tK          |          rt          |          tL          k    rut          
                    d<t          |          d#z  tL          d#z             t=          ||           }||d         d=         d>         d,         d-<   tI          dUi | d{V }n Y d}~nd}~ww xY wtO          |          }|s;t          	                    d?           tI          dUi | d{V }tO          |          }t          |          }t          
                    d@|           d|pdAdB} d|dC<   ||dD<   tP          )                    dE|           tP          *                                 tW          j,        | dFdG          |rz|ry|                                rf	 |                                 t                              d           S # t          $ r'}t          	                    d|d           Y d}~S d}~ww xY wS S S # t          $ r}!dHt[          |!           }"t          .                    dI|"d           t[          |!          /                                #ta          #fdJdKD                       rdL|! }n6ta          #fdMdND                       r| dO|! }ndP#v sd,#v rdQ|! }ndR|! }d|"|dS} |"|dT<   tP          )                    dE|           tP          *                                 tW          j,        | dFdG          cY d}!~!|rz|ry|                                rf	 |                                 t                              d           S # t          $ r'}t          	                    d|d           Y d}~S d}~ww xY wS S S d}!~!ww xY w# |rz|ry|                                rf	 |                                 t                              d           w # t          $ r'}t          	                    d|d           Y d}~w d}~ww xY ww w w xY w)Va  
    Analyze an image from a URL or local file path using vision AI.
    
    This tool accepts either an HTTP/HTTPS URL or a local file path. For URLs,
    it downloads the image first. In both cases, the image is converted to base64
    and processed using Gemini 3 Flash Preview via OpenRouter API.
    
    The user_prompt parameter is expected to be pre-formatted by the calling
    function (typically model_tools.py) to include both full description
    requests and specific questions.
    
    Args:
        image_url (str): The URL or local file path of the image to analyze.
                         Accepts http://, https:// URLs or absolute/relative file paths.
        user_prompt (str): The pre-formatted prompt for the vision model
        model (str): The vision model to use (default: google/gemini-3-flash-preview)
    
    Returns:
        str: JSON string containing the analysis results with the following structure:
             {
                 "success": bool,
                 "analysis": str (defaults to error message if None)
             }
    
    Raises:
        Exception: If download fails, analysis fails, or API key is not set
        
    Note:
        - For URLs, temporary images are stored under $HERMES_HOME/cache/vision/ and cleaned up
        - For local file paths, the file is used directly and NOT deleted
        - Supports common image formats (JPEG, PNG, GIF, WebP, etc.)
       Nz...)rO   r   r   Fr   )
parametersry   successanalysis_length
model_usedimage_size_bytesT)is_interruptedInterrupted)r   zCleaned up temporary image filez#Could not delete temporary file: %srf   zAnalyzing image: %s<   zUser prompt: %sre   zfile://zUsing local image file: %sr[   zDownloading image from URL...zcache/visiontemp_vision_imagestemp_image_r   zKInvalid image source. Provide an HTTP/HTTPS URL or a valid local file path.i   zImage ready (%.1f KB)z8Only real image files are supported for vision analysis.zConverting image to base64...r   z#Image converted to base64 (%.1f KB)z2Image too large for vision API: base64 payload is r   z.1fz MB (limit z.0fzw MB) even after resizing. Install Pillow (`pip install Pillow`) for better auto-resize, or compress the image manually.r   usertext)typer   rO   r'   )r   rO   )rolert   z%Processing image with vision model...g      ^@g?r   r   r   )defaultr\   temperaturei  )taskmessagesr   
max_tokensr\   r   zYAPI rejected image (%.1f MB, likely too large); auto-resizing to ~%.0f MB and retrying...rt   rb   z0Vision LLM returned empty content, retrying oncez(Image analysis completed (%s characters)zIThere was a problem with the request and the image could not be analyzed.)r   analysisr   r   vision_analyze_toolrc   )indentensure_asciizError analyzing image: z%sc              3       K   | ]}|v V  	d S r   r   r   s     r%   r   z&vision_analyze_tool.<locals>.<genexpr>y  s7        4tw      r2   )402insufficientzpayment requiredcreditsbillingzhInsufficient credits or payment required. Please top up your API provider account and try again. Error: c              3       K   | ]}|v V  	d S r   r   r   s     r%   r   z&vision_analyze_tool.<locals>.<genexpr>  s7        T      r2   )zdoes not supportznot support imagecontent_policy
multimodalzunrecognized request argumentzimage inputzO does not support vision or our request was not accepted by the server. Error: r   zThe vision API rejected the image. This can happen when the image is in an unsupported format, corrupted, or still too large after auto-resize. Try a smaller JPEG/PNG and retry. Error: zQThere was a problem with the request and the image could not be analyzed. Error: )r   ry   r   ry   r   )1rF   tools.interruptr   
tool_errorexistsunlinkrv   debugr!   rw   r   r-   r   r   path
expanduseris_filer1   r   rl   r   uuiduuid4r   r   r   r   rM   r   _MAX_BASE64_BYTESr   r    r   r   rp   r   r	   r   _RESIZE_TARGET_BYTESr
   _debuglog_callr   jsondumpsr,   ry   rH   r   )$rO   r   r   debug_call_datatemp_image_pathshould_cleanupdetected_mime_typer   cleanup_errorresolved_url
local_pathr}   temp_dirr   image_size_kbimage_data_urldata_size_kbcomprehensive_promptr   vision_timeoutvision_temperaturer   r   _cfg_vision_cfg_vt_vtempcall_kwargsrX   _api_errr   r   resultr   	error_msgr   s$                                      @r%   r   r     so     N #8;K8H8H38N8N;tt,u44T_
 

  O O N^222222> 	<mU;;;h  	o 	/2H2H2J2J 	&&(((>????   9=SW         		 	 	e 	)9SbS>:::%{4C4'8999 !""9-- 	9'I8L"',,\::;;
 	KK4i@@@(O"NN ++ 	*955G :%gi&8999KK7888%n6JKKH&)Itz||)I)I)IIO!)_=========!NN]  
 +//119(4/+];;;4_EE! 	YWXXX 	34442?N`aaa>**T19<HHH ~!2225+=? ? ?N>""%666 7>**k:D7 7/;?J7 7 7   /?*+  +
  !' 4 
 !,!>&  
$ 	;<<<
  	>>>>>>>>;==D!'$XrJJJK//),,C!&s __]33F!%*6]]" 	 	 	D	  -%
 
  	)#(K 	+::k::::::::HH 	 	 	$X.. N++.BBB@'';7(K8	   ":#/A"C "C "C@NI&q)+6u=!/!>!>+!>!>>>>>>> 	" 099  	>NNMNNN+::k::::::::H3H==Hh-->PPP  o$o
 

 &*	"-<)* 	-???z&???j  	o 	/2H2H2J2J 	&&(((>????   9=SW         		 	 	g  /@ /@ /@6c!ff66	T9t444 a&&,,..     ,
      	B>?B B H      .
      	  6 6236 6 H '))[G-C-C   H+'(+ +   
 
 $- -???z&????????  	o 	/2H2H2J2J 	&&(((>????   9=SW         		 	 	g/@f  	o 	/2H2H2J2J 	&&(((>????   9=SW         		 	 	s    Y	 1.B  
C*CCKY	 $A-P Y	 
PY	 PY	 2Q Y	 
S6BS1,Y	 1S66CY	 #.X
YX>>Y	_C<___ ..^
_'_		___ a6.`%$a%
a/aaaac                  V    	 ddl m}   |             \  }}}|duS # t          $ r Y dS w xY w)zACheck if the configured runtime vision path can resolve a client.r   )resolve_vision_provider_clientNF)agent.auxiliary_clientr$  r!   )r$  	_providerr~   _models       r%   check_vision_requirementsr(    s[    IIIIII$B$B$D$D!	66T!!   uus    
((__main__u   👁️ Vision Tools Modulez(========================================u'   ❌ No auxiliary vision model availablezvConfigure a supported multimodal backend (OpenRouter, Nous, Codex, Anthropic, or a custom OpenAI-compatible endpoint).rb   u   ✅ Vision model availableu#   🛠️ Vision tools ready for use!u&   🐛 Debug mode ENABLED - Session ID: z:   Debug logs will be saved to: ./logs/vision_tools_debug_z.jsonu@   🐛 Debug mode disabled (set VISION_TOOLS_DEBUG=true to enable)z
Basic usage:z.  from vision_tools import vision_analyze_toolz  import asyncior   z  async def main():z)      result = await vision_analyze_tool(z4          image_url='https://example.com/image.jpg',z6          user_prompt='What do you see in this image?'z      )z      print(result)z  asyncio.run(main())z
Example prompts:z0  - 'What architectural style is this building?'z2  - 'Describe the emotions and mood in this image'z+  - 'What text can you read in this image?'z)  - 'Identify any safety hazards visible'z(  - 'What products or brands are shown?'z
Debug mode:z  # Enable debug loggingz   export VISION_TOOLS_DEBUG=truez<  # Debug logs capture all vision analysis calls and resultsz6  # Logs saved to: ./logs/vision_tools_debug_UUID.json)registryr   vision_analyzeu  Inspect an image from a URL, file path, or tool output when you need closer detail than what's visible in the conversation. If the user's image is already attached to the conversation and you can see it, just answer directly — only call this tool for images referenced by URL/path, images returned inside other tool results (browser screenshots, search thumbnails), or when you need a deeper look at a specific region the main model's vision may have missed.objectstringz5Image URL (http/https) or local file path to analyze.)r   descriptionzYour specific question or request about the image to resolve. The AI will automatically provide a complete image description AND answer your specific question.)rO   questionr/  )r   
propertiesrequired)namer.  r   argskwc                     |                      dd          }|                      dd          }d| }t          j        dd                                          pd }t	          |||          S )NrO   r   r/  z]Fully describe and explain everything about this image, then answer the following question:

AUXILIARY_VISION_MODEL)rp   r   r   r   r   )r3  r4  rO   r/  full_promptr   s         r%   _handle_vision_analyzer8    sv    b))Ixx
B''H	-"*	- 	-  I.3399;;CtEy+u===r2   r   Tu   👁️)r2  toolsetschemahandlercheck_fnis_asyncemoji)rN   r   );__doc__r   r  loggingr   r  pathlibr   typingr   r   r   r   urllib.parser   rm   r%  r	   r
   hermes_constantsr   tools.debug_helpersr   tools.website_policyr   	getLogger__name__rv   r	  r   r&   ro   rs   r,   boolr1   rM   rr   r   r   r   r  r  r!   r   r   r   r(  printapi_availableexitactive
session_idtools.registryr*  r   VISION_ANALYZE_SCHEMAr8  registerr   r2   r%   <module>rR     si   :    				        1 1 1 1 1 1 1 1 1 1 1 1 ! ! ! ! ! !  O O O O O O O O + + + + + + , , , , , , 5 5 5 5 5 5		8	$	$	n.B	C	C	C
5    " 5466  . S T    > #    ,d dS dt d# dVZ d d d dN3T 3c 3 3 3 3. $ 8C= TW    8 %  ' 	 d     KO6JgR gR gR(3- gR03gRORgR gR gR gRZ V VVV V 		V V V Vr4     z 
E
'(((	E(OOO .-//M ,7888  G  	H  	H  	HQ*+++	E
/000 } RJv7HJJKKKc6K\cccddddPQQQ	E
	E
:;;;	E
	E"III	E
   	E
5666	E
@AAA	E
BCCC	E)	E
   	E
!"""	E
	E
<===	E
>???	E
7888	E
5666	E
4555	E/	E
$%%%	E
,---	E
HIII	E
BCCC 0 / / / / / / / 	E  !V 
 !  A 	
 	
 !*-   8>c3h >s >y~ > > > >  	 "&
     r2   