1pub mod basetenkenizer;
5pub mod cache;
6pub mod fastokens;
7pub mod hf;
8pub mod tiktoken;
9
10use std::hash::{DefaultHasher, Hash, Hasher};
15use std::sync::Arc;
16use std::{fs::File, io::BufReader, ops::Deref, path::Path};
17
18use anyhow::Context as _;
19pub use anyhow::{Error, Result};
20
21pub use basetenkenizer::BasetenTokenizer;
22pub use cache::{CacheTokenUsage, CacheTokenUsageFn, CachedTokenizer, L1CacheStats};
23pub use fastokens::FastTokenizer;
24pub use hf::HuggingFaceTokenizer;
25pub use tiktoken::TikTokenTokenizer;
26pub use traits::DecodeResult;
27
28pub type TokenIdType = u32;
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub struct EncodeSegment<'a> {
33 pub text: &'a str,
34 pub allow_special: bool,
39}
40
41impl<'a> EncodeSegment<'a> {
42 pub const fn new(text: &'a str, allow_special: bool) -> Self {
43 Self {
44 text,
45 allow_special,
46 }
47 }
48
49 pub const fn ordinary(text: &'a str) -> Self {
50 Self::new(text, false)
51 }
52
53 pub const fn control(text: &'a str) -> Self {
54 Self::new(text, true)
55 }
56}
57
58#[derive(Debug)]
60pub enum TokenizerType {
61 HuggingFace(String),
62 TikToken(String),
63}
64
65pub type Offsets = (usize, usize);
67
68#[derive(Debug, Clone)]
70pub enum Encoding {
71 Hf(Box<tokenizers::tokenizer::Encoding>),
73 Sp(Vec<TokenIdType>),
75}
76
77impl Encoding {
78 pub fn token_ids(&self) -> &[u32] {
79 match self {
80 Encoding::Hf(inner) => inner.get_ids(),
81 Encoding::Sp(inner) => inner,
82 }
83 }
84}
85
86impl Hash for Encoding {
87 fn hash<H: Hasher>(&self, state: &mut H) {
88 self.token_ids().hash(state);
89 }
90}
91
92pub mod traits {
93 use super::*;
94
95 pub trait Encoder: Send + Sync {
96 fn encode(&self, input: &str) -> Result<Encoding>;
97 fn encode_batch(&self, inputs: &[&str]) -> Result<Vec<Encoding>>;
98
99 fn encode_segments(&self, _segments: &[EncodeSegment<'_>]) -> Result<Encoding> {
117 Err(Error::msg(
118 "tokenizer backend does not support segmented encoding",
119 ))
120 }
121 }
122
123 #[derive(Debug, Clone, PartialEq, Eq, strum::EnumIs)]
130 pub enum DecodeResult {
131 Complete(String),
135 Partial(String),
138 }
139
140 impl DecodeResult {
141 pub fn as_str(&self) -> &str {
143 match self {
144 DecodeResult::Complete(s) | DecodeResult::Partial(s) => s,
145 }
146 }
147
148 pub fn from_decoded(text: String) -> Self {
150 if text.ends_with('\u{FFFD}') {
151 DecodeResult::Partial(text)
152 } else {
153 DecodeResult::Complete(text)
154 }
155 }
156 }
157
158 impl From<String> for DecodeResult {
159 fn from(text: String) -> Self {
160 DecodeResult::from_decoded(text)
161 }
162 }
163
164 impl From<DecodeResult> for String {
165 fn from(result: DecodeResult) -> Self {
166 match result {
167 DecodeResult::Complete(s) | DecodeResult::Partial(s) => s,
168 }
169 }
170 }
171
172 pub trait Decoder: Send + Sync {
178 fn decode(
179 &self,
180 token_ids: &[TokenIdType],
181 skip_special_tokens: bool,
182 ) -> Result<DecodeResult>;
183 }
184
185 pub trait Tokenizer: Encoder + Decoder {
186 fn validate_prefix_cache(&self) -> Result<()> {
191 Err(Error::msg("tokenizer does not support prefix caching"))
192 }
193
194 fn with_options(self, options: TokenizerOptions) -> Self
199 where
200 Self: Sized,
201 {
202 let _ = options;
203 self
204 }
205 }
208}
209
210pub fn file_json_field<T: serde::de::DeserializeOwned>(
211 json_file_path: &Path,
212 field_name: &str,
213) -> anyhow::Result<T> {
214 let file = File::open(json_file_path)
215 .with_context(|| format!("Failed to open file: {:?}", json_file_path))?;
216 let reader = BufReader::new(file);
217
218 let json_data: serde_json::Value = serde_json::from_reader(reader)
219 .with_context(|| format!("Failed to parse JSON from file: {:?}", json_file_path))?;
220
221 let map = json_data.as_object().ok_or_else(|| {
222 anyhow::anyhow!("JSON root is not an object in file: {:?}", json_file_path)
223 })?;
224
225 let field_value = map.get(field_name).ok_or_else(|| {
226 anyhow::anyhow!(
227 "Field '{}' not found in JSON file: {:?}",
228 field_name,
229 json_file_path
230 )
231 })?;
232
233 serde_json::from_value(field_value.clone()).with_context(|| {
234 format!(
235 "Failed to deserialize field '{}' (value: {:?}) to the expected type from file: {:?}",
236 field_name, field_value, json_file_path
237 )
238 })
239}
240
241pub fn log_json_err(filename: &str, json: &str, err: &serde_json::Error) {
242 const ERROR_PREFIX: &str = ">> ";
243
244 if !(err.is_syntax() || err.is_data()) {
245 return;
246 }
247
248 let line = err.line().saturating_sub(1);
249 let column = err.column().saturating_sub(1);
250
251 let json_lines: Vec<&str> = json.lines().collect();
252 if json_lines.is_empty() {
253 tracing::error!("JSON parsing error in {filename}: File is empty.");
254 return;
255 }
256
257 let start_index = line.saturating_sub(2);
258 let end_index = line.saturating_add(3).min(json_lines.len());
259
260 let mut context_lines: Vec<String> = (start_index..end_index)
261 .map(|i| {
262 if i == line {
263 format!("{ERROR_PREFIX}{}", json_lines[i])
264 } else {
265 format!("{:06} {}", i + 1, json_lines[i])
266 }
267 })
268 .collect();
269
270 let col_indicator = "_".to_string().repeat(column + ERROR_PREFIX.len()) + "^";
271 let error_in_context_idx = line - start_index;
272 if error_in_context_idx < context_lines.len() {
273 context_lines.insert(error_in_context_idx + 1, col_indicator);
274 }
275
276 tracing::error!(
277 "JSON parsing error in {filename}: Line {}, column {}:\n{}",
278 err.line(),
279 err.column(),
280 context_lines.join("\n")
281 );
282}
283
284impl Encoding {
285 pub fn get_hash(&self) -> u64 {
286 let mut hasher = DefaultHasher::new();
287 self.hash(&mut hasher);
288 hasher.finish()
289 }
290}
291
292#[derive(Debug, Clone, Copy, Default)]
296pub struct TokenizerOptions {
297 pub add_special_tokens: bool,
304}
305
306#[derive(Clone)]
308pub struct Tokenizer(Arc<dyn traits::Tokenizer>);
309
310impl Tokenizer {
311 pub fn from_file(file_path: &str) -> Result<Tokenizer> {
312 Ok(Tokenizer(create_tokenizer_from_file(file_path)?))
313 }
314
315 pub fn from_file_with_options(file_path: &str, options: TokenizerOptions) -> Result<Tokenizer> {
316 Ok(Tokenizer(create_tokenizer_from_file_with_options(
317 file_path, options,
318 )?))
319 }
320
321 pub fn decode_stream(
323 &self,
324 prompt_token_ids: &[TokenIdType],
325 skip_special_tokens: bool,
326 ) -> DecodeStream {
327 DecodeStream::new(self.0.clone(), prompt_token_ids, skip_special_tokens)
328 }
329}
330
331impl Deref for Tokenizer {
332 type Target = Arc<dyn traits::Tokenizer>;
333
334 fn deref(&self) -> &Self::Target {
335 &self.0
336 }
337}
338
339impl From<Arc<dyn traits::Tokenizer>> for Tokenizer {
340 fn from(tokenizer: Arc<dyn traits::Tokenizer>) -> Self {
341 Tokenizer(tokenizer)
342 }
343}
344
345impl<T> From<Arc<T>> for Tokenizer
346where
347 T: traits::Tokenizer + 'static, {
349 fn from(tokenizer: Arc<T>) -> Self {
350 Tokenizer(tokenizer)
351 }
352}
353
354pub fn create_tokenizer_from_file(file_path: &str) -> Result<Arc<dyn traits::Tokenizer>> {
361 create_tokenizer_from_file_with_options(file_path, Default::default())
362}
363
364pub fn create_tokenizer_from_file_with_options(
371 file_path: &str,
372 options: TokenizerOptions,
373) -> Result<Arc<dyn traits::Tokenizer>> {
374 use traits::Tokenizer as _;
375
376 let path = Path::new(file_path);
377 let extension = path
378 .extension()
379 .and_then(std::ffi::OsStr::to_str)
380 .ok_or_else(|| Error::msg("Failed to read file extension".to_string()))?;
381
382 match extension {
383 "json" => {
384 let tokenizer = HuggingFaceTokenizer::from_file(file_path)?.with_options(options);
385 Ok(Arc::new(tokenizer))
386 }
387 "model" | "tiktoken" => {
388 let tokenizer = TikTokenTokenizer::from_file_auto(file_path)?.with_options(options);
389 Ok(Arc::new(tokenizer))
390 }
391 _ => Err(Error::msg(format!(
392 "Unsupported tokenizer file type: .{extension}"
393 ))),
394 }
395}
396
397const INITIAL_INCREMENTAL_DETOKENIZATION_OFFSET: usize = 5;
403
404pub struct DecodeStream {
410 tokenizer: Arc<dyn traits::Tokenizer>,
412
413 skip_special_tokens: bool,
414 all_token_ids: Vec<u32>,
426
427 prefix_offset: usize,
428
429 read_offset: usize,
430
431 has_emitted: bool,
433}
434
435impl DecodeStream {
436 pub fn new(
437 tokenizer: Arc<dyn traits::Tokenizer>,
438 prompt_token_ids: &[TokenIdType],
439 skip_special_tokens: bool,
440 ) -> Self {
441 let num_input_tokens = prompt_token_ids.len();
442 let prompt_token_ids = prompt_token_ids.to_vec();
443 Self {
444 tokenizer,
445 skip_special_tokens,
446 all_token_ids: prompt_token_ids,
447 prefix_offset: num_input_tokens
448 .saturating_sub(INITIAL_INCREMENTAL_DETOKENIZATION_OFFSET),
449 read_offset: num_input_tokens,
450 has_emitted: false,
451 }
452 }
453
454 pub fn step(&mut self, id: u32) -> Result<Option<String>> {
467 self.all_token_ids.push(id);
468
469 let prefix_text: String = self
470 .tokenizer
471 .decode(
472 &self.all_token_ids[self.prefix_offset..self.read_offset],
473 self.skip_special_tokens,
474 )?
475 .into();
476
477 let new_result = self.tokenizer.decode(
478 &self.all_token_ids[self.prefix_offset..],
479 self.skip_special_tokens,
480 )?;
481
482 let new_text = new_result.as_str();
483 let is_partial = new_result.is_partial();
484
485 if self.has_emitted && !is_partial && !new_text.starts_with(prefix_text.as_str()) {
488 return Err(Error::msg(
489 "incremental decoding rewrote already emitted text",
490 ));
491 }
492
493 if new_text.len() > prefix_text.len() && !is_partial {
494 let requested_split = prefix_text.len();
495 let split = if self.has_emitted {
496 requested_split
499 } else {
500 new_text.floor_char_boundary(requested_split)
503 };
504
505 let emitted = new_text[split..].to_string();
506
507 self.prefix_offset = self.read_offset;
508 self.read_offset = self.all_token_ids.len();
509 self.has_emitted = true;
510
511 Ok(Some(emitted))
512 } else {
513 Ok(None)
514 }
515 }
516}
517
518#[cfg(test)]
519mod decode_stream_unicode_tests {
520 use super::{DecodeResult, DecodeStream, Encoding, Result, TokenIdType};
521 use std::sync::Arc;
522
523 struct RewritingTokenizer {
524 prefix_text: &'static str,
525 rewritten_text: &'static str,
526 prefix_is_partial: bool,
527 }
528
529 impl super::traits::Encoder for RewritingTokenizer {
530 fn encode(&self, _input: &str) -> Result<Encoding> {
531 Ok(Encoding::Sp(vec![]))
532 }
533
534 fn encode_batch(&self, _inputs: &[&str]) -> Result<Vec<Encoding>> {
535 Ok(vec![])
536 }
537 }
538
539 impl super::traits::Decoder for RewritingTokenizer {
540 fn decode(
541 &self,
542 token_ids: &[TokenIdType],
543 _skip_special_tokens: bool,
544 ) -> Result<DecodeResult> {
545 let result = match token_ids.len() {
546 1 if self.prefix_is_partial => DecodeResult::Partial(self.prefix_text.to_string()),
547 1 => DecodeResult::Complete(self.prefix_text.to_string()),
548 2 => DecodeResult::Complete(self.rewritten_text.to_string()),
549 _ => DecodeResult::Complete(String::new()),
550 };
551 Ok(result)
552 }
553 }
554
555 impl super::traits::Tokenizer for RewritingTokenizer {}
556
557 #[test]
558 fn allows_boundary_recovery_before_generated_text_is_emitted() {
559 for (prefix_text, rewritten_text, expected) in [
560 ("㺄馉凓鄗\u{FFFD}", "㺄馉凓鄗𫷲", "𫷲"),
561 (
562 "JUnitworkflow Completion intuition\u{FFFD}",
563 "JUnitworkflow Completion intuition𝟙",
564 "𝟙",
565 ),
566 ] {
567 let tokenizer: Arc<dyn super::traits::Tokenizer> = Arc::new(RewritingTokenizer {
568 prefix_text,
569 rewritten_text,
570 prefix_is_partial: true,
571 });
572 let mut stream = DecodeStream::new(tokenizer, &[1], false);
573
574 assert_eq!(stream.step(2).unwrap(), Some(expected.to_string()));
575 }
576 }
577
578 #[test]
579 fn errors_when_incremental_decode_rewrites_emitted_text() {
580 for (prefix_text, rewritten_text) in [
581 ("abcde", "abcdXY"),
582 ("abcde", "abcdX"),
583 ("abcde", "abcd"),
584 ("㺄馉凓鄗abc", "㺄馉凓鄗𫷲"),
585 (
586 "JUnitworkflow Completion intuitionabc",
587 "JUnitworkflow Completion intuition𝟙",
588 ),
589 ] {
590 let tokenizer: Arc<dyn super::traits::Tokenizer> = Arc::new(RewritingTokenizer {
591 prefix_text,
592 rewritten_text,
593 prefix_is_partial: false,
594 });
595 let mut stream = DecodeStream::new(tokenizer, &[], false);
596
597 assert_eq!(stream.step(1).unwrap(), Some(prefix_text.to_string()));
598
599 let error = stream.step(2).unwrap_err();
600 assert!(error.to_string().contains("already emitted text"));
601 }
602 }
603
604 #[test]
605 fn emits_suffix_when_incremental_decode_preserves_emitted_text() {
606 let tokenizer: Arc<dyn super::traits::Tokenizer> = Arc::new(RewritingTokenizer {
607 prefix_text: "abcde",
608 rewritten_text: "abcdeXY",
609 prefix_is_partial: false,
610 });
611 let mut stream = DecodeStream::new(tokenizer, &[], false);
612
613 assert_eq!(stream.step(1).unwrap(), Some("abcde".to_string()));
614 assert_eq!(stream.step(2).unwrap(), Some("XY".to_string()));
615 }
616}
617
618pub struct Sequence {
620 tokenizer: Tokenizer,
622
623 token_ids: Vec<TokenIdType>,
625
626 prefix_offset: usize,
628
629 read_offset: usize,
631}
632
633impl std::fmt::Debug for Sequence {
634 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
635 f.debug_struct("Sequence")
636 .field("tokenizer", &"Arc<dyn Tokenizer>")
637 .field(
638 "token_ids",
639 &format_args!("{}", {
640 let token_ids = self.token_ids();
641 if token_ids.len() <= 20 {
642 format!("{:?}", token_ids)
643 } else {
644 let first_ten = &token_ids[..10];
645 let last_ten = &token_ids[token_ids.len() - 10..];
646 format!("{:?} ... {:?}", first_ten, last_ten)
647 }
648 }),
649 )
650 .field("prefix_offset", &self.prefix_offset)
651 .field("read_offset", &self.read_offset)
652 .field("token count", &self.token_ids.len())
653 .finish()
654 }
655}
656
657impl Sequence {
658 pub fn new(tokenizer: Tokenizer) -> Self {
659 Self {
660 tokenizer,
661 token_ids: Vec::new(),
662 prefix_offset: 0,
663 read_offset: 0,
664 }
665 }
666
667 pub fn is_empty(&self) -> bool {
668 self.token_ids.is_empty()
669 }
670
671 pub fn len(&self) -> usize {
672 self.token_ids.len()
673 }
674
675 pub fn clear(&mut self) {
676 self.token_ids.clear();
677 self.prefix_offset = 0;
678 self.read_offset = 0;
679 }
680
681 pub fn append_text(&mut self, input: &str) -> Result<()> {
682 let encoding = self.tokenizer.encode(input)?;
687 self.token_ids.extend(encoding.token_ids());
688 Ok(())
689 }
690
691 pub fn append_token_id(&mut self, token_id: TokenIdType) -> Result<String> {
695 self.token_ids.push(token_id);
696 let prefix_text: String = self
699 .tokenizer
700 .decode(&self.token_ids[self.prefix_offset..self.read_offset], false)?
701 .into();
702
703 let new_result = self
704 .tokenizer
705 .decode(&self.token_ids[self.prefix_offset..], false)?;
706
707 let new_text = new_result.as_str();
708
709 let mut prefix_text_len = prefix_text.len();
713 while !new_text.is_char_boundary(prefix_text_len) && prefix_text_len > 0 {
714 prefix_text_len -= 1;
715 }
716 let prefix_text_len = prefix_text_len;
717
718 if new_text.len() > prefix_text.len() {
719 if new_result.is_partial() {
720 return Ok("".to_string());
721 } else {
722 let new_text = new_text[prefix_text_len..]
724 .to_string()
725 .replace('\u{FFFD}', "");
726 self.prefix_offset = self.read_offset;
727 self.read_offset = self.token_ids.len();
728 return Ok(new_text);
729 }
730 }
731
732 Ok("".to_string())
733 }
734
735 pub fn tokenizer(&self) -> Tokenizer {
736 self.tokenizer.clone()
737 }
738
739 pub fn token_ids(&self) -> &[TokenIdType] {
740 &self.token_ids
741 }
742
743 pub fn text(&self) -> Result<String> {
744 Ok(self.tokenizer.decode(&self.token_ids, false)?.into())
748 }
749}
750
751pub enum SequenceDecoderOutput {
754 Text(String),
756
757 Held,
760
761 Stopped,
764
765 StoppedWithText(String),
769}
770
771#[derive(Debug)]
777pub struct StopSequenceDecoder {
778 sequence: Sequence,
780
781 stop_token_ids_visible: Vec<TokenIdType>,
784
785 stop_token_ids_hidden: Vec<TokenIdType>,
788
789 #[allow(dead_code)]
792 stop_sequences_visible: Vec<String>,
793
794 stop_sequences_hidden: Vec<String>,
797
798 stopped: bool,
801
802 state: String,
805}
806
807impl StopSequenceDecoder {
808 pub fn builder(tokenizer: Tokenizer) -> StopSequenceDecoderBuilder {
810 StopSequenceDecoderBuilder::new(tokenizer)
811 }
812
813 pub fn append_token_id(&mut self, token_id: TokenIdType) -> Result<SequenceDecoderOutput> {
815 if self.stopped {
816 return Err(Error::msg("Decoder is stopped"));
817 }
818
819 let text = self.sequence.append_token_id(token_id)?;
821
822 self.state.push_str(text.as_str());
824
825 let mut stop: bool = false;
826 let mut visible: bool = false;
827
828 if self.stop_token_ids_visible.contains(&token_id) {
829 stop = true;
830 visible = true;
831 }
832
833 if self.stop_token_ids_hidden.contains(&token_id) {
834 stop = true;
835 visible = false;
836 }
837
838 if stop {
839 self.stopped = true;
840 let state = std::mem::take(&mut self.state);
841 if visible {
842 return Ok(SequenceDecoderOutput::StoppedWithText(state));
843 }
844 return Ok(SequenceDecoderOutput::Stopped);
845 }
846
847 for stop_sequence in self.stop_sequences_hidden.iter() {
849 if stop_sequence.starts_with(&self.state) {
850 if stop_sequence == &self.state {
851 self.stopped = true;
853 return Ok(SequenceDecoderOutput::Stopped);
854 } else {
855 return Ok(SequenceDecoderOutput::Held);
856 }
857 }
858 }
859
860 let state = std::mem::take(&mut self.state);
861 Ok(SequenceDecoderOutput::Text(state))
862 }
863
864 pub fn is_empty(&self) -> bool {
865 self.sequence.token_ids.is_empty()
866 }
867
868 pub fn len(&self) -> usize {
869 self.sequence.token_ids.len()
870 }
871
872 pub fn is_complete(&self) -> bool {
873 self.stopped
874 }
875
876 pub fn close(&mut self) {
877 self.stopped = true;
878 }
879}
880
881pub struct StopSequenceDecoderBuilder {
882 tokenizer: Tokenizer,
883 stop_token_ids_visible: Vec<TokenIdType>,
884 stop_token_ids_hidden: Vec<TokenIdType>,
885 stop_sequences_visible: Vec<String>,
886 stop_sequences_hidden: Vec<String>,
887}
888
889impl StopSequenceDecoderBuilder {
890 pub fn new(tokenizer: Tokenizer) -> Self {
891 Self {
892 tokenizer,
893 stop_token_ids_visible: Vec::new(),
894 stop_token_ids_hidden: Vec::new(),
895 stop_sequences_visible: Vec::new(),
896 stop_sequences_hidden: Vec::new(),
897 }
898 }
899
900 pub fn add_stop_token_id_visible(mut self, token_id: TokenIdType) -> Self {
902 self.stop_token_ids_visible.push(token_id);
903 self
904 }
905
906 pub fn add_stop_token_ids_visible(mut self, token_ids: &[TokenIdType]) -> Self {
909 self.stop_token_ids_visible.extend(token_ids);
910 self
911 }
912
913 pub fn add_stop_token_id_hidden(mut self, token_id: TokenIdType) -> Self {
915 self.stop_token_ids_hidden.push(token_id);
916 self
917 }
918
919 pub fn add_stop_token_ids_hidden(mut self, token_ids: &[TokenIdType]) -> Self {
922 self.stop_token_ids_hidden.extend(token_ids);
923 self
924 }
925
926 pub fn add_stop_sequence_visible(mut self, text: &str) -> Self {
927 self.stop_sequences_visible.push(text.to_string());
928 self
929 }
930
931 pub fn add_stop_sequences_visible(mut self, strings: &[&str]) -> Self {
932 self.stop_sequences_visible
933 .extend(strings.iter().map(|text| text.to_string()));
934 self
935 }
936
937 pub fn add_stop_sequence_hidden(mut self, text: &str) -> Self {
938 self.stop_sequences_hidden.push(text.to_string());
939 self
940 }
941
942 pub fn add_stop_sequences_hidden(mut self, strings: &[&str]) -> Self {
943 self.stop_sequences_hidden
944 .extend(strings.iter().map(|text| text.to_string()));
945 self
946 }
947
948 pub fn build(self) -> Result<StopSequenceDecoder> {
949 Ok(StopSequenceDecoder {
950 sequence: Sequence::new(self.tokenizer.clone()),
951 stop_token_ids_visible: self.stop_token_ids_visible,
952 stop_token_ids_hidden: self.stop_token_ids_hidden,
953 stop_sequences_visible: self.stop_sequences_visible,
954 stop_sequences_hidden: self.stop_sequences_hidden,
955 stopped: false,
956 state: String::new(),
957 })
958 }
959}