Skip to main content

ik_llama_cpp_2/context/
embeddings.rs

1//! Embeddings + reranker support: the `llama_get_embeddings*` accessors.
2//!
3//! Embeddings are produced when the context is built with
4//! `LlamaContextParams::with_embeddings(true)` and (usually) a pooling type set
5//! via `with_pooling_type`. Fetch a per-token vector with
6//! [`LlamaContext::embeddings_ith`] or a pooled per-sequence vector with
7//! [`LlamaContext::embeddings_seq_ith`]; each slice is `n_embd` long.
8//!
9//! A **reranker** is just an embedding model run with a rank/classification
10//! pooling head: the pooled "embedding" of a query+document sequence is a
11//! relevance score. Read it with [`LlamaContext::embeddings_seq_ith`] after a
12//! decode (with a rank pooling type, the slice collapses to the score). ik does
13//! not expose a dedicated `LLAMA_POOLING_TYPE_RANK` constant — the available
14//! pooling types are `NONE`, `MEAN`, `CLS`, and `LAST`.
15
16use ik_llama_cpp_sys as sys;
17
18use crate::context::LlamaContext;
19
20/// Errors returned by the embedding accessors.
21#[derive(Debug, PartialEq, Eq, thiserror::Error)]
22pub enum EmbeddingsError {
23    /// The context was created without `with_embeddings(true)`.
24    #[error("embeddings were not enabled in the context params")]
25    NotEnabled,
26    /// No embeddings for the given token: its batch entry did not request
27    /// logits/embeddings, or the index was out of range.
28    #[error("no embeddings available for token index {0}")]
29    LogitsNotEnabled(i32),
30    /// No per-sequence embeddings for the given sequence: the pooling type is
31    /// `LLAMA_POOLING_TYPE_NONE`, or the sequence id was out of range.
32    #[error("no sequence embeddings for seq {0} (pooling type NONE, or out of range)")]
33    NonePoolType(i32),
34}
35
36impl LlamaContext<'_> {
37    /// The model's embedding dimension (`n_embd`).
38    ///
39    /// ik exposes only `llama_model_n_embd` (there is no plain `llama_n_embd`),
40    /// so we resolve the model from the context pointer first. The context does
41    /// not hold a model reference, hence the round-trip through `llama_get_model`.
42    #[must_use]
43    fn n_embd(&self) -> usize {
44        // SAFETY: the context owns a valid model pointer for its whole lifetime.
45        let n = unsafe { sys::llama_model_n_embd(sys::llama_get_model(self.context.as_ptr())) };
46        usize::try_from(n).unwrap_or(0)
47    }
48
49    /// Get the embeddings for the `i`th token in the current context.
50    ///
51    /// # Returns
52    ///
53    /// A slice with the embeddings for the last decoded batch of the given
54    /// token. The size corresponds to the `n_embd` of the context's model.
55    ///
56    /// # Errors
57    ///
58    /// - [`EmbeddingsError::NotEnabled`] if the context was built without
59    ///   `with_embeddings(true)`.
60    /// - [`EmbeddingsError::LogitsNotEnabled`] if that token was not decoded
61    ///   with logits enabled (ik returns a null pointer), or `i` is out of range.
62    pub fn embeddings_ith(&self, i: i32) -> Result<&[f32], EmbeddingsError> {
63        if !self.raw_params.embeddings {
64            return Err(EmbeddingsError::NotEnabled);
65        }
66        let n_embd = self.n_embd();
67        // SAFETY: ptr valid until the next decode; slice length is n_embd.
68        let ptr = unsafe { sys::llama_get_embeddings_ith(self.context.as_ptr(), i) };
69        if ptr.is_null() {
70            return Err(EmbeddingsError::LogitsNotEnabled(i));
71        }
72        Ok(unsafe { std::slice::from_raw_parts(ptr, n_embd) })
73    }
74
75    /// Get the pooled embeddings for the `seq`th sequence in the current context.
76    ///
77    /// # Returns
78    ///
79    /// A slice with the pooled embeddings for the last decoded batch. The size
80    /// corresponds to the `n_embd` of the context's model.
81    ///
82    /// # Errors
83    ///
84    /// - [`EmbeddingsError::NotEnabled`] if the context was built without
85    ///   `with_embeddings(true)`.
86    /// - [`EmbeddingsError::NonePoolType`] if the model uses
87    ///   `LLAMA_POOLING_TYPE_NONE` (ik returns a null pointer), or `seq` exceeds
88    ///   the max sequence id.
89    pub fn embeddings_seq_ith(&self, seq: i32) -> Result<&[f32], EmbeddingsError> {
90        if !self.raw_params.embeddings {
91            return Err(EmbeddingsError::NotEnabled);
92        }
93        let n_embd = self.n_embd();
94        // SAFETY: ptr valid until the next decode; slice length is n_embd.
95        let ptr = unsafe { sys::llama_get_embeddings_seq(self.context.as_ptr(), seq) };
96        if ptr.is_null() {
97            return Err(EmbeddingsError::NonePoolType(seq));
98        }
99        Ok(unsafe { std::slice::from_raw_parts(ptr, n_embd) })
100    }
101}