
    `gjC                        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ZddlZddlm	Z	 ddl
mZmZ ddlmZ ddlmZmZmZmZ ddlmZmZmZmZmZ  ej4                  e      Z e       dz  Zd	Zd
efdZ ded
dfdZ!de"d
e"fdZ#de"d
e$e"dz  e%f   fdZ&de"de"d
dfdZ'dde"de"dz  d
dfdZ(ddZ)ded
efdZ* G d d      Z+ G d de      Z,y)zModal cloud execution environment using the native Modal SDK directly.

Uses ``Sandbox.create()`` + ``Sandbox.exec()`` instead of the older runtime
wrapper, while preserving Hermes' persistent snapshot behavior across sessions.
    N)Path)AnyOptional)get_hermes_home)BaseEnvironment_ThreadedProcessHandle_load_json_store_save_json_store)FileSyncManageriter_sync_filesquoted_mkdir_commandquoted_rm_commandunique_parent_dirszmodal_snapshots.jsondirectreturnc                       t        t              S N)r	   _SNAPSHOT_STORE     K/root/.hermes/venv/lib/python3.12/site-packages/tools/environments/modal.py_load_snapshotsr   &   s    O,,r   datac                 $    t        t        |        y r   )r
   r   )r   s    r   _save_snapshotsr   *   s    _d+r   task_idc                     t          d|  S )N:)_DIRECT_SNAPSHOT_NAMESPACE)r   s    r   _direct_snapshot_keyr    .   s    ()7)44r   c                     t               }t        |       }|j                  |      }t        |t              r|r|dfS |j                  |       }t        |t              r|r|dfS y)NFT)NF)r   r    get
isinstancestr)r   	snapshotsnamespaced_keysnapshot_idlegacy_snapshot_ids        r   _get_snapshot_restore_candidater)   2   sd    !I)'2N--/K+s#E!!"w/$c*/A!4''r   r'   c                 n    t               }||t        |       <   |j                  | d        t        |       y r   )r   r    popr   )r   r'   r%   s      r   _store_direct_snapshotr,   >   s0    !I/:I"7+,MM'4 Ir   c                     t               }d}t        |       | fD ]2  }|j                  |      }||||k(  s|j                  |d        d}4 |rt	        |       y y )NFT)r   r    r"   r+   r   )r   r'   r%   updatedkeyvalues         r   _delete_direct_snapshotr1   E   sp    !IG$W-w7 c"=%;"6MM#t$G 	" r   c                      	 ddl m}   | dd       y# t        $ r Y yt        $ r}t        t	        |            d}~ww xY w)uG   Lazy-install modal on demand. Idempotent — fast no-op once installed.r   )ensurezterminal.modalF)promptN)tools.lazy_depsr3   ImportError	Exceptionr$   )_lazy_ensurees     r   _ensure_modal_sdkr:   S   s?    ":%e4  "#a&!!"s    	??:?
image_specc                 L   t                ddl}t        | t              s| S | j	                  d      r|j
                  j                  |       S | j                         t        fddD              }dg}|r|j                  dd       |j
                  j                  | |      S )	zConvert registry references or snapshot ids into Modal image objects.

    Includes add_python support for ubuntu/debian images (absorbed from PR 4511).
    r   Nzim-c              3   &   K   | ]  }|v  
 y wr   r   ).0baselowers     r   	<genexpr>z'_resolve_modal_image.<locals>.<genexpr>n   s     DtTU]Ds   )ubuntudebianzRUN rm -rf /usr/local/lib/python*/site-packages/pip* 2>/dev/null; python -m ensurepip --upgrade --default-pip 2>/dev/null || truez^RUN apt-get update -qq && apt-get install -y -qq python3 python3-venv > /dev/null 2>&1 || true)setup_dockerfile_commands)r:   modalr#   r$   
startswithImagefrom_idr@   anyinsertfrom_registry)r;   _modal
add_pythonsetup_commandsr@   s       @r   _resolve_modal_imagerO   ^   s    
 j#&U#||##J// ED/CDDJ	JN al	
 <<%%"0 &  r   c                   0    e Zd ZdZd Zd Zd ZddZd Zy)	_AsyncWorkerzEBackground thread with its own event loop for async-safe Modal calls.c                 R    d | _         d | _        t        j                         | _        y r   )_loop_thread	threadingEvent_startedselfs    r   __init__z_AsyncWorker.__init__   s    :>
37!)r   c                     t        j                  | j                  d      | _        | j                  j	                          | j
                  j                  d       y )NT)targetdaemon   timeout)rU   Thread	_run_looprT   startrW   waitrX   s    r   rc   z_AsyncWorker.start   s?     ''t~~dK2&r   c                     t        j                         | _        t        j                  | j                         | j                  j                          | j                  j                          y r   )asyncionew_event_looprS   set_event_looprW   setrun_foreverrX   s    r   rb   z_AsyncWorker._run_loop   sD    ++-
tzz*

 r   c                    ddl m} | j                  | j                  j                         r0t	        j
                  |      r|j                          t        d       ||| j                        }|t        d      |j                  |      S )Nr   )safe_schedule_threadsafezAsyncWorker loop is not runningr_   )	agent.async_utilsrl   rS   	is_closedrf   iscoroutinecloseRuntimeErrorresult)rY   coror`   rl   futures        r   run_coroutinez_AsyncWorker.run_coroutine   ss    >::!5!5!7""4(

@AA)$

