
    j2:                    .   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 ddlmZ erddlmZmZ  ej        e          ZdZdZdZd	Zd
ZdZddZd dZ G d d          ZdZ d!dZ!d Z" G d d          Z# G d d          Z$dS )"a  TCP tunnel for accessing services running inside sandboxes.

Establishes a WebSocket connection to the daemon's ``/tunnel`` endpoint,
runs a yamux multiplexing session on top, and forwards local TCP connections
through yamux streams to the target port inside the sandbox.
    )annotationsN)Mapping)TYPE_CHECKINGAnyOptional)merge_headers)YamuxSessionYamuxStream         z>BHstreamr
   portintreturnNonec                l    |                      t          j        t          t          |                     dS )zAWrite the 3-byte connect header on a freshly opened yamux stream.N)writestructpack_CONNECT_HEADER_FMTPROTOCOL_VERSION)r   r   s     c/lsinfo/ai/hellotax_ai/base_platform/venv/lib/python3.11/site-packages/langsmith/sandbox/_tunnel.py_write_connect_headerr   (   s*    
LL02BDIIJJJJJ    c                ^    |                      d          }|st          d          |d         S )z0Read the 1-byte status response from the daemon.r   z'tunnel: connection closed before statusr   )readConnectionError)r   datas     r   _read_statusr    -   s2    ;;q>>D IGHHH7Nr   c                  2    e Zd ZdZddZdd
ZddZddZdS )
_WSAdaptera
  Adapts the ``websockets`` message API to a byte-stream interface.

    yamux requires a plain read/write/close byte stream.  WebSocket is
    message-based, so this adapter buffers partially consumed messages on
    reads and sends one binary message per write.
    wsr   r   r   c                j    || _         t                      | _        t          j                    | _        d S N)_ws	bytearray_buf	threadingLock_write_lock)selfr#   s     r   __init__z_WSAdapter.__init__B   s*    KK	$>++r   nr   bytesc                j   t          | j                  |k     rt| j                                        }t	          |t
                    r|                                }| j                            |           t          | j                  |k     tt          | j        d |                   }| j        d |= |S r%   )	lenr(   r&   recv
isinstancestrencodeextendr/   )r,   r.   msgresults       r   r   z_WSAdapter.readG   s    $)nnq  (--//C#s## #jjllIS!!!	 $)nnq   ty!}%%IbqbMr   r   c                    | j         5  | j                            |           d d d            n# 1 swxY w Y   t          |          S r%   )r+   r&   sendr1   )r,   r   s     r   r   z_WSAdapter.writeR   s     	  	 HMM$	  	  	  	  	  	  	  	  	  	  	  	  	  	  	 4yys   /33c                \    	 | j                                          d S # t          $ r Y d S w xY wr%   )r&   close	Exceptionr,   s    r   r<   z_WSAdapter.closeW   sA    	HNN 	 	 	DD	s    
++N)r#   r   r   r   )r.   r   r   r/   )r   r/   r   r   r   r   )__name__
__module____qualname____doc__r-   r   r   r<    r   r   r"   r"   :   sn         , , , ,
	 	 	 	   
     r   r"   i @  tcp_connsocket.socketc                t    t          j                    d
 fd}d
 fd}t          j        |d          }t          j        |d          }|                                 |                                                                  	                                   n# t          $ r Y nw xY w	                     t          j	                   n# t          $ r Y nw xY w	                                  n# t          $ r Y nw xY w|                    d           |                    d           d	S )z:Copy data bidirectionally until one side closes or errors.r   r   c                     	 	                      t                    } | sn                    |            3n# t          $ r Y nw xY w                                 d S #                                  w xY wr%   )r   _BRIDGE_BUF_SIZEsendallr=   setr   doner   rE   s    r   _stream_to_tcpz_bridge.<locals>._stream_to_tcpi   s    		'{{#344   &&&	'  	 	 	D	 HHJJJJJDHHJJJJ&   48 A 
