
    `gjn)                       d Z ddlmZ ddlZddlZddlZddlmZ 	 ddlZddlm	Z	 dZ
ddlmZmZ  ej                  e      Z eh d	      Zd
ZdZdZdddZddZddZddZeedf	 	 	 	 	 	 	 	 	 ddZg dZy# e$ r	 dZdZ	dZ
Y ^w xY w)a  HTTP server that forwards OpenAI-compatible requests to a configured upstream.

Listens on ``http://<host>:<port>/v1/<path>`` and forwards each request to
``<upstream-base-url>/<path>`` with the client's ``Authorization`` header
replaced by a freshly-resolved bearer from the configured adapter. The
response is streamed back unmodified, preserving SSE.

The server is intentionally minimal: it does NOT mediate, log, transform,
or rewrite request/response bodies. It's a credential-attaching forwarder.
    )annotationsN)Optional)webTF)UpstreamAdapterUpstreamCredential>   
keep-alivetransfer-encodingproxy-authenticateproxy-authorizationtehostupgradetrailers
connectionauthorizationcontent-lengthi!  z	127.0.0.1i c                @    d|||di}t        j                  ||       S )z+Return an OpenAI-style error JSON response.error)messagetypecode)status)r   json_response)r   r   r   bodys       J/root/.hermes/venv/lib/python3.12/site-packages/hermes_cli/proxy/server.py_json_errorr   ;   s&    $EFDT&11    c                r    i }| j                         D ]!  \  }}|j                         t        v r|||<   # |S )z9Strip hop-by-hop + auth headers from the inbound request.itemslower_HOP_BY_HOP_HEADERSheadersoutkeyvalues       r   _filter_request_headersr(   A   sB    
Cmmo 
U99;--C Jr   c                    i }| j                         D ]4  \  }}|j                         t        v r|j                         dv r0|||<   6 |S )z4Strip hop-by-hop headers from the upstream response.>   content-encodingr   r   r#   s       r   _filter_response_headersr+   K   sT    
Cmmo 
U99;--99;@@C Jr   c                &    t         st        d      t        j                  t              }t        j
                  dt              } ||<   d	 fd}d
 fd}|j                  j                  d|       |j                  j                  dd|       |S )zCBuild the aiohttp application bound to a specific upstream adapter.uaiohttp is required for `hermes proxy`. Install with: pip install 'hermes-agent[messaging]' or `pip install aiohttp`.)client_max_sizeadapterc                n   K   t        j                  dj                  j                         d      S w)Nok)r   upstreamauthenticated)r   r   display_nameis_authenticated)requestr/   s    r   handle_healthz!create_app.<locals>.handle_healthf   s7       #00!(!9!9!;
 	
s   25c                L   K    j                   j                  dd      dj                  d      z   j                  vr8dj	                  t        j                              }t        dd d| d	      S 	 j                         } j                          d {   t        j                  d dd      d fddfd} ||       d {   \  }}||S |}|j                  dv ra	 j!                  ||j                        }|A|j#                          |j%                          d {     ||       d {   \  }}||S |}t'        j(                  |j                  t+        |j,                              }	|	j/                          d {    	 |j0                  j3                         2 3 d {   }
|
s|	j5                  |
       d {    &# t        $ r7}t        j                  d
|       t        dt        |      d	      cY d }~S d }~ww xY w7 7 Q# t        $ r#}t        j                  d|       d }Y d }~Ad }~ww xY w7 $7 7 7 7 6 nF# t        j6                  t8        j:                  f$ r }t        j                  d|       Y d }~nd }~ww xY w|j#                          |j%                          d {  7   n.# |j#                          |j%                          d {  7   w xY w|	j=                          d {  7   |	S w)Ntail /z, i  zPath /v1z* is not forwarded by this proxy. Allowed: path_not_allowedr   z'proxy: credential resolution failed: %s  upstream_auth_failed   i,  )totalsock_connect	sock_readc           	     d  K   | j                   j                  d        }j                  r| dj                   }t        j                        }| j
                   d| j                   |d<   t        j                  dj                  |t                     	 t        j                  	      }	 |j                  j                  |rnd |d	       d {   }||fS # t        $ r}t        d|       |d }~ww xY w7 (# t        $ r |j!                          d {  7    w xY ww)
Nr;   ? Authorizationz-proxy: forwarding %s %s -> %s (body=%d bytes))timeoutzproxy session init failed: F)datar$   allow_redirects)base_urlrstripquery_stringr(   r$   
token_typebearerloggerdebugmethodlenaiohttpClientSession	ExceptionRuntimeErrorr6   close)
active_credupstream_urlfwd_headerssessionexcupstream_respr   rel_pathr6   rH   s
         r   _send_upstreamz8create_app.<locals>.handle_proxy.<locals>._send_upstream   s?    )2299#>?zJL##".q1E1E0FG1'//BK.9.D.D-EQ{GYGYFZ+[K(LL?,D	
Q!//@
&-ooNN !%4'$) '6 ' ! M))  Q"%@#FGSPQ!  mmo%%sZ   BD0 C' 7'D	 DD	 #D0'	D0C??DD0D	 	D-%D(&D--D0c                \  K   	  |        d {   S 7 # t         $ r!}t        dt        |            d fcY d }~S d }~wt        j                  $ r3}t
        j                  d|       t        dd| d      d fcY d }~S d }~wt        j                  $ r t        ddd	      d fcY S w xY ww)
Ni  z%proxy: upstream connection failed: %si  zupstream connection failed: upstream_unreachabler=   i  zupstream request timed outupstream_timeout)	rW   r   strrT   ClientErrorrP   warningasyncioTimeoutError)rY   r]   r`   s     r   _open_upstreamz8create_app.<locals>.handle_proxy.<locals>._open_upstream   s     +K8888 8"3C1477&& 	FL6se<3
   '' 4/
  s\   B,  B, 	B)<B)B,B)(B :B);B, &B)&B,(B))B,>     r>   )failed_credentialstatus_codez-proxy: retry credential resolution failed: %s)r   r$   z proxy: streaming interrupted: %s)rY   r   )
