token-sampling 0.1.0

Composable token sampling over f32 logits with caller-supplied randomness
Documentation
use crate::candidates::{Candidates, Error};
use crate::rng::Rng;
use crate::samplers::{
    Dist, Dry, DynTemperature, MinP, Penalties, Sampler, SamplerCtx, TailFree, TopK, TopP, Typical,
    Xtc,
};
use core::fmt;

/// Configuration for the standard stage order.
#[derive(Clone, Debug, Default, PartialEq)]
pub struct ChainConfig {
    /// Repetition, frequency, and presence penalties.
    pub penalties: Penalties,
    /// Sequence-extension penalty.
    pub dry: Dry,
    /// Largest-logit truncation.
    pub top_k: TopK,
    /// Tail-free truncation.
    pub tail_free: TailFree,
    /// Locally typical truncation.
    pub typical: Typical,
    /// Cumulative probability truncation.
    pub top_p: TopP,
    /// Maximum-relative probability truncation.
    pub min_p: MinP,
    /// Top-choice exclusion.
    pub xtc: Xtc,
    /// Fixed or entropy-mapped temperature.
    pub temperature: DynTemperature,
}

/// An ordered set of sampling stages with reusable candidate storage.
pub struct SamplerChain {
    stages: Vec<Box<dyn Sampler>>,
    candidates: Candidates,
}

impl SamplerChain {
    /// Creates an empty chain.
    pub const fn new() -> Self {
        Self {
            stages: Vec::new(),
            candidates: Candidates::new(),
        }
    }

    /// Appends one concrete stage and returns the chain.
    pub fn push(mut self, sampler: impl Sampler + 'static) -> Self {
        self.stages.push(Box::new(sampler));
        self
    }

    /// Appends a boxed stage.
    pub fn push_dyn(&mut self, sampler: Box<dyn Sampler>) {
        self.stages.push(sampler);
    }

    /// Builds the standard stage order from `config`.
    pub fn llama_default(config: ChainConfig) -> Self {
        Self::new()
            .push(config.penalties)
            .push(config.dry)
            .push(config.top_k)
            .push(config.tail_free)
            .push(config.typical)
            .push(config.top_p)
            .push(config.min_p)
            .push(config.xtc)
            .push(config.temperature)
            .push(Dist)
    }

    /// Loads logits, runs every stage, and returns the selected token.
    pub fn sample(
        &mut self,
        logits: &mut [f32],
        history: &[u32],
        rng: &mut dyn Rng,
    ) -> Result<u32, Error> {
        self.candidates.load(logits)?;
        let mut context = SamplerCtx { rng, history };
        for stage in &mut self.stages {
            stage.apply(&mut self.candidates, &mut context)?;
        }
        self.candidates.selected().ok_or(Error::NoSelection)
    }

    /// Returns the candidate set left by the most recent sample call.
    pub fn candidates(&self) -> &Candidates {
        &self.candidates
    }

    /// Clears selection and per-sequence sampler state.
    pub fn reset(&mut self) {
        for stage in &mut self.stages {
            stage.reset();
        }
        self.candidates.selected = None;
    }
}

impl fmt::Debug for SamplerChain {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter
            .debug_struct("SamplerChain")
            .field("stage_count", &self.stages.len())
            .field("candidates", &self.candidates)
            .finish()
    }
}

impl Default for SamplerChain {
    fn default() -> Self {
        Self::new()
    }
}