libmir-cuda 0.2.0

CUDA inference backend for libmir
use mircuda::{DeviceBuffer, bf16};
use runtime::{
    backend::SamplingLogits,
    kv::{BlockTable, KvWritePlan},
};
use uuid::Uuid;

use super::CudaMoeModelSession;
use crate::{
    Error, Result, backend::attention::ImageAttentionSpan, kernels::VisionEmbeddingSplice,
};

impl CudaMoeModelSession {
    #[allow(clippy::too_many_arguments)]
    pub(crate) fn prefill_vision_for_sampling(
        &mut self,
        session_id: Uuid,
        tokens: &[u32],
        image: &DeviceBuffer<bf16>,
        image_start: usize,
        image_end: usize,
        bidirectional: bool,
        table: &BlockTable,
        sampling: SamplingLogits,
    ) -> Result<&DeviceBuffer<bf16>> {
        self.validate_prefill(tokens, 0, table)?;
        if image_start >= image_end
            || image_end > tokens.len()
            || image.len() != (image_end - image_start) * self.hidden_size
        {
            return Err(Error::InvalidVisionKernel("image embeddings differ from prompt span"));
        }
        self.ensure_vision_capacity(tokens.len())?;
        self.prefill_tokens.upload(&self.stream, tokens)?;
        self.embedding.execute_batch(
            self.prefill_tokens.device(),
            0,
            tokens.len(),
            &self.embedding_weight,
            &mut self.prefill_first,
        )?;
        VisionEmbeddingSplice::compile(
            &self.backend.inner.compiler,
            image_end - image_start,
            self.hidden_size,
            image_start,
        )?
        .execute(&self.stream, image, &mut self.prefill_first)?;
        let image =
            bidirectional.then_some(ImageAttentionSpan { start: image_start, end: image_end });
        self.execute_vision_layers(session_id, tokens.len(), table, image)?;
        self.finish_prefill(tokens.len(), sampling)
    }

    fn execute_vision_layers(
        &mut self,
        session_id: Uuid,
        tokens: usize,
        table: &BlockTable,
        image: Option<ImageAttentionSpan>,
    ) -> Result<()> {
        for (index, layer) in self.layers.iter_mut().enumerate() {
            let plan = KvWritePlan::prefill(session_id, layer.layer(), table, 0, tokens)?;
            let (input, output) = if index.is_multiple_of(2) {
                (&self.prefill_first, &mut self.prefill_second)
            } else {
                (&self.prefill_second, &mut self.prefill_first)
            };
            layer.prepare_prefill(tokens)?;
            if let Some(graph) = self.decode_graph.as_mut() {
                graph.execute_prefill_masked(
                    index,
                    layer.prefill_plan(tokens)?,
                    input,
                    &plan,
                    table,
                    0,
                    output,
                    image,
                )?;
            } else {
                layer.execute_prefill_masked(input, output, &plan, table, 0, tokens, image)?;
            }
        }
        Ok(())
    }

    fn ensure_vision_capacity(&mut self, tokens: usize) -> Result<()> {
        if tokens <= self.prefill_tokens.capacity() {
            return Ok(());
        }
        let elements = tokens
            .checked_mul(self.hidden_size)
            .ok_or(Error::InvalidDecoderKernel("CUDA vision prefill size overflow"))?;
        self.prefill_tokens.ensure_capacity(&self.backend, tokens)?;
        self.prefill_first = self.backend.inner.pool.allocate(&self.stream, elements)?;
        self.prefill_second = self.backend.inner.pool.allocate(&self.stream, elements)?;
        Ok(())
    }
}