use crate::context::LlamaContext;
use crate::token::LlamaToken;
use std::ffi::{CString, NulError};
use std::fmt::{Debug, Formatter};
use std::path::{Path, PathBuf};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct LlamaStateSeqFlags(pub(crate) ik_llama_cpp_sys::llama_state_seq_flags);
impl LlamaStateSeqFlags {
pub const PARTIAL_ONLY: LlamaStateSeqFlags = LlamaStateSeqFlags(1);
pub const ON_DEVICE: LlamaStateSeqFlags = LlamaStateSeqFlags(2);
pub const fn empty() -> LlamaStateSeqFlags {
LlamaStateSeqFlags(0)
}
pub const fn bits(&self) -> u32 {
self.0
}
pub const fn from_bits(bits: u32) -> LlamaStateSeqFlags {
LlamaStateSeqFlags(bits)
}
pub const fn contains(&self, other: LlamaStateSeqFlags) -> bool {
(self.0 & other.0) != 0
}
}
impl Default for LlamaStateSeqFlags {
fn default() -> Self {
Self::empty()
}
}
impl std::ops::BitOr for LlamaStateSeqFlags {
type Output = Self;
fn bitor(self, other: Self) -> Self {
Self(self.0 | other.0)
}
}
#[derive(Debug, Eq, PartialEq, thiserror::Error)]
pub enum StateSeqError {
#[error("state seq size mismatch: expected {expected}, actual {actual}")]
SizeMismatch {
expected: usize,
actual: usize,
},
}
#[derive(Debug, Eq, PartialEq, thiserror::Error)]
pub enum SaveSeqStateError {
#[error("Failed to save sequence state file")]
FailedToSave,
#[error("null byte in string {0}")]
NullError(#[from] NulError),
#[error("failed to convert path {0} to str")]
PathToStrError(PathBuf),
}
#[derive(Debug, Eq, PartialEq, thiserror::Error)]
pub enum LoadSeqStateError {
#[error("Failed to load sequence state file")]
FailedToLoad,
#[error("null byte in string {0}")]
NullError(#[from] NulError),
#[error("failed to convert path {0} to str")]
PathToStrError(PathBuf),
#[error("max_length is not large enough to hold {n_out} (was {max_tokens})")]
InsufficientMaxLength {
n_out: usize,
max_tokens: usize,
},
}
#[derive(Debug, Eq, PartialEq, thiserror::Error)]
pub enum SaveSessionError {
#[error("Failed to save session file")]
FailedToSave,
#[error("null byte in string {0}")]
NullError(#[from] NulError),
#[error("failed to convert path {0} to str")]
PathToStrError(PathBuf),
}
#[derive(Debug, Eq, PartialEq, thiserror::Error)]
pub enum LoadSessionError {
#[error("Failed to load session file")]
FailedToLoad,
#[error("null byte in string {0}")]
NullError(#[from] NulError),
#[error("failed to convert path {0} to str")]
PathToStrError(PathBuf),
#[error("max_length is not large enough to hold {n_out} (was {max_tokens})")]
InsufficientMaxLength {
n_out: usize,
max_tokens: usize,
},
}
impl LlamaContext<'_> {
#[deprecated(since = "0.1.136", note = "Use `state_save_file` instead")]
pub fn save_session_file(
&self,
path_session: impl AsRef<Path>,
tokens: &[LlamaToken],
) -> Result<(), SaveSessionError> {
let path = path_session.as_ref();
let path = path
.to_str()
.ok_or_else(|| SaveSessionError::PathToStrError(path.to_path_buf()))?;
let cstr = CString::new(path)?;
if unsafe {
ik_llama_cpp_sys::llama_save_session_file(
self.context.as_ptr(),
cstr.as_ptr(),
tokens.as_ptr().cast::<ik_llama_cpp_sys::llama_token>(),
tokens.len(),
)
} {
Ok(())
} else {
Err(SaveSessionError::FailedToSave)
}
}
#[deprecated(since = "0.1.136", note = "Use `state_load_file` instead")]
pub fn load_session_file(
&mut self,
path_session: impl AsRef<Path>,
max_tokens: usize,
) -> Result<Vec<LlamaToken>, LoadSessionError> {
let path = path_session.as_ref();
let path = path
.to_str()
.ok_or(LoadSessionError::PathToStrError(path.to_path_buf()))?;
let cstr = CString::new(path)?;
let mut tokens: Vec<LlamaToken> = Vec::with_capacity(max_tokens);
let mut n_out = 0;
let tokens_out = tokens.as_mut_ptr().cast::<ik_llama_cpp_sys::llama_token>();
let load_session_success = unsafe {
ik_llama_cpp_sys::llama_load_session_file(
self.context.as_ptr(),
cstr.as_ptr(),
tokens_out,
max_tokens,
&mut n_out,
)
};
if load_session_success {
if n_out > max_tokens {
return Err(LoadSessionError::InsufficientMaxLength { n_out, max_tokens });
}
unsafe {
tokens.set_len(n_out);
}
Ok(tokens)
} else {
Err(LoadSessionError::FailedToLoad)
}
}
pub fn state_save_file(
&self,
path_session: impl AsRef<Path>,
tokens: &[LlamaToken],
) -> Result<(), SaveSessionError> {
let path = path_session.as_ref();
let path = path
.to_str()
.ok_or_else(|| SaveSessionError::PathToStrError(path.to_path_buf()))?;
let cstr = CString::new(path)?;
if unsafe {
ik_llama_cpp_sys::llama_state_save_file(
self.context.as_ptr(),
cstr.as_ptr(),
tokens.as_ptr().cast::<ik_llama_cpp_sys::llama_token>(),
tokens.len(),
)
} {
Ok(())
} else {
Err(SaveSessionError::FailedToSave)
}
}
pub fn state_load_file(
&mut self,
path_session: impl AsRef<Path>,
max_tokens: usize,
) -> Result<Vec<LlamaToken>, LoadSessionError> {
let path = path_session.as_ref();
let path = path
.to_str()
.ok_or(LoadSessionError::PathToStrError(path.to_path_buf()))?;
let cstr = CString::new(path)?;
let mut tokens: Vec<LlamaToken> = Vec::with_capacity(max_tokens);
let mut n_out = 0;
let tokens_out = tokens.as_mut_ptr().cast::<ik_llama_cpp_sys::llama_token>();
let success = unsafe {
ik_llama_cpp_sys::llama_state_load_file(
self.context.as_ptr(),
cstr.as_ptr(),
tokens_out,
max_tokens,
&mut n_out,
)
};
if success {
if n_out > max_tokens {
return Err(LoadSessionError::InsufficientMaxLength { n_out, max_tokens });
}
unsafe {
tokens.set_len(n_out);
}
Ok(tokens)
} else {
Err(LoadSessionError::FailedToLoad)
}
}
pub fn state_seq_save_file(
&self,
filepath: impl AsRef<Path>,
seq_id: i32,
tokens: &[LlamaToken],
) -> Result<usize, SaveSeqStateError> {
let path = filepath.as_ref();
let path = path
.to_str()
.ok_or_else(|| SaveSeqStateError::PathToStrError(path.to_path_buf()))?;
let cstr = CString::new(path)?;
let bytes_written = unsafe {
ik_llama_cpp_sys::llama_state_seq_save_file(
self.context.as_ptr(),
cstr.as_ptr(),
seq_id,
tokens.as_ptr().cast::<ik_llama_cpp_sys::llama_token>(),
tokens.len(),
)
};
if bytes_written == 0 {
Err(SaveSeqStateError::FailedToSave)
} else {
Ok(bytes_written)
}
}
pub fn state_seq_load_file(
&mut self,
filepath: impl AsRef<Path>,
dest_seq_id: i32,
max_tokens: usize,
) -> Result<(Vec<LlamaToken>, usize), LoadSeqStateError> {
let path = filepath.as_ref();
let path = path
.to_str()
.ok_or(LoadSeqStateError::PathToStrError(path.to_path_buf()))?;
let cstr = CString::new(path)?;
let mut tokens: Vec<LlamaToken> = Vec::with_capacity(max_tokens);
let mut n_out = 0;
let tokens_out = tokens.as_mut_ptr().cast::<ik_llama_cpp_sys::llama_token>();
let bytes_read = unsafe {
ik_llama_cpp_sys::llama_state_seq_load_file(
self.context.as_ptr(),
cstr.as_ptr(),
dest_seq_id,
tokens_out,
max_tokens,
&mut n_out,
)
};
if bytes_read == 0 {
return Err(LoadSeqStateError::FailedToLoad);
}
if n_out > max_tokens {
return Err(LoadSeqStateError::InsufficientMaxLength { n_out, max_tokens });
}
unsafe {
tokens.set_len(n_out);
}
Ok((tokens, bytes_read))
}
#[must_use]
pub fn get_state_size(&self) -> usize {
unsafe { ik_llama_cpp_sys::llama_get_state_size(self.context.as_ptr()) }
}
pub unsafe fn copy_state_data(&self, dest: *mut u8) -> usize {
unsafe { ik_llama_cpp_sys::llama_copy_state_data(self.context.as_ptr(), dest) }
}
pub unsafe fn set_state_data(&mut self, src: &[u8]) -> usize {
unsafe { ik_llama_cpp_sys::llama_set_state_data(self.context.as_ptr(), src.as_ptr()) }
}
#[must_use]
pub fn state_seq_get_size_ext(&self, seq_id: i32, flags: LlamaStateSeqFlags) -> usize {
unsafe {
ik_llama_cpp_sys::llama_state_seq_get_size(self.context.as_ptr(), seq_id, flags.0)
}
}
pub unsafe fn state_seq_get_data_ext(
&self,
dest: *mut u8,
seq_id: i32,
flags: LlamaStateSeqFlags,
) -> usize {
unsafe {
ik_llama_cpp_sys::llama_state_seq_get_data(
self.context.as_ptr(),
dest,
usize::MAX,
seq_id,
flags.0,
)
}
}
pub unsafe fn state_seq_set_data_ext(
&mut self,
src: &[u8],
dest_seq_id: i32,
flags: LlamaStateSeqFlags,
) -> bool {
let n = unsafe {
ik_llama_cpp_sys::llama_state_seq_set_data(
self.context.as_ptr(),
src.as_ptr(),
src.len(),
dest_seq_id,
flags.0,
)
};
n == src.len()
}
pub fn state_seq_get(
&self,
seq_id: i32,
flags: LlamaStateSeqFlags,
) -> Result<SeqState, StateSeqError> {
let size = unsafe {
ik_llama_cpp_sys::llama_state_seq_get_size(self.context.as_ptr(), seq_id, flags.0)
};
let mut bytes = vec![0u8; size];
let n = unsafe {
ik_llama_cpp_sys::llama_state_seq_get_data(
self.context.as_ptr(),
bytes.as_mut_ptr(),
size,
seq_id,
flags.0,
)
};
if n != size {
return Err(StateSeqError::SizeMismatch {
expected: size,
actual: n,
});
}
Ok(SeqState { bytes, flags })
}
pub fn state_seq_set(&mut self, state: &SeqState, seq_id: i32) -> Result<(), StateSeqError> {
let n = unsafe {
ik_llama_cpp_sys::llama_state_seq_set_data(
self.context.as_ptr(),
state.bytes.as_ptr(),
state.bytes.len(),
seq_id,
state.flags.0,
)
};
if n != state.bytes.len() {
return Err(StateSeqError::SizeMismatch {
expected: state.bytes.len(),
actual: n,
});
}
Ok(())
}
}
#[derive(Clone)]
pub struct SeqState {
bytes: Vec<u8>,
flags: LlamaStateSeqFlags,
}
impl SeqState {
#[must_use]
pub fn flags(&self) -> LlamaStateSeqFlags {
self.flags
}
#[must_use]
pub fn byte_len(&self) -> usize {
self.bytes.len()
}
}
impl Debug for SeqState {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SeqState")
.field("byte_len", &self.bytes.len())
.field("flags", &self.flags)
.finish()
}
}