libmir-cuda 0.3.0

CUDA inference backend for libmir
use runtime::backend::{DecodeSequence, LogitsTrace, SamplingLogits};

use crate::{
    CudaBackend, CudaClampedRoutedModelSession, Error, Result,
    engine::execution::{Output, device_sampling, generation_output},
};

pub(super) fn decode(
    session: &mut CudaClampedRoutedModelSession,
    backend: &CudaBackend,
    sequences: &[DecodeSequence],
) -> Result<Option<Vec<Output>>> {
    if sequences.len() < 2 {
        return Ok(None);
    }
    let sessions = sequences.iter().map(|sequence| sequence.session_id).collect::<Vec<_>>();
    let tokens = sequences.iter().map(|sequence| sequence.token_id).collect::<Vec<_>>();
    let tables = sequences.iter().map(|sequence| &sequence.block_table).collect::<Vec<_>>();
    let policies = sequences.iter().map(|sequence| sequence.sampling_logits).collect::<Vec<_>>();
    session.decode_packed_chunk(&sessions, &tokens, &tables)?;
    let device_policies = policies
        .iter()
        .copied()
        .map(|policy| {
            if device_sampling(policy) {
                policy
            } else {
                SamplingLogits::None
            }
        })
        .collect::<Vec<_>>();
    let rows = (0..sequences.len()).collect::<Vec<_>>();
    let history = policies.iter().any(|policy| policy.requires_history());
    let Some(result) =
        session.finish_packed_device_rows(&rows, sequences.len(), &device_policies, history)?
    else {
        let outputs = policies
            .iter()
            .copied()
            .enumerate()
            .map(|(row, policy)| {
                session.finish_packed_prefill_row(row, sequences.len(), policy)?;
                generation_output(backend, session, policy)
            })
            .collect::<Result<Vec<_>>>()?;
        return Ok(Some(outputs));
    };
    policies
        .iter()
        .copied()
        .enumerate()
        .map(|(row, policy)| {
            let token = device_sampling(policy).then(|| result.selected[row]);
            let logits = if policy.requires_history() {
                let values = result
                    .logits
                    .as_deref()
                    .and_then(|values| values.chunks_exact(result.vocab).nth(row))
                    .ok_or_else(|| Error::InvalidSampling("missing packed logits row".into()))?;
                Some(LogitsTrace {
                    shape: vec![1, 1, i32::try_from(result.vocab)?],
                    values: values.to_vec(),
                })
            } else {
                None
            };
            Ok(Output { token, logits })
        })
        .collect::<Result<Vec<_>>>()
        .map(Some)
}