ai 0.4.0

Simple to use LLM library for Rust with streaming, tool calling, OAuth helpers, and a lightweight agent loop
Documentation
pub fn short_hash(input: &str) -> String {
    let mut h1 = 0xdead_beefu32;
    let mut h2 = 0x41c6_ce57u32;
    for ch in input.encode_utf16() {
        let ch = u32::from(ch);
        h1 = (h1 ^ ch).wrapping_mul(2_654_435_761);
        h2 = (h2 ^ ch).wrapping_mul(1_597_334_677);
    }
    h1 = ((h1 ^ (h1 >> 16)).wrapping_mul(2_246_822_507))
        ^ ((h2 ^ (h2 >> 13)).wrapping_mul(3_266_489_909));
    h2 = ((h2 ^ (h2 >> 16)).wrapping_mul(2_246_822_507))
        ^ ((h1 ^ (h1 >> 13)).wrapping_mul(3_266_489_909));
    format!("{}{}", to_base36(h2), to_base36(h1))
}

fn to_base36(mut value: u32) -> String {
    if value == 0 {
        return "0".to_string();
    }
    let mut digits = Vec::new();
    while value > 0 {
        let digit = (value % 36) as u8;
        digits.push(match digit {
            0..=9 => b'0' + digit,
            _ => b'a' + (digit - 10),
        });
        value /= 36;
    }
    digits.reverse();
    String::from_utf8(digits).expect("base36 digits are valid utf8")
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn short_hash_matches_expected_value() {
        assert_eq!(short_hash(""), "k4n83c7h0j2b");
        assert_eq!(short_hash("hello"), "1h6qa0qrowduu");
        assert_eq!(short_hash("foreign-tool-call-id"), "1r12n89219uo");
        assert_eq!(short_hash("😀"), "13wj7r7usi372");
    }
}