match_infogetlstripallowed_pathsjoinsortedr   get_credentialrV   rP   rf   rd   readrT   ClientTimeoutr   get_retry_credentialreleaserX   r   StreamResponser+   r$   preparecontentiter_anywritere   rg   CancelledError	write_eof)r6   allowedcredr]   ri   session_or_responser^   r\   
retry_credrespchunkr`   r   r_   rH   r/   s   `          @@@@r   handle_proxyz create_app.<locals>.handle_proxyo   s    %%))&"5--7000iiw'<'< =>G8* %#9&'	 	K))+D \\^#''dsS	* 	*@	2 4B$3G-G*] &&%:-"$99&* - 4 4 : 
 %%%'mmo%%;I*;U5U2#] (..- !! '',]-B-BC
 ll7###	",44==? , ,e**U+++M  	KNNDcJsCH3IJJ	K $z .H  "NPST!
" &5U 	$,+  @ ##W%;%;< 	DNN=sCC	D !!#--/!! !!#--/!!nnsK  A7L$ G% L$#H($6L$H+L$7H. %L$9I:L$	I 
AL$I# L$%I+ ?I)I%I)I+ I+ I' I+ %	H%.,H H%L$ H%%L$+L$.	I7IL$IL$ L$#L$%I)'I+ )I+ *K +#J.J)$K )J..K 1#L$KL$$L?L LL$LL$z/health*z/v1/{tail:.*})r6   'web.Request'return'web.Response')r6   r   r   z'web.StreamResponse')
AIOHTTP_AVAILABLErW   r   ApplicationMAX_REQUEST_BYTESAppKeyr   routeradd_get	add_route)r/   app_adapter_keyr7   r   s   `    r   
create_appr   X   s    N
 	

 //*;
<C ::i9LC
~B JJy-0JJo|<Jr   c                H  K   t         st        d      t        |       }t        j                  |d      }|j                          d{    t        j                  |||      }|j                          d{    t        j                  d||| j                         |xs t        j                         }|Vt        j                         }t        j                  t        j                   fD ]  }		 |j#                  |	|j$                         ! 	 |j)                          d{    t        j                  d       |j+                          d{    y7 7 # t&        $ r Y yw xY w7 F7 # t        j                  d       |j+                          d{  7   w xY ww)zRun the proxy in the current event loop until shutdown_event is set.

    If shutdown_event is None, runs until cancelled (Ctrl+C or SIGTERM).
    r-   N)
access_log)r   portz)proxy: listening on http://%s:%d/v1 -> %szproxy: shutting down)r   rW   r   r   	AppRunnersetupTCPSitestartrP   infor4   rg   Eventget_running_loopsignalSIGINTSIGTERMadd_signal_handlersetNotImplementedErrorwaitcleanup)
r/   r   r   shutdown_eventr   runnersite
stop_eventloopsigs
             r   
run_serverr      s_     N
 	

 W
C]]340F
,,.;;vDt4D
**,
KK3dG((
  27==?J '')MM6>>2 	C''Z^^<	oo*+nn5  '   	  	 	*+nns   AF"	E
/F"9E:A7F"2EF"E/ $E+%E/ )(F"E-F"F"	E(%F"'E((F"+E/ -F"/)FFFF")r   r   DEFAULT_HOSTDEFAULT_PORTr   )proxy_error)r   intr   rd   r   rd   r   r   )r$   z'aiohttp.typedefs.LooseHeaders'r   dict)r   r   )r/   r   r   z'web.Application')
r/   r   r   rd   r   r   r   zOptional[asyncio.Event]r   None)__doc__
__future__r   rg   loggingr   typingr   rT   r   r   ImportErrorhermes_cli.proxy.adapters.baser   r   	getLogger__name__rP   	frozensetr"   r   r   r   r   r(   r+   r   r   __all__ r   r   <module>r      s   	 #     O			8	$
       2
\B .2	,,
, , ,	,
 
,^_  G
Cs   A9 9BB