;>@AA}}W}--r   c                     | j                   rI| j                   j                         r/| j                   j                  | j                   j                         | j                  r| j                  j                  d       y y )N
   r_   )rS   
is_runningcall_soon_threadsafestoprT   joinrX   s    r   rz   z_AsyncWorker.stop   sS    ::$**//1JJ++DJJOO<<<LLb) r   N)iX  )	__name__
__module____qualname____doc__rZ   rc   rb   ru   rz   r   r   r   rQ   rQ      s    O*
'
!	.*r   rQ   c                        e Zd ZdZdZdZ	 	 	 	 	 d dedededee	ee
f      d	ed
ef fdZdededdfdZdZdeeeef      ddfdZdeddfdZdee   ddfdZd!dZdddddededededz  fdZd Z xZS )"ModalEnvironmentzModal cloud execution via native Modal sandboxes.

    Spawn-per-call via _ThreadedProcessHandle wrapping async SDK calls.
    cancel_fn wired to sandbox.terminate for interrupt support.
    heredoc<   Nimagecwdr`   modal_sandbox_kwargspersistent_filesystemr   c                    t         |   ||       || _        || _        d | _        d | _        t               | _        d | _        t        |xs i       d }d}| j                  r3t        | j                        \  }}|rt        j                  d|d d        t                dd lg 	 ddlm}	m}
m}  |	       D ]4  }j'                  j(                  j+                  |d   |d   	             6  |
       D ]4  }j'                  j(                  j+                  |d   |d   	             6  |       }|D ]4  }j'                  j(                  j+                  |d   |d   	             6 	 | j                  j1                          dt2        ffd}	 |xs |}	 t5        |      }| j                  j7                   ||      d      \  | _        | _        |r|rt9        | j                  |       	 t        j                  d| j                         tA        d | jB                  | jD                  | jF                  | jH                        | _        | j                  jK                  d       | jM                          y # t,        $ r!}t        j/                  d
|       Y d }~6d }~ww xY w# t,        $ ry}|s t        j;                  d|d d |       t=        | j                  |       t5        |      }| j                  j7                   ||      d      \  | _        | _        Y d }~5d }~ww xY w# t,        $ r | j                  j?                           w xY w)N)r   r`   Fz!Modal: restoring from snapshot %s   r   )get_credential_file_mountsiter_skills_filesiter_cache_files	host_pathcontainer_path)remote_pathz0Modal: could not load credential file mounts: %sr;   c                   K   j                   j                  j                  dd       d {   }t              }r1t	        |j                  dg             }|j                         ||d<    j                  j                  j                  	 d| |t        |j                  dd            d| d {   }||fS 7 7 
w)	Nzhermes-agentT)create_if_missingmountsr`   i  )r   appr`   )sleepinfinity)
Applookupaiodictlistr+   extendSandboxcreateint)r;   r   create_kwargsexisting_mountssandboxrL   cred_mountssandbox_kwargss        r   _create_sandboxz2ModalEnvironment.__init__.<locals>._create_sandbox   s     

))--nPT-UUC 0M"&}'8'82'F"G&&{3*9h'5FNN1155# M--i>?	
   G < Vs"   +CCB	C8C9	CCi,  r_   zBModal: failed to restore snapshot %s, retrying with base image: %sz Modal: sandbox created (task=%s)c                      t        d      S )Nz/root/.hermes)r   r   r   r   <lambda>z+ModalEnvironment.__init__.<locals>.<lambda>  s    !A r   )get_files_fn	upload_fn	delete_fnbulk_upload_fnbulk_download_fnT)force)'superrZ   _persistent_task_id_sandbox_apprQ   _worker_sync_managerr   r)   loggerinfor:   rE   tools.credential_filesr   r   r   appendMountfrom_local_filer7   debugrc   r   rO   ru   r,   warningr1   rz   r   _modal_upload_modal_delete_modal_bulk_upload_modal_bulk_downloadsyncinit_session)rY   r   r   r`   r   r   r   restored_snapshot_idrestored_from_legacy_keyr   r   r   mount_entryentrycache_filesr9   r   target_image_speceffective_imageexc
base_imagerL   r   r   	__class__s                        @@@r   rZ   zModalEnvironment.__init__   sX    	S'20	#~5928b9##( =\>: ": $?AUVYWYAZ[	P   :; ""LL00#K0$/0@$A 1  +, ""LL00k*$)*:$; 1  +,K$ ""LL00k*$)*:$; 1  		 c 	  	 4 =P"67H"I+/<<+E+E#O4c ,F ,(	4=  (,D*4==:NO
 	6F,A((((22!66
 	d+s  	PLLKQOO	P8  +X("-s (7KL1%8
+/<<+E+E#J/ ,F ,(	4==  	LL	sP   +CJ L5 ";J0 L5 	J-J((J-0	L29A.L-'L5 -L22L5 5%Mr   r   r   c                 n    t        |      j                         }t        j                  |      j	                  d      t        t        |      j                        }dt        j                  |       dt        j                  |        fd} j                  j                   |       d       y)z4Upload a single file via base64 piped through stdin.asciiz	mkdir -p z && base64 -d > c                  F  K   j                   j                  j                  dd       d {   } d}j                  }|t	              k  ra| j
                  j                  |||z           | j
                  j                  j                          d {    ||z  }|t	              k  ra| j
                  j                          | j
                  j                  j                          d {    | j                  j                          d {    y 7 7 7 +7 w)Nbash-cr   )
r   execr   _STDIN_CHUNK_SIZElenstdinwritedrain	write_eofrd   )procoffset
chunk_sizeb64cmdrY   s      r   _writez.ModalEnvironment._modal_upload.<locals>._write1  s     ++//cBBDF//J3s8#

  VFZ,?!@Ajj&&**,,,*$ 3s8# JJ  "**""&&((())--/!! C
 - )!sM   +D!DA(D!DD!0AD!1D2!D!DD!D!D!D!r^   r_   N)r   
read_bytesbase64	b64encodedecoder$   parentshlexquoter   ru   )rY   r   r   contentcontainer_dirr   r   r   s   `     @@r   r   zModalEnvironment._modal_upload'  s    y/,,.w'..w7D-445M23 4 ;;{346 	

	" 	""68R"8r   i   filesc                    	
 |syt        j                         }t        j                  |d      5 }|D ]'  \  }}|j	                  ||j                  d             ) 	 ddd       t        j                  |j                               j                  d      
t        |      }t        |      }| d		
 fd} j                  j                   |       d	
       y# 1 sw Y   xY w)a  Upload many files via tar archive piped through stdin.

        Builds a gzipped tar archive in memory and streams it into a
        ``base64 -d | tar xzf -`` pipeline via the process's stdin,
        avoiding the Modal SDK's 64 KB ``ARG_MAX_BYTES`` exec-arg limit.
        Nzw:gz)fileobjmode/)arcnamer   z && base64 -d | tar xzf - -C /c                    K   j                   j                  j                  dd       d {   } d}j                  }|t	              k  ra| j
                  j                  |||z           | j
                  j                  j                          d {    ||z  }|t	              k  ra| j
                  j                          | j
                  j                  j                          d {    | j                  j                          d {   }|dk7  r=| j                  j                  j                          d {   }t        d| d|       y 7 -7 7 n7 N7 w)Nr   r   r   zModal bulk upload failed (exit z): )r   r   r   r   r   r   r   r   r   rd   stderrreadrq   )r   r   r   	exit_codestderr_textr   payloadrY   s        r   _bulkz2ModalEnvironment._modal_bulk_upload.<locals>._bulkY  s6    ++//cBBD F//J3w<'

  0C!DEjj&&**,,,*$ 3w<'
 JJ  "**""&&((("iimmo-IA~$(KK$4$4$8$8$::"5i[K=Q   C - )-:s_   +E&EA(E&EE&0AE&1E 2!E&E"0E&E$E&E& E&"E&$E&x   r_   )ioBytesIOtarfileopenaddlstripr   r   getvaluer   r   r   r   ru   )rY   r   buftarr   r   parents
mkdir_partr   r   r   s   `        @@r   r   z#ModalEnvironment._modal_bulk_uploadE  s     jjl\\#F3 	Ds*/ D&	;	;+=+=c+BCD	D ""3<<>299'B$U+)'2
:;	, 	""57C"8?	D 	Ds   -CC(destc                       fd} j                   j                   |       d      }t        |t              r|j	                         }|j                  |       y)zDownload remote .hermes/ as a tar archive.

        Modal sandboxes always run as root, so /root/.hermes is hardcoded
        (consistent with iter_sync_files call on line 269).
        c                  >  K   j                   j                  j                  ddd       d {   } | j                  j                  j                          d {   }| j
                  j                          d {   }|dk7  rt        d| d      |S 7 h7 >7 w)Nr   r   ztar cf - -C / root/.hermesr   z!Modal bulk download failed (exit ))r   r   r   stdoutr   rd   rq   )r   r   r   rY   s      r   	_downloadz8ModalEnvironment._modal_bulk_download.<locals>._downloadw  s     ++//: D ))--//D"iimmo-IA~"%FykQR#STTK 0-s3   +BB+BB!B<B=BBBr   r_   N)r   ru   r#   r$   encodewrite_bytes)rY   r   r  	tar_bytess   `   r   r   z%ModalEnvironment._modal_bulk_downloadq  sK    	 LL..y{C.H	i%!((*I#r   remote_pathsc                 n     t        |       fd} j                  j                   |       d       y)z#Batch-delete remote files via exec.c                     K   j                   j                  j                  dd       d {   } | j                  j                          d {    y 7 '7 w)Nr   r   )r   r   r   rd   )r   rm_cmdrY   s    r   _rmz+ModalEnvironment._modal_delete.<locals>._rm  sE     ++//fEED))--/!! F!s!   +AA!AAAA   r_   N)r   r   ru   )rY   r	  r  r  s   `  @r   r   zModalEnvironment._modal_delete  s-    "<0	" 	""35""5r   c                 8    | j                   j                          y)zDSync files to sandbox via FileSyncManager (rate-limited internally).N)r   r   rX   s    r   _before_executez ModalEnvironment._before_execute  s    !r   Fr   )loginr`   
