tiktoken-stream 0.1.0

Streaming token counter for partial LLM responses. Accumulates token count across chunks without holding the full text. Pluggable estimator function. Zero deps.
Documentation
use tiktoken_stream::TokenStream;

#[test]
fn empty_stream_is_zero() {
    let s = TokenStream::new();
    assert_eq!(s.count(), 0);
    assert_eq!(s.chars(), 0);
}

#[test]
fn default_estimator_accumulates() {
    let mut s = TokenStream::new();
    s.push("Hello, "); // 7 chars -> 2
    s.push("world!"); // 6 chars -> 2
    // total 13 chars / 4 = 4 (sum of per-chunk ceilings)
    assert_eq!(s.count(), 4);
    assert_eq!(s.chars(), 13);
}

#[test]
fn custom_estimator_word_count() {
    let mut s = TokenStream::with_estimator(|c: &str| c.split_whitespace().count() as u64);
    s.push("the quick brown");
    s.push(" fox jumps over");
    s.push(" the lazy dog");
    assert_eq!(s.count(), 9);
}

#[test]
fn push_returns_new_count() {
    let mut s = TokenStream::with_estimator(|c: &str| c.split_whitespace().count() as u64);
    assert_eq!(s.push("a b"), 2);
    assert_eq!(s.push(" c d e"), 5);
}

#[test]
fn reset_clears_counts_keeps_estimator() {
    let mut s = TokenStream::with_estimator(|c: &str| c.split_whitespace().count() as u64);
    s.push("a b c");
    s.reset();
    assert_eq!(s.count(), 0);
    assert_eq!(s.chars(), 0);
    s.push("x y");
    assert_eq!(s.count(), 2);
}