1use super::{
2 normalizer::Range, Model, NormalizedString, Normalizer, Offsets, PreTokenizedString, Token,
3};
4use ahash::{AHashMap, AHashSet};
5use aho_corasick::{AhoCorasick, AhoCorasickBuilder, MatchKind};
6use regex::Regex;
7use serde::{ser::SerializeSeq, Deserialize, Serialize, Serializer};
8use std::sync::LazyLock;
9
10#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
16pub struct AddedToken {
17 pub content: String,
19 pub single_word: bool,
21 pub lstrip: bool,
23 pub rstrip: bool,
25 pub normalized: bool,
27 pub special: bool,
29}
30
31impl AddedToken {
32 pub fn from<S: Into<String>>(content: S, special: bool) -> Self {
35 Self {
36 content: content.into(),
37 normalized: !special,
38 special,
39 ..Default::default()
40 }
41 }
42 #[must_use]
45 pub fn single_word(mut self, single_word: bool) -> Self {
46 self.single_word = single_word;
47 self
48 }
49 #[must_use]
52 pub fn lstrip(mut self, lstrip: bool) -> Self {
53 self.lstrip = lstrip;
54 self
55 }
56 #[must_use]
59 pub fn rstrip(mut self, rstrip: bool) -> Self {
60 self.rstrip = rstrip;
61 self
62 }
63 #[must_use]
66 pub fn normalized(mut self, normalized: bool) -> Self {
67 self.normalized = normalized;
68 self
69 }
70 #[must_use]
72 pub fn special(mut self, special: bool) -> Self {
73 self.special = special;
74 self
75 }
76}
77impl Default for AddedToken {
78 fn default() -> Self {
79 Self {
80 content: String::new(),
81 single_word: false,
82 lstrip: false,
83 rstrip: false,
84 normalized: true,
85 special: false,
86 }
87 }
88}
89impl std::hash::Hash for AddedToken {
91 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
92 self.content.hash(state);
93 }
94}
95
96type MatchingSet = (AhoCorasick, Vec<u32>);
97
98static STARTS_WITH_WORD: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"^\w").unwrap());
99static ENDS_WITH_WORD: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"\w$").unwrap());
100static RIGHTMOST_SPACE_AT_START: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"^\s*").unwrap());
101static LEFTMOST_SPACE_AT_END: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"\s*$").unwrap());
102
103fn ends_with_word(sentence: &str) -> bool {
104 ENDS_WITH_WORD.is_match(sentence)
105}
106
107fn starts_with_word(sentence: &str) -> bool {
108 STARTS_WITH_WORD.is_match(sentence)
109}
110
111fn space_leftmost_at_end(sentence: &str) -> usize {
112 if let Some(match_) = LEFTMOST_SPACE_AT_END.find(sentence) {
113 match_.start()
114 } else {
115 sentence.len()
116 }
117}
118fn space_rightmost_at_start(sentence: &str) -> usize {
119 if let Some(match_) = RIGHTMOST_SPACE_AT_START.find(sentence) {
120 match_.end()
121 } else {
122 0
123 }
124}
125#[derive(Clone, Debug)]
141pub struct AddedVocabulary {
142 added_tokens_map: AHashMap<String, u32>,
145 added_tokens_map_r: AHashMap<u32, AddedToken>,
148
149 added_tokens: Vec<AddedToken>,
151 special_tokens: Vec<AddedToken>,
153
154 special_tokens_set: AHashSet<String>,
157
158 split_trie: MatchingSet,
160 split_normalized_trie: MatchingSet,
162
163 encode_special_tokens: bool,
165}
166
167impl AddedVocabulary {
168 pub fn new() -> Self {
169 let trie = AhoCorasickBuilder::new()
170 .match_kind(MatchKind::LeftmostLongest)
171 .build::<_, &&[u8]>([])
172 .expect("The trie should build correctly");
173 let normalized_trie = AhoCorasickBuilder::new()
174 .match_kind(MatchKind::LeftmostLongest)
175 .build::<_, &&[u8]>([])
176 .expect("The normalized trie should build correctly");
177 Self {
178 added_tokens_map: AHashMap::new(),
179 added_tokens_map_r: AHashMap::new(),
180 added_tokens: vec![],
181 special_tokens: vec![],
182 special_tokens_set: AHashSet::new(),
183 split_trie: (trie, vec![]),
184 split_normalized_trie: (normalized_trie, vec![]),
185 encode_special_tokens: false,
186 }
187 }
188 #[allow(dead_code)] pub fn len(&self) -> usize {
191 self.added_tokens_map.len()
192 }
193
194 pub fn is_empty(&self) -> bool {
196 self.added_tokens_map.is_empty()
197 }
198
199 pub fn get_vocab(&self) -> &AHashMap<String, u32> {
201 &self.added_tokens_map
202 }
203
204 pub fn get_added_tokens_decoder(&self) -> &AHashMap<u32, AddedToken> {
206 &self.added_tokens_map_r
207 }
208
209 pub fn token_to_id(&self, token: &str, model: &impl Model) -> Option<u32> {
211 self.added_tokens_map
212 .get(token)
213 .copied()
214 .or_else(|| model.token_to_id(token))
215 }
216
217 #[deprecated(
219 since = "0.19.0",
220 note = "please use `added_vocabulary.simple_id_to_token(id).or_else(|| model.id_to_token(id)` instead"
221 )]
222 pub fn id_to_token(&self, id: u32, model: &impl Model) -> Option<String> {
223 self.added_tokens_map_r
224 .get(&id)
225 .map(|t| t.content.clone())
226 .or_else(|| model.id_to_token(id))
227 }
228
229 pub fn simple_id_to_token(&self, id: u32) -> Option<String> {
230 self.added_tokens_map_r.get(&id).map(|t| t.content.clone())
231 }
232
233 pub fn set_encode_special_tokens(&mut self, value: bool) {
235 self.encode_special_tokens = value;
236 }
237
238 pub fn get_encode_special_tokens(&self) -> bool {
239 self.encode_special_tokens
240 }
241
242 pub fn is_special_token(&self, token: &str) -> bool {
244 self.special_tokens_set.contains(token)
245 }
246
247 pub fn add_special_tokens<N: Normalizer>(
249 &mut self,
250 tokens: &[AddedToken],
251 model: &impl Model,
252 normalizer: Option<&N>,
253 ) -> usize {
254 self.add_tokens(tokens, model, normalizer)
255 }
256
257 pub fn add_tokens<N: Normalizer>(
259 &mut self,
260 tokens: &[AddedToken],
261 model: &impl Model,
262 normalizer: Option<&N>,
263 ) -> usize {
264 for token in tokens {
266 if token.special
267 && !token.content.is_empty()
268 && !self.special_tokens_set.contains(&token.content)
269 {
270 self.special_tokens.push(token.to_owned());
271 self.special_tokens_set.insert(token.content.clone());
272 }
273 }
274
275 let mut ignored = 0;
277 for token in tokens {
278 if token.content.is_empty() || self.added_tokens_map_r.values().any(|val| val == token)
279 {
280 ignored += 1;
281 continue;
282 }
283 let new_id = if let Some(new_id) = self.token_to_id(&token.content, model) {
285 new_id
286 } else {
287 self.added_tokens_map.values().cloned().max().map_or(
288 model.get_vocab_size() as u32,
289 |max| {
290 if (max >= model.get_vocab_size() as u32) || model.get_vocab_size() == 0 {
291 max + 1
292 } else {
293 model.get_vocab_size() as u32
294 }
295 },
296 )
297 };
298 *self
300 .added_tokens_map
301 .entry(token.content.clone())
302 .or_default() = new_id;
303 *self.added_tokens_map_r.entry(new_id).or_default() = token.clone();
305 if !self.special_tokens_set.contains(&token.content) {
309 self.added_tokens.push(token.clone());
310 }
311 }
312
313 self.refresh_added_tokens(model, normalizer);
314
315 tokens.len() - ignored
317 }
318
319 fn refresh_added_tokens<N: Normalizer>(&mut self, model: &impl Model, normalizer: Option<&N>) {
324 type TupleTokenId<'a> = (&'a AddedToken, u32);
325 let (normalized, non_normalized): (Vec<TupleTokenId>, Vec<TupleTokenId>) = self
326 .special_tokens
327 .iter()
328 .chain(self.added_tokens.iter())
329 .map(|token| {
330 (
331 token,
332 self.token_to_id(&token.content, model)
333 .expect("Missing additional token"),
334 )
335 })
336 .partition(|(token, _)| token.normalized);
337
338 let (tokens, ids): (Vec<&AddedToken>, Vec<u32>) = non_normalized.into_iter().unzip();
339 let trie = AhoCorasickBuilder::new()
340 .match_kind(MatchKind::LeftmostLongest)
341 .build(tokens.iter().map(|token| &token.content))
342 .expect("Failed to build tried when refreshing tokens");
343 self.split_trie = (trie, ids);
344
345 let (ntokens, nids): (Vec<&AddedToken>, Vec<u32>) = normalized.into_iter().unzip();
346 let patterns: Vec<_> = ntokens
347 .iter()
348 .map(|token| {
349 let mut content = NormalizedString::from(token.content.as_ref());
350 if let Some(n) = normalizer {
351 n.normalize(&mut content).unwrap();
352 }
353 content
354 })
355 .collect();
356 let normalized_trie = AhoCorasickBuilder::new()
357 .match_kind(MatchKind::LeftmostLongest)
358 .build(patterns.iter().map(|content| content.get()))
359 .expect("Failed to build tried when refreshing tokens (normalized)");
360 self.split_normalized_trie = (normalized_trie, nids);
361 }
362
363 fn find_matches(&self, sentence: &str, split_re: &MatchingSet) -> Vec<(Option<u32>, Offsets)> {
368 if sentence.is_empty() {
369 return vec![(None, (0, 0))];
370 }
371
372 let mut start_offset = 0;
373 let mut splits = vec![];
374
375 for mat in split_re.0.find_iter(sentence) {
376 let mut start = mat.start();
377 let mut stop = mat.end();
378 let aho_id = mat.pattern();
379 let id = split_re.1[aho_id];
380 let added_token = &self.added_tokens_map_r.get(&id).unwrap();
381
382 if self.encode_special_tokens && self.special_tokens_set.contains(&added_token.content)
383 {
384 continue;
385 }
386
387 if added_token.single_word {
388 let start_space = start == 0 || !ends_with_word(&sentence[..start]);
389 let stop_space = stop == sentence.len() || !starts_with_word(&sentence[stop..]);
390
391 if !stop_space || !start_space {
392 continue;
394 }
395 }
396 if added_token.lstrip {
397 let newstart = space_leftmost_at_end(&sentence[..start]);
399
400 start = std::cmp::max(newstart, start_offset);
403 }
404 if added_token.rstrip {
405 stop += space_rightmost_at_start(&sentence[stop..])
408 }
409 if start_offset < start {
410 splits.push((None, (start_offset, start)));
411 }
412 splits.push((Some(id), (start, stop)));
413 start_offset = stop;
414 }
415
416 let total_byte_len = sentence.len();
417 if start_offset != total_byte_len {
418 splits.push((None, (start_offset, total_byte_len)));
419 }
420
421 splits
422 }
423
424 fn split_with_indices(
428 &self,
429 sentence: NormalizedString,
430 split_re: &MatchingSet,
431 ) -> Vec<(NormalizedString, Option<Vec<Token>>)> {
432 self.find_matches(sentence.get(), split_re)
433 .into_iter()
434 .map(|(id, byte_offsets)| {
435 let slice = sentence
436 .slice(Range::Normalized(byte_offsets.0..byte_offsets.1))
437 .expect("AddedVocabulary bad split");
438 if let Some(id) = id {
439 let value = slice.get().to_owned();
440 let len = value.len();
441 (slice, Some(vec![Token::new(id, value, (0, len))]))
442 } else {
443 (slice, None)
444 }
445 })
446 .collect()
447 }
448
449 pub fn extract_and_normalize<N: Normalizer>(
456 &self,
457 normalizer: Option<&N>,
458 sequence: &str,
459 ) -> PreTokenizedString {
460 let mut pretokenized: PreTokenizedString = sequence.into();
461
462 pretokenized
464 .split(|_, sequence| Ok(self.split_with_indices(sequence, &self.split_trie)))
465 .expect("AddedVocabulary bad split");
466
467 pretokenized
481 .split(|_, mut sequence| {
482 normalizer.map(|n| n.normalize(&mut sequence));
483 Ok(self.split_with_indices(sequence, &self.split_normalized_trie))
484 })
485 .expect("AddedVocabulary bad split");
486
487 pretokenized
496 }
497}
498
499impl Default for AddedVocabulary {
500 fn default() -> Self {
501 Self::new()
502 }
503}
504
505#[derive(Debug, Serialize, Deserialize)]
506pub(super) struct AddedTokenWithId {
507 pub id: u32,
509 #[serde(flatten)]
510 pub token: AddedToken,
512}
513
514impl Serialize for AddedVocabulary {
515 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
516 where
517 S: Serializer,
518 {
519 let mut added_tokens = self
520 .added_tokens_map_r
521 .iter()
522 .map(|(id, token)| AddedTokenWithId {
523 id: *id,
524 token: token.clone(),
525 })
526 .collect::<Vec<_>>();
527 added_tokens.sort_unstable_by_key(|o| o.id);
529
530 let mut vocabulary = serializer.serialize_seq(Some(added_tokens.len()))?;
531 for token in added_tokens {
532 vocabulary.serialize_element(&token)?;
533 }
534
535 vocabulary.end()
536 }
537}
538
539#[cfg(test)]
540mod tests {
541 use super::*;
542 use crate::normalizers::byte_level::ByteLevel as ByteLevelNormalizer;
543 use crate::normalizers::utils::Lowercase;
544 use crate::normalizers::NormalizerWrapper;
545 use crate::{OffsetReferential, OffsetType, Result, Token, Trainer};
546 use std::collections::HashMap;
547 use std::path::{Path, PathBuf};
548
549 #[derive(Serialize, Deserialize)]
550 struct ModelMock {
551 vocab: AHashMap<String, u32>,
552 vocab_r: AHashMap<u32, String>,
553 }
554 impl ModelMock {
555 pub fn new<I>(iter: I) -> Self
556 where
557 I: IntoIterator<Item = &'static (&'static str, u32)>,
558 {
559 let vocab: AHashMap<String, u32> = iter
560 .into_iter()
561 .map(|&(tok, id)| (tok.to_string(), id))
562 .collect();
563 Self {
564 vocab_r: vocab
565 .iter()
566 .map(|(tok, id)| (*id, tok.to_owned()))
567 .collect(),
568 vocab,
569 }
570 }
571 }
572
573 fn simplify_output(result: &'_ PreTokenizedString) -> Vec<(&'_ str, Option<Vec<u32>>)> {
574 result
575 .get_splits(OffsetReferential::Original, OffsetType::Byte)
576 .into_iter()
577 .map(|(s, _, tokens)| {
578 (
579 s,
580 tokens
581 .as_ref()
582 .map(|t| t.iter().map(|t| t.id).collect::<Vec<_>>()),
583 )
584 })
585 .collect::<Vec<_>>()
586 }
587
588 struct TrainerMock;
589 impl Trainer for TrainerMock {
590 type Model = ModelMock;
591 fn should_show_progress(&self) -> bool {
592 true
593 }
594 fn train(&self, _model: &mut ModelMock) -> Result<Vec<AddedToken>> {
595 unimplemented!()
596 }
597 fn feed<I, S, F>(&mut self, _iterator: I, _process: F) -> Result<()>
598 where
599 I: Iterator<Item = S> + Send,
600 S: AsRef<str> + Send,
601 F: Fn(&str) -> Result<Vec<String>> + Sync,
602 {
603 unimplemented!()
604 }
605 }
606
607 impl Model for ModelMock {
608 type Trainer = TrainerMock;
609
610 fn tokenize(&self, _sequence: &str) -> Result<Vec<Token>> {
611 unimplemented!()
612 }
613 fn token_to_id(&self, token: &str) -> Option<u32> {
614 self.vocab.get(token).copied()
615 }
616 fn id_to_token(&self, id: u32) -> Option<String> {
617 self.vocab_r.get(&id).cloned()
618 }
619 fn get_vocab(&self) -> HashMap<String, u32> {
620 self.vocab.clone().into_iter().collect()
621 }
622 fn get_vocab_size(&self) -> usize {
623 self.vocab.len()
624 }
625 fn save(&self, _folder: &Path, _name: Option<&str>) -> Result<Vec<PathBuf>> {
626 unimplemented!()
627 }
628 fn get_trainer(&self) -> Self::Trainer {
629 TrainerMock
630 }
631 }
632
633 #[test]
634 fn can_add_tokens() {
635 let model = ModelMock::new(&[("test", 0), ("tost", 1)]);
636 let mut vocab = AddedVocabulary::new();
637 let normalizer: Option<&NormalizerWrapper> = None;
638
639 assert_eq!(
641 vocab.add_tokens(
642 &[AddedToken::from("added_token_1", false)],
643 &model,
644 normalizer
645 ),
646 1
647 );
648
649 let vocab_len: usize = vocab.len();
650 assert_eq!(vocab_len, 1);
651
652 assert_eq!(
654 vocab.add_tokens(
655 &[
656 AddedToken::from("added_token_2", false),
657 AddedToken::from("added_token_2", false)
658 ],
659 &model,
660 normalizer
661 ),
662 1
663 );
664 assert_eq!(vocab.len(), 2);
665
666 let added_token = AddedToken::from("test", false);
668 assert_eq!(
669 vocab.add_tokens(std::slice::from_ref(&added_token), &model, normalizer),
670 1
671 );
672 assert_eq!(vocab.len(), 3);
673
674 assert_eq!(vocab.get_added_tokens_decoder()[&0], added_token);
675 }
676
677 #[test]
678 fn can_add_special_tokens() {
679 let model = ModelMock::new(&[("test", 0), ("tost", 1)]);
680 let mut vocab = AddedVocabulary::new();
681 let normalizer: Option<&NormalizerWrapper> = None;
682 assert_eq!(
684 vocab.add_special_tokens(
685 &[AddedToken::from("added_token_1", true)],
686 &model,
687 normalizer
688 ),
689 1
690 );
691 assert_eq!(vocab.len(), 1);
692
693 assert_eq!(
695 vocab.add_special_tokens(
696 &[
697 AddedToken::from("added_token_2", true),
698 AddedToken::from("added_token_2", true)
699 ],
700 &model,
701 normalizer
702 ),
703 1
704 );
705 assert_eq!(vocab.len(), 2);
706
707 assert_eq!(
709 vocab.add_special_tokens(&[AddedToken::from("test", true)], &model, normalizer),
710 1
711 );
712 assert_eq!(vocab.len(), 3); assert!(vocab.is_special_token("test"));
714 assert_eq!(
715 *vocab.get_added_tokens_decoder(),
716 AHashMap::from([
717 (0, AddedToken::from("test", true)),
718 (2, AddedToken::from("added_token_1", true)),
719 (3, AddedToken::from("added_token_2", true)),
720 ])
721 );
722 assert!(vocab.added_tokens_map.contains_key("test"));
723 assert!(vocab.added_tokens_map_r.contains_key(&0));
724
725 vocab.add_tokens(
726 &[
727 AddedToken::from("tost", true),
728 AddedToken::from("another_two", false),
729 ],
730 &model,
731 normalizer,
732 );
733 assert_eq!(vocab.len(), 5); assert_eq!(vocab.get_vocab()["another_two"], 4); assert_eq!(
738 vocab.add_special_tokens(&[AddedToken::from("another_two", true)], &model, normalizer),
739 1
740 );
741 assert_eq!(vocab.len(), 5); assert_eq!(vocab.get_vocab()["another_two"], 4); let mut token: AddedToken = AddedToken::from("Hey", false);
746 token.content = "hey".to_string();
747 assert_eq!(token.content, "hey"); token.special = true;
750 assert!(token.special); }
752
753 #[test]
754 fn can_extract_added_tokens() {
755 let model = ModelMock::new(&[]);
757 let mut vocab = AddedVocabulary::new();
758 let normalizer: Option<&NormalizerWrapper> = None;
759
760 vocab.add_tokens(
761 &[
762 AddedToken::from("my", false),
763 AddedToken::from("name", false),
764 ],
765 &model,
766 normalizer,
767 );
768 vocab.add_special_tokens(
769 &[
770 AddedToken::from("[CLS]", true),
771 AddedToken::from("[SEP]", true),
772 ],
773 &model,
774 normalizer,
775 );
776
777 let result = vocab.extract_and_normalize(normalizer, "[CLS] My name is Anthony [SEP]");
778 assert_eq!(
779 result
780 .get_splits(OffsetReferential::Original, OffsetType::Byte)
781 .into_iter()
782 .map(|(s, _, tokens)| (
783 s,
784 tokens
785 .as_ref()
786 .map(|t| t.iter().map(|t| t.id).collect::<Vec<_>>())
787 ))
788 .collect::<Vec<_>>(),
789 vec![
790 ("[CLS]", Some(vec![2])),
791 (" My ", None),
792 ("name", Some(vec![1])),
793 (" is Anthony ", None),
794 ("[SEP]", Some(vec![3]))
795 ]
796 );
797 }
798
799 #[test]
800 fn options_use_cases() {
801 let model = ModelMock::new(&[]);
804 let normalizer = Lowercase;
805 let mut vocab = AddedVocabulary::new();
806
807 vocab.add_tokens(
808 &[
809 AddedToken::from("my", false).lstrip(true).rstrip(true),
810 AddedToken::from("name", false),
811 AddedToken::from("ony", false).single_word(true),
812 ],
813 &model,
814 Some(&normalizer),
815 );
816 vocab.add_special_tokens(
817 &[
818 AddedToken::from("[CLS]", true),
819 AddedToken::from("[SEP]", true),
820 ],
821 &model,
822 Some(&normalizer),
823 );
824
825 let result =
826 vocab.extract_and_normalize(Some(&normalizer), "[CLS] My name is Anthony [SEP]");
827
828 assert_eq!(
829 simplify_output(&result),
830 vec![
831 ("[CLS]", Some(vec![3])),
832 (" my ", Some(vec![0])),
835 ("name", Some(vec![1])),
836 (" is anthony ", None),
838 ("[SEP]", Some(vec![4])),
839 ]
840 );
841 }
842
843 #[test]
844 fn empty_matches() {
845 let vocab = AddedVocabulary::new();
846 let matches = vocab.find_matches("", &vocab.split_trie);
847 assert_eq!(matches, vec![(None, (0, 0))]);
848 }
849
850 #[test]
851 fn test_single_word_is_correct() {
852 let model = ModelMock::new(&[]);
855 let mut vocab = AddedVocabulary::new();
856 let normalizer = Lowercase;
857
858 vocab.add_tokens(
859 &[AddedToken::from("<mask>", false).single_word(true)],
860 &model,
861 Some(&normalizer),
862 );
863 let result = vocab.extract_and_normalize(
865 Some(&normalizer),
866 "<mask> My name <mask> A<mask> <mask>ony <mask>",
867 );
868 assert_eq!(
869 simplify_output(&result),
870 vec![
871 ("<mask>", Some(vec![0])),
872 (" my name ", None),
873 ("<mask>", Some(vec![0])),
874 (" a<mask> <mask>ony ", None),
875 ("<mask>", Some(vec![0]))
876 ]
877 );
878 }
879
880 #[test]
881 fn test_single_word_is_unicode_correct() {
882 let model = ModelMock::new(&[]);
883 let mut vocab = AddedVocabulary::new();
884 let normalizer = Lowercase;
885
886 assert_eq!(vocab.len(), 0);
887
888 vocab.add_tokens(
889 &[AddedToken::from("<mask>", false).single_word(true)],
890 &model,
891 Some(&normalizer),
892 );
893 let result = vocab.extract_and_normalize(Some(&normalizer), "<mask>, <mask>- ◌̰<mask>");
894 assert_eq!(
895 simplify_output(&result),
896 vec![
897 ("<mask>", Some(vec![0])),
899 (", ", None),
900 ("<mask>", Some(vec![0])),
902 ("- ◌̰<mask>", None),
904 ]
905 );
906 }
907
908 #[test]
909 fn test_lstrip_unicode_space() {
910 let model = ModelMock::new(&[]);
911 let mut vocab = AddedVocabulary::new();
912 let normalizer = Lowercase;
913
914 vocab.add_tokens(
915 &[AddedToken::from("<mask>", false)
916 .lstrip(true)
917 .rstrip(true)
918 .single_word(true)],
919 &model,
920 Some(&normalizer),
921 );
922 let result = vocab
923 .extract_and_normalize(Some(&normalizer), "Hi <mask> there\t<mask>\t<mask>\u{2000}");
924 assert_eq!(
925 simplify_output(&result),
926 vec![
927 ("hi", None),
928 (" <mask> ", Some(vec![0])),
930 ("there", None),
931 ("\t<mask>\t", Some(vec![0])),
933 ("<mask>\u{2000}", Some(vec![0])),
936 ]
937 );
938 }
939
940 #[test]
941 fn test_encode_special_tokens() {
942 let model = ModelMock::new(&[]);
943 let mut vocab = AddedVocabulary::new();
944 let normalizer = Lowercase;
945
946 vocab.add_tokens(
947 &[
948 AddedToken::from("<mask>", true)
949 .lstrip(true)
950 .rstrip(true)
951 .single_word(true),
952 AddedToken::from("ask>", false),
953 AddedToken::from("<pad>", true),
954 ],
955 &model,
956 Some(&normalizer),
957 );
958 vocab.set_encode_special_tokens(true);
959
960 let result = vocab.extract_and_normalize(
961 Some(&normalizer),
962 "Hi <mask> there\t<mask>\t<mask>\u{2000} <pad> <mask><pad><pad>",
963 );
964
965 assert_eq!(
966 simplify_output(&result),
967 vec![
968 ("hi <m", None),
969 ("ask>", Some(vec![1])),
970 (" there\t<m", None),
971 ("ask>", Some(vec![1])),
972 ("\t<m", None),
973 ("ask>", Some(vec![1])),
974 ("\u{2000} <pad> <m", None),
975 ("ask>", Some(vec![1])),
976 ("<pad><pad>", None)
977 ]
978 );
979
980 vocab.set_encode_special_tokens(false);
981
982 let result = vocab.extract_and_normalize(
983 Some(&normalizer),
984 "Hi <mask> there\t<mask>\t<mask>\u{2000} <pad> <mask><pad><pad>",
985 );
986 assert_eq!(
987 simplify_output(&result),
988 vec![
989 ("hi", None),
990 (" <mask> ", Some(vec![0])),
991 ("there", None),
992 ("\t<mask>\t", Some(vec![0])),
993 ("<mask>\u{2000} ", Some(vec![0])),
994 ("<pad>", Some(vec![2])),
995 (" <mask>", Some(vec![0])),
996 ("<pad>", Some(vec![2])),
997 ("<pad>", Some(vec![2]))
998 ]
999 );
1000 }
1001 #[test]
1002 fn byte_level_normalizer() {
1003 let model = ModelMock::new(&[]);
1005 let mut vocab = AddedVocabulary::new();
1006 let from = NormalizerWrapper::from(ByteLevelNormalizer::new());
1007 let normalizer: Option<&NormalizerWrapper> = Some(&from);
1008
1009 vocab.add_tokens(
1010 &[AddedToken::from("my", false), AddedToken::from("今", false)],
1011 &model,
1012 normalizer,
1013 );
1014 let result = vocab.extract_and_normalize(normalizer, "my今");
1015 assert_eq!(
1016 result
1017 .get_splits(OffsetReferential::Original, OffsetType::Byte)
1018 .into_iter()
1019 .map(|(s, _, tokens)| (
1020 s,
1021 tokens
1022 .as_ref()
1023 .map(|t| t.iter().map(|t| t.id).collect::<Vec<_>>())
1024 ))
1025 .collect::<Vec<_>>(),
1026 vec![("my", Some(vec![0])), ("ä»Ĭ", Some(vec![1])),]
1027 );
1028 }
1029}