AA AA A4c                     	 	                      t                    } | sn                    |            3n# t          $ r Y nw xY w                                 d S #                                  w xY wr%   )r2   rI   r   r=   rK   rL   s    r   _tcp_to_streamz_bridge.<locals>._tcp_to_streamu   s    		#}}%566 T"""	#  	 	 	D	 HHJJJJJDHHJJJJrO   T)targetdaemon   )timeoutNr?   )r)   EventThreadstartwaitr<   r=   shutdownsocket	SHUT_RDWROSErrorjoin)r   rE   rN   rQ   t1t2rM   s   ``    @r   _bridgera   e   s   ?D
 
 
 
 
 
 
 

 
 
 
 
 
 
 
 
		=	=	=B			=	=	=BHHJJJHHJJJIIKKK   &*++++       GGAGGGAGs6   B% %
B21B26C 
C#"C#'C< <
D	D	c                 P    	 ddl m}  | S # t          $ r t          d          dw xY w)z5Import websockets sync client or raise a clear error.r   )connectz_TCP tunnel requires the 'websockets' package. Install it with: pip install 'langsmith[sandbox]'N)websockets.sync.clientrc   ImportError)
ws_connects    r   _ensure_websocketsrg      sW    @@@@@@   @
 
 	s   
 %c                      e Zd ZdZdZdZddddd&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Zd*d Zd*d!Zd,d$Zd-d%ZdS ).Tunnela  TCP tunnel to a port inside a sandbox.

    Opens a local TCP listener and forwards each accepted connection through
    a yamux-multiplexed WebSocket to the daemon, which dials the target port
    inside the sandbox.

    Typically used as a context manager::

        with sandbox.tunnel(remote_port=5432) as t:
            conn = psycopg2.connect(host="127.0.0.1", port=t.local_port)

    Or with explicit lifecycle::

        t = sandbox.tunnel(remote_port=5432)
        # ... use tunnel ...
        t.close()
          ?g       @r   r   N
local_portmax_reconnectsheadersdataplane_urlr4   api_keyOptional[str]remote_portr   rl   rm   rn   Optional[Mapping[str, str]]r   r   c                   || _         || _        || _        || _        |p|| _        | j        | _        || _        d | _        d | _        d | _	        d | _
        t          j                    | _        d| _        d| _        d S )NF)_dataplane_url_api_key_headers_remote_port_requested_local_port_local_port_max_reconnectsr&   _yamux_server_socket_accept_threadr)   r*   _reconnect_lock_closed_startedr,   ro   rp   rr   rl   rm   rn   s          r   r-   zTunnel.__init__   s     ,'%/%>;"5-.27;:>(~//r   c                    | j         S )z&Local port the tunnel is listening on.)rz   r>   s    r   rl   zTunnel.local_port   s     r   c                    | j         S )z4Port inside the sandbox that the tunnel connects to.)rx   r>   s    r   rr   zTunnel.remote_port   s       r   c                    | S r%   rD   r>   s    r   	__enter__zTunnel.__enter__   s    r   argsobjectc                .    |                                   d S r%   )r<   )r,   r   s     r   __exit__zTunnel.__exit__   s    

r   c                    | j         rd S d| _         	 |                                  d S # t          $ r |                                   w xY w)NT)r   	_do_startr=   r<   r>   s    r   _startzTunnel._start   s^    = 	F	NN 	 	 	JJLLL	s	   (  Ac                   |                                   t          j        t          j        t          j                  | _        | j                            t          j        t          j        d           | j        }|dk    r&t          j        t          j        t          j                  }	 |	                    d           |
                    d|f           |                                 t          d| d          # t          $ r Y nMt          $ rA}dt          |          v rn%dt          |                                          v r 	 Y d }~nd }~ww xY w	 |                                 n:# t          $ r Y n.w xY w# 	 |                                 w # t          $ r Y w w xY wxY w| j                            d| j        f           | j                            d	           | j                                        d         | _        t)          j        | j        d
