pub struct TransformerModel { /* private fields */ }Expand description
2026-09-25: One loaded model: its layers, weights, kernel handles, KV and
SSM pools and CUDA-graph caches. trait_impl implements Model for it.
Implementations§
Source§impl TransformerModel
impl TransformerModel
pub fn new( config: ModelConfig, embed_tokens: DenseWeight, final_norm: DenseWeight, lm_head_weight: DenseWeight, lm_head_nvfp4: Option<QuantizedWeight>, lm_head_fp8: Option<Fp8DenseWeight>, mtp_lm_head_nvfp4: Option<QuantizedWeight>, lm_head_nvfp4_t: Option<(QuantizedWeight, u32)>, layers: Vec<Box<dyn TransformerLayer>>, buffers: BufferArena, kv_cache: PagedKvCache, mtp_weights: Vec<MtpWeights>, gpu: Box<dyn GpuBackend>, max_seq_len: usize, max_batch_size: usize, mtp_quant: MtpQuantization, use_speculative: bool, prefix_cache: Box<dyn PrefixCache>, mtp_vocab_size: u32, comm: Option<Arc<dyn CommBackend>>, self_speculative: bool, num_drafts: usize, vision_encoder: Option<VisionTower>, ssm_cache_slots: usize, ssm_checkpoint_interval: usize, ) -> Result<Self>
Source§impl TransformerModel
impl TransformerModel
Sourcepub fn decode_logits_fp32(&self) -> bool
pub fn decode_logits_fp32(&self) -> bool
2026-09-25: Whether the single-token decode lm_head writes FP32 logits
to logits_fp32_buf (use_fp32_logits, which TransformerModel::new
sets to false). Other lm_head paths always write BF16.
Sourcepub fn decode_logits_ptr(&self) -> DevicePtr
pub fn decode_logits_ptr(&self) -> DevicePtr
2026-09-25: The buffer the single-token decode lm_head writes:
logits_fp32_buf when use_fp32_logits, else the shared BF16 logits
buffer. Read it with the dtype decode_logits_fp32 reports.
Source§impl TransformerModel
impl TransformerModel
Sourcepub fn gpu_backend(&self) -> &dyn GpuBackend
pub fn gpu_backend(&self) -> &dyn GpuBackend
2026-09-25: Borrow the GPU backend. The factory uses it to build the proposers installed after construction and to read allocation reports.
Sourcepub fn config_ref(&self) -> &ModelConfig
pub fn config_ref(&self) -> &ModelConfig
2026-09-25: Borrow the model config, for building the proposers that are installed after construction.
Sourcepub fn set_dflash_proposer(&mut self, proposer: Arc<dyn DraftProposer>)
pub fn set_dflash_proposer(&mut self, proposer: Arc<dyn DraftProposer>)
2026-09-25: Install a proposer built after construction (the
DeepSeek-V4 or GLM-5.3 MTP head, or a DFlash drafter), replacing any
MTP proposer TransformerModel::new built. It also shrinks the MTP
prefill capture buffer to the rows this proposer can use.
It does not check flag combinations; clap rejects --dflash with
--speculative.
Sourcepub fn set_ngram_embedding(&mut self, ngram: NgramEmbedding)
pub fn set_ngram_embedding(&mut self, ngram: NgramEmbedding)
2026-09-25: Install the fused n-gram input embedding (only the LongCat
loader builds one). Once set, embed_ctx embeds through it and the
bare-token embed refuses to run.
Sourcepub fn has_ngram_embedding(&self) -> bool
pub fn has_ngram_embedding(&self) -> bool
2026-09-25: True when this model fuses n-gram lookups into its input embedding.
Source§impl TransformerModel
impl TransformerModel
Sourcepub fn adapter_id_for_slot(&self, slot: i32) -> u64
pub fn adapter_id_for_slot(&self, slot: i32) -> u64
2026-09-25: Adapter id for a request’s adapter_slot (< 0 resolves
to the active slot): a hash of the slot’s name and generation, or 0
when no LoRA pool is resident or the slot is empty or out of range.
Sourcepub fn acquire_adapter_slot(&self, slot: i32) -> i32
pub fn acquire_adapter_slot(&self, slot: i32) -> i32
2026-09-25: Take a ref on the pool slot a sequence uses (< 0
resolves to the active slot, as in Self::adapter_id_for_slot).
Returns the resolved index; the caller stores it and releases that
index at terminal free, so a rotate that changes active in between
cannot move the release to another counter. Returns -1 (nothing
acquired) when no LoRA pool is resident or the slot is out of range.
Sourcepub fn release_adapter_slot(&self, resolved: i32)
pub fn release_adapter_slot(&self, resolved: i32)
2026-09-25: Release a ref taken by Self::acquire_adapter_slot, by
the resolved index it returned. -1 and no pool are no-ops.
pub fn set_lora_weights(&mut self, lora: Option<LoraWeights>) -> Result<()>
Source§impl TransformerModel
impl TransformerModel
Sourcepub fn rotate_lora_to(&mut self, name: &str) -> Result<()>
pub fn rotate_lora_to(&mut self, name: &str) -> Result<()>
2026-09-25: Make the resident adapter name the active one: re-install
its per-layer pairs with install_lora_layers, then
destroy_lora_decode_graphs. Call it only at a scheduler-quiescent
point.
Refuses, changing nothing, when no adapter is loaded, name is not
resident, rotation is not armed (lora_rotatable), or the current
active slot has in-flight sequences. If the install walk fails, the
active slot has already switched.
Source§impl TransformerModel
impl TransformerModel
Sourcepub fn swap_lora_slot_from_peer(
&mut self,
peer_addr: &str,
adapter_id: &str,
adapter_name: &str,
slot: usize,
peft: PeftAdapterConfig,
) -> Result<()>
pub fn swap_lora_slot_from_peer( &mut self, peer_addr: &str, adapter_id: &str, adapter_name: &str, slot: usize, peft: PeftAdapterConfig, ) -> Result<()>
2026-09-25: Stage the adapter adapter_name (held by the peer at
peer_addr as adapter_id) into pool slot in place and make it that
slot’s resident adapter. Call it only at a scheduler-quiescent point.
Refuses when no pool is loaded, rotation is not armed
(lora_rotatable), slot >= max_loras, or the slot has in-flight
sequences. Otherwise the slot region is zeroed, the tensors land over
RDMA, the slot’s pairs are rebuilt with the new r/scale, its tables are
refreshed and its generation bumped. If the slot is active, its pairs
are re-installed and the LoRA graph caches destroyed.
Sourcepub fn promote_lora_slot_from_peer(
&mut self,
peer_addr: &str,
adapter_id: &str,
adapter_name: &str,
peft: PeftAdapterConfig,
) -> Result<(usize, Option<String>)>
pub fn promote_lora_slot_from_peer( &mut self, peer_addr: &str, adapter_id: &str, adapter_name: &str, peft: PeftAdapterConfig, ) -> Result<(usize, Option<String>)>
2026-09-25: Promote the adapter adapter_name from the peer into a
cache-region pool slot and make it active; returns (slot, evicted_name). Call it only at a scheduler-quiescent point.
The victim (select_victim_slot) is a never-filled placeholder first,
else the least recently used idle (ref_count == 0) cache slot. With
every cache slot busy it fails with POOL_FULL; a busy slot is never
evicted. Self::swap_lora_slot_from_peer re-checks the ref count and
bumps the slot generation.
Sourcepub fn promote_lora_slot_from_disk(
&mut self,
adapter_dir: &Path,
name: &str,
) -> Result<(usize, Option<String>)>
pub fn promote_lora_slot_from_disk( &mut self, adapter_dir: &Path, name: &str, ) -> Result<(usize, Option<String>)>
2026-09-25: Self::promote_lora_slot_from_peer with the adapter
loaded from adapter_dir (named name) instead of a peer: same victim
choice, same make-active step, returns (slot, evicted_name).
Self::swap_lora_slot_from_disk reads the directory’s
adapter_config.json, refuses when rotation is not armed, re-checks
the ref count and bumps the slot generation. Call it only at a
scheduler-quiescent point.
Sourcepub fn swap_lora_slot_from_disk(
&mut self,
adapter_dir: &Path,
name: &str,
slot: usize,
) -> Result<()>
pub fn swap_lora_slot_from_disk( &mut self, adapter_dir: &Path, name: &str, slot: usize, ) -> Result<()>
2026-09-25: Load the adapter at adapter_dir into pool slot in place
and make it that slot’s resident adapter, using the directory’s own
adapter_config.json. If the slot is active, its pairs are re-installed
and the LoRA graph caches destroyed. Call it only at a
scheduler-quiescent point.
Refuses when rotation is not armed (lora_rotatable) or the slot has
in-flight sequences. pack_store_into_slot also refuses adapters with
router/expert deltas or token-overlay tensors.
Source§impl TransformerModel
impl TransformerModel
Sourcepub fn install_declared_lm_head_w8a8(&mut self) -> Result<()>
pub fn install_declared_lm_head_w8a8(&mut self) -> Result<()>
2026-09-28: Install the W8A8 head when the declared-precision policy wants the head run
W8A8 (WeightQuantPolicy::fp8_decode_act("lm_head") == Some(Fp8)) and the model loaded
the FP8 head (lm_head_fp8, the declared head format under declared).
Sourcepub fn set_lm_head_w8a8(&mut self, ctx: W8a8Ctx) -> Result<()>
pub fn set_lm_head_w8a8(&mut self, ctx: W8a8Ctx) -> Result<()>
2026-09-28: Run the FP8 head W8A8 (the checkpoint’s declared precision) from now on.
Needs lm_head_fp8, and kernels and scratch that serve one row.
Source§impl TransformerModel
impl TransformerModel
Sourcepub fn install_declared_lm_head_w4a16_rows(&mut self) -> Result<()>
pub fn install_declared_lm_head_w4a16_rows(&mut self) -> Result<()>
2026-10-02: Install the W4A16 row-tile head when the declared-precision policy declares an NVFP4 head with 16-bit activations, the model loaded the NVFP4 head, the target ships the kernels, the head fits their shape contract, and the vocab is not sharded.
Source§impl TransformerModel
impl TransformerModel
Sourcepub fn set_lm_head_q6k(&mut self, blocks: DevicePtr) -> Result<()>
pub fn set_lm_head_q6k(&mut self, blocks: DevicePtr) -> Result<()>
2026-09-25: Install the raw Q6_K head. blocks is the store’s
lm_head.weight pointer, [vocab, hidden] in Q6_K super-blocks. Fails
when hidden_size is not a multiple of 256.
Source§impl TransformerModel
impl TransformerModel
Sourcepub fn adopt_weight_store(&mut self, store: WeightStore)
pub fn adopt_weight_store(&mut self, store: WeightStore)
2026-09-25: Hand the model the ledger of its own weights, for teardown.
Trait Implementations§
Source§impl Drop for TransformerModel
impl Drop for TransformerModel
Source§impl ModelAdapters for TransformerModel
impl ModelAdapters for TransformerModel
Source§fn set_active_lora(&mut self, name: &str) -> Result<()>
fn set_active_lora(&mut self, name: &str) -> Result<()>
name the active one. Takes &mut self, so it
runs only while nothing else holds the model. Default: an error.Source§fn adapter_id_for(&self, slot: i32) -> u64
fn adapter_id_for(&self, slot: i32) -> u64
>= 0 names a resident
slot, -1 the active adapter. Default 0, the base model’s id.Source§fn acquire_adapter_slot(&self, slot: i32) -> i32
fn acquire_adapter_slot(&self, slot: i32) -> i32
-1 to the active adapter as Self::adapter_id_for does. Returns the resolved slot,
which the caller must pass to Self::release_adapter_slot, or -1 when no ref was
taken. Default -1.Source§fn release_adapter_slot(&self, resolved: i32)
fn release_adapter_slot(&self, resolved: i32)
Self::acquire_adapter_slot, by the slot it returned.
Default: no-op.Source§fn swap_lora_from_disk(
&mut self,
dir: &Path,
name: &str,
slot: usize,
) -> Result<()>
fn swap_lora_from_disk( &mut self, dir: &Path, name: &str, slot: usize, ) -> Result<()>
dir into pool slot slot. Takes &mut self, so it
runs only while nothing else holds the model. Default: an error.Source§fn promote_lora_from_peer(
&mut self,
peer_addr: &str,
adapter_id: &str,
name: &str,
peft: PeftAdapterConfig,
) -> Result<(usize, Option<String>)>
fn promote_lora_from_peer( &mut self, peer_addr: &str, adapter_id: &str, name: &str, peft: PeftAdapterConfig, ) -> Result<(usize, Option<String>)>
name (staged on peer_addr as adapter_id) from the peer
into a pool slot and make it active; returns (slot, evicted adapter name). peft
supplies the rank, alpha and scaling. Default: an error.Source§impl ModelCircuit for TransformerModel
impl ModelCircuit for TransformerModel
Source§fn set_forward(&self, sel: &ForwardSelect) -> Result<()>
fn set_forward(&self, sel: &ForwardSelect) -> Result<()>
sel from the next step. Captured decode graphs are dropped.
Default: only Legacy is accepted.Source§fn forward_disclosure(&self) -> ForwardDisclosure
fn forward_disclosure(&self) -> ForwardDisclosure
Source§impl ModelDeviceFeed for TransformerModel
impl ModelDeviceFeed for TransformerModel
Source§fn supports_device_token_feed(&self) -> bool
fn supports_device_token_feed(&self) -> bool
decode_batch_fed) and answer it with a masked argmax written into the feed cells
(argmax_batch_to_feed). Default false.Source§fn decode_batch_fed(
&self,
sources: &[FeedSource],
seqs: &mut [&mut SequenceState],
stream: u64,
) -> Result<DevicePtr>
fn decode_batch_fed( &self, sources: &[FeedSource], seqs: &mut [&mut SequenceState], stream: u64, ) -> Result<DevicePtr>
seqs with each row’s input id read on the device from
sources: the forward, KV writes and block allocation of decode_batch, but it does not
push the token or advance seq_len, because the router does not know a fed row’s id at
launch; the scheduler core does that once the previous step is committed. Position
seq.seq_len is written. Default: an error.Source§fn argmax_batch_to_feed(
&self,
logits: DevicePtr,
masks: &[RowMask],
dst: *mut u32,
event: u64,
stream: u64,
) -> Result<()>
fn argmax_batch_to_feed( &self, logits: DevicePtr, masks: &[RowMask], dst: *mut u32, event: u64, stream: u64, ) -> Result<()>
masks.len() rows of logits, applying each row’s two-id mask
(argmax_feed.cu): writes the feed cells, copies the ids to the pinned dst with an
async D2H on stream, then records event. It never synchronises; dst is valid once
event completes. Default: an error.Source§fn reserve_decode_block(&self, seq: &mut SequenceState) -> Result<usize>
fn reserve_decode_block(&self, seq: &mut SequenceState) -> Result<usize>
seq.seq_len) if the
block table lacks it; returns the blocks added (0 or 1). Err when the pool is empty.
Default: an error.Source§fn release_decode_blocks(
&self,
seq: &mut SequenceState,
blocks: usize,
) -> Result<()>
fn release_decode_blocks( &self, seq: &mut SequenceState, blocks: usize, ) -> Result<()>
blocks entries of the block table, added by
reserve_decode_block for a discarded step, to the pool. Default: an error.Source§fn fed_step_settled(&self)
fn fed_step_settled(&self)
Source§fn event_query(&self, event: u64) -> Result<bool>
fn event_query(&self, event: u64) -> Result<bool>
event has completed; never blocks.
Default Ok(true).Source§fn event_synchronize(&self, event: u64) -> Result<()>
fn event_synchronize(&self, event: u64) -> Result<()>
event has completed.
Default: Ok(()).fn destroy_event(&self, event: u64) -> Result<()>
Source§fn copy_d2h_async(
&self,
src: DevicePtr,
dst: &mut [u8],
stream: u64,
) -> Result<()>
fn copy_d2h_async( &self, src: DevicePtr, dst: &mut [u8], stream: u64, ) -> Result<()>
stream; dst must stay valid and unread until
stream is synchronised. Default: an error.Source§fn alloc_host_pinned(&self, bytes: usize) -> Result<*mut u8>
fn alloc_host_pinned(&self, bytes: usize) -> Result<*mut u8>
fn free_host_pinned(&self, ptr: *mut u8, bytes: usize) -> Result<()>
Source§impl ModelDraft for TransformerModel
impl ModelDraft for TransformerModel
Source§fn mtp_slot_draft_capacity(&self, slot_idx: usize) -> usize
fn mtp_slot_draft_capacity(&self, slot_idx: usize) -> usize
slot_idx without overflowing that slot’s per-token intermediate state. The scheduler
uses the minimum over the active slots. Default usize::MAX (no SSM verify pools).Source§fn generate_speculative(
&self,
prompt_tokens: &[u32],
params: &SamplingParams,
num_drafts: usize,
) -> Result<GenerateResult>
fn generate_speculative( &self, prompt_tokens: &[u32], params: &SamplingParams, num_drafts: usize, ) -> Result<GenerateResult>
Source§fn has_proposer(&self) -> bool
fn has_proposer(&self) -> bool
Source§fn dflash_gamma(&self) -> Option<usize>
fn dflash_gamma(&self) -> Option<usize>
num_drafts = γ - 1 from it. Default None.fn has_self_speculative(&self) -> bool
Source§fn decode_draft(
&self,
token: u32,
seq: &mut SequenceState,
stream: u64,
) -> Result<DevicePtr>
fn decode_draft( &self, token: u32, seq: &mut SequenceState, stream: u64, ) -> Result<DevicePtr>
rows[i] of the last batched verify into stash slot i,
before a propose overwrites the shared hidden buffer. Default: an error.Self::save_hidden_for_mtp from stash slot idx (written by
Self::stash_verify_hidden_rows) instead of the live verify rows, which a propose may
have overwritten. Default: an error.Source§fn stash_verify_catchup_rows(&self, slot_rows: &[(usize, usize)]) -> Result<()>
fn stash_verify_catchup_rows(&self, slot_rows: &[(usize, usize)]) -> Result<()>
slot_rows[j] = (slot, row). Default: Ok(()).Source§fn run_mtp_catchup_batched(
&self,
tokens: &[Vec<u32>],
first_slot: &[usize],
first_pos: &[usize],
seqs: &mut [&mut SequenceState],
) -> Result<usize>
fn run_mtp_catchup_batched( &self, tokens: &[Vec<u32>], first_slot: &[usize], first_pos: &[usize], seqs: &mut [&mut SequenceState], ) -> Result<usize>
Self::stash_verify_catchup_rows); returns the rows written. Default: 0.Source§fn run_mtp_propose_batched(
&self,
tokens: &[u32],
positions: &[usize],
stash_idx: &[usize],
num_drafts: usize,
seqs: &mut [&mut SequenceState],
_stream: u64,
out_conf: Option<&mut Vec<Vec<f32>>>,
) -> Result<Option<Vec<Vec<u32>>>>
fn run_mtp_propose_batched( &self, tokens: &[u32], positions: &[usize], stash_idx: &[usize], num_drafts: usize, seqs: &mut [&mut SequenceState], _stream: u64, out_conf: Option<&mut Vec<Vec<f32>>>, ) -> Result<Option<Vec<Vec<u32>>>>
num_drafts MTP drafts for each of tokens.len() sequences in one drafter
forward per draft position. stash_idx[i] is the verify-stash slot holding sequence
i’s hidden, and positions[i] its propose position, as for
Self::run_mtp_propose_multi. Read moreSource§fn mtp_propose_batch_max(&self) -> usize
fn mtp_propose_batch_max(&self) -> usize
Self::run_mtp_propose_batched takes in one drafter
forward. Default 1 (per sequence only).pos in the drafter catch-up
ring. Default: Ok(()).token_idx into the MTP input buffer, which
run_mtp_propose and run_mtp_propose_multi read. TransformerModel copies the hidden
state before the final norm, because the MTP head applies its own norm.token_idx from every DFlash capture layer for the next propose.
Default: Ok(()).Source§fn dflash_accept_append(&self, seq: &mut SequenceState) -> Result<()>
fn dflash_accept_append(&self, seq: &mut SequenceState) -> Result<()>
Ok(()).Source§fn dflash_eagle_accept_append(&self, seq: &mut SequenceState) -> Result<()>
fn dflash_eagle_accept_append(&self, seq: &mut SequenceState) -> Result<()>
seq_len - 2
and seq_len - 1, row 1 last, before the propose. Default: Ok(()).Source§fn dflash_eagle_kgamma_append(
&self,
seq: &mut SequenceState,
num_accepted: usize,
base_pos: usize,
) -> Result<()>
fn dflash_eagle_kgamma_append( &self, seq: &mut SequenceState, num_accepted: usize, base_pos: usize, ) -> Result<()>
0..=num_accepted to the DFlash context at positions
base_pos..=base_pos + num_accepted, row num_accepted last. Default: Ok(()).Source§fn dflash_capture_band(&self) -> usize
fn dflash_capture_band(&self) -> usize
i of a batched
verify captures into band i, so its commit_ctx scratch_row is
i * dflash_capture_band(). Default 0 (no DFlash drafter).Source§fn commit_ctx(
&self,
seq: &mut SequenceState,
num_committed: usize,
base_pos: usize,
scratch_row: usize,
) -> Result<()>
fn commit_ctx( &self, seq: &mut SequenceState, num_committed: usize, base_pos: usize, scratch_row: usize, ) -> Result<()>
num_committed DFlash scratch rows, from scratch_row, at the end of
the context, stamped with positions base_pos..base_pos + num_committed, after sliding
the window if it would overflow. base_pos is a position, not a context row: the two
differ after a slide. Default: Ok(()).Source§fn dflash_serial_ctx_append(&self, seq: &mut SequenceState) -> Result<()>
fn dflash_serial_ctx_append(&self, seq: &mut SequenceState) -> Result<()>
seq_len - 1, for a sequence decoded serially while speculation is
suspended; otherwise that capture is overwritten and the context has a hole. It sets
skip_next_decode_append so a later propose does not append it again. No-op without a
DFlash proposer state. Default: Ok(()).Source§fn run_mtp_propose(
&self,
token: u32,
position: usize,
seq: &mut SequenceState,
_stream: u64,
) -> Result<Option<u32>>
fn run_mtp_propose( &self, token: u32, position: usize, seq: &mut SequenceState, _stream: u64, ) -> Result<Option<u32>>
None without a proposer.Source§fn run_mtp_propose_multi(
&self,
token: u32,
position: usize,
num_drafts: usize,
seq: &mut SequenceState,
_stream: u64,
grammar_bitmask: Option<&[i32]>,
) -> Result<Vec<u32>>
fn run_mtp_propose_multi( &self, token: u32, position: usize, num_drafts: usize, seq: &mut SequenceState, _stream: u64, grammar_bitmask: Option<&[i32]>, ) -> Result<Vec<u32>>
Source§fn read_deferred_draft_token(&self) -> Result<u32>
fn read_deferred_draft_token(&self) -> Result<u32>
0.Source§fn trim_proposer_state(
&self,
seq: &mut SequenceState,
num_accepted: usize,
_stream: u64,
) -> Result<()>
fn trim_proposer_state( &self, seq: &mut SequenceState, num_accepted: usize, _stream: u64, ) -> Result<()>
Source§impl ModelEp for TransformerModel
impl ModelEp for TransformerModel
Source§fn ep_worker_step(&self, slots: &mut [Option<SequenceState>]) -> Result<bool>
fn ep_worker_step(&self, slots: &mut [Option<SequenceState>]) -> Result<bool>
(seq_id, cmd) from rank 0 and
run the command in slot seq_id. Returns Ok(false) when the worker should shut down. Read moreSource§fn is_ep(&self) -> bool
fn is_ep(&self) -> bool
false.Source§fn ep_broadcast_cmd(&self, cmd: u32) -> Result<()>
fn ep_broadcast_cmd(&self, cmd: u32) -> Result<()>
Ok(()).Source§fn ep_broadcast_cmd_for_seq(&self, seq_id: u32, cmd: u32) -> Result<()>
fn ep_broadcast_cmd_for_seq(&self, seq_id: u32, cmd: u32) -> Result<()>
(seq_id, cmd) pair to every worker rank, as the first broadcast of a
command; its follow-up broadcasts use Self::ep_broadcast_cmd. When
Self::ep_protocol_v2 is false, seq_id is not sent. Default: Ok(()).Source§fn ep_protocol_v2(&self) -> bool
fn ep_protocol_v2(&self) -> bool
seq_id with each command
(METRALE_EP_PROTOCOL=v2). Default false.Source§impl ModelForward for TransformerModel
impl ModelForward for TransformerModel
Source§fn prefill_batch_chunk(
&self,
streams: &mut [PrefillSlice<'_>],
stream: u64,
) -> Result<Vec<DevicePtr>>
fn prefill_batch_chunk( &self, streams: &mut [PrefillSlice<'_>], stream: u64, ) -> Result<Vec<DevicePtr>>
2026-09-25: prefill_batch_chunk_dispatch sends a batch that is
ineligible, or whose cache plan is not admitted, to its per-stream loop
before any sequence is mutated. An error from an admitted kernel batch is
returned: a per-stream retry could apply prefix-cache and KV state twice.
Source§fn prefill_batch_chunk_rows(
&self,
streams: &mut [PrefillSlice<'_>],
stream: u64,
row_base: usize,
) -> Result<Vec<DevicePtr>>
fn prefill_batch_chunk_rows( &self, streams: &mut [PrefillSlice<'_>], stream: u64, row_base: usize, ) -> Result<Vec<DevicePtr>>
2026-09-25: Mixed-step variant: finishing streams write their logits from
row row_base, clear of the decode lanes. The trait docs describe the
aliasing this prevents.
Source§fn prefill(
&self,
tokens: &[u32],
seq: &mut SequenceState,
stream: u64,
) -> Result<DevicePtr>
fn prefill( &self, tokens: &[u32], seq: &mut SequenceState, stream: u64, ) -> Result<DevicePtr>
Source§fn prefill_chunk(
&self,
tokens: &[u32],
seq: &mut SequenceState,
chunk_start: usize,
chunk_len: usize,
is_last_chunk: bool,
stream: u64,
) -> Result<DevicePtr>
fn prefill_chunk( &self, tokens: &[u32], seq: &mut SequenceState, chunk_start: usize, chunk_len: usize, is_last_chunk: bool, stream: u64, ) -> Result<DevicePtr>
chunk_len prompt tokens from chunk_start. Only a chunk with
is_last_chunk returns logits (of the last prompt position); any other chunk returns
DevicePtr::NULL.Source§fn prefill_twophase(
&self,
tokens: &[u32],
seq: &mut SequenceState,
chunk_size: usize,
stream: u64,
) -> Result<DevicePtr>
fn prefill_twophase( &self, tokens: &[u32], seq: &mut SequenceState, chunk_size: usize, stream: u64, ) -> Result<DevicePtr>
chunk_size as the chunk length
when an implementation splits it. Returns the last position’s logits. Default: one
prefill_chunk over all of tokens.Source§fn decode(
&self,
token: u32,
seq: &mut SequenceState,
_stream: u64,
) -> Result<DevicePtr>
fn decode( &self, token: u32, seq: &mut SequenceState, _stream: u64, ) -> Result<DevicePtr>
Source§fn decode_batch(
&self,
tokens: &[u32],
seqs: &mut [&mut SequenceState],
stream: u64,
) -> Result<DevicePtr>
fn decode_batch( &self, tokens: &[u32], seqs: &mut [&mut SequenceState], stream: u64, ) -> Result<DevicePtr>
[seqs.len(), vocab].Source§fn mixed_forward(
&self,
decode_tokens: &[u32],
decode_seqs: &mut [&mut SequenceState],
prefill_tokens: &[u32],
prefill_seq: &mut SequenceState,
prefill_chunk_start: usize,
prefill_chunk_len: usize,
prefill_is_last: bool,
stream: u64,
) -> Result<MixedForwardResult>
fn mixed_forward( &self, decode_tokens: &[u32], decode_seqs: &mut [&mut SequenceState], prefill_tokens: &[u32], prefill_seq: &mut SequenceState, prefill_chunk_start: usize, prefill_chunk_len: usize, prefill_is_last: bool, stream: u64, ) -> Result<MixedForwardResult>
decode_seqs plus one prefill chunk. Default:
decode_batch (skipped when there are no decode tokens), then prefill_chunk.Source§fn normalize_ssm_states(&self, seq: &SequenceState, stream: u64) -> Result<()>
fn normalize_ssm_states(&self, seq: &SequenceState, stream: u64) -> Result<()>
h_state. The scheduler calls it between
prefill chunks. Default: no-op.Source§fn hc_mult(&self) -> usize
fn hc_mult(&self) -> usize
config.hc_mult in TransformerModel);
0 means no highway. Default 0.Source§fn is_mla(&self) -> bool
fn is_mla(&self) -> bool
TransformerModel answers
requires_single_chunk_prefill: MLA models (kv_lora_rank > 0) except glm5_next and
kimi_k3. Default false.Source§fn kv_block_size(&self) -> Option<usize>
fn kv_block_size(&self) -> Option<usize>
None without a paged KV cache. The scheduler
uses it to end a prefill chunk at metrale_gpu_runtime::ssm_tail_boundary. Default
None.Source§fn prefill_tail_split(&self, tokens: &[u32]) -> Option<usize>
fn prefill_tail_split(&self, tokens: &[u32]) -> Option<usize>
tokens is split so that an SSM
snapshot lands at prefill_plan::tail_split_point, or None when this model does
not split it. The scheduler ends a non-last chunk there
(prefill_plan::plan_chunk_len). Default None.Source§fn supports_beam(&self) -> bool
fn supports_beam(&self) -> bool
Self::generate_beam_batch. Default false;
NllbGpuModel returns true.Source§fn generate_beam_batch(&self, _reqs: &[BeamReq]) -> Result<Vec<Vec<u32>>>
fn generate_beam_batch(&self, _reqs: &[BeamReq]) -> Result<Vec<Vec<u32>>>
num_beams > 1, instead of the
token-by-token decode loop. Default: an error.Source§fn mixed_forward_batch(
&self,
decode_tokens: &[u32],
decode_seqs: &mut [&mut SequenceState],
prefill_streams: &mut [PrefillSlice<'_>],
stream: u64,
) -> Result<MixedBatchResult>
fn mixed_forward_batch( &self, decode_tokens: &[u32], decode_seqs: &mut [&mut SequenceState], prefill_streams: &mut [PrefillSlice<'_>], stream: u64, ) -> Result<MixedBatchResult>
decode_seqs plus one prefill chunk for each of
prefill_streams. Default: decode_batch, a synchronise of the default stream, then
prefill_batch_chunk_rows with the prefill rows placed after the decode rows.Source§impl ModelLifecycle for TransformerModel
impl ModelLifecycle for TransformerModel
Source§fn poll_innerq(&self)
fn poll_innerq(&self)
2026-09-25: Poll this model’s InnerQ calibration driver, if it has one.
A maybe_finalize error is logged as a warning and not returned.
Source§fn teardown(&mut self) -> Result<()>
fn teardown(&mut self) -> Result<()>
core/finish.rs). Default: Ok(()), for models that own no pooled
device memory; a model that owns pools and keeps the default leaks them.Source§fn high_speed_swap_dims(&self) -> Option<ModelDims>
fn high_speed_swap_dims(&self) -> Option<ModelDims>
--high-speed-swap orchestrator; None for a model
that does not support it (the default).Source§fn bind_gpu_to_thread(&self) -> Result<()>
fn bind_gpu_to_thread(&self) -> Result<()>
Source§fn alloc_sequence(&self) -> Result<SequenceState>
fn alloc_sequence(&self) -> Result<SequenceState>
Source§fn alloc_sequence_for(&self, budget_tokens: usize) -> Result<SequenceState>
fn alloc_sequence_for(&self, budget_tokens: usize) -> Result<SequenceState>
Self::alloc_sequence for a request that can reach at most budget_tokens
(prompt_len + max_tokens), so proposer state that scales with context can be sized to
that (DraftProposer::alloc_state_for). Default: Self::alloc_sequence.Source§fn cache_sequence(&self, seq: &SequenceState)
fn cache_sequence(&self, seq: &SequenceState)
Self::free_sequence, while its blocks are still valid.Source§fn free_sequence(&self, seq: &mut SequenceState) -> Result<()>
fn free_sequence(&self, seq: &mut SequenceState) -> Result<()>
Source§fn compact_sequence(
&self,
seq: &mut SequenceState,
new_slot: usize,
) -> Result<()>
fn compact_sequence( &self, seq: &mut SequenceState, new_slot: usize, ) -> Result<()>
h_state and conv_state of every SSM layer)
to pool slot new_slot.Source§fn detach_slot_for_reuse(&self, seq: &mut SequenceState)
fn detach_slot_for_reuse(&self, seq: &mut SequenceState)
compact_sequence moved a
surviving sequence into it: sets slot_idx to usize::MAX and empties the slot guard, so
freeing or dropping this sequence does not release the slot. Call it right after that
compact_sequence, before any step that can drop the sequence.Source§fn save_sequence_state(
&self,
seq: &SequenceState,
writer: &mut dyn Write,
) -> Result<()>
fn save_sequence_state( &self, seq: &SequenceState, writer: &mut dyn Write, ) -> Result<()>
writer, in a format the
model owns, without freeing anything. Default: an error.Source§fn restore_sequence_state(
&self,
seq: &mut SequenceState,
num_blocks: usize,
reader: &mut dyn Read,
) -> Result<()>
fn restore_sequence_state( &self, seq: &mut SequenceState, num_blocks: usize, reader: &mut dyn Read, ) -> Result<()>
Self::save_sequence_state into an allocated
sequence, allocating num_blocks KV blocks. Default: an error.Source§fn num_free_blocks(&self) -> usize
fn num_free_blocks(&self) -> usize
0.Source§fn num_total_blocks(&self) -> usize
fn num_total_blocks(&self) -> usize
0.Source§fn kv_block_bytes(&self) -> usize
fn kv_block_bytes(&self) -> usize
num_total_blocks() * kv_block_bytes() is the pool’s size. 0 for a model without a
paged cache. No default: a model that has a pool must say what its blocks cost.Source§fn reclaim_prefix_blocks(&self, num_blocks: usize) -> usize
fn reclaim_prefix_blocks(&self, num_blocks: usize) -> usize
num_blocks blocks from the prefix cache; returns how many became
free. Swap-in and preemption resume check num_free_blocks() before they allocate, so
they call this to reach blocks the cache holds. 0 means nothing was evictable, and the
caller stops asking. Default 0.Source§fn reclaim_prefix_blocks_for(
&self,
prompt: &[u32],
adapter_slot: i32,
blocks_needed: usize,
) -> PrefixReclaim
fn reclaim_prefix_blocks_for( &self, prompt: &[u32], adapter_slot: i32, blocks_needed: usize, ) -> PrefixReclaim
num_free_blocks() covers what a prefill
of prompt under adapter_slot allocates: blocks_needed (the caller’s count for the
whole prompt, headroom included) less the blocks of the cached prefix that prefill will
reuse. An empty prompt means the prefill reuses nothing (prompt logprobs). Admission
and preemption resume call it before they allocate. Default: no prefix is reused, and
Self::reclaim_prefix_blocks runs until blocks_needed is free or it returns 0.Source§impl ModelLogits for TransformerModel
impl ModelLogits for TransformerModel
fn vocab_size(&self) -> usize
Source§fn copy_logits_to_host(
&self,
logits_ptr: DevicePtr,
dst: &mut [u8],
) -> Result<()>
fn copy_logits_to_host( &self, logits_ptr: DevicePtr, dst: &mut [u8], ) -> Result<()>
dst is vocab_size * 2 bytes for BF16
logits.Source§fn logits_ptr_is_fp32(&self, logits_ptr: DevicePtr) -> bool
fn logits_ptr_is_fp32(&self, logits_ptr: DevicePtr) -> bool
logits_ptr holds FP32 logits (vocab * 4 bytes per row). Default
false; TransformerModel returns false too, because its use_fp32_logits is fixed to
false in TransformerModel::new.Source§fn logits_buffer_ptr(&self) -> DevicePtr
fn logits_buffer_ptr(&self) -> DevicePtr
[k, vocab] rows.Source§fn argmax_on_device(&self, logits_ptr: DevicePtr, _stream: u64) -> Result<u32>
fn argmax_on_device(&self, logits_ptr: DevicePtr, _stream: u64) -> Result<u32>
Source§fn argmax_batch(
&self,
logits_ptr: DevicePtr,
n: usize,
_stream: u64,
) -> Result<Vec<u32>>
fn argmax_batch( &self, logits_ptr: DevicePtr, n: usize, _stream: u64, ) -> Result<Vec<u32>>
n logits rows on the device.Source§fn decode_logits_fp32(&self) -> bool
fn decode_logits_fp32(&self) -> bool
Self::decode_logits_ptr at 4 bytes per element. Default false; TransformerModel
returns its use_fp32_logits, which is always false.Source§fn decode_logits_ptr(&self) -> DevicePtr
fn decode_logits_ptr(&self) -> DevicePtr
Self::decode_logits_fp32 is true, else BF16. The default panics, so a model must
override it; TransformerModel does.Source§impl ModelSsmState for TransformerModel
impl ModelSsmState for TransformerModel
fn checkpoint_ssm_states(&self, seq: &mut SequenceState) -> Result<()>
Source§fn rollback_ssm_states(
&self,
seq: &mut SequenceState,
num_accepted: usize,
) -> Result<()>
fn rollback_ssm_states( &self, seq: &mut SequenceState, num_accepted: usize, ) -> Result<()>
num_accepted tokens of the last verify were
accepted.Source§fn has_ssm_layers(&self) -> bool
fn has_ssm_layers(&self) -> bool
seq_len rewinds paged KV but not that state, so a decode rollback
must also restore it from a snapshot. Default false.Source§fn decode_rollback_unsupported(&self) -> bool
fn decode_rollback_unsupported(&self) -> bool
LayerCapabilities::decode_rollback_unsupported); the scheduler then declines
a decode rollback (rollback_to_boundary). Default false.Source§fn decode_rollback_ring_slots(&self) -> usize
fn decode_rollback_ring_slots(&self) -> usize
0 (the default) means no decode-rollback
snapshots. TransformerModel returns the depth ssm_reserve::decode_rollback_ring_slots
chose.Source§fn save_decode_ssm_snapshot(
&self,
seq: &SequenceState,
ring_slot: usize,
) -> Result<()>
fn save_decode_ssm_snapshot( &self, seq: &SequenceState, ring_slot: usize, ) -> Result<()>
seq’s SSM h_state and conv_state (every SSM layer) into its
decode-rollback ring slot ring_slot, in 0..decode_rollback_ring_slots(). Default:
Ok(()).Source§fn restore_decode_ssm_snapshot(
&self,
seq: &SequenceState,
ring_slot: usize,
) -> Result<()>
fn restore_decode_ssm_snapshot( &self, seq: &SequenceState, ring_slot: usize, ) -> Result<()>
seq’s SSM state from ring slot ring_slot, written earlier by
Self::save_decode_ssm_snapshot. Default: Ok(()).Source§fn decode_marconi_checkpoint(&self, seq: &mut SequenceState)
fn decode_marconi_checkpoint(&self, seq: &mut SequenceState)
Source§fn ssm_snapshot_occupancy(&self) -> Option<(u32, u32)>
fn ssm_snapshot_occupancy(&self) -> Option<(u32, u32)>
(used, total), or None without a snapshot
pool (the default; TransformerModel also returns None when the pool has no slots).Source§fn start_checkpoint_async(&self, seq: &mut SequenceState) -> Result<()>
fn start_checkpoint_async(&self, seq: &mut SequenceState) -> Result<()>
Self::sync_secondary before the next verify. Default: the synchronous
Self::checkpoint_ssm_states.Source§fn start_rollback_and_checkpoint_async(
&self,
seq: &mut SequenceState,
num_accepted: usize,
) -> Result<()>
fn start_rollback_and_checkpoint_async( &self, seq: &mut SequenceState, num_accepted: usize, ) -> Result<()>
num_accepted and checkpoint it again, on the
secondary stream. Default: the synchronous rollback, then checkpoint.Source§fn sync_secondary(&self) -> Result<()>
fn sync_secondary(&self) -> Result<()>
Ok(()).Source§fn commit_accepted_prefix(
&self,
seq: &mut SequenceState,
num_accepted: usize,
k: usize,
) -> Result<()>
fn commit_accepted_prefix( &self, seq: &mut SequenceState, num_accepted: usize, k: usize, ) -> Result<()>
h_state and
conv_state, on the secondary stream (pair with Self::sync_secondary). Default:
Ok(()).Source§impl ModelStreams for TransformerModel
impl ModelStreams for TransformerModel
Source§fn default_stream(&self) -> u64
fn default_stream(&self) -> u64
0.Source§fn create_stream(&self) -> Result<u64>
fn create_stream(&self) -> Result<u64>
Ok(0).Source§fn create_event(&self) -> Result<u64>
fn create_event(&self) -> Result<u64>
Ok(0).Source§fn record_event(&self, event: u64, stream: u64) -> Result<()>
fn record_event(&self, event: u64, stream: u64) -> Result<()>
event on stream. Default: Ok(()).Source§impl ModelVerify for TransformerModel
impl ModelVerify for TransformerModel
Source§fn decode_verify(
&self,
tokens: &[u32],
seq: &mut SequenceState,
stream: u64,
) -> Result<Vec<u32>>
fn decode_verify( &self, tokens: &[u32], seq: &mut SequenceState, stream: u64, ) -> Result<Vec<u32>>
tokens through the model as one verify pass and return the argmax id at
each position; every token advances the KV and SSM state.Source§fn decode_verify_graphed(
&self,
tokens: &[u32; 2],
seq: &mut SequenceState,
_stream: u64,
) -> Result<[u32; 2]>
fn decode_verify_graphed( &self, tokens: &[u32; 2], seq: &mut SequenceState, _stream: u64, ) -> Result<[u32; 2]>
[last, draft]); returns the argmax id at each position.Source§fn decode_verify_graphed_k3(
&self,
tokens: &[u32; 3],
seq: &mut SequenceState,
_stream: u64,
) -> Result<[u32; 3]>
fn decode_verify_graphed_k3( &self, tokens: &[u32; 3], seq: &mut SequenceState, _stream: u64, ) -> Result<[u32; 3]>
Source§fn decode_verify_graphed_k4(
&self,
tokens: &[u32; 4],
seq: &mut SequenceState,
_stream: u64,
) -> Result<[u32; 4]>
fn decode_verify_graphed_k4( &self, tokens: &[u32; 4], seq: &mut SequenceState, _stream: u64, ) -> Result<[u32; 4]>
Source§fn can_batch_verify(&self, ks: &[usize]) -> bool
fn can_batch_verify(&self, ks: &[usize]) -> bool
Self::decode_verify_batched can run ks.len() sequences with
ks[i] verify rows each (the sequence’s draft count plus one; the rows may differ per
sequence). Default false, and there is no default batched implementation: a loop of
per-sequence verifies would leave only the last sequence’s rows in the shared logits
buffer.Source§fn decode_verify_batched(
&self,
tokens: &[u32],
ks: &[usize],
seqs: &mut [&mut SequenceState],
_stream: u64,
opts: VerifyBatchedOpts,
) -> Result<Vec<u32>>
fn decode_verify_batched( &self, tokens: &[u32], ks: &[usize], seqs: &mut [&mut SequenceState], _stream: u64, opts: VerifyBatchedOpts, ) -> Result<Vec<u32>>
ks.len() sequences in one forward. Rows are flat and sequence-major
(tokens.len() == Σ ks): sequence i holds [last_verified, d0, ..] in rows
off_i..off_i + ks[i], with off_i = Σ_{t<i} ks[t]. Returns the Σ ks argmax ids in the
same order. On success each sequence advances by its own ks[i], and the caller rewinds
by its verdict. Read moreSource§fn decode_verify_graphed_kgamma(
&self,
tokens: &[u32],
seq: &mut SequenceState,
_stream: u64,
) -> Result<Vec<u32>>
fn decode_verify_graphed_kgamma( &self, tokens: &[u32], seq: &mut SequenceState, _stream: u64, ) -> Result<Vec<u32>>
tokens (1 verified token and γ drafts). Default:
Self::decode_verify.Source§fn decode_and_verify_fused(
&self,
tokens: &[u32],
seq: &mut SequenceState,
_stream: u64,
) -> Result<Vec<u32>>
fn decode_and_verify_fused( &self, tokens: &[u32], seq: &mut SequenceState, _stream: u64, ) -> Result<Vec<u32>>
tokens[0] is the accepted token and
tokens[1..] the draft block. Default: Self::decode_verify_graphed_kgamma.Source§fn gdn_fold_accepted(
&self,
slots: &[usize],
accepted_rows: &[u32],
k_rows: usize,
) -> Result<bool>
fn gdn_fold_accepted( &self, slots: &[usize], accepted_rows: &[u32], k_rows: usize, ) -> Result<bool>
write_on_accept, commit the GDN h state of
each slot slots[i] at accepted_rows[i] verify rows (anchor included), in the verify’s
batch order. Ok(true) means the h states were committed here, and
commit_accepted_prefix then restores only conv state for those slots. Ok(false)
means nothing was folded. Default: Ok(false).Source§fn decode_verify_dflash(
&self,
tokens: &[u32],
seq: &mut SequenceState,
stream: u64,
) -> Result<Vec<u32>>
fn decode_verify_dflash( &self, tokens: &[u32], seq: &mut SequenceState, stream: u64, ) -> Result<Vec<u32>>
Self::decode_verify_graphed_kgamma.Source§impl ModelVision for TransformerModel
impl ModelVision for TransformerModel
Source§fn prepare_vision_embed(&self, images: &[VisionItem]) -> Result<()>
fn prepare_vision_embed(&self, images: &[VisionItem]) -> Result<()>
prefill_chunk for a prompt with image pad tokens. Default:
Ok(()).Source§fn prepare_vision_embed_batched(
&self,
per_request: &[Vec<VisionItem>],
) -> Result<Vec<(usize, usize, usize, usize)>>
fn prepare_vision_embed_batched( &self, per_request: &[Vec<VisionItem>], ) -> Result<Vec<(usize, usize, usize, usize)>>
(patch_row_offset, grid_index_offset, num_images, patch_row_count)
in the shared output buffer. Default: an empty Vec.Source§fn ep_sync_vision_embeds(&self, tokens: &[u32]) -> Result<()>
fn ep_sync_vision_embeds(&self, tokens: &[u32]) -> Result<()>
Ok(()).Source§fn set_vision_slice_base(
&self,
row_base: usize,
grid_base: usize,
owned_images: usize,
)
fn set_vision_slice_base( &self, row_base: usize, grid_base: usize, owned_images: usize, )
prefill_chunk reads its vision rows in the batched encoder
output: row offset, grid index offset and image count; (0, 0, 0) resets. Default:
no-op.Source§fn tokens_contain_vision_pad(&self, tokens: &[u32]) -> bool
fn tokens_contain_vision_pad(&self, tokens: &[u32]) -> bool
tokens holds a vision pad token, whose KV came from image
embeddings that re-prefilling the tokens cannot reproduce. Decode preemption then does
not pick the sequence for requeue-and-re-prefill unless it can be spilled. Default
false.