
    Zǻi]                       d Z ddlmZ ddlmZmZ ddlmZmZ ddl	m
Z
mZmZ ddlmZmZmZ e
rddlmZ ddlmZ dd	lmZmZ e G d
 d             Ze G d d             Ze G d d             Ze G d d             Ze G d d             Ze G d d             Ze G d d             Ze G d d             Z G d d      Z  G d d      Z!y)z#Data models for the sandbox client.    )annotations)AsyncIteratorIterator)	dataclassfield)TYPE_CHECKINGAnyOptional)SandboxConnectionErrorSandboxOperationErrorSandboxServerReloadError)AsyncSandbox)Sandbox)_AsyncWSStreamControl_WSStreamControlc                  B    e Zd ZU dZded<   ded<   ded<   ed	d       Zy)
ExecutionResultz+Result of executing a command in a sandbox.strstdoutstderrint	exit_codec                     | j                   dk(  S )z.Return True if the command exited with code 0.r   )r   selfs    W/opt/lhia/marcimex/agent/venv/lib/python3.12/site-packages/langsmith/sandbox/_models.pysuccesszExecutionResult.success    s     ~~""    N)returnbool)__name__
__module____qualname____doc____annotations__propertyr    r   r   r   r      s&    5KKN# #r   r   c                  <    e Zd ZU dZdZded<   dZded<   dZded	<   y)
ResourceSpecz%Resource specification for a sandbox.500mr   cpu512MimemoryNOptional[str]storage)r!   r"   r#   r$   r+   r%   r-   r/   r'   r   r   r)   r)   &   s#    /CFC!G]!r   r)   c                  l    e Zd ZU dZded<   ded<   ded<   dZded<   dZded	<   dZded
<   edd       Z	y)Volumea@  Represents a persistent volume.

    Volumes are persistent storage that can be mounted in sandboxes.

    Attributes:
        id: Unique identifier (UUID). Remains constant even if name changes.
            May be None for resources created before ID support was added.
        name: Display name (can be updated).
    r   namesizestorage_classNr.   id
created_at
updated_atc           
          | |j                  dd      |j                  dd      |j                  dd      |j                  d      |j                  d      |j                  d	      
      S )z'Create a Volume from API response dict.r2    r3   unknownr4   defaultr5   r6   r7   )r2   r3   r4   r5   r6   r7   getclsdatas     r   	from_dictzVolume.from_dictB   s^     &"%&),((?I>xx~xx-xx-
 	
r   )r@   dict[str, Any]r   r1   
r!   r"   r#   r$   r%   r5   r6   r7   classmethodrA   r'   r   r   r1   r1   /   sI     I
IB $J$ $J$	
 	
r   r1   c                  &    e Zd ZU dZded<   ded<   y)VolumeMountSpecz:Specification for mounting a volume in a sandbox template.r   volume_name
mount_pathNr!   r"   r#   r$   r%   r'   r   r   rF   rF   O   s    DOr   rF   c                      e Zd ZU dZded<   ded<   ded<    ee      Zded	<   d
Zded<   d
Z	ded<   d
Z
ded<   edd       Zy
)SandboxTemplatea  Represents a SandboxTemplate.

    Templates define the image, resource limits, and volume mounts for sandboxes.
    All other container details are handled by the server with secure defaults.

    Attributes:
        id: Unique identifier (UUID). Remains constant even if name changes.
            May be None for resources created before ID support was added.
        name: Display name (can be updated).
    r   r2   imager)   	resources)default_factoryzlist[VolumeMountSpec]volume_mountsNr.   r5   r6   r7   c                   |j                  di       }|j                  dg       } | |j                  dd      |j                  dd      t        |j                  dd      |j                  d	d
      |j                  d            |D cg c]/  }t        |j                  dd      |j                  dd            1 c}|j                  d      |j                  d      |j                  d            S c c}w )z0Create a SandboxTemplate from API response dict.rM   rO   r2   r9   rL   r:   r+   r*   r-   r,   r/   )r+   r-   r/   rG   rH   )rG   rH   r5   r6   r7   )r2   rL   rM   rO   r5   r6   r7   )r=   r)   rF   )r?   r@   resources_datavolume_mounts_datavms        r   rA   zSandboxTemplate.from_dictl   s     +r2!XXor:&"%((7I.""&&uf5%))(G<&**95 -
 	   "}b 9!vvlB7 xx~xx-xx-#
 	