d          | _        | j                                         d S )Nr   r   rj   z	127.0.0.1zPort zE is already in use by another service. Choose a different local_port.zConnection refusedzalready in use   Tztunnel-accept)rR   rS   name)_connectr[   AF_INETSOCK_STREAMr}   
setsockopt
SOL_SOCKETSO_REUSEADDRry   
settimeoutrc   r<   r]   ConnectionRefusedErrorr4   lowerbindlistengetsocknamerz   r)   rW   _accept_loopr~   rX   )r,   r   probees       r   r   zTunnel._do_start   sH   $mFNF<NOO&&v'8&:MqQQQ
 )199M&.&2DEEE  %%%{D12226D 6 6 6   *      '3q6611%Q77KKMMMM   DKKMMMM   D 	  +t/I!JKKK""3'''.::<<Q?'.$T
 
 
 	!!#####sm   +AC> >
EE= 
	E7E
E= EE= E- -
E:9E:=F$?FF$
F!F$ F!!F$c                p   ddl m} | j        }|r&	 |                                 n# t          $ r Y nw xY wt                      }|                                 }t          | j        r	d| j        ind| j	                  } |||ddd          | _
        t          | j
                  } ||          | _        dS )z:Establish (or re-establish) the WebSocket + yamux session.r   )r	   z	X-Api-KeyN   rT   )additional_headersopen_timeoutclose_timeoutping_interval)langsmith.sandbox._yamuxr	   r|   r<   r=   rg   _build_ws_urlr   rv   rw   r&   r"   )r,   r	   	old_yamuxrf   ws_urlrn   adapters          r   r   zTunnel._connect&  s    999999K	 	!!!!    ())
##%%,0MC[$-((tM
 

 :&
 
 
 TX&&"l7++s   & 
33r	   c                X   ddl m} | j        r| j        j        s| j        S | j        5  | j        r| j        j        s| j        cddd           S d}t          | j                  D ]}	 |                                  t          	                    d|dz              | j        c cddd           S # t          $ rN}|}|| j        dz
  k     r4t          | j        d|z  z  | j                  }t          j        |           Y d}~d}~ww xY w |d| j         d          |# 1 swxY w Y   dS )	z4Return a live yamux session, reconnecting if needed.r   )TunnelErrorNz tunnel: reconnected (attempt %d)r   r   ztunnel: reconnect failed after z	 attempts)langsmith.sandbox._exceptionsr   r|   	is_closedr   ranger{   r   loggerdebugr=   min_BACKOFF_BASE_BACKOFF_MAXtimesleep)r,   r   last_errattemptexcdelays         r   _ensure_sessionzTunnel._ensure_sessionC  s   ======; 	t{4 	;! 	 	{ #4;#8 #{	 	 	 	 	 	 	 	 -1H !566 * **MMOOOLL!CWq[QQQ;&&	 	 	 	 	 	 	 	 ! * * *"H!5!999 # .!W*= -! ! 
5)))* +Q$2FQQQ '	 	 	 	 	 	 	 	 	 	sB   DD(8B/ D/
D9AD=DDDD#&D#c                    | j         rdS d| _         | j        r+	 | j                                         n# t          $ r Y nw xY w| j        r| j                                         dS dS )z.Shut down the tunnel, closing all connections.NT)r   r}   r<   r]   r|   r>   s    r   r<   zTunnel.closea  s    < 	F 	#))++++    ; 	 K	  	 s   3 
A A c                    | j         sh	 | j                                        \  }}n# t          $ r Y d S w xY wt	          j        | j        |fdd                                           | j         fd S d S )NTztunnel-bridge)rR   r   rS   r   )r   r}   acceptr]   r)   rW   _handle_connrX   )r,   conn_s      r   r   zTunnel._accept_loopr  s    , 
	-4466aa   (W$	  
 eggg , 
	 
	 
	 
	 
	s   & 
44rE   rF   c                ,   	 |                                  }|                                }t          || j                   t	          |          }|t
          k    rt          ||           d S |                                 |                                 |t          k    r"t          
                    d| j                   d S |t          k    r"t          
                    d| j                   d S |t          k    r"t          
                    dt                     d S t          
                    d|           d S # t          $ rR}t                              d|           	 |                                 n# t           $ r Y n