stdin_data
cmd_stringr  r  c                    | j                   | j                  fd}dt        t        t        f   ffd}t        ||      S )zEReturn a _ThreadedProcessHandle wrapping an async Modal sandbox exec.c                  \    j                   j                  j                         d       y )Nr  r_   )ru   	terminater   )r   workers   r   cancelz*ModalEnvironment._run_bash.<locals>.cancel  s%      !2!2!6!6!8" Er   r   c                  J    fd} j                   |        dz         S )Nc                  d  K   dg} r| j                  ddg       n| j                  dg        j                  j                  | d	i d {   }|j                  j                  j                          d {   }|j
                  j                  j                          d {   }|j                  j                          d {   }t        |t              r|j                  dd      }t        |t              r|j                  dd      }|}|r|r| d| n|}||fS 7 7 7 7 cw)	Nr   z-lr   r`   zutf-8replace)errors
)
r   r   r   r  r   r   rd   r#   bytesr   )
argsprocessr  r   r   outputr  r  r   r`   s
         r   _doz8ModalEnvironment._run_bash.<locals>.exec_fn.<locals>._do  s    xKKtZ 89KKz 23 0 0 0$ H HH&~~226688&~~226688"),,"2"2"44	fe,#]]79]EFfe,#]]79]EF6<xr&2&Fy(( I884sI   AD0D(+D0<D*=+D0(D,)!D0
