metrale_model_engine/traits/model/device_feed.rs
1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3//! 2026-09-26: `ModelDeviceFeed`, one of the supertraits `Model` is made of. Its methods, default
4//! bodies and docs are the ones `Model` declared before the split.
5//!
6//! Owner: model-engine.
7//! Invariants: none beyond the types.
8
9use super::{FeedSource, RowMask};
10use crate::traits::SequenceState;
11use anyhow::{Result, bail};
12use metrale_gpu_runtime::gpu::DevicePtr;
13
14/// 2026-09-26: The device token feed of the asynchronous scheduler router, with the event queries,
15/// async copies and pinned host memory it uses.
16pub trait ModelDeviceFeed {
17 // 2026-09-25: Device token feed: what the asynchronous scheduler router needs to run a
18 // plain decode step ahead of the host. `AsyncDeviceIo::new` refuses a model whose
19 // `supports_device_token_feed` is `false`, the default.
20
21 /// 2026-09-25: Whether the model can run a decode step whose input ids live on the device
22 /// (`decode_batch_fed`) and answer it with a masked argmax written into the feed cells
23 /// (`argmax_batch_to_feed`). Default `false`.
24 fn supports_device_token_feed(&self) -> bool {
25 false
26 }
27
28 /// 2026-09-25: The decode step over `seqs` with each row's input id read on the device from
29 /// `sources`: the forward, KV writes and block allocation of `decode_batch`, but it does not
30 /// push the token or advance `seq_len`, because the router does not know a fed row's id at
31 /// launch; the scheduler core does that once the previous step is committed. Position
32 /// `seq.seq_len` is written. Default: an error.
33 fn decode_batch_fed(
34 &self,
35 _sources: &[FeedSource],
36 _seqs: &mut [&mut SequenceState],
37 _stream: u64,
38 ) -> Result<DevicePtr> {
39 bail!("decode_batch_fed: this model has no device token feed")
40 }
41
42 /// 2026-09-25: Argmax over `masks.len()` rows of `logits`, applying each row's two-id mask
43 /// (`argmax_feed.cu`): writes the feed cells, copies the ids to the pinned `dst` with an
44 /// async D2H on `stream`, then records `event`. It never synchronises; `dst` is valid once
45 /// `event` completes. Default: an error.
46 fn argmax_batch_to_feed(
47 &self,
48 _logits: DevicePtr,
49 _masks: &[RowMask],
50 _dst: *mut u32,
51 _event: u64,
52 _stream: u64,
53 ) -> Result<()> {
54 bail!("argmax_batch_to_feed: this model has no device token feed")
55 }
56
57 /// 2026-09-25: Allocate the KV block for the next decode position (`seq.seq_len`) if the
58 /// block table lacks it; returns the blocks added (0 or 1). `Err` when the pool is empty.
59 /// Default: an error.
60 fn reserve_decode_block(&self, _seq: &mut SequenceState) -> Result<usize> {
61 bail!("reserve_decode_block: this model has no device token feed")
62 }
63
64 /// 2026-09-25: Return the last `blocks` entries of the block table, added by
65 /// `reserve_decode_block` for a discarded step, to the pool. Default: an error.
66 fn release_decode_blocks(&self, _seq: &mut SequenceState, _blocks: usize) -> Result<()> {
67 bail!("release_decode_blocks: this model has no device token feed")
68 }
69
70 /// 2026-09-25: A fed step has completed on the device, so the CUDA graph it replayed may be
71 /// evicted. Default: no-op.
72 fn fed_step_settled(&self) {}
73
74 /// 2026-09-25: Whether the work recorded against `event` has completed; never blocks.
75 /// Default `Ok(true)`.
76 fn event_query(&self, _event: u64) -> Result<bool> {
77 Ok(true)
78 }
79
80 /// 2026-09-25: Block the host until the work recorded against `event` has completed.
81 /// Default: `Ok(())`.
82 fn event_synchronize(&self, _event: u64) -> Result<()> {
83 Ok(())
84 }
85
86 fn destroy_event(&self, _event: u64) -> Result<()> {
87 Ok(())
88 }
89
90 /// 2026-09-25: Async device-to-host copy on `stream`; `dst` must stay valid and unread until
91 /// `stream` is synchronised. Default: an error.
92 fn copy_d2h_async(&self, _src: DevicePtr, _dst: &mut [u8], _stream: u64) -> Result<()> {
93 bail!("copy_d2h_async: this model has no device token feed")
94 }
95
96 /// 2026-09-25: Zeroed page-locked host memory for the router's readback ring. Default: an
97 /// error.
98 fn alloc_host_pinned(&self, _bytes: usize) -> Result<*mut u8> {
99 bail!("alloc_host_pinned: this model has no device token feed")
100 }
101
102 fn free_host_pinned(&self, _ptr: *mut u8, _bytes: usize) -> Result<()> {
103 bail!("free_host_pinned: this model has no device token feed")
104 }
105}