use crate::tokenizer::TokenizerHandle;
use anyhow::Result;
pub struct StreamingDetokenizer<'t> {
tok: &'t TokenizerHandle,
skip_special: bool,
ids: Vec<u32>,
emitted: usize,
}
impl<'t> StreamingDetokenizer<'t> {
pub fn new(tok: &'t TokenizerHandle, skip_special: bool) -> Self {
Self {
tok,
skip_special,
ids: Vec::new(),
emitted: 0,
}
}
pub fn push(&mut self, id: u32) -> Result<String> {
self.ids.push(id);
let full = self.tok.decode(&self.ids, self.skip_special)?;
let (delta, emitted) = advance(&full, self.emitted);
self.emitted = emitted;
Ok(delta.to_string())
}
pub fn finish(&mut self) -> Result<String> {
let full = self.tok.decode(&self.ids, self.skip_special)?;
let out = if self.emitted < full.len() {
full[self.emitted..].to_string()
} else {
String::new()
};
self.emitted = full.len();
Ok(out)
}
pub fn text(&self) -> Result<String> {
self.tok.decode(&self.ids, self.skip_special)
}
pub fn ids(&self) -> &[u32] {
&self.ids
}
}
pub fn incremental_emit(
tok: &TokenizerHandle,
ids: &[u32],
emitted: usize,
skip_special: bool,
) -> Result<(String, usize)> {
let full = tok.decode(ids, skip_special)?;
let (delta, new_emitted) = advance(&full, emitted);
Ok((delta.to_string(), new_emitted))
}
fn advance(full: &str, emitted: usize) -> (&str, usize) {
let stable = full.trim_end_matches('\u{FFFD}').len();
if stable <= emitted || emitted > full.len() || !full.is_char_boundary(emitted) {
return ("", emitted.min(full.len()));
}
(&full[emitted..stable], stable)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn emits_growing_suffix() {
let (d, e) = advance("Hello", 0);
assert_eq!(d, "Hello");
assert_eq!(e, 5);
let (d, e) = advance("Hello world", e);
assert_eq!(d, " world");
assert_eq!(e, 11);
}
#[test]
fn holds_back_incomplete_multibyte() {
let (d, e) = advance("hi\u{FFFD}", 2);
assert_eq!(d, "");
assert_eq!(e, 2);
let (d, e) = advance("hié", 2);
assert_eq!(d, "é");
assert_eq!(e, "hié".len());
}
#[test]
fn no_change_emits_nothing() {
let (d, e) = advance("abc", 3);
assert_eq!(d, "");
assert_eq!(e, 3);
}
#[test]
fn trailing_replacement_run_held_back() {
let (d, e) = advance("ok\u{FFFD}\u{FFFD}", 2);
assert_eq!(d, "");
assert_eq!(e, 2);
}
}