eredu_core/execution_control.rs
1//! Portable discovery and resource contracts for completed-token execution control.
2
3use crate::generation::GenerationCancellationToken;
4use serde::{Deserialize, Serialize};
5use std::sync::{
6 atomic::{AtomicBool, Ordering},
7 Arc,
8};
9
10/// Native persistent-state mechanisms for ordinary text generation.
11///
12/// These operations cover the model's complete mutable execution state and
13/// native metadata, not the sampler, pending input or facade semantic state.
14/// Portable composition must reserve known costs before copying and combine all
15/// of those owners before advertising a complete generation snapshot. Backends
16/// cooperate with their existing submission authority and recovery owner.
17pub trait NativeTextStateBackend: crate::TextGenerationBackend {
18 /// Independently writable, opaque, in-process native state slot. Sharing
19 /// immutable weights is allowed; cloning mutable storage handles is not.
20 type NativeTextState;
21
22 /// Side-effect-free support facts for this exact loaded execution. This is
23 /// a primitive report, not full generation-snapshot capability discovery.
24 fn native_text_state_support(runtime: &crate::ModelRuntime<Self>) -> ControlSupport;
25
26 /// Estimates copying installed state (`None`) or a compatible saved slot.
27 /// Unknown costs remain explicit and must be rejected before copying.
28 fn estimate_native_text_state(
29 runtime: &crate::ModelRuntime<Self>,
30 saved: Option<&Self::NativeTextState>,
31 ) -> Result<Option<SnapshotEstimate>, Self::Error>;
32
33 /// Conservative additional logical retention through at most this many new
34 /// input tokens, starting from the saved state. Includes cache capacity growth
35 /// and initially absent recurrent/convolution components, without executing
36 /// input. Unknown growth disables runnable branches, not immutable snapshots.
37 fn estimate_native_text_growth(
38 _runtime: &crate::ModelRuntime<Self>,
39 _saved: &Self::NativeTextState,
40 _additional_input_tokens: u64,
41 ) -> Result<Option<u64>, Self::Error> {
42 Ok(None)
43 }
44
45 /// Captures independent state after portable resource reservation. Returns
46 /// only after successful exact native completion. Failure never changes
47 /// the logical source state; unresolved work remains retained and fenced.
48 fn capture_native_text_state(
49 runtime: &mut crate::ModelRuntime<Self>,
50 ) -> Result<Self::NativeTextState, Self::Error>;
51
52 /// Copies a reusable saved slot without installing it or replaying input.
53 /// The source remains unchanged even if allocation or completion fails.
54 fn copy_native_text_state(
55 runtime: &mut crate::ModelRuntime<Self>,
56 saved: &Self::NativeTextState,
57 ) -> Result<Self::NativeTextState, Self::Error>;
58
59 /// Validates exact executable identity, geometry and safe native boundary
60 /// before any state mutation. A different compatible-looking model fails.
61 fn validate_native_text_state(
62 runtime: &crate::ModelRuntime<Self>,
63 saved: &Self::NativeTextState,
64 ) -> Result<(), Self::Error>;
65
66 /// Atomically exchanges installed and saved state, without copying tensor
67 /// data. The old installed state is returned in `slot`. On error neither
68 /// logical state changes; unresolved native failure may fence the engine.
69 fn exchange_native_text_state(
70 runtime: &mut crate::ModelRuntime<Self>,
71 slot: &mut Self::NativeTextState,
72 ) -> Result<(), Self::Error>;
73}
74
75/// Version of execution-control metadata and lifecycle records.
76pub const EXECUTION_CONTROL_SCHEMA_VERSION: u32 = 1;
77
78/// Observable lifecycle of one controllable ordinary generation session.
79#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
80#[serde(rename_all = "snake_case")]
81pub enum GenerationStatus {
82 /// Prepared, with no model prediction yet committed.
83 Prepared,
84 /// Quiescent, resumable boundary; no further work advances without a request.
85 Paused,
86 /// A prediction or its associated delivery is being completed.
87 Running,
88 /// Normal terminal outcome; restoration preserves that outcome.
89 Completed,
90 /// Cooperatively cancelled, distinct from a resumable pause.
91 Cancelled,
92 /// Failed, including unresolved native work; not a snapshot boundary.
93 Failed,
94}
95
96/// Support for one operation on the actual selected execution configuration.
97#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
98#[serde(tag = "support", rename_all = "snake_case")]
99pub enum ControlSupport {
100 /// Implemented with the scope and guarantees in the enclosing report.
101 Supported,
102 /// Rejected before work begins.
103 Unsupported {
104 /// Concrete missing mechanism or unsupported execution combination.
105 reason: String,
106 },
107}
108
109/// Mutable-state isolation promised by an opaque native in-process snapshot.
110#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
111#[serde(rename_all = "snake_case")]
112pub enum SnapshotIsolation {
113 /// Mutable state uses independent storage; immutable weights may be shared.
114 DeepCopy,
115 /// Storage is shared only until writing, with independent semantic state.
116 CopyOnWrite,
117}
118
119/// Exact loaded-session execution-control capabilities. Native handles and
120/// estimators never enter this serializable report.
121#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
122pub struct ExecutionControlCapabilities {
123 /// Schema version.
124 pub schema_version: u32,
125 /// At most one ordinary committed prediction per step.
126 pub step: ControlSupport,
127 /// Pause and resume at a completed, delivered token boundary.
128 pub pause_resume: ControlSupport,
129 /// Complete in-process snapshot creation.
130 pub snapshot: ControlSupport,
131 /// Same-session state restoration from an immutable reusable snapshot.
132 pub restore: ControlSupport,
133 /// Isolated child creation without prompt replay or weight reload.
134 pub fork: ControlSupport,
135 /// Prospective canonical token restriction through ordinary sampling.
136 pub force_next_token: ControlSupport,
137 /// Prospective temperature changes and explicit native RNG reseeding.
138 pub sampling_overrides: ControlSupport,
139 /// Native snapshot isolation, absent when snapshots are unsupported.
140 pub isolation: Option<SnapshotIsolation>,
141 /// Determinism, scope, execution restrictions and compatibility conditions.
142 pub conditions: Vec<String>,
143}
144
145impl ExecutionControlCapabilities {
146 /// Explicitly rejects all operations for a configuration lacking support.
147 pub fn unsupported(reason: impl Into<String>) -> Self {
148 let support = ControlSupport::Unsupported {
149 reason: reason.into(),
150 };
151 Self {
152 schema_version: EXECUTION_CONTROL_SCHEMA_VERSION,
153 step: support.clone(),
154 pause_resume: support.clone(),
155 snapshot: support.clone(),
156 restore: support.clone(),
157 fork: support.clone(),
158 force_next_token: support.clone(),
159 sampling_overrides: support,
160 isolation: None,
161 conditions: Vec::new(),
162 }
163 }
164}
165
166/// Thread-safe requests for a worker whose native session can remain thread-affine.
167/// Pausing is sticky until the owning worker explicitly resumes. Cancellation
168/// retains the ordinary permanent cancellation-token semantics.
169#[derive(Debug, Clone, Default)]
170pub struct GenerationControlHandle {
171 pause: Arc<AtomicBool>,
172 cancellation: GenerationCancellationToken,
173}
174
175impl GenerationControlHandle {
176 /// Creates a handle using the ordinary caller's cancellation token.
177 pub fn new(cancellation: GenerationCancellationToken) -> Self {
178 Self {
179 pause: Arc::new(AtomicBool::new(false)),
180 cancellation,
181 }
182 }
183 /// Requests pause at the next successful completed-token boundary.
184 pub fn request_pause(&self) {
185 self.pause.store(true, Ordering::Release);
186 }
187 /// Whether a pause request is pending. This does not itself prove completion.
188 pub fn pause_requested(&self) -> bool {
189 self.pause.load(Ordering::Acquire)
190 }
191 /// Acknowledges an explicit resume before checking for a new pause request.
192 pub fn acknowledge_resume(&self) {
193 self.pause.store(false, Ordering::Release);
194 }
195 /// Permanently cancels this run, without reinterpreting cancellation as pause.
196 pub fn cancel(&self) {
197 self.cancellation.cancel();
198 }
199 /// Ordinary cancellation token, shared with existing generation consumers.
200 pub fn cancellation(&self) -> &GenerationCancellationToken {
201 &self.cancellation
202 }
203}
204
205/// Logical storage facts supplied without allocating or evaluating native state.
206/// Values include conservative copy-on-write allowances where applicable. They
207/// do not promise a physical allocator/private-workspace ceiling.
208#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
209pub struct SnapshotEstimate {
210 /// Incremental logical state retained for the lifetime of this object.
211 pub retained_bytes: u64,
212 /// Logical copying/materialization allowance charged cumulatively per attempt.
213 pub copy_bytes: u64,
214}
215
216/// Independently bounded snapshot and child-state resources.
217#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
218pub struct SnapshotLimits {
219 /// Maximum simultaneously retained snapshot objects, including live handles.
220 pub max_snapshots: u64,
221 /// Maximum simultaneously retained child states in this budget owner.
222 pub max_branches: u64,
223 /// Maximum sum of retained logical state estimates.
224 pub retained_bytes: u64,
225 /// Cumulative logical copying allowance; restore and failed attempts never refund it.
226 pub cumulative_copy_bytes: u64,
227}
228
229/// Kind of state retained under a snapshot resource reservation.
230#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
231#[serde(rename_all = "snake_case")]
232pub enum SnapshotResourceKind {
233 /// Immutable reusable saved state.
234 Snapshot,
235 /// Independently mutable child execution state.
236 Branch,
237 /// Provisional replacement during restoration; no additional persistent object.
238 Restore,
239}
240
241/// Observed logical snapshot accounting, separate from capture/transport limits.
242#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
243pub struct SnapshotUsage {
244 /// Retained snapshot count.
245 pub snapshots: u64,
246 /// Retained branch count.
247 pub branches: u64,
248 /// Sum of retained logical estimates, including provisional copies.
249 pub retained_bytes: u64,
250 /// Monotone copying allowance consumed by all admitted attempts.
251 pub cumulative_copy_bytes: u64,
252}
253
254/// Portable invalid-state or budget failure, before a native operation is called.
255#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
256pub enum ExecutionControlError {
257 /// The current state does not permit the requested lifecycle edge.
258 #[error("invalid generation transition from {from:?} to {to:?}")]
259 Transition {
260 /// Current lifecycle state.
261 from: GenerationStatus,
262 /// Requested lifecycle state.
263 to: GenerationStatus,
264 },
265 /// A complete estimate is required before copying or retaining state.
266 #[error("complete snapshot resource estimate is unavailable")]
267 UnknownEstimate,
268 /// Checked arithmetic failed.
269 #[error("execution-control accounting overflow")]
270 Overflow,
271 /// The explicitly admitted logical resource limit was exceeded.
272 #[error("snapshot resource limit exceeded: {0}")]
273 Limit(&'static str),
274}