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}