llama_cpp_4/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::path::{Path, PathBuf};
7
8/// Failed to save a Session file
9#[derive(Debug, Eq, PartialEq, thiserror::Error)]
10pub enum SaveSessionError {
11 /// llama.cpp failed to save the session file
12 #[error("Failed to save session file")]
13 FailedToSave,
14
15 /// null byte in string
16 #[error("null byte in string {0}")]
17 NullError(#[from] NulError),
18
19 /// failed to convert path to str
20 #[error("failed to convert path {0} to str")]
21 PathToStrError(PathBuf),
22}
23
24/// Failed to load a Session file
25#[derive(Debug, Eq, PartialEq, thiserror::Error)]
26pub enum LoadSessionError {
27 /// llama.cpp failed to load the session file
28 #[error("Failed to load session file")]
29 FailedToLoad,
30
31 /// null byte in string
32 #[error("null byte in string {0}")]
33 NullError(#[from] NulError),
34
35 /// failed to convert path to str
36 #[error("failed to convert path {0} to str")]
37 PathToStrError(PathBuf),
38
39 /// Insufficient max length
40 #[error("max_length is not large enough to hold {n_out} (was {max_tokens})")]
41 InsufficientMaxLength {
42 /// The length of the session file
43 n_out: usize,
44 /// The maximum length
45 max_tokens: usize,
46 },
47}
48
49impl LlamaContext<'_> {
50 /// Save the current session to a file.
51 ///
52 /// # Parameters
53 ///
54 /// * `path_session` - The file to save to.
55 /// * `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.
56 ///
57 /// # Errors
58 ///
59 /// Fails if the path is not a valid utf8, is not a valid c string, or llama.cpp fails to save the session file.
60 pub fn save_session_file(
61 &self,
62 path_session: impl AsRef<Path>,
63 tokens: &[LlamaToken],
64 ) -> Result<(), SaveSessionError> {
65 let path = path_session.as_ref();
66 let path = path
67 .to_str()
68 .ok_or_else(|| SaveSessionError::PathToStrError(path.to_path_buf()))?;
69
70 let cstr = CString::new(path)?;
71
72 // SAFETY: `self.context` is non-null and kept alive by this borrow; the path is a `CString` alive for the
73 // call, and the token pointer and count come from the same slice.
74 if unsafe {
75 llama_cpp_sys_4::llama_save_session_file(
76 self.context.as_ptr(),
77 cstr.as_ptr(),
78 tokens.as_ptr().cast::<llama_cpp_sys_4::llama_token>(),
79 tokens.len(),
80 )
81 } {
82 Ok(())
83 } else {
84 Err(SaveSessionError::FailedToSave)
85 }
86 }
87 /// Load a session file into the current context.
88 ///
89 /// 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.
90 ///
91 /// # Parameters
92 ///
93 /// * `path_session` - The file to load from. It must be a session file from a compatible context, otherwise the function will error.
94 /// * `max_tokens` - The maximum token length of the loaded session. If the session was saved with a longer length, the function will error.
95 ///
96 /// # Errors
97 ///
98 /// 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.)
99 pub fn load_session_file(
100 &mut self,
101 path_session: impl AsRef<Path>,
102 max_tokens: usize,
103 ) -> Result<Vec<LlamaToken>, LoadSessionError> {
104 let path = path_session.as_ref();
105 let path = path
106 .to_str()
107 .ok_or(LoadSessionError::PathToStrError(path.to_path_buf()))?;
108
109 let cstr = CString::new(path)?;
110 let mut tokens: Vec<LlamaToken> = Vec::with_capacity(max_tokens);
111 let mut n_out = 0;
112
113 // SAFETY: cast is valid as LlamaToken is repr(transparent)
114 let tokens_out = tokens.as_mut_ptr().cast::<llama_cpp_sys_4::llama_token>();
115
116 // SAFETY: `self.context` is non-null and kept alive by this borrow; the path is a `CString` alive for the
117 // call, and the output buffer's capacity is passed with its pointer.
118 let load_session_success = unsafe {
119 llama_cpp_sys_4::llama_load_session_file(
120 self.context.as_ptr(),
121 cstr.as_ptr(),
122 tokens_out,
123 max_tokens,
124 &raw mut n_out,
125 )
126 };
127 if load_session_success {
128 if n_out > max_tokens {
129 return Err(LoadSessionError::InsufficientMaxLength { n_out, max_tokens });
130 }
131 // SAFETY: we checked that n_out <= max_tokens and llama.cpp promises that n_out tokens will be written
132 unsafe {
133 tokens.set_len(n_out);
134 }
135 Ok(tokens)
136 } else {
137 Err(LoadSessionError::FailedToLoad)
138 }
139 }
140
141 /// Returns the maximum size in bytes of the state (rng, logits, embedding
142 /// and `kv_cache`) - will often be smaller after compacting tokens
143 #[must_use]
144 pub fn get_state_size(&self) -> usize {
145 // SAFETY: a plain read through a live context.
146 unsafe { llama_cpp_sys_4::llama_get_state_size(self.context.as_ptr()) }
147 }
148
149 /// Copies the state to the specified destination address.
150 ///
151 /// Returns the number of bytes copied
152 ///
153 /// # Safety
154 ///
155 /// Destination needs to have allocated enough memory.
156 pub unsafe fn copy_state_data(&self, dest: *mut u8) -> usize {
157 // SAFETY: this function is itself `unsafe`; its contract puts the
158 // "`dest` is big enough" requirement on the caller, and `get_state_size`
159 // is how they size it.
160 unsafe { llama_cpp_sys_4::llama_copy_state_data(self.context.as_ptr(), dest) }
161 }
162
163 /// Set the state reading from the specified address
164 /// Returns the number of bytes read
165 ///
166 /// # Safety
167 ///
168 /// `src` must hold a complete state previously produced by
169 /// [`copy_state_data`](Self::copy_state_data). llama.cpp reads as much as
170 /// the serialized header says is there and does not consult `src.len()`,
171 /// so a truncated or foreign buffer is read past its end. The state must
172 /// also come from a context built from the same model with the same
173 /// parameters; `LLAMA_SESSION_VERSION` guards the format across releases
174 /// but not the shape within one.
175 pub unsafe fn set_state_data(&mut self, src: &[u8]) -> usize {
176 // SAFETY: this function is itself `unsafe`; its contract requires `src`
177 // to hold a complete state written by `copy_state_data`.
178 unsafe { llama_cpp_sys_4::llama_set_state_data(self.context.as_ptr(), src.as_ptr()) }
179 }
180}