use anyhow::Result;
use tiktoken_rs::{get_bpe_from_model, CoreBPE};
pub struct TokenCounter {
bpe: CoreBPE,
}
impl TokenCounter {
pub fn new() -> Result<Self> {
let bpe = get_bpe_from_model("gpt-4")?;
Ok(Self { bpe })
}
pub fn count_tokens(&self, text: &str) -> usize {
self.bpe.encode_with_special_tokens(text).len()
}
pub fn truncate_to_token_limit(&self, text: &str, max_tokens: usize) -> String {
let tokens = self.bpe.encode_with_special_tokens(text);
if tokens.len() <= max_tokens {
return text.to_string();
}
let truncated_tokens = &tokens[..max_tokens];
match self.bpe.decode(truncated_tokens.to_vec()) {
Ok(truncated_text) => truncated_text,
Err(_) => {
let char_limit = (text.len() * max_tokens) / tokens.len();
text.chars().take(char_limit).collect()
}
}
}
}
pub struct ContentPrioritizer {
token_counter: TokenCounter,
}
impl ContentPrioritizer {
pub fn new() -> Result<Self> {
Ok(Self {
token_counter: TokenCounter::new()?,
})
}
pub fn prioritize_content(
&self,
sections: Vec<ContentSection>,
max_tokens: usize,
) -> Vec<ContentSection> {
let mut prioritized = sections;
prioritized.sort_by(|a, b| b.priority.cmp(&a.priority));
let mut total_tokens = 0;
let mut result = Vec::new();
for mut section in prioritized {
let section_tokens = self.token_counter.count_tokens(§ion.content);
if total_tokens + section_tokens <= max_tokens {
total_tokens += section_tokens;
result.push(section);
} else {
let remaining_tokens = max_tokens - total_tokens;
if remaining_tokens > 100 {
section.content = self
.token_counter
.truncate_to_token_limit(§ion.content, remaining_tokens);
section.truncated = true;
result.push(section);
break;
}
}
}
result
}
}
#[derive(Debug, Clone)]
pub struct ContentSection {
pub title: String,
pub content: String,
pub priority: u8,
pub truncated: bool,
}
impl ContentSection {
pub fn new(title: String, content: String, priority: u8) -> Self {
Self {
title,
content,
priority,
truncated: false,
}
}
pub fn high_priority(title: String, content: String) -> Self {
Self::new(title, content, 9)
}
pub fn medium_priority(title: String, content: String) -> Self {
Self::new(title, content, 5)
}
pub fn low_priority(title: String, content: String) -> Self {
Self::new(title, content, 1)
}
}