Skip to main content

aisimulate_core/engine/
protocol.rs

1// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4//! Commands and effects exchanged with one scheduler rank.
5
6use serde::{Deserialize, Serialize};
7use uuid::Uuid;
8
9use super::{HandoffId, HandoffTransferTiming};
10
11/// Runtime-neutral request accepted by the rank engine.
12#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
13pub struct Request {
14    /// Stable request identity allocated by the caller.
15    pub request_id: Uuid,
16    /// Prompt token IDs.
17    pub tokens: Vec<u32>,
18    /// Requested output length.
19    pub max_output_tokens: usize,
20    /// Optional exact output IDs. Its length overrides `max_output_tokens`.
21    #[serde(default, skip_serializing_if = "Option::is_none")]
22    pub output_token_ids: Option<Vec<u32>>,
23}
24
25/// Commands supported by the standalone scheduler.
26#[derive(Debug, Clone, PartialEq, Eq)]
27pub enum Command {
28    /// Submit one request.
29    Submit(Request),
30    /// Submit a request whose completed prefill KV must remain source-held.
31    SubmitHandoffPrefill {
32        handoff_id: HandoffId,
33        request: Request,
34    },
35    /// Accept a decode request and reserve its destination KV footprint.
36    ReserveDestination {
37        handoff_id: HandoffId,
38        request: Request,
39    },
40    /// Make a reserved destination runnable after transfer completion.
41    ActivateDestination { handoff_id: HandoffId },
42    /// Release a successfully transferred source hold.
43    ReleaseSource { handoff_id: HandoffId },
44    /// Cancel pending or held source ownership.
45    CancelSource { handoff_id: HandoffId },
46    /// Cancel pending, reserved, or active destination ownership.
47    CancelDestination { handoff_id: HandoffId },
48    /// Cancel one request.
49    ///
50    /// A command that removes scheduler-owned state also suppresses output
51    /// retained by an in-flight pass. `discard_pending_output` additionally
52    /// requests suppression when scheduler cancellation is a no-op, which is
53    /// needed after an external driver has already retired the request.
54    CancelRequest {
55        request_id: Uuid,
56        discard_pending_output: bool,
57    },
58}
59
60/// Result of applying a scheduler command.
61#[derive(Debug, Clone, Copy, PartialEq, Eq)]
62pub enum CommandResult {
63    /// A new request was accepted.
64    Submitted(Uuid),
65    /// A destination request was accepted; physical reservation may be pending.
66    DestinationAccepted { request_id: Uuid },
67    /// State or retained effects were changed.
68    Applied,
69    /// The command addressed no owned state or retained effect.
70    Noop,
71}
72
73/// Asynchronous scheduler lifecycle fact consumed by the Replayer's handoff
74/// coordinator.
75#[derive(Debug, Clone, Copy, PartialEq)]
76pub enum LifecycleEvent {
77    /// Prefill computation completed and source KV ownership is retained.
78    SourceHeld {
79        handoff_id: HandoffId,
80        request_id: Uuid,
81        transfer_timing: HandoffTransferTiming,
82    },
83    /// Decode-side physical KV capacity has been reserved.
84    DestinationReserved {
85        handoff_id: HandoffId,
86        request_id: Uuid,
87        transferable_prompt_tokens: usize,
88    },
89}
90
91/// One runtime-neutral KV block identity.
92#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
93pub struct KvBlock {
94    /// Sequence-aware block hash.
95    pub block_hash: u64,
96    /// Token-only local block hash.
97    pub tokens_hash: u64,
98    /// Token IDs retained only when explicitly configured.
99    #[serde(default, skip_serializing_if = "Option::is_none")]
100    pub token_ids: Option<Vec<u32>>,
101}
102
103/// A consecutive set of newly visible blocks.
104#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
105pub struct StoredBlocks {
106    /// Parent sequence hash immediately preceding the batch.
107    pub parent_hash: Option<u64>,
108    /// Optional absolute zero-based position of the first block.
109    ///
110    /// `None` preserves the parent-linked stream emitted by the native
111    /// schedulers; adapters must not invent an absolute position because that
112    /// changes how downstream radix indexes reconcile stores and removals.
113    #[serde(default, skip_serializing_if = "Option::is_none")]
114    pub start_position: Option<usize>,
115    /// Blocks in sequence order.
116    pub blocks: Vec<KvBlock>,
117}
118
119/// Runtime-neutral KV event payload.
120#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
121#[serde(rename_all = "snake_case")]
122pub enum KvEventData {
123    /// Blocks became prefix-cache visible.
124    Stored(StoredBlocks),
125    /// The final physical copy of these hashes was evicted.
126    Removed { block_hashes: Vec<u64> },
127}
128
129/// Ordered runtime-neutral KV event emitted by one rank.
130#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
131pub struct KvEvent {
132    /// Monotonic rank-local event sequence.
133    pub event_id: u64,
134    /// Attention-DP rank.
135    pub dp_rank: u32,
136    /// Event payload.
137    pub data: KvEventData,
138}
139
140/// Request admission exposed at pass start.
141#[derive(Debug, Clone, Copy, PartialEq, Eq)]
142pub struct Admission {
143    /// Admitted request.
144    pub request_id: Uuid,
145    /// Prompt tokens reused from native G1.
146    pub reused_input_tokens: usize,
147}
148
149/// Scheduler action taken to relieve KV pressure.
150#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
151#[serde(rename_all = "snake_case")]
152pub enum PressureKind {
153    /// vLLM evicted a running request and returned it to the waiting queue.
154    VllmPreemption,
155    /// SGLang retracted a running decode request for later readmission.
156    SglangRetraction,
157}
158
159/// Runtime-neutral scheduler and KV occupancy around one pressure action.
160#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
161pub struct PressureState {
162    /// Requests runnable before or after the action.
163    pub running_requests: usize,
164    /// Requests waiting for admission, when the scheduler exposes that count.
165    pub waiting_requests: Option<usize>,
166    /// Physically active native-G1 blocks.
167    pub active_blocks: usize,
168}
169
170/// One scheduler-owned KV pressure action emitted with pass-start effects.
171///
172/// The Replayer may attach topology/pool identity and correlate a later
173/// [`Admission`] for the same request. The engine always produces this
174/// lightweight fact; capture policy belongs to the consuming runtime.
175#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
176pub struct PressureEvent {
177    /// Modeled timestamp at which the scheduler took the action.
178    pub at_ms: f64,
179    /// Scheduler-specific pressure action.
180    pub kind: PressureKind,
181    /// Request removed from the running set.
182    pub request_id: Uuid,
183    /// Scheduler/KV state immediately before the action.
184    pub state_before: PressureState,
185    /// Scheduler/KV state immediately after the action.
186    pub state_after: PressureState,
187    /// Blocks owned by this request before it was preempted or retracted.
188    pub request_active_blocks_before: usize,
189    /// Logically available blocks used by the SGLang retraction decision.
190    pub logical_available_blocks_before: Option<usize>,
191    /// Blocks required by the SGLang retraction decision.
192    pub required_blocks_before: Option<usize>,
193}
194
195/// One client-visible output.
196#[derive(Debug, Clone, PartialEq)]
197pub struct Output {
198    /// Request producing the output.
199    pub request_id: Uuid,
200    /// Generated token, or `None` for a terminal-without-token signal.
201    pub token_id: Option<u32>,
202    /// Whether request ownership ended with this output.
203    pub completed: bool,
204    /// Whether admission rejected the request as physically impossible.
205    pub rejected: bool,
206    /// Prompt tokens served from KV cache at first admission, reported once
207    /// on the request's first output.
208    pub cached_tokens: Option<usize>,
209}
210
211/// Rank-local scheduler and G1 metrics.
212#[derive(Debug, Clone, Default, PartialEq)]
213pub struct Metrics {
214    pub dp_rank: u32,
215    pub active_blocks: u64,
216    pub total_blocks: u64,
217    pub cache_usage: f64,
218    pub running_requests: u64,
219    pub waiting_requests: u64,
220    pub preemptions_total: u64,
221    /// SGLang radix-cache tokens reused by the most recently completed pass.
222    ///
223    /// Backends without an equivalent pass-local metric report zero.
224    pub sglang_cache_hit_tokens: u64,
225    /// Total SGLang prefill tokens considered by the most recently completed
226    /// pass. Backends without an equivalent pass-local metric report zero.
227    pub sglang_cache_total_tokens: u64,
228}
229
230/// Per-pass scheduling statistics.
231#[derive(Debug, Clone, Default, PartialEq)]
232pub struct ForwardPassMetrics {
233    pub num_prefill_requests: u32,
234    pub sum_prefill_tokens: u64,
235    pub var_prefill_length: f64,
236    pub sum_prefill_kv_tokens: u64,
237    pub num_decode_requests: u32,
238    pub sum_decode_kv_tokens: u64,
239    pub var_decode_kv_tokens: f64,
240    pub num_queued_prefill: u32,
241    pub sum_queued_prefill_tokens: u64,
242    pub var_queued_prefill_length: f64,
243    pub num_queued_decode: u32,
244    pub sum_queued_decode_kv_tokens: u64,
245    pub var_queued_decode_kv_tokens: f64,
246    pub duration_ms: f64,
247}
248
249/// Effects of a scheduler command.
250#[derive(Debug, Clone, PartialEq)]
251pub struct CommandEffects {
252    pub result: CommandResult,
253    pub lifecycle_events: Vec<LifecycleEvent>,
254    pub kv_events: Vec<KvEvent>,
255    /// Requests whose scheduler/KV ownership ended while applying this command.
256    ///
257    /// Replayers use this authoritative delta for cancellation and handoff
258    /// cleanup instead of inferring ownership from command ordering.
259    pub retired_requests: Vec<Uuid>,
260    /// Scheduler state after the command and all immediately admitted work.
261    ///
262    /// Live drivers use this to acknowledge commands while the engine is idle
263    /// without waiting for an otherwise unrelated forward pass.
264    pub metrics: Metrics,
265    /// Whether an output already computed by an in-flight pass was suppressed.
266    pub suppressed_pending_output: bool,
267}
268
269/// Effects visible as soon as an engine pass starts.
270#[derive(Debug, Clone, Default, PartialEq)]
271pub struct PassStartEffects {
272    pub admissions: Vec<Admission>,
273    pub pressure_events: Vec<PressureEvent>,
274    pub kv_events: Vec<KvEvent>,
275}
276
277/// Effects released at the modeled pass completion boundary.
278#[derive(Debug, Clone, Default, PartialEq)]
279pub struct PassCompletionEffects {
280    pub outputs: Vec<Output>,
281    pub lifecycle_events: Vec<LifecycleEvent>,
282    /// KV events whose scheduler visibility boundary is pass completion.
283    pub kv_events: Vec<KvEvent>,
284    pub metrics: Metrics,
285    pub forward_pass_metrics: ForwardPassMetrics,
286}
287
288/// Retained completion effects of an eagerly executed engine pass.
289#[doc(hidden)]
290pub struct PendingPass {
291    pub(crate) started_at_ms: f64,
292    pub(crate) effects: PassCompletionEffects,
293}