use crate::pooling::{pool, PoolingError, PoolingType};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum EncodeError {
#[error("an encoder needs at least one token; got an empty sequence")]
EmptySequence,
#[error(
"sequence of {got} tokens exceeds the {max} learned position embeddings this \
checkpoint carries ({arch}.context_length). A learned position table cannot be \
extrapolated the way RoPE can, so this is a hard limit, not a quality cliff — \
truncate the input or use a longer-context embedding model"
)]
TooLong {
got: usize,
max: usize,
arch: String,
},
#[error("token id {id} is outside this checkpoint's {vocab_size}-entry vocabulary")]
TokenOutOfRange { id: u32, vocab_size: usize },
#[error(
"segment id {id} at position {pos} is outside this checkpoint's {n_segments}-row \
token-type table"
)]
SegmentOutOfRange {
id: u32,
pos: usize,
n_segments: usize,
},
#[error(
"{tokens} token(s) were given {segments} segment id(s); every position needs exactly \
one, or the graph would add a segment embedding to the wrong row"
)]
RaggedSegments { tokens: usize, segments: usize },
#[error(transparent)]
Pooling(#[from] PoolingError),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PairSequence {
pub tokens: Vec<u32>,
pub segments: Vec<u32>,
}
pub trait TextEncoder {
fn n_embd(&self) -> usize;
fn n_ctx_train(&self) -> usize;
fn pooling_type(&self) -> PoolingType;
fn wrap_special(&self, pieces: &[u32]) -> Vec<u32> {
pieces.to_vec()
}
fn n_segments(&self) -> usize {
1
}
fn wrap_special_pair(&self, _a: &[u32], _b: &[u32]) -> Option<PairSequence> {
None
}
fn encode(&self, tokens: &[u32], segments: Option<&[u32]>) -> Result<Vec<f32>, EncodeError>;
fn encode_tokens(&self, tokens: &[u32]) -> Result<Vec<f32>, EncodeError> {
self.encode(tokens, None)
}
fn embed_tokens(&self, tokens: &[u32]) -> Result<Vec<f32>, EncodeError> {
let hidden = self.encode_tokens(tokens)?;
Ok(pool(&hidden, self.n_embd(), self.pooling_type())?)
}
}