use std::fmt::{Debug, Formatter};
use std::num::NonZeroI32;
use std::ptr::NonNull;
use std::slice;
use crate::llama_batch::LlamaBatch;
use crate::model::{LlamaLoraAdapter, LlamaModel};
use crate::timing::LlamaTimings;
use crate::token::data::LlamaTokenData;
use crate::token::data_array::LlamaTokenDataArray;
use crate::token::LlamaToken;
use crate::{
DecodeError, EmbeddingsError, EncodeError, LlamaLoraAdapterRemoveError,
LlamaLoraAdapterSetError,
};
pub mod kv_cache;
pub mod params;
pub mod session;
pub struct LlamaModelContext<'a> {
pub context: NonNull<infrastructure_llama_bindings::llama_context>,
pub model: &'a LlamaModel,
initialized_logits: Vec<i32>,
embeddings_enabled: bool,
}
impl Debug for LlamaModelContext<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("LlamaModelContext")
.field("context", &self.context)
.finish()
}
}
impl<'model> LlamaModelContext<'model> {
#[must_use]
pub fn new(
llama_model: &'model LlamaModel,
llama_context: NonNull<infrastructure_llama_bindings::llama_context>,
embeddings_enabled: bool,
) -> Self {
Self {
context: llama_context,
model: llama_model,
initialized_logits: Vec::new(),
embeddings_enabled,
}
}
#[must_use]
pub fn n_batch(&self) -> u32 {
unsafe { infrastructure_llama_bindings::llama_n_batch(self.context.as_ptr()) }
}
#[must_use]
pub fn n_ubatch(&self) -> u32 {
unsafe { infrastructure_llama_bindings::llama_n_ubatch(self.context.as_ptr()) }
}
#[must_use]
pub fn n_ctx(&self) -> u32 {
unsafe { infrastructure_llama_bindings::llama_n_ctx(self.context.as_ptr()) }
}
pub fn decode(&mut self, batch: &mut LlamaBatch) -> Result<(), DecodeError> {
let result = unsafe {
infrastructure_llama_bindings::llama_decode(self.context.as_ptr(), batch.llama_batch)
};
match NonZeroI32::new(result) {
None => {
self.initialized_logits
.clone_from(&batch.initialized_logits);
Ok(())
}
Some(error) => Err(DecodeError::from(error)),
}
}
pub fn encode(&mut self, batch: &mut LlamaBatch) -> Result<(), EncodeError> {
let result = unsafe {
infrastructure_llama_bindings::llama_encode(self.context.as_ptr(), batch.llama_batch)
};
match NonZeroI32::new(result) {
None => {
self.initialized_logits
.clone_from(&batch.initialized_logits);
Ok(())
}
Some(error) => Err(EncodeError::from(error)),
}
}
pub fn embeddings_seq_ith(&self, i: i32) -> Result<&[f32], EmbeddingsError> {
if !self.embeddings_enabled {
return Err(EmbeddingsError::NotEnabled);
}
let n_embd =
usize::try_from(self.model.n_embd()).expect("n_embd does not fit into a usize");
unsafe {
let embedding =
infrastructure_llama_bindings::llama_get_embeddings_seq(self.context.as_ptr(), i);
if embedding.is_null() {
Err(EmbeddingsError::NonePoolType)
} else {
Ok(slice::from_raw_parts(embedding, n_embd))
}
}
}
pub fn embeddings_ith(&self, i: i32) -> Result<&[f32], EmbeddingsError> {
if !self.embeddings_enabled {
return Err(EmbeddingsError::NotEnabled);
}
let n_embd =
usize::try_from(self.model.n_embd()).expect("n_embd does not fit into a usize");
unsafe {
let embedding =
infrastructure_llama_bindings::llama_get_embeddings_ith(self.context.as_ptr(), i);
if embedding.is_null() {
Err(EmbeddingsError::LogitsNotEnabled)
} else {
Ok(slice::from_raw_parts(embedding, n_embd))
}
}
}
pub fn candidates(&self) -> impl Iterator<Item = LlamaTokenData> + '_ {
(0_i32..).zip(self.get_logits()).map(|(i, logit)| {
let token = LlamaToken::new(i);
LlamaTokenData::new(token, *logit, 0_f32)
})
}
#[must_use]
pub fn token_data_array(&self) -> LlamaTokenDataArray {
LlamaTokenDataArray::from_iter(self.candidates(), false)
}
#[must_use]
pub fn get_logits(&self) -> &[f32] {
let data =
unsafe { infrastructure_llama_bindings::llama_get_logits(self.context.as_ptr()) };
assert!(!data.is_null(), "logits data for last token is null");
let len = usize::try_from(self.model.n_vocab()).expect("n_vocab does not fit into a usize");
unsafe { slice::from_raw_parts(data, len) }
}
pub fn candidates_ith(&self, i: i32) -> impl Iterator<Item = LlamaTokenData> + '_ {
(0_i32..).zip(self.get_logits_ith(i)).map(|(i, logit)| {
let token = LlamaToken::new(i);
LlamaTokenData::new(token, *logit, 0_f32)
})
}
#[must_use]
pub fn token_data_array_ith(&self, i: i32) -> LlamaTokenDataArray {
LlamaTokenDataArray::from_iter(self.candidates_ith(i), false)
}
#[must_use]
pub fn get_logits_ith(&self, i: i32) -> &[f32] {
assert!(
self.initialized_logits.contains(&i),
"logit {i} is not initialized. only {:?} is",
self.initialized_logits
);
assert!(
self.n_ctx() > u32::try_from(i).expect("i does not fit into a u32"),
"n_ctx ({}) must be greater than i ({})",
self.n_ctx(),
i
);
let data = unsafe {
infrastructure_llama_bindings::llama_get_logits_ith(self.context.as_ptr(), i)
};
let len = usize::try_from(self.model.n_vocab()).expect("n_vocab does not fit into a usize");
unsafe { slice::from_raw_parts(data, len) }
}
pub fn reset_timings(&mut self) {
unsafe { infrastructure_llama_bindings::llama_perf_context_reset(self.context.as_ptr()) }
}
pub fn timings(&mut self) -> LlamaTimings {
let timings =
unsafe { infrastructure_llama_bindings::llama_perf_context(self.context.as_ptr()) };
LlamaTimings { timings }
}
pub fn lora_adapter_set(
&self,
adapter: &mut LlamaLoraAdapter,
scale: f32,
) -> Result<(), LlamaLoraAdapterSetError> {
let mut adapters = [adapter.lora_adapter.as_ptr()];
let mut scales = [scale];
let err_code = unsafe {
infrastructure_llama_bindings::llama_set_adapters_lora(
self.context.as_ptr(),
adapters.as_mut_ptr(),
adapters.len(),
scales.as_mut_ptr(),
)
};
if err_code != 0 {
return Err(LlamaLoraAdapterSetError::ErrorResult(err_code));
}
tracing::debug!("Set lora adapter");
Ok(())
}
pub fn lora_adapter_remove(
&self,
adapter: &mut LlamaLoraAdapter,
) -> Result<(), LlamaLoraAdapterRemoveError> {
let mut adapters = [adapter.lora_adapter.as_ptr()];
let mut scales = [0.0f32];
let err_code = unsafe {
infrastructure_llama_bindings::llama_set_adapters_lora(
self.context.as_ptr(),
adapters.as_mut_ptr(),
adapters.len(),
scales.as_mut_ptr(),
)
};
if err_code != 0 {
return Err(LlamaLoraAdapterRemoveError::ErrorResult(err_code));
}
tracing::debug!("Remove lora adapter");
Ok(())
}
}
impl Drop for LlamaModelContext<'_> {
fn drop(&mut self) {
unsafe { infrastructure_llama_bindings::llama_free(self.context.as_ptr()) }
}
}