w xY wY d }~d S Y d }~d S d }~ww xY w)Nz%tunnel: port %d not allowed by daemonz3tunnel: nothing listening on port %d inside sandboxz.tunnel: protocol version mismatch (client v%d)ztunnel: unknown status %dz$tunnel: connection handler error: %s)r   open_streamr   rx   r    	STATUS_OKra   r<   STATUS_PORT_NOT_ALLOWEDr   warningSTATUS_DIAL_FAILEDSTATUS_UNSUPPORTED_VERSIONr   r=   r   r]   )r,   rE   sessionr   statusr   s         r   r   zTunnel._handle_conn  s   $	**,,G((**F!&$*;<<<!&))F"")))LLNNNNN000;%     ---I%     555D$    
 :FCCCCC 	 	 	LL?EEE        !     	sU   A'D7 +AD7  +D7 -+D7 D7 7
FFE21F2
E?<F>E??FFc                    | j                             d          }|                    dd                              dd          }| dS )N/zhttps://zwss://zhttp://zws://z/tunnel)ru   rstripreplace)r,   urls     r   r   zTunnel._build_ws_url  sG    !((--kk*h//77	7KKr   ro   r4   rp   rq   rr   r   rl   r   rm   r   rn   rs   r   r   r   r   )r   ri   r   r   r   r   r?   )r   r	   )rE   rF   r   r   )r   r4   )r@   rA   rB   rC   r   r   r-   propertyrl   rr   r   r   r   r   r   r   r<   r   r   r   rD   r   r   ri   ri      s]        $ ML /3     4       X  ! ! ! X!      
	 	 	 	*$ *$ *$ *$X, , , ,:   <       "   % % % %N     r   ri   c                  l    e Zd ZdZddddddZedd            Zedd            ZddZddZ	ddZ
dS )AsyncTunnelaw  Async wrapper around :class:`Tunnel`.

    The underlying tunnel runs in background threads (TCP listener + bridges);
    async context-manager methods delegate to the sync tunnel via the event
    loop's executor.

    Usage::

        async with await sandbox.tunnel(remote_port=5432) as t:
            conn = await asyncpg.connect(host="127.0.0.1", port=t.local_port)
    r   r   Nrk   ro   r4   rp   rq   rr   r   rl   rm   rn   rs   r   r   c               :    t          ||||||          | _        d S )Nrk   )ri   _tunnelr   s          r   r-   zAsyncTunnel.__init__  s0     !)
 
 
r   c                    | j         j        S r%   )r   rl   r>   s    r   rl   zAsyncTunnel.local_port  s    |&&r   c                    | j         j        S r%   )r   rr   r>   s    r   rr   zAsyncTunnel.remote_port  s    |''r   c                |   K   t          j                    }|                    d | j        j                   d {V  | S r%   )asyncioget_running_looprun_in_executorr   r   )r,   loops     r   
__aenter__zAsyncTunnel.__aenter__  sF      '))""4)<=========r   r   r   c                |   K   t          j                    }|                    d | j        j                   d {V  d S r%   )r   r   r   r   r<   )r,   r   r   s      r   	__aexit__zAsyncTunnel.__aexit__  sG      '))""4);<<<<<<<<<<<r   c                8    | j                                          dS )z;Shut down the tunnel (sync, safe to call from any context).N)r   r<   r>   s    r   r<   zAsyncTunnel.close  s    r   r   r   )r   r   r   r?   )r@   rA   rB   rC   r-   r   rl   rr   r   r   r<   rD   r   r   r   r     s        
 
$ /3
 
 
 
 
 
& ' ' ' X' ( ( ( X(   
= = = =     r   r   )r   r
   r   r   r   r   )r   r
   r   r   )r   r
   rE   rF   r   r   )%rC   
__future__r   r   loggingr[   r   r)   r   collections.abcr   typingr   r   r   langsmith.sandbox._helpersr   r   r	   r
   	getLoggerr@   r   r   r   r   r   r   r   r   r    r"   rI   ra   rg   ri   r   rD   r   r   <module>r      s    # " " " " "          # # # # # # / / / / / / / / / / 4 4 4 4 4 4 CBBBBBBBB		8	$	$  	  !  K K K K
   ! ! ! ! ! ! ! !P  1 1 1 1r
 
 
~ ~ ~ ~ ~ ~ ~ ~L3 3 3 3 3 3 3 3 3 3r   