1use std::fmt::{Debug, Formatter};
4use std::num::NonZeroI32;
5use std::ptr::NonNull;
6use std::slice;
7
8use crate::llama_batch::LlamaBatch;
9use crate::model::{LlamaLoraAdapter, LlamaModel};
10use crate::sampling::LlamaSampler;
11use crate::timing::LlamaTimings;
12use crate::token::data::LlamaTokenData;
13use crate::token::data_array::LlamaTokenDataArray;
14use crate::token::LlamaToken;
15use crate::{
16 DecodeError, EmbeddingsError, EncodeError, LlamaLoraAdapterRemoveError,
17 LlamaLoraAdapterSetError,
18};
19
20pub mod kv_cache;
21pub mod params;
22pub mod session;
23
24#[allow(clippy::module_name_repetitions)]
26pub struct LlamaContext<'a> {
27 pub(crate) context: NonNull<llama_cpp_sys_2::llama_context>,
28 pub model: &'a LlamaModel,
30 initialized_logits: Vec<i32>,
31 embeddings_enabled: bool,
32 _backend_samplers: Vec<(i32, LlamaSampler)>,
34}
35
36impl Debug for LlamaContext<'_> {
37 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
38 f.debug_struct("LlamaContext")
39 .field("context", &self.context)
40 .finish()
41 }
42}
43
44impl<'model> LlamaContext<'model> {
45 pub(crate) fn new(
46 llama_model: &'model LlamaModel,
47 llama_context: NonNull<llama_cpp_sys_2::llama_context>,
48 embeddings_enabled: bool,
49 ) -> Self {
50 Self {
51 context: llama_context,
52 model: llama_model,
53 initialized_logits: Vec::new(),
54 embeddings_enabled,
55 _backend_samplers: Vec::new(),
56 }
57 }
58
59 pub(crate) fn with_samplers(
60 llama_model: &'model LlamaModel,
61 llama_context: NonNull<llama_cpp_sys_2::llama_context>,
62 embeddings_enabled: bool,
63 backend_samplers: Vec<(i32, LlamaSampler)>,
64 ) -> Self {
65 Self {
66 context: llama_context,
67 model: llama_model,
68 initialized_logits: Vec::new(),
69 embeddings_enabled,
70 _backend_samplers: backend_samplers,
71 }
72 }
73
74 #[must_use]
76 pub fn n_batch(&self) -> u32 {
77 unsafe { llama_cpp_sys_2::llama_n_batch(self.context.as_ptr()) }
78 }
79
80 #[must_use]
82 pub fn n_ubatch(&self) -> u32 {
83 unsafe { llama_cpp_sys_2::llama_n_ubatch(self.context.as_ptr()) }
84 }
85
86 #[must_use]
88 pub fn n_ctx(&self) -> u32 {
89 unsafe { llama_cpp_sys_2::llama_n_ctx(self.context.as_ptr()) }
90 }
91
92 pub fn decode(&mut self, batch: &mut LlamaBatch) -> Result<(), DecodeError> {
102 let result =
103 unsafe { llama_cpp_sys_2::llama_decode(self.context.as_ptr(), batch.llama_batch) };
104
105 match NonZeroI32::new(result) {
106 None => {
107 self.initialized_logits
108 .clone_from(&batch.initialized_logits);
109 Ok(())
110 }
111 Some(error) => Err(DecodeError::from(error)),
112 }
113 }
114
115 pub fn encode(&mut self, batch: &mut LlamaBatch) -> Result<(), EncodeError> {
125 let result =
126 unsafe { llama_cpp_sys_2::llama_encode(self.context.as_ptr(), batch.llama_batch) };
127
128 match NonZeroI32::new(result) {
129 None => {
130 self.initialized_logits
131 .clone_from(&batch.initialized_logits);
132 Ok(())
133 }
134 Some(error) => Err(EncodeError::from(error)),
135 }
136 }
137
138 pub fn embeddings_seq_ith(&self, i: i32) -> Result<&[f32], EmbeddingsError> {
155 if !self.embeddings_enabled {
156 return Err(EmbeddingsError::NotEnabled);
157 }
158
159 let n_embd =
160 usize::try_from(self.model.n_embd()).expect("n_embd does not fit into a usize");
161
162 unsafe {
163 let embedding = llama_cpp_sys_2::llama_get_embeddings_seq(self.context.as_ptr(), i);
164
165 if embedding.is_null() {
167 Err(EmbeddingsError::NonePoolType)
168 } else {
169 Ok(slice::from_raw_parts(embedding, n_embd))
170 }
171 }
172 }
173
174 pub fn embeddings_ith(&self, i: i32) -> Result<&[f32], EmbeddingsError> {
191 if !self.embeddings_enabled {
192 return Err(EmbeddingsError::NotEnabled);
193 }
194
195 let n_embd =
196 usize::try_from(self.model.n_embd()).expect("n_embd does not fit into a usize");
197
198 unsafe {
199 let embedding = llama_cpp_sys_2::llama_get_embeddings_ith(self.context.as_ptr(), i);
200 if embedding.is_null() {
202 Err(EmbeddingsError::LogitsNotEnabled)
203 } else {
204 Ok(slice::from_raw_parts(embedding, n_embd))
205 }
206 }
207 }
208
209 pub fn candidates(&self) -> impl Iterator<Item = LlamaTokenData> + '_ {
219 (0_i32..).zip(self.get_logits()).map(|(i, logit)| {
220 let token = LlamaToken::new(i);
221 LlamaTokenData::new(token, *logit, 0_f32)
222 })
223 }
224
225 #[must_use]
236 pub fn token_data_array(&self) -> LlamaTokenDataArray {
237 LlamaTokenDataArray::from_iter(self.candidates(), false)
238 }
239
240 #[must_use]
256 pub fn get_logits(&self) -> &[f32] {
257 let data = unsafe { llama_cpp_sys_2::llama_get_logits(self.context.as_ptr()) };
258 assert!(!data.is_null(), "logits data for last token is null");
259 let len = usize::try_from(self.model.n_vocab()).expect("n_vocab does not fit into a usize");
260
261 unsafe { slice::from_raw_parts(data, len) }
262 }
263
264 pub fn candidates_ith(&self, i: i32) -> impl Iterator<Item = LlamaTokenData> + '_ {
270 (0_i32..).zip(self.get_logits_ith(i)).map(|(i, logit)| {
271 let token = LlamaToken::new(i);
272 LlamaTokenData::new(token, *logit, 0_f32)
273 })
274 }
275
276 #[must_use]
287 pub fn token_data_array_ith(&self, i: i32) -> LlamaTokenDataArray {
288 LlamaTokenDataArray::from_iter(self.candidates_ith(i), false)
289 }
290
291 #[must_use]
299 pub fn get_logits_ith(&self, i: i32) -> &[f32] {
300 assert!(
301 self.initialized_logits.contains(&i),
302 "logit {i} is not initialized. only {:?} is",
303 self.initialized_logits
304 );
305 assert!(
306 self.n_ctx() > u32::try_from(i).expect("i does not fit into a u32"),
307 "n_ctx ({}) must be greater than i ({})",
308 self.n_ctx(),
309 i
310 );
311
312 let data = unsafe { llama_cpp_sys_2::llama_get_logits_ith(self.context.as_ptr(), i) };
313 let len = usize::try_from(self.model.n_vocab()).expect("n_vocab does not fit into a usize");
314
315 unsafe { slice::from_raw_parts(data, len) }
316 }
317
318 pub fn reset_timings(&mut self) {
320 unsafe { llama_cpp_sys_2::llama_perf_context_reset(self.context.as_ptr()) }
321 }
322
323 pub fn timings(&mut self) -> LlamaTimings {
325 let timings = unsafe { llama_cpp_sys_2::llama_perf_context(self.context.as_ptr()) };
326 LlamaTimings { timings }
327 }
328
329 pub fn lora_adapter_set(
335 &self,
336 adapter: &mut LlamaLoraAdapter,
337 scale: f32,
338 ) -> Result<(), LlamaLoraAdapterSetError> {
339 let mut adapters = [adapter.lora_adapter.as_ptr()];
340 let mut scales = [scale];
341 let err_code = unsafe {
342 llama_cpp_sys_2::llama_set_adapters_lora(
343 self.context.as_ptr(),
344 adapters.as_mut_ptr(),
345 1,
346 scales.as_mut_ptr(),
347 )
348 };
349 if err_code != 0 {
350 return Err(LlamaLoraAdapterSetError::ErrorResult(err_code));
351 }
352
353 tracing::debug!("Set lora adapter");
354 Ok(())
355 }
356
357 pub fn lora_adapter_remove(
366 &self,
367 _adapter: &mut LlamaLoraAdapter,
368 ) -> Result<(), LlamaLoraAdapterRemoveError> {
369 let err_code = unsafe {
370 llama_cpp_sys_2::llama_set_adapters_lora(
371 self.context.as_ptr(),
372 std::ptr::null_mut(),
373 0,
374 std::ptr::null_mut(),
375 )
376 };
377 if err_code != 0 {
378 return Err(LlamaLoraAdapterRemoveError::ErrorResult(err_code));
379 }
380
381 tracing::debug!("Remove lora adapter");
382 Ok(())
383 }
384
385 #[must_use]
397 pub fn sampled_token_ith(&self, i: i32) -> Option<LlamaToken> {
398 let token =
399 unsafe { llama_cpp_sys_2::llama_get_sampled_token_ith(self.context.as_ptr(), i) };
400 if token == -1 {
402 None
403 } else {
404 Some(LlamaToken(token))
405 }
406 }
407
408 #[cfg(feature = "common")]
410 pub fn print_memory_breakdown(&self) {
411 unsafe { llama_cpp_sys_2::llama_rs_memory_breakdown_print(self.context.as_ptr()) }
412 }
413}
414
415impl Drop for LlamaContext<'_> {
416 fn drop(&mut self) {
417 unsafe { llama_cpp_sys_2::llama_free(self.context.as_ptr()) }
418 }
419}