D.AD0*D0,D0.D0r^   r_   )ru   )r"  r  r  r   r`   r  s    r   exec_fnz+ModalEnvironment._run_bash.<locals>.exec_fn  s&    )& ''w|'DDr   )	cancel_fn)r   r   tupler$   r   r   )	rY   r  r  r`   r  r  r#  r   r  s	    ```   @@r   	_run_bashzModalEnvironment._run_bash  sH     --	F	EsCx 	E 	E, &g@@r   c                 :     j                   y j                  r/t        j                  d        j                  j	                           j
                  re	  fd}	  j                  j                   |       d      }|r:t         j                  |       t        j                  d|dd  j                         	  j                  j                   j                   j                  j                         d	        j                  j                          d _         d _        y# t        $ r d}Y w xY w# t        $ r }t        j                  d|       Y d}~d}~ww xY w# t        $ r Y qw xY w#  j                  j                          d _         d _        w xY w)
z>Snapshot the filesystem (if persistent) then stop the sandbox.Nz$Modal: syncing files from sandbox...c                     K   j                   j                  j                          d {   } | j                  S 7 wr   )r   snapshot_filesystemr   	object_id)imgrY   s    r   	_snapshotz+ModalEnvironment.cleanup.<locals>._snapshot  s2      $ A A E E GGC==( Hs   (><>r   r_   z/Modal: saved filesystem snapshot %s for task %sr   z%Modal: filesystem snapshot failed: %sr  )r   r   r   r   	sync_backr   r   ru   r7   r,   r   r   r  r   rz   r   )rY   r,  r'   r9   s   `   r   cleanupzModalEnvironment.cleanup  s]   == KK>?((*K)'"&,,"<"<Y[RT"<"UK *4==+FKKI#CR($--	LL&&t}}'>'>'B'B'Db&Q LL DMDI' ! '"&K'  KFJJK
  		 LL DMDIs`   D5 "D$ ?<D5 <?E! $D2/D5 1D22D5 5	E>EE!	E-*E0 ,E--E0 0*F)z/rootr   NTdefaultr   N)r|   r}   r~   r   _stdin_mode_snapshot_timeoutr$   r   r   r   r   boolrZ   r   r   r   r%  r   r   r   r   r  r&  r.  __classcell__)r   s   @r   r   r      s*    K
 9=&* ww w 	w
 'tCH~6w  $w wr9s 9 9 98 (*9U38_(= *9$ *9X$ $$ $*6$s) 6 6" ;@!$+/ AC  A4  A A!Dj AD$r   r   r   r0  )-r   rf   r   r   loggingr   r   rU   pathlibr   typingr   r   hermes_constantsr   tools.environments.baser   r   r	   r
   tools.environments.file_syncr   r   r   r   r   	getLoggerr|   r   r   r   r   r   r   r$   r    r%  r3  r)   r,   r1   r:   rO   rQ   r   r   r   r   <module>r<     s#     	        ,   
		8	$!#&<<% - -,$ ,4 ,5# 5# 5	S 	U3:t;K5L 	C c d #S #sTz #T #"S S B"* "*Jz zr   