use serde_json::Value;
pub fn estimate_tokens(value: &Value) -> u32 {
if value.is_null() {
return 0;
}
let len = value.to_string().len();
len.div_ceil(4) as u32
}
pub fn estimate_tokens_str(s: &str) -> u32 {
s.len().div_ceil(4) as u32
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn null_returns_zero() {
assert_eq!(estimate_tokens(&json!(null)), 0);
}
#[test]
fn empty_object_is_nonzero() {
assert_eq!(estimate_tokens(&json!({})), 1);
}
#[test]
fn four_char_string_is_one_token() {
let v = json!("test");
assert_eq!(estimate_tokens(&v), 2);
}
#[test]
fn typical_tool_call_args() {
let args = json!({"path": "/etc/passwd", "encoding": "utf-8"});
let tokens = estimate_tokens(&args);
assert!(tokens > 0);
let expected = args.to_string().len().div_ceil(4) as u32;
assert_eq!(tokens, expected);
}
#[test]
fn estimate_tokens_str_rounds_up() {
assert_eq!(estimate_tokens_str("abc"), 1); assert_eq!(estimate_tokens_str("abcd"), 1); assert_eq!(estimate_tokens_str("abcde"), 2); }
#[test]
fn large_response_scales_linearly() {
let big = json!({"content": "a".repeat(400)});
let tokens = estimate_tokens(&big);
assert!(tokens >= 100);
}
}