use compute::whisper::{
cross_kv, whisper_decoder_layer_step, WhisperCrossKv, WhisperDecoderLayerWeights, WhisperSelfKv,
};
use model_io::whisper_config::WhisperSpecialTokens;
use super::WhisperRunner;
pub fn build_prompt(tokens: &WhisperSpecialTokens, language_token: u32) -> Vec<u32> {
if tokens.multilingual {
vec![
tokens.sot,
language_token,
tokens.transcribe,
tokens.no_timestamps,
]
} else {
vec![tokens.sot, tokens.no_timestamps]
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StopReason {
EndOfText,
TokenBudget,
RepetitionLimit,
}
fn step_layers(
runner: &WhisperRunner,
input: &[f32],
pos: usize,
state: &mut [WhisperSelfKv],
cross: &[WhisperCrossKv],
) -> Result<Vec<f32>, String> {
let d = runner.config.d_model;
let heads = runner.config.decoder_attention_heads();
let eps = runner.config.layer_norm_eps;
let mut h = input.to_vec();
for (i, layer) in runner.weights.dec_layers.iter().enumerate() {
let w = borrow_decoder(layer);
h = whisper_decoder_layer_step(&h, pos, &w, &mut state[i], &cross[i], d, heads, eps);
}
Ok(h)
}
pub(crate) fn borrow_decoder(
layer: &super::weights::WhisperDecoderLayerOwned,
) -> WhisperDecoderLayerWeights<'_> {
WhisperDecoderLayerWeights {
self_q_weight: &layer.self_q,
self_q_bias: &layer.self_q_bias,
self_k_weight: &layer.self_k,
self_v_weight: &layer.self_v,
self_v_bias: &layer.self_v_bias,
self_out_weight: &layer.self_out,
self_out_bias: &layer.self_out_bias,
ln_self_weight: &layer.ln_self_weight,
ln_self_bias: &layer.ln_self_bias,
cross_q_weight: &layer.cross_q,
cross_q_bias: &layer.cross_q_bias,
cross_k_weight: &layer.cross_k,
cross_v_weight: &layer.cross_v,
cross_v_bias: &layer.cross_v_bias,
cross_out_weight: &layer.cross_out,
cross_out_bias: &layer.cross_out_bias,
ln_cross_weight: &layer.ln_cross_weight,
ln_cross_bias: &layer.ln_cross_bias,
fc1_weight: &layer.fc1,
fc1_bias: &layer.fc1_bias,
fc2_weight: &layer.fc2,
fc2_bias: &layer.fc2_bias,
ln_fc_weight: &layer.ln_fc_weight,
ln_fc_bias: &layer.ln_fc_bias,
}
}
fn embed_with_position(runner: &WhisperRunner, token: u32, pos: usize) -> Result<Vec<f32>, String> {
let d = runner.config.d_model;
let row = token as usize * d;
if row + d > runner.weights.embed_tokens.len() {
return Err(format!(
"token id {token} exceeds the embedding table ({} rows)",
runner.weights.embed_tokens.len() / d
));
}
if (pos + 1) * d > runner.weights.dec_positions.len() {
return Err(format!(
"decoder position {pos} exceeds max_target_positions {}",
runner.config.max_target_positions
));
}
let scale = runner.config.embed_scale();
let out = runner.weights.embed_tokens[row..row + d]
.iter()
.zip(&runner.weights.dec_positions[pos * d..pos * d + d])
.map(|(&e, &p)| e * scale + p)
.collect();
Ok(out)
}
pub struct WindowDecoder<'a> {
runner: &'a WhisperRunner,
cross: Vec<WhisperCrossKv>,
state: Vec<WhisperSelfKv>,
prompt_len: usize,
hidden: Vec<f32>,
next_pos: usize,
emitted: Vec<u32>,
}
impl<'a> WindowDecoder<'a> {
pub fn new(
runner: &'a WhisperRunner,
encoder_out: &[f32],
seq: usize,
prompt: &[u32],
) -> Result<Self, String> {
let d = runner.config.d_model;
if prompt.is_empty() || prompt.len() > runner.config.max_target_positions {
return Err("decoder prompt must be nonempty and fit max_target_positions".to_string());
}
if seq == 0 || seq.checked_mul(d) != Some(encoder_out.len()) {
return Err("encoded window shape disagrees with its sequence length".to_string());
}
let mut cross = Vec::with_capacity(runner.weights.dec_layers.len());
for layer in &runner.weights.dec_layers {
let w = borrow_decoder(layer);
cross.push(cross_kv(encoder_out, seq, &w, d));
}
let mut state: Vec<WhisperSelfKv> = (0..runner.weights.dec_layers.len())
.map(|_| WhisperSelfKv::default())
.collect();
let mut hidden = Vec::new();
let mut input = embed_with_position(runner, prompt[0], 0)?;
for pos in 0..prompt.len() {
hidden = step_layers(runner, &input, pos, &mut state, &cross)?;
if pos + 1 < prompt.len() {
input = embed_with_position(runner, prompt[pos + 1], pos + 1)?;
}
}
Ok(Self {
runner,
cross,
state,
prompt_len: prompt.len(),
hidden,
next_pos: prompt.len(),
emitted: Vec::new(),
})
}
pub fn decode(self) -> Result<DecodedWindow, String> {
self.decode_cancellable(&|| false)
}
pub fn decode_cancellable(self, cancelled: &dyn Fn() -> bool) -> Result<DecodedWindow, String> {
self.decode_with_progress(cancelled, &mut |_, _| true)
}
pub fn decode_with_progress(
mut self,
cancelled: &dyn Fn() -> bool,
progress: &mut dyn FnMut(usize, usize) -> bool,
) -> Result<DecodedWindow, String> {
let tokens_cfg = self.runner.tokens;
let vocab = self.runner.config.vocab_size;
let budget = self
.runner
.config
.max_target_positions
.saturating_sub(self.prompt_len)
.min(440);
let stop = loop {
if cancelled() || !progress(self.emitted.len(), budget) {
return Err("audio job cancelled".into());
}
if self.emitted.len() >= budget {
break StopReason::TokenBudget;
}
let logits = self.runner.logits(&self.hidden)?;
let next = argmax_suppressed(&logits, &tokens_cfg, vocab);
if next == tokens_cfg.eot {
break StopReason::EndOfText;
}
self.emitted.push(next);
if self.emitted.len() >= 8
&& self.emitted[self.emitted.len() - 8..]
.iter()
.all(|&t| t == next)
{
break StopReason::RepetitionLimit;
}
if self.emitted.len() >= budget {
break StopReason::TokenBudget;
}
let input = embed_with_position(self.runner, next, self.next_pos)?;
self.hidden = step_layers(
self.runner,
&input,
self.next_pos,
&mut self.state,
&self.cross,
)?;
self.next_pos += 1;
};
Ok(DecodedWindow {
text_tokens: std::mem::take(&mut self.emitted),
stop,
})
}
}
pub struct DecodedWindow {
pub text_tokens: Vec<u32>,
pub stop: StopReason,
}
pub fn argmax_suppressed(logits: &[f32], tokens: &WhisperSpecialTokens, vocab: usize) -> u32 {
let mut best: Option<(f32, u32)> = None;
for (id, &score) in logits.iter().enumerate().take(vocab) {
let id = id as u32;
if id > tokens.eot {
continue;
}
if best.is_none_or(|(b, _)| score > b) {
best = Some((score, id));
}
}
best.map(|(_, id)| id).unwrap_or(tokens.eot)
}
pub fn detect_language(
runner: &WhisperRunner,
encoder_out: &[f32],
seq: usize,
) -> Result<u32, String> {
let tokens_cfg = runner.tokens;
let mut cross = Vec::with_capacity(runner.weights.dec_layers.len());
for layer in &runner.weights.dec_layers {
let w = borrow_decoder(layer);
cross.push(cross_kv(encoder_out, seq, &w, runner.config.d_model));
}
let mut state: Vec<WhisperSelfKv> = (0..runner.weights.dec_layers.len())
.map(|_| WhisperSelfKv::default())
.collect();
let input = embed_with_position(runner, tokens_cfg.sot, 0)?;
let hidden = step_layers(runner, &input, 0, &mut state, &cross)?;
let logits = runner.logits(&hidden)?;
let mut best: Option<(f32, u32)> = None;
for id in tokens_cfg.first_language..=tokens_cfg.last_language {
if id as usize >= logits.len() {
break;
}
let score = logits[id as usize];
if best.is_none_or(|(b, _)| score > b) {
best = Some((score, id));
}
}
best.map(|(_, id)| id).ok_or_else(|| {
"no language tokens in the vocabulary; this looks like an English-only model, pass \"en\" explicitly".to_string()
})
}