@@ -56,6 +56,10 @@ def __init__(self, error_type: str, message: str) -> None:
5656 self .message = message
5757
5858
59+ class DecodeWorkerSessionClosed (DecodeWorkerError ):
60+ """A verifier proxy was used after its router session was closed."""
61+
62+
5963@dataclass (frozen = True )
6064class DecodeWorkerConfig :
6165 model_id : str
@@ -824,6 +828,15 @@ def _mark_current(self, session_id: str) -> None:
824828 with self ._lock :
825829 self ._restored_generation [session_id ] = self ._generation
826830
831+ def _checkpoint_locked (self , session_id : str ) -> ProofCheckpoint :
832+ """Return a live checkpoint while the caller holds ``_lock``."""
833+ try :
834+ return self ._checkpoints [session_id ]
835+ except KeyError as exc :
836+ raise DecodeWorkerSessionClosed (
837+ f"decode session { session_id !r} is closed"
838+ ) from exc
839+
827840
828841class DecodeWorkerSession :
829842 """Verifier-shaped proxy used by existing append/generate coordinators."""
@@ -837,6 +850,16 @@ def __init__(self, client: DecodeWorkerClient, session_id: str) -> None:
837850 self .next_global_position = 0
838851 self ._kv_live_bytes = 0
839852
853+ def _sink_window_slice (self , sequence : list [int ]) -> list [int ]:
854+ """Mirror the child verifier's bounded token-id cache layout."""
855+ sink_size = int (self .client .config .sink_size )
856+ window_size = int (self .client .config .window_size )
857+ budget = sink_size + window_size
858+ if len (sequence ) <= budget :
859+ return list (sequence )
860+ tail = list (sequence [- window_size :]) if window_size else []
861+ return list (sequence [:sink_size ]) + tail
862+
840863 def _apply (self , state : dict [str , Any ]) -> dict [str , Any ]:
841864 self .cached_token_sequence = [
842865 int (token ) for token in state .get ("cached_token_ids" , ())
@@ -853,85 +876,97 @@ def prefill(
853876 cancel_event : threading .Event | None = None ,
854877 ) -> None :
855878 tokens = [int (token ) for token in prompt_ids ]
856- state = self .client ._session_request (
857- self .session_id ,
858- "Init" ,
859- {"token_ids" : tokens },
860- cancel_event = cancel_event ,
861- )
862- checkpoint = self .client ._checkpoints [self .session_id ]
863- checkpoint .snapshot = None
864- checkpoint .compatibility = None
865- checkpoint .replay_token_ids = list (tokens )
866- checkpoint .initialized = True
867- self .client ._mark_current (self .session_id )
868- self ._apply (state )
879+ with self .client ._lock :
880+ checkpoint = self .client ._checkpoint_locked (self .session_id )
881+ state = self .client ._session_request (
882+ self .session_id ,
883+ "Init" ,
884+ {"token_ids" : tokens },
885+ cancel_event = cancel_event ,
886+ )
887+ checkpoint .snapshot = None
888+ checkpoint .compatibility = None
889+ checkpoint .replay_token_ids = list (tokens )
890+ checkpoint .initialized = True
891+ self .client ._mark_current (self .session_id )
892+ self ._apply (state )
869893
870894 def append_accepted_tokens (
871895 self ,
872896 tokens : list [int ],
873897 cancel_event : threading .Event | None = None ,
874898 ) -> None :
875899 committed = [int (token ) for token in tokens ]
876- state = self .client ._session_request (
877- self .session_id ,
878- "Append" ,
879- {"token_ids" : committed },
880- cancel_event = cancel_event ,
881- )
882- self .client ._checkpoints [self .session_id ].replay_token_ids .extend (committed )
883- self ._apply (state )
900+ with self .client ._lock :
901+ checkpoint = self .client ._checkpoint_locked (self .session_id )
902+ state = self .client ._session_request (
903+ self .session_id ,
904+ "Append" ,
905+ {"token_ids" : committed },
906+ cancel_event = cancel_event ,
907+ )
908+ checkpoint .replay_token_ids .extend (committed )
909+ self ._apply (state )
884910
885911 def generate_step (
886912 self ,
887913 cancel_event : threading .Event | None = None ,
888914 ) -> int :
889- state = self .client ._session_request (
890- self .session_id ,
891- "GenerateStep" ,
892- {},
893- cancel_event = cancel_event ,
894- )
895- token_id = int (state ["token_id" ])
896- self .client ._checkpoints [self .session_id ].replay_token_ids .append (token_id )
897- self ._apply (state )
898- return token_id
915+ with self .client ._lock :
916+ checkpoint = self .client ._checkpoint_locked (self .session_id )
917+ state = self .client ._session_request (
918+ self .session_id ,
919+ "GenerateStep" ,
920+ {},
921+ cancel_event = cancel_event ,
922+ )
923+ token_id = int (state ["token_id" ])
924+ checkpoint .replay_token_ids .append (token_id )
925+ self ._apply (state )
926+ return token_id
899927
900928 def import_snapshot (
901929 self ,
902930 payload : bytes ,
903931 compatibility : Any ,
904932 ) -> dict [str , Any ]:
905933 compat = asdict (compatibility )
906- # Ensure a worker-side session exists before importing its cache.
907- self .client ._session_request (
908- self .session_id , "Init" , {"token_ids" : []}
909- )
910- state = self .client ._session_request (
911- self .session_id ,
912- "ImportSnapshot" ,
913- {"compatibility" : compat },
914- bytes (payload ),
915- )
916- checkpoint = self .client ._checkpoints [self .session_id ]
917- checkpoint .snapshot = bytes (payload )
918- checkpoint .compatibility = compat
919- checkpoint .replay_token_ids = []
920- checkpoint .initialized = True
921- self .client ._mark_current (self .session_id )
922- return self ._apply (state )
934+ snapshot = bytes (payload )
935+ # Pin the router checkpoint and proxy lifecycle across the full
936+ # Init -> ImportSnapshot -> checkpoint-publication transaction.
937+ # Close/cancel cleanup uses the same RLock and therefore cannot remove
938+ # restart state after the child accepted the import but before it is
939+ # made durable on the router.
940+ with self .client ._lock :
941+ checkpoint = self .client ._checkpoint_locked (self .session_id )
942+ self .client ._session_request (
943+ self .session_id , "Init" , {"token_ids" : []}
944+ )
945+ state = self .client ._session_request (
946+ self .session_id ,
947+ "ImportSnapshot" ,
948+ {"compatibility" : compat },
949+ snapshot ,
950+ )
951+ checkpoint .snapshot = snapshot
952+ checkpoint .compatibility = compat
953+ checkpoint .replay_token_ids = []
954+ checkpoint .initialized = True
955+ self .client ._mark_current (self .session_id )
956+ return self ._apply (state )
923957
924958 def reset (self ) -> None :
925- state = self .client ._session_request (
926- self .session_id , "Init" , {"token_ids" : []}
927- )
928- checkpoint = self .client ._checkpoints [self .session_id ]
929- checkpoint .snapshot = None
930- checkpoint .compatibility = None
931- checkpoint .replay_token_ids = []
932- checkpoint .initialized = True
933- self .client ._mark_current (self .session_id )
934- self ._apply (state )
959+ with self .client ._lock :
960+ checkpoint = self .client ._checkpoint_locked (self .session_id )
961+ state = self .client ._session_request (
962+ self .session_id , "Init" , {"token_ids" : []}
963+ )
964+ checkpoint .snapshot = None
965+ checkpoint .compatibility = None
966+ checkpoint .replay_token_ids = []
967+ checkpoint .initialized = True
968+ self .client ._mark_current (self .session_id )
969+ self ._apply (state )
935970
936971 def k_seq_length (self , _session : Any ) -> int :
937972 return len (self .cached_token_sequence )
0 commit comments