s   	4C5)r@   rB   r   rK   )r!   r"   r#   r$   r%   r   listrO   r5   r6   r7   rD   rA   r'   r   r   rK   rK   W   sY    	 IJ+0+FM(FB $J$ $J$
 
r   rK   c                  <    e Zd ZU dZded<   dZded<   edd       Zy)	ResourceStatusa  Lightweight provisioning status for any async-created resource.

    Attributes:
        status: Resource lifecycle status. One of "provisioning", "ready", "failed".
        status_message: Human-readable details when status is "failed", None otherwise.
    r   statusNr.   status_messagec                T     | |j                  dd      |j                  d            S )z/Create a ResourceStatus from API response dict.rW   provisioningrX   )rW   rX   r<   r>   s     r   rA   zResourceStatus.from_dict   s,     88Hn588$45
 	
r   )r@   rB   r   rV   )r!   r"   r#   r$   r%   rX   rD   rA   r'   r   r   rV   rV      s*     K$(NM(
 
r   rV   c                  l    e Zd ZU dZded<   ded<   ded<   dZded	<   dZded
<   dZded<   edd       Z	y)Poola  Represents a Sandbox Pool for pre-provisioned sandboxes.

    Pools pre-provision sandboxes from a template for faster startup.
    Instead of waiting for a new sandbox to be created, sandboxes can
    be served from a pre-warmed pool.

    Note: Templates with volume mounts cannot be used in pools.

    Attributes:
        id: Unique identifier (UUID). Remains constant even if name changes.
            May be None for resources created before ID support was added.
        name: Display name (can be updated).
    r   r2   template_namer   replicasNr.   r5   r6   r7   c           
          | |j                  dd      |j                  dd      |j                  dd      |j                  d      |j                  d      |j                  d      	      S )
z%Create a Pool from API response dict.r2   r9   r]   r^   r   r5   r6   r7   )r2   r]   r^   r5   r6   r7   r<   r>   s     r   rA   zPool.from_dict   s^     &"%((?B7XXj!,xx~xx-xx-
 	
r   )r@   rB   r   r\   rC   r'   r   r   r\   r\      sI     IMB $J$ $J$	
 	
r   r\   c                  0    e Zd ZU dZded<   ded<   ded<   y)OutputChunkaf  A single chunk of streaming output from command execution.

    Attributes:
        stream: Either "stdout" or "stderr".
        data: The text content of this chunk (valid UTF-8, server handles
            boundary splitting).
        offset: Byte offset within the stream. Used internally for
            reconnection; users typically don't need this.
    r   streamr@   r   offsetNrI   r'   r   r   ra   ra      s     K
IKr   ra   c                      e Zd ZdZdZdZdZdddd	 	 	 	 	 	 	 	 	 	 	 	 	 ddZdd	Ze	dd
       Z
e	dd       Ze	dd       ZddZddZddZddZe	dd       Ze	dd       ZddZy)CommandHandleaE  Handle to a running command with streaming output and auto-reconnect.

    Iterable, yielding OutputChunk objects (stdout and stderr interleaved
    in arrival order). Access .result after iteration to get the full
    ExecutionResult.

    Auto-reconnect behavior:
    - Server hot-reload (1001 Going Away): reconnect immediately
    - Network error / unexpected close:    reconnect with exponential backoff
    - User called kill():                  do NOT reconnect (propagate error)

    The auto-reconnect is transparent -- the iterator reconnects and
    continues yielding chunks without any user intervention. If all
    reconnect attempts are exhausted, SandboxConnectionError is raised.

    Construction modes (controlled by ``command_id``):
    - **New execution** (``command_id=""``, the default): the constructor
      eagerly reads the server's ``"started"`` message to populate
      ``command_id`` and ``pid`` before returning.
    - **Reconnection** (``command_id`` set): skips the started-message
      read, since reconnect streams don't emit one.

    Example:
        handle = sandbox.run("make build", timeout=600, wait=False)

        for chunk in handle:          # auto-reconnects on transient errors
            print(chunk.data, end="")

        result = handle.result
        print(f"Exit code: {result.exit_code}")
             ?       @r9   r   
command_idstdout_offsetstderr_offsetc                   || _         || _        || _        d | _        d | _        d | _        g | _        g | _        d| _        || _	        || _
        |r|| _        y | j                          y )NF)_stream_control_sandbox_command_id_pid_result_stdout_parts_stderr_parts
