feat: implement async token counter with network resilience and performance optimizations (#3111)

Co-authored-by: jack <>
Co-authored-by: Salman Mohammed <smohammed@squareup.com>
This commit is contained in:
jack
2025-06-30 14:45:17 +02:00
committed by GitHub
parent fdafbca92e
commit 495cdfb33c
8 changed files with 981 additions and 28 deletions
@@ -0,0 +1,108 @@
/// Demo showing the async token counter improvement
///
/// This example demonstrates the key improvement: no blocking runtime creation
///
/// BEFORE (blocking):
/// ```rust
/// let content = tokio::runtime::Runtime::new()?.block_on(async {
/// let response = reqwest::get(&file_url).await?;
/// // ... download logic
/// })?;
/// ```
///
/// AFTER (async):
/// ```rust
/// let client = reqwest::Client::new();
/// let response = client.get(&file_url).send().await?;
/// let bytes = response.bytes().await?;
/// tokio::fs::write(&file_path, bytes).await?;
/// ```
use goose::token_counter::{create_async_token_counter, TokenCounter};
use std::time::Instant;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("🚀 Async Token Counter Demo");
println!("===========================");
// Test text samples
let samples = vec![
"Hello, world!",
"This is a longer text sample for tokenization testing.",
"The quick brown fox jumps over the lazy dog.",
"Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
"async/await patterns eliminate blocking operations",
];
println!("\n📊 Performance Comparison");
println!("-------------------------");
// Test original TokenCounter
let start = Instant::now();
let sync_counter = TokenCounter::new("Xenova--gpt-4o");
let sync_init_time = start.elapsed();
let start = Instant::now();
let mut sync_total = 0;
for sample in &samples {
sync_total += sync_counter.count_tokens(sample);
}
let sync_count_time = start.elapsed();
println!("🔴 Synchronous TokenCounter:");
println!(" Init time: {:?}", sync_init_time);
println!(" Count time: {:?}", sync_count_time);
println!(" Total tokens: {}", sync_total);
// Test AsyncTokenCounter
let start = Instant::now();
let async_counter = create_async_token_counter("Xenova--gpt-4o").await?;
let async_init_time = start.elapsed();
let start = Instant::now();
let mut async_total = 0;
for sample in &samples {
async_total += async_counter.count_tokens(sample);
}
let async_count_time = start.elapsed();
println!("\n🟢 Async TokenCounter:");
println!(" Init time: {:?}", async_init_time);
println!(" Count time: {:?}", async_count_time);
println!(" Total tokens: {}", async_total);
println!(" Cache size: {}", async_counter.cache_size());
// Test caching benefit
let start = Instant::now();
let mut cached_total = 0;
for sample in &samples {
cached_total += async_counter.count_tokens(sample); // Should hit cache
}
let cached_time = start.elapsed();
println!("\n⚡ Cached TokenCounter (2nd run):");
println!(" Count time: {:?}", cached_time);
println!(" Total tokens: {}", cached_total);
println!(" Cache size: {}", async_counter.cache_size());
// Verify same results
assert_eq!(sync_total, async_total);
assert_eq!(async_total, cached_total);
println!("\n✅ Key Improvements:");
println!(" • No blocking runtime creation (eliminates deadlock risk)");
println!(" • Global tokenizer caching with DashMap (lock-free concurrent access)");
println!(" • Fast AHash for better cache performance");
println!(" • Cache size management (prevents unbounded growth)");
println!(
" • Token result caching ({}x faster on repeated text)",
async_count_time.as_nanos() / cached_time.as_nanos().max(1)
);
println!(" • Proper async patterns throughout");
println!(" • Robust network failure handling with exponential backoff");
println!(" • Download validation and corruption detection");
println!(" • Progress reporting for large tokenizer downloads");
println!(" • Smart retry logic (3 attempts, server errors only)");
Ok(())
}