Skip to main content

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}