_exhausted_last_stdout_offset_last_stderr_offset_consume_startedr   message_streamcontrolsandboxrj   rk   rl   s          r   __init__zCommandHandle.__init__   so     &*.#'	26(*(*#0 #0 
 )D!!#r   c                $   	 t        | j                        }|j	                  d      dk7  r t        d|j	                  d       dd      |j	                  d      | _        |j	                  d	      | _        y
# t        $ r t        dd      w xY w)aH  Eagerly read the 'started' message to populate command_id and pid.

        Blocks briefly until the server sends the started message (arrives
        near-instantly after connection). After this call, command_id and
        pid are available, and the WebSocket is bound to the control object
        (so kill() works).
        -Command stream ended before 'started' messagecommand	operationtypestarted!Expected 'started' message, got ''rj   pidN)nextrn   StopIterationr   r=   rq   rr   r   	first_msgs     r   ry   zCommandHandle._consume_started  s    	T\\*I == I-'3IMM&4I3J!L#  %==6MM%(	  	'?# 	s   A8 8Bc                    | j                   S )z=The server-assigned command ID. Available after construction.rq   r   s    r   rj   zCommandHandle.command_id/       r   c                    | j                   S )z<The process ID on the sandbox. Available after construction.rr   r   s    r   r   zCommandHandle.pid4       yyr   c                r    | j                   | D ]  } | j                   t        dd      | j                   S )zThe final execution result. Blocks until the command completes.

        Drains the remaining stream if not already exhausted, then returns
        the ExecutionResult with aggregated stdout, stderr, and exit_code.
        )Command stream ended without exit messager   r   rs   r   r   _s     r   resultzCommandHandle.result9  sI     << <<';#  ||r   c           	   #    K   | j                   ry| j                  D ]  }|j                  d      }|dv rht        ||d   |j                  dd            }|dk(  r| j                  j                  |d          n| j                  j                  |d          | |d	k(  st        d
j                  | j                        d
j                  | j                        |d         | _	        d| _          y d| _         ywzBIterate over output chunks from the current stream (no reconnect).Nr   )r   r   r@   rc   r   )rb   r@   rc   r   exitr9   r   )r   r   r   T)
rv   rn   r=   ra   rt   appendru   r   joinrs   r   msgmsg_typechunks       r   _iter_streamzCommandHandle._iter_streamJ  s     ??<< 	CwwvH//##V778Q/
 x'&&--c&k:&&--c&k:V#.774#5#56774#5#56!+. 
 #')	* s   BC<"AC<c              #    K   ddl }d}	 	 | j                         D ]  }d}|j                  dk(  r7|j                  t	        |j
                  j                  d            z   | _        n6|j                  t	        |j
                  j                  d            z   | _        |  y# t        $ r}| j                  r| j                  j                  r |dz  }|| j                  kD  rt        d| d      |t        |t              }|s:t        | j                   d|dz
  z  z  | j"                        }|j%                  |       | j&                  J | j(                  j+                  | j&                  | j                  | j                  	      }|j,                  | _        |j                  | _
        d
| _        Y d}~nd}~ww xY ww)aE  Iterate over output chunks, auto-reconnecting on transient errors.

        Reconnect strategy:
        - 1001 Going Away (hot-reload): immediate reconnect, no delay
        - Other SandboxConnectionError:  exponential backoff (0.5s, 1s, 2s...)
        - After kill():                  no reconnect, error propagates
        r   Nr   utf-8   Lost connection  times in succession, giving up   rk   rl   F)timer   rb   rc   lenr@   encoderw   rx   r   ro   killedMAX_AUTO_RECONNECTS
isinstancer   min_BACKOFF_BASE_BACKOFF_MAXsleeprq   rp   	reconnectrn   rv   )r   r   reconnect_attemptsr   eis_hot_reloaddelay
new_handles           r   __iter__zCommandHandle.__iter__e  s     	)(!..0 
 E)*&||x/38<<#!JJ--g6C 40 49<<#!JJ--g6C 40  K
  ) (==T]]%9%9"a'"%(@(@@0*+=*> ?0 1 
 !+1.F G$**a4F4J.KL))E JJu%''333!]]44$$"&":":"&":": 5 

  *11 * 3 3"'7( s0   GBB# "G#
F=-DF83G8F==Gc                R    | j                   r| j                   j                          yy)a/  Send a kill signal to the running command (SIGKILL).

        The server kills the entire process group. The stream will
        subsequently yield an exit message with a non-zero exit code.

        Has no effect if the command has already exited or the
        WebSocket connection is closed.
        Nro   	send_killr   s    r   killzCommandHandle.kill  s      ==MM##% r   c                T    | j                   r| j                   j                  |       yy)zWrite data to the command's stdin.

        Args:
            data: String data to write to stdin.

        Has no effect if the command has already exited or the
        WebSocket connection is closed.
        Nro   
