Skip to main content

ik_llama_cpp_2/context/
session.rs

1//! utilities for working with session files
2
3use crate::context::LlamaContext;
4use crate::token::LlamaToken;
5use std::ffi::{CString, NulError};
6use std::fmt::{Debug, Formatter};
7use std::path::{Path, PathBuf};
8
9/// Flags for state sequence operations.
10///
11/// These flags control what parts of the state are included when saving/restoring
12/// sequence state.
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub struct LlamaStateSeqFlags(pub(crate) ik_llama_cpp_sys::llama_state_seq_flags);
15
16impl LlamaStateSeqFlags {
17    /// Work only with partial states, such as SWA KV cache or recurrent cache (e.g. Mamba).
18    ///
19    /// This flag is useful when you only want to save/restore the recurrent state
20    /// without affecting the KV cache.
21    pub const PARTIAL_ONLY: LlamaStateSeqFlags = LlamaStateSeqFlags(1);
22
23    /// Keep the copied data on device (GPU) memory rather than host.
24    ///
25    /// Getting the state for a `seq_id` with this flag invalidates all
26    /// prior states obtained for that `seq_id` with this flag set.
27    pub const ON_DEVICE: LlamaStateSeqFlags = LlamaStateSeqFlags(2);
28
29    /// Create an empty flags set.
30    pub const fn empty() -> LlamaStateSeqFlags {
31        LlamaStateSeqFlags(0)
32    }
33
34    /// Get the raw flags value.
35    pub const fn bits(&self) -> u32 {
36        self.0
37    }
38
39    /// Construct from raw bits.
40    pub const fn from_bits(bits: u32) -> LlamaStateSeqFlags {
41        LlamaStateSeqFlags(bits)
42    }
43
44    /// Check if a flag is set.
45    pub const fn contains(&self, other: LlamaStateSeqFlags) -> bool {
46        (self.0 & other.0) != 0
47    }
48}
49
50impl Default for LlamaStateSeqFlags {
51    fn default() -> Self {
52        Self::empty()
53    }
54}
55
56impl std::ops::BitOr for LlamaStateSeqFlags {
57    type Output = Self;
58    fn bitor(self, other: Self) -> Self {
59        Self(self.0 | other.0)
60    }
61}
62
63/// Errors from the sequence-state save/restore API.
64///
65/// Module-local mirror of the anchor crate's `crate::StateSeqError`, which
66/// lives in the crate root there; kept local here so this module compiles
67/// independently.
68#[derive(Debug, Eq, PartialEq, thiserror::Error)]
69pub enum StateSeqError {
70    /// llama.cpp read or wrote a different number of bytes than expected.
71    #[error("state seq size mismatch: expected {expected}, actual {actual}")]
72    SizeMismatch {
73        /// Number of bytes the caller expected to transfer.
74        expected: usize,
75        /// Number of bytes llama.cpp actually transferred.
76        actual: usize,
77    },
78}
79
80/// Failed to save a sequence state file
81#[derive(Debug, Eq, PartialEq, thiserror::Error)]
82pub enum SaveSeqStateError {
83    /// llama.cpp failed to save the sequence state file
84    #[error("Failed to save sequence state file")]
85    FailedToSave,
86
87    /// null byte in string
88    #[error("null byte in string {0}")]
89    NullError(#[from] NulError),
90
91    /// failed to convert path to str
92    #[error("failed to convert path {0} to str")]
93    PathToStrError(PathBuf),
94}
95
96/// Failed to load a sequence state file
97#[derive(Debug, Eq, PartialEq, thiserror::Error)]
98pub enum LoadSeqStateError {
99    /// llama.cpp failed to load the sequence state file
100    #[error("Failed to load sequence state file")]
101    FailedToLoad,
102
103    /// null byte in string
104    #[error("null byte in string {0}")]
105    NullError(#[from] NulError),
106
107    /// failed to convert path to str
108    #[error("failed to convert path {0} to str")]
109    PathToStrError(PathBuf),
110
111    /// Insufficient max length
112    #[error("max_length is not large enough to hold {n_out} (was {max_tokens})")]
113    InsufficientMaxLength {
114        /// The length of the loaded sequence
115        n_out: usize,
116        /// The maximum length
117        max_tokens: usize,
118    },
119}
120
121/// Failed to save a Session file
122#[derive(Debug, Eq, PartialEq, thiserror::Error)]
123pub enum SaveSessionError {
124    /// llama.cpp failed to save the session file
125    #[error("Failed to save session file")]
126    FailedToSave,
127
128    /// null byte in string
129    #[error("null byte in string {0}")]
130    NullError(#[from] NulError),
131
132    /// failed to convert path to str
133    #[error("failed to convert path {0} to str")]
134    PathToStrError(PathBuf),
135}
136
137/// Failed to load a Session file
138#[derive(Debug, Eq, PartialEq, thiserror::Error)]
139pub enum LoadSessionError {
140    /// llama.cpp failed to load the session file
141    #[error("Failed to load session file")]
142    FailedToLoad,
143
144    /// null byte in string
145    #[error("null byte in string {0}")]
146    NullError(#[from] NulError),
147
148    /// failed to convert path to str
149    #[error("failed to convert path {0} to str")]
150    PathToStrError(PathBuf),
151
152    /// Insufficient max length
153    #[error("max_length is not large enough to hold {n_out} (was {max_tokens})")]
154    InsufficientMaxLength {
155        /// The length of the session file
156        n_out: usize,
157        /// The maximum length
158        max_tokens: usize,
159    },
160}
161
162impl LlamaContext<'_> {
163    /// Save the current session to a file.
164    ///
165    /// # Parameters
166    ///
167    /// * `path_session` - The file to save to.
168    /// * `tokens` - The tokens to associate the session with. This should be a prefix of a sequence of tokens that the context has processed, so that the relevant KV caches are already filled.
169    ///
170    /// # Errors
171    ///
172    /// Fails if the path is not a valid utf8, is not a valid c string, or llama.cpp fails to save the session file.
173    #[deprecated(since = "0.1.136", note = "Use `state_save_file` instead")]
174    pub fn save_session_file(
175        &self,
176        path_session: impl AsRef<Path>,
177        tokens: &[LlamaToken],
178    ) -> Result<(), SaveSessionError> {
179        let path = path_session.as_ref();
180        let path = path
181            .to_str()
182            .ok_or_else(|| SaveSessionError::PathToStrError(path.to_path_buf()))?;
183
184        let cstr = CString::new(path)?;
185
186        if unsafe {
187            ik_llama_cpp_sys::llama_save_session_file(
188                self.context.as_ptr(),
189                cstr.as_ptr(),
190                tokens.as_ptr().cast::<ik_llama_cpp_sys::llama_token>(),
191                tokens.len(),
192            )
193        } {
194            Ok(())
195        } else {
196            Err(SaveSessionError::FailedToSave)
197        }
198    }
199    /// Load a session file into the current context.
200    ///
201    /// You still need to pass the returned tokens to the context for inference to work. What this function buys you is that the KV caches are already filled with the relevant data.
202    ///
203    /// # Parameters
204    ///
205    /// * `path_session` - The file to load from. It must be a session file from a compatible context, otherwise the function will error.
206    /// * `max_tokens` - The maximum token length of the loaded session. If the session was saved with a longer length, the function will error.
207    ///
208    /// # Errors
209    ///
210    /// Fails if the path is not a valid utf8, is not a valid c string, or llama.cpp fails to load the session file. (e.g. the file does not exist, is not a session file, etc.)
211    #[deprecated(since = "0.1.136", note = "Use `state_load_file` instead")]
212    pub fn load_session_file(
213        &mut self,
214        path_session: impl AsRef<Path>,
215        max_tokens: usize,
216    ) -> Result<Vec<LlamaToken>, LoadSessionError> {
217        let path = path_session.as_ref();
218        let path = path
219            .to_str()
220            .ok_or(LoadSessionError::PathToStrError(path.to_path_buf()))?;
221
222        let cstr = CString::new(path)?;
223        let mut tokens: Vec<LlamaToken> = Vec::with_capacity(max_tokens);
224        let mut n_out = 0;
225
226        // SAFETY: cast is valid as LlamaToken is repr(transparent)
227        let tokens_out = tokens.as_mut_ptr().cast::<ik_llama_cpp_sys::llama_token>();
228
229        let load_session_success = unsafe {
230            ik_llama_cpp_sys::llama_load_session_file(
231                self.context.as_ptr(),
232                cstr.as_ptr(),
233                tokens_out,
234                max_tokens,
235                &mut n_out,
236            )
237        };
238        if load_session_success {
239            if n_out > max_tokens {
240                return Err(LoadSessionError::InsufficientMaxLength { n_out, max_tokens });
241            }
242            // SAFETY: we checked that n_out <= max_tokens and llama.cpp promises that n_out tokens will be written
243            unsafe {
244                tokens.set_len(n_out);
245            }
246            Ok(tokens)
247        } else {
248            Err(LoadSessionError::FailedToLoad)
249        }
250    }
251
252    /// Save the full state to a file.
253    ///
254    /// This is the non-deprecated replacement for [`save_session_file`](Self::save_session_file).
255    ///
256    /// # Parameters
257    ///
258    /// * `path_session` - The file to save to.
259    /// * `tokens` - The tokens to associate the state with. This should be a prefix of a sequence
260    ///   of tokens that the context has processed, so that the relevant KV caches are already filled.
261    ///
262    /// # Errors
263    ///
264    /// Fails if the path is not a valid utf8, is not a valid c string, or llama.cpp fails to save
265    /// the state file.
266    pub fn state_save_file(
267        &self,
268        path_session: impl AsRef<Path>,
269        tokens: &[LlamaToken],
270    ) -> Result<(), SaveSessionError> {
271        let path = path_session.as_ref();
272        let path = path
273            .to_str()
274            .ok_or_else(|| SaveSessionError::PathToStrError(path.to_path_buf()))?;
275
276        let cstr = CString::new(path)?;
277
278        if unsafe {
279            ik_llama_cpp_sys::llama_state_save_file(
280                self.context.as_ptr(),
281                cstr.as_ptr(),
282                tokens.as_ptr().cast::<ik_llama_cpp_sys::llama_token>(),
283                tokens.len(),
284            )
285        } {
286            Ok(())
287        } else {
288            Err(SaveSessionError::FailedToSave)
289        }
290    }
291
292    /// Load a state file into the current context.
293    ///
294    /// This is the non-deprecated replacement for [`load_session_file`](Self::load_session_file).
295    ///
296    /// You still need to pass the returned tokens to the context for inference to work. What this
297    /// function buys you is that the KV caches are already filled with the relevant data.
298    ///
299    /// # Parameters
300    ///
301    /// * `path_session` - The file to load from. It must be a state file from a compatible context,
302    ///   otherwise the function will error.
303    /// * `max_tokens` - The maximum token length of the loaded state. If the state was saved with a
304    ///   longer length, the function will error.
305    ///
306    /// # Errors
307    ///
308    /// Fails if the path is not a valid utf8, is not a valid c string, or llama.cpp fails to load
309    /// the state file.
310    pub fn state_load_file(
311        &mut self,
312        path_session: impl AsRef<Path>,
313        max_tokens: usize,
314    ) -> Result<Vec<LlamaToken>, LoadSessionError> {
315        let path = path_session.as_ref();
316        let path = path
317            .to_str()
318            .ok_or(LoadSessionError::PathToStrError(path.to_path_buf()))?;
319
320        let cstr = CString::new(path)?;
321        let mut tokens: Vec<LlamaToken> = Vec::with_capacity(max_tokens);
322        let mut n_out = 0;
323
324        // SAFETY: cast is valid as LlamaToken is repr(transparent)
325        let tokens_out = tokens.as_mut_ptr().cast::<ik_llama_cpp_sys::llama_token>();
326
327        let success = unsafe {
328            ik_llama_cpp_sys::llama_state_load_file(
329                self.context.as_ptr(),
330                cstr.as_ptr(),
331                tokens_out,
332                max_tokens,
333                &mut n_out,
334            )
335        };
336        if success {
337            if n_out > max_tokens {
338                return Err(LoadSessionError::InsufficientMaxLength { n_out, max_tokens });
339            }
340            // SAFETY: we checked that n_out <= max_tokens and llama.cpp promises that n_out tokens will be written
341            unsafe {
342                tokens.set_len(n_out);
343            }
344            Ok(tokens)
345        } else {
346            Err(LoadSessionError::FailedToLoad)
347        }
348    }
349
350    /// Save state for a single sequence to a file.
351    ///
352    /// This enables saving state for individual sequences, which is useful for multi-sequence
353    /// inference scenarios.
354    ///
355    /// # Parameters
356    ///
357    /// * `filepath` - The file to save to.
358    /// * `seq_id` - The sequence ID whose state to save.
359    /// * `tokens` - The tokens to associate with the saved state.
360    ///
361    /// # Errors
362    ///
363    /// Fails if the path is not a valid utf8, is not a valid c string, or llama.cpp fails to save
364    /// the sequence state file.
365    ///
366    /// # Returns
367    ///
368    /// The number of bytes written on success.
369    pub fn state_seq_save_file(
370        &self,
371        filepath: impl AsRef<Path>,
372        seq_id: i32,
373        tokens: &[LlamaToken],
374    ) -> Result<usize, SaveSeqStateError> {
375        let path = filepath.as_ref();
376        let path = path
377            .to_str()
378            .ok_or_else(|| SaveSeqStateError::PathToStrError(path.to_path_buf()))?;
379
380        let cstr = CString::new(path)?;
381
382        let bytes_written = unsafe {
383            ik_llama_cpp_sys::llama_state_seq_save_file(
384                self.context.as_ptr(),
385                cstr.as_ptr(),
386                seq_id,
387                tokens.as_ptr().cast::<ik_llama_cpp_sys::llama_token>(),
388                tokens.len(),
389            )
390        };
391
392        if bytes_written == 0 {
393            Err(SaveSeqStateError::FailedToSave)
394        } else {
395            Ok(bytes_written)
396        }
397    }
398
399    /// Load state for a single sequence from a file.
400    ///
401    /// This enables loading state for individual sequences, which is useful for multi-sequence
402    /// inference scenarios.
403    ///
404    /// # Parameters
405    ///
406    /// * `filepath` - The file to load from.
407    /// * `dest_seq_id` - The destination sequence ID to load the state into.
408    /// * `max_tokens` - The maximum number of tokens to read.
409    ///
410    /// # Errors
411    ///
412    /// Fails if the path is not a valid utf8, is not a valid c string, or llama.cpp fails to load
413    /// the sequence state file.
414    ///
415    /// # Returns
416    ///
417    /// A tuple of `(tokens, bytes_read)` on success.
418    pub fn state_seq_load_file(
419        &mut self,
420        filepath: impl AsRef<Path>,
421        dest_seq_id: i32,
422        max_tokens: usize,
423    ) -> Result<(Vec<LlamaToken>, usize), LoadSeqStateError> {
424        let path = filepath.as_ref();
425        let path = path
426            .to_str()
427            .ok_or(LoadSeqStateError::PathToStrError(path.to_path_buf()))?;
428
429        let cstr = CString::new(path)?;
430        let mut tokens: Vec<LlamaToken> = Vec::with_capacity(max_tokens);
431        let mut n_out = 0;
432
433        // SAFETY: cast is valid as LlamaToken is repr(transparent)
434        let tokens_out = tokens.as_mut_ptr().cast::<ik_llama_cpp_sys::llama_token>();
435
436        let bytes_read = unsafe {
437            ik_llama_cpp_sys::llama_state_seq_load_file(
438                self.context.as_ptr(),
439                cstr.as_ptr(),
440                dest_seq_id,
441                tokens_out,
442                max_tokens,
443                &mut n_out,
444            )
445        };
446
447        if bytes_read == 0 {
448            return Err(LoadSeqStateError::FailedToLoad);
449        }
450
451        if n_out > max_tokens {
452            return Err(LoadSeqStateError::InsufficientMaxLength { n_out, max_tokens });
453        }
454
455        // SAFETY: we checked that n_out <= max_tokens and llama.cpp promises that n_out tokens will be written
456        unsafe {
457            tokens.set_len(n_out);
458        }
459
460        Ok((tokens, bytes_read))
461    }
462
463    /// Returns the maximum size in bytes of the state (rng, logits, embedding
464    /// and `kv_cache`) - will often be smaller after compacting tokens
465    #[must_use]
466    pub fn get_state_size(&self) -> usize {
467        unsafe { ik_llama_cpp_sys::llama_get_state_size(self.context.as_ptr()) }
468    }
469
470    /// Copies the state to the specified destination address.
471    ///
472    /// Returns the number of bytes copied
473    ///
474    /// # Safety
475    ///
476    /// Destination needs to have allocated enough memory.
477    pub unsafe fn copy_state_data(&self, dest: *mut u8) -> usize {
478        unsafe { ik_llama_cpp_sys::llama_copy_state_data(self.context.as_ptr(), dest) }
479    }
480
481    /// Restore context state from a buffer previously produced by
482    /// [`copy_state_data`](Self::copy_state_data) (or `llama_state_get_data`) for
483    /// a *compatible* context. Returns the number of bytes read.
484    ///
485    /// The buffer length is forwarded to ik's **sized** reader
486    /// (`llama_state_set_data`), so each field read is bounds-checked against
487    /// `src.len()` — a too-short buffer is rejected C-side instead of over-reading
488    /// past the slice (as the deprecated no-size `llama_set_state_data` would).
489    ///
490    /// # Safety
491    ///
492    /// `src` must be a valid serialized state blob for a context created with the
493    /// same model and compatible parameters. A malformed or mismatched blob can
494    /// make the C++ deserializer throw, which aborts the process across the FFI
495    /// boundary; only the out-of-bounds *read* is prevented here.
496    pub unsafe fn set_state_data(&mut self, src: &[u8]) -> usize {
497        unsafe {
498            ik_llama_cpp_sys::llama_state_set_data(self.context.as_ptr(), src.as_ptr(), src.len())
499        }
500    }
501
502    /// Get the size of the state for a single sequence with optional flags.
503    ///
504    /// This is the extended version that supports flags for partial state operations.
505    ///
506    /// # Parameters
507    ///
508    /// * `seq_id` - The sequence ID to get the state size for.
509    /// * `flags` - Optional flags (e.g., [`LlamaStateSeqFlags::PARTIAL_ONLY`]).
510    ///
511    /// # Returns
512    ///
513    /// The size in bytes needed to store the sequence state.
514    #[must_use]
515    pub fn state_seq_get_size_ext(&self, seq_id: i32, flags: LlamaStateSeqFlags) -> usize {
516        unsafe {
517            ik_llama_cpp_sys::llama_state_seq_get_size(self.context.as_ptr(), seq_id, flags.0)
518        }
519    }
520
521    /// Copy the state of a single sequence into the specified buffer with optional flags.
522    ///
523    /// This is the extended version that supports flags for partial state operations.
524    ///
525    /// # Parameters
526    ///
527    /// * `dest` - Destination buffer to copy state into.
528    /// * `seq_id` - The sequence ID to get the state for.
529    /// * `flags` - Optional flags (e.g., [`LlamaStateSeqFlags::PARTIAL_ONLY`]).
530    ///
531    /// # Safety
532    ///
533    /// Destination needs to have allocated enough memory.
534    ///
535    /// # Returns
536    ///
537    /// The number of bytes copied.
538    pub unsafe fn state_seq_get_data_ext(
539        &self,
540        dest: *mut u8,
541        seq_id: i32,
542        flags: LlamaStateSeqFlags,
543    ) -> usize {
544        unsafe {
545            ik_llama_cpp_sys::llama_state_seq_get_data(
546                self.context.as_ptr(),
547                dest,
548                usize::MAX,
549                seq_id,
550                flags.0,
551            )
552        }
553    }
554
555    /// Set the state for a single sequence from the specified buffer with optional flags.
556    ///
557    /// This is the extended version that supports flags for partial state operations.
558    /// Useful for restoring only the recurrent/partial state without affecting the KV cache.
559    ///
560    /// # Parameters
561    ///
562    /// * `src` - Source buffer containing the state data.
563    /// * `dest_seq_id` - The destination sequence ID to load the state into.
564    /// * `flags` - Optional flags (e.g., [`LlamaStateSeqFlags::PARTIAL_ONLY`]).
565    ///
566    /// # Safety
567    ///
568    /// The source buffer must contain valid state data.
569    ///
570    /// # Returns
571    ///
572    /// `true` on success (the whole `src` buffer was consumed). ik returns
573    /// `SIZE_MAX` when state IO is unsupported and `0` on other failures; both
574    /// are reported as `false` (only `n == src.len()` counts as success).
575    pub unsafe fn state_seq_set_data_ext(
576        &mut self,
577        src: &[u8],
578        dest_seq_id: i32,
579        flags: LlamaStateSeqFlags,
580    ) -> bool {
581        let n = unsafe {
582            ik_llama_cpp_sys::llama_state_seq_set_data(
583                self.context.as_ptr(),
584                src.as_ptr(),
585                src.len(),
586                dest_seq_id,
587                flags.0,
588            )
589        };
590        n == src.len()
591    }
592
593    /// Serialize sequence `seq_id`'s state into an opaque [`SeqState`].
594    ///
595    /// Enables save/restore of context state at arbitrary points in a
596    /// sequence. This is particularly useful on architectures where
597    /// `clear_kv_cache_seq` cannot roll back partial state (Mamba, RWKV,
598    /// Gated Delta Networks, and other recurrent / hybrid-recurrent
599    /// models): pair with [`LlamaStateSeqFlags::PARTIAL_ONLY`] to save
600    /// just the running recurrent and SWA state, then restore it via
601    /// [`Self::state_seq_set`] to effectively "rewind" the sequence.
602    ///
603    /// The returned [`SeqState`] is opaque — its bytes cannot be
604    /// inspected or forged from safe code, so [`Self::state_seq_set`]
605    /// only ever sees data produced by this method.
606    ///
607    /// Wraps `llama_state_seq_get_data`.
608    ///
609    /// # Errors
610    ///
611    /// Returns [`StateSeqError::SizeMismatch`] if llama.cpp writes a
612    /// different number of bytes than the reported state size.
613    pub fn state_seq_get(
614        &self,
615        seq_id: i32,
616        flags: LlamaStateSeqFlags,
617    ) -> Result<SeqState, StateSeqError> {
618        let size = unsafe {
619            ik_llama_cpp_sys::llama_state_seq_get_size(self.context.as_ptr(), seq_id, flags.0)
620        };
621        let mut bytes = vec![0u8; size];
622        let n = unsafe {
623            ik_llama_cpp_sys::llama_state_seq_get_data(
624                self.context.as_ptr(),
625                bytes.as_mut_ptr(),
626                size,
627                seq_id,
628                flags.0,
629            )
630        };
631        if n != size {
632            return Err(StateSeqError::SizeMismatch {
633                expected: size,
634                actual: n,
635            });
636        }
637        Ok(SeqState { bytes, flags })
638    }
639
640    /// Restore sequence state previously captured by [`Self::state_seq_get`]
641    /// into `seq_id`.
642    ///
643    /// Cross-sequence restore (`seq_id` different from the sequence the
644    /// state was captured from) is supported — llama.cpp treats the
645    /// destination sequence id independently of the source.
646    ///
647    /// Wraps `llama_state_seq_set_data`.
648    ///
649    /// # Errors
650    ///
651    /// Returns [`StateSeqError::SizeMismatch`] if llama.cpp reads a
652    /// different number of bytes than the state buffer contains — this
653    /// covers shape mismatches (different `n_ctx`, `n_layer`, quantization,
654    /// etc.) that llama.cpp's own deserializer detects and aborts on.
655    pub fn state_seq_set(&mut self, state: &SeqState, seq_id: i32) -> Result<(), StateSeqError> {
656        let n = unsafe {
657            ik_llama_cpp_sys::llama_state_seq_set_data(
658                self.context.as_ptr(),
659                state.bytes.as_ptr(),
660                state.bytes.len(),
661                seq_id,
662                state.flags.0,
663            )
664        };
665        if n != state.bytes.len() {
666            return Err(StateSeqError::SizeMismatch {
667                expected: state.bytes.len(),
668                actual: n,
669            });
670        }
671        Ok(())
672    }
673}
674
675/// Opaque, immutable snapshot of a single sequence's state.
676///
677/// Produced by [`LlamaContext::state_seq_get`] and consumed by
678/// [`LlamaContext::state_seq_set`]. Bytes cannot be constructed, forged,
679/// or mutated from safe code — the only way to obtain a `SeqState` is
680/// via a get call, which guarantees the payload came from llama.cpp
681/// itself. Combined with llama.cpp's own shape validation on the
682/// deserialize path, this closes the "arbitrary bytes into C parser"
683/// unsoundness that motivates the unsafe raw setters.
684///
685/// The state carries the flag set it was captured with; restore uses
686/// those same flags automatically so the byte layout always matches.
687#[derive(Clone)]
688pub struct SeqState {
689    bytes: Vec<u8>,
690    flags: LlamaStateSeqFlags,
691}
692
693impl SeqState {
694    /// The flag set that was used to capture this state.
695    #[must_use]
696    pub fn flags(&self) -> LlamaStateSeqFlags {
697        self.flags
698    }
699
700    /// Size in bytes of the serialized state.
701    #[must_use]
702    pub fn byte_len(&self) -> usize {
703        self.bytes.len()
704    }
705}
706
707impl Debug for SeqState {
708    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
709        f.debug_struct("SeqState")
710            .field("byte_len", &self.bytes.len())
711            .field("flags", &self.flags)
712            .finish()
713    }
714}