lc_core/token_counter/
counter.rs1use lc_schema::Message;
4
5pub trait TokenCounter: Send + Sync {
7 fn count_tokens(&self, text: &str) -> u32;
9 fn count_messages(&self, messages: &[Message]) -> u32;
11}
12
13#[derive(Debug, Clone)]
20pub struct CharRatioCounter {
21 ratio: u32,
22}
23
24impl CharRatioCounter {
25 pub fn new(ratio: u32) -> Self {
29 Self {
30 ratio: ratio.max(1),
31 }
32 }
33}
34
35impl TokenCounter for CharRatioCounter {
36 fn count_tokens(&self, text: &str) -> u32 {
37 text.len() as u32 / self.ratio
38 }
39
40 fn count_messages(&self, messages: &[Message]) -> u32 {
41 let mut total = 0u32;
42 for msg in messages {
43 total += 4; total += self.count_tokens(&msg.content);
45 if let Some(name) = &msg.name {
46 total += self.count_tokens(name);
47 }
48 for _ in &msg.images {
50 total += 1000;
51 }
52 }
53 total += 2; total
55 }
56}
57
58#[cfg(test)]
59mod char_ratio_tests {
60 use super::*;
61
62 #[test]
63 fn test_char_ratio_counts_bytes() {
64 let counter = CharRatioCounter::new(4);
65 assert_eq!(counter.count_tokens(""), 0);
66 assert_eq!(counter.count_tokens("Hello"), 1); assert_eq!(counter.count_tokens("Hello World"), 2); }
69
70 #[test]
71 fn test_char_ratio_ratio_at_least_one() {
72 let counter = CharRatioCounter::new(0);
73 assert!(counter.count_tokens("x") >= 1);
74 }
75
76 #[test]
77 fn test_char_ratio_count_messages_matches_structure() {
78 let counter = CharRatioCounter::new(4);
79 let msgs = vec![Message::system("You are helpful."), Message::human("Hi")];
80 let n = counter.count_messages(&msgs);
81 assert!(n >= 10);
83 }
84}
85
86#[derive(Debug, Clone, Default, PartialEq)]
93pub struct TrackerTokenUsage {
94 pub prompt_tokens: usize,
96 pub completion_tokens: usize,
98 pub total_tokens: usize,
100}
101
102impl TrackerTokenUsage {
103 pub fn new() -> Self {
105 Self::default()
106 }
107
108 pub fn add(&mut self, prompt: usize, completion: usize) {
110 self.prompt_tokens += prompt;
111 self.completion_tokens += completion;
112 self.total_tokens = self.prompt_tokens + self.completion_tokens;
113 }
114
115 pub fn reset(&mut self) {
117 *self = Self::default();
118 }
119}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124
125 #[test]
126 fn test_usage_add() {
127 let mut u = TrackerTokenUsage::new();
128 u.add(10, 20);
129 assert_eq!(u.prompt_tokens, 10);
130 assert_eq!(u.completion_tokens, 20);
131 assert_eq!(u.total_tokens, 30);
132 }
133
134 #[test]
135 fn test_usage_accumulate() {
136 let mut u = TrackerTokenUsage::new();
137 u.add(10, 20);
138 u.add(5, 5);
139 assert_eq!(u.prompt_tokens, 15);
140 assert_eq!(u.total_tokens, 40);
141 }
142
143 #[test]
144 fn test_usage_reset() {
145 let mut u = TrackerTokenUsage::new();
146 u.add(10, 20);
147 u.reset();
148 assert_eq!(u, TrackerTokenUsage::new());
149 }
150}