send_inputr   r@   s     r   r   zCommandHandle.send_input  s"     ==MM$$T* r   c                    | j                   S z8Last known stdout byte offset (for manual reconnection).rw   r   s    r   last_stdout_offsetz CommandHandle.last_stdout_offset       '''r   c                    | j                   S z8Last known stderr byte offset (for manual reconnection).rx   r   s    r   last_stderr_offsetz CommandHandle.last_stderr_offset  r   r   c                    | j                   J | j                  j                  | j                   | j                  | j                        S )a  Reconnect to this command from the last known offsets.

        Returns a new handle that resumes output from where this one
        left off. Any output produced while disconnected is replayed
        from the server's ring buffer.

        Returns:
            A new CommandHandle.

        Raises:
            SandboxOperationError: If command_id is not found or
                session expired.
            SandboxConnectionError: If connection to sandbox fails.
        r   rq   rp   r   rw   rx   r   s    r   r   zCommandHandle.reconnect  sM     +++}}&&2222 ' 
 	
r   N)r{   zIterator[dict]r|   zOptional[_WSStreamControl]r}   r   rj   r   rk   r   rl   r   r   Noner   r   r   r.   r   zOptional[int]r   r   )r   zIterator[OutputChunk]r@   r   r   r   r   r   )r   re   )r!   r"   r#   r$   r   r   r   r~   ry   r&   rj   r   r   r   r   r   r   r   r   r   r'   r   r   re   re      s    @ ML $&$ ,$ 	$ $ $ $ 
$<).         65(n
&
+ ( ( ( (
r   re   c                      e Zd ZdZdZdZdZdddd	 	 	 	 	 	 	 	 	 	 	 	 	 ddZdd	Ze	dd
       Z
e	dd       Ze	dd       ZddZddZddZddZe	dd       Ze	dd       ZddZy)AsyncCommandHandlea  Async handle to a running command with streaming output and auto-reconnect.

    Async iterable, yielding OutputChunk objects (stdout and stderr interleaved
    in arrival order). Access .result after iteration to get the full
    ExecutionResult.

    Auto-reconnect behavior:
    - Server hot-reload (1001 Going Away): reconnect immediately
    - Network error / unexpected close:    reconnect with exponential backoff
    - User called kill():                  do NOT reconnect (propagate error)

    Construction modes (controlled by ``command_id``):
    - **New execution** (``command_id=""``, the default): call
      ``await handle._ensure_started()`` after construction to read the
      server's ``"started"`` message and populate ``command_id`` / ``pid``.
    - **Reconnection** (``command_id`` set): skips the started-message
      read, since reconnect streams don't emit one.

    Example:
        handle = await sandbox.run("make build", timeout=600, wait=False)

        async for chunk in handle:    # auto-reconnects on transient errors
            print(chunk.data, end="")

        result = await handle.result
        print(f"Exit code: {result.exit_code}")
    rf   rg   rh   r9   r   ri   c                   || _         || _        || _        d | _        d | _        d | _        g | _        g | _        d| _        || _	        || _
        |r|| _        d| _        y d| _        y )NFT)rn   ro   rp   rq   rr   rs   rt   ru   rv   rw   rx   _startedrz   s          r   r~   zAsyncCommandHandle.__init__  sq     &*.#'	26(*(*#0 #0 
 )D DM!DMr   c                r  K   | j                   ry	 | j                  j                          d{   }|j                  d      dk7  r t	        d|j                  d       dd      |j                  d	      | _        |j                  d
      | _        d| _         y7 l# t        $ r t	        dd      w xY ww)z:Read the 'started' message to populate command_id and pid.Nr   r   r   r   r   r   r   rj   r   T)r   rn   	__anext__StopAsyncIterationr   r=   rq   rr   r   s     r   _ensure_startedz"AsyncCommandHandle._ensure_started  s     ==	"ll4466I == I-'3IMM&4I3J!L#  %==6MM%(	 7! 	'?# 	s,   B7B BB A(B7B B44B7c                    | j                   S )z@The server-assigned command ID. Available after _ensure_started.r   r   s    r   rj   zAsyncCommandHandle.command_id)  r   r   c                    | j                   S )z?The process ID on the sandbox. Available after _ensure_started.r   r   s    r   r   zAsyncCommandHandle.pid.  r   r   c                   K   | j                   | 2 3 d{   }
| j                   t        dd      | j                   S 7 *6 )w)z&The final execution result. Awaitable.Nr   r   r   r   r   s     r   r   zAsyncCommandHandle.result3  sU      <<  a<<';#  ||4s%   AAA A'A AAc           	    B  K   | j                          d{    | j                  ry| j                  2 3 d{   }|j                  d      }|dv rit	        ||d   |j                  dd            }|dk(  r| j
                  j                  |d          n| j                  j                  |d          | |d	k(  st        d
j                  | j
                        d
j                  | j                        |d         | _
        d| _         y7 7 6 d| _        ywr   )r   rv   rn   r=   ra   rt   r   ru   r   r   rs   r   s       r   _aiter_streamz AsyncCommandHandle._aiter_stream@  s    ""$$$?? 	 	#wwvH//##V778Q/
 x'&&--c&k:&&--c&k:V#.774#5#56774#5#56!+. 
 #'/ 	%	* s7   DDDDDDBDADD	Dc                 K   ddl }d}	 	 | j                         2 3 d{   }d}|j                  dk(  r7|j                  t	        |j
                  j                  d            z   | _        n6|j                  t	        |j
                  j                  d            z   | _        | 7 6 y# t        $ r"}| j                  r| j                  j                  r |dz  }|| j                  kD  rt        d| d      |t        |t              }|sCt        | j                   d|dz
  z  z  | j"                        }|j%                  |       d{  7   | j&                  J | j(                  j+                  | j&                  | j                  | j                  	       d{  7  }|j,                  | _        |j                  | _
        d
| _        Y d}~nd}~ww xY wӭw)z6Async iterate with auto-reconnect on transient errors.r   Nr   r   r   r   r   r   r   F)asyncior   rb   rc   r   r@   r   rw   rx   r   ro   r   r   r   r   r   r   r   r   rq   rp   r   rn   rv   )r   r   r   r   r   r   r   r   s           r   	__aiter__zAsyncCommandHandle.__aiter__\  s    )(#'#5#5#7 
  
 %)*&||x/38<<#!JJ--g6C 40 49<<#!JJ--g6C 40  K
 #7 ) (==T]]%9%9"a'"%(@(@@0*+=*> ?3 4 
 !+1.F G$**a4F4J.KL))E "--...''333#'==#:#:$$"&":":"&":": $; $  

  *11 * 3 3"'7( si   GB, B*B(B*BB, (B**B, +G,
G6BGEAGF" .GGGGc                n   K   | j                   r#| j                   j                          d{    yy7 w)z*Send a kill signal to the running command.Nr   r   s    r   r   zAsyncCommandHandle.kill  s+     ==--))+++ +s   *535c                p   K   | j                   r$| j                   j                  |       d{    yy7 w)z"Write data to the command's stdin.Nr   r   s     r   r   zAsyncCommandHandle.send_input  s-     ==--**4000 0s   +646c                    | j                   S r   r   r   s    r   r   z%AsyncCommandHandle.last_stdout_offset  r   r   c                    | j                   S r   r   r   s    r   r   z%AsyncCommandHandle.last_stderr_offset  r   r   c                   K   | j                   J | j                  j                  | j                   | j                  | j                         d{   S 7 w)z6Reconnect to this command from the last known offsets.Nr   r   r   s    r   r   zAsyncCommandHandle.reconnect  sY     +++]],,2222 - 
 
 	
 
s   AAAAN)r{   zAsyncIterator[dict]r|   zOptional[_AsyncWSStreamControl]r}   r   rj   r   rk   r   rl   r   r   r   r   r   r   r   )r   zAsyncIterator[OutputChunk]r   r   )r   r   )r!   r"   r#   r$   r   r   r   r~   r   r&   rj   r   r   r   r   r   r   r   r   r   r'   r   r   r   r     s    8 ML "+" 1" 	" " " " 
">(       
 
8/(b,
1
 ( ( ( (
r   r   N)"r$   
__future__r   collections.abcr   r   dataclassesr   r   typingr   r	   r
   langsmith.sandbox._exceptionsr   r   r    langsmith.sandbox._async_sandboxr   langsmith.sandbox._sandboxr   langsmith.sandbox._ws_executer   r   r   r)   r1   rF   rK   rV   r\   ra   re   r   r'   r   r   <module>r      s   ) " 3 ( / /  =2 
# 
# 
# " " " 
 
 
>    +
 +
 +
\ 
 
 
(  
  
  
P    }
 }
@S
 S
r   