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 fn vocab_size(&self) -> Option<usize> {
209 None
210 }
211
212 fn token_to_id(&self, _token: &str) -> Result<Option<TokenIdType>> {
222 Err(Error::msg(
223 "tokenizer backend does not support token lookup",
224 ))
225 }
226
227 fn special_token_ids(&self) -> Result<Vec<TokenIdType>> {
237 Err(Error::msg(
238 "tokenizer backend does not support special token enumeration",
239 ))
240 }
241
242 fn num_special_tokens_added(&self) -> Result<usize> {
253 Err(Error::msg(
254 "tokenizer backend does not support special token accounting",
255 ))
256 }
257 }
259}
260
261pub fn file_json_field<T: serde::de::DeserializeOwned>(
262 json_file_path: &Path,
263 field_name: &str,
264) -> anyhow::Result<T> {
265 let file = File::open(json_file_path)
266 .with_context(|| format!("Failed to open file: {:?}", json_file_path))?;
267 let reader = BufReader::new(file);
268
269 let json_data: serde_json::Value = serde_json::from_reader(reader)
270 .with_context(|| format!("Failed to parse JSON from file: {:?}", json_file_path))?;
271
272 let map = json_data.as_object().ok_or_else(|| {
273 anyhow::anyhow!("JSON root is not an object in file: {:?}", json_file_path)
274 })?;
275
276 let field_value = map.get(field_name).ok_or_else(|| {
277 anyhow::anyhow!(
278 "Field '{}' not found in JSON file: {:?}",
279 field_name,
280 json_file_path
281 )
282 })?;
283
284 serde_json::from_value(field_value.clone()).with_context(|| {
285 format!(
286 "Failed to deserialize field '{}' (value: {:?}) to the expected type from file: {:?}",
287 field_name, field_value, json_file_path
288 )
289 })
290}
291
292pub fn log_json_err(filename: &str, json: &str, err: &serde_json::Error) {
293 const ERROR_PREFIX: &str = ">> ";
294
295 if !(err.is_syntax() || err.is_data()) {
296 return;
297 }
298
299 let line = err.line().saturating_sub(1);
300 let column = err.column().saturating_sub(1);
301
302 let json_lines: Vec<&str> = json.lines().collect();
303 if json_lines.is_empty() {
304 tracing::error!("JSON parsing error in {filename}: File is empty.");
305 return;
306 }
307
308 let start_index = line.saturating_sub(2);
309 let end_index = line.saturating_add(3).min(json_lines.len());
310
311 let mut context_lines: Vec<String> = (start_index..end_index)
312 .map(|i| {
313 if i == line {
314 format!("{ERROR_PREFIX}{}", json_lines[i])
315 } else {
316 format!("{:06} {}", i + 1, json_lines[i])
317 }
318 })
319 .collect();
320
321 let col_indicator = "_".to_string().repeat(column + ERROR_PREFIX.len()) + "^";
322 let error_in_context_idx = line - start_index;
323 if error_in_context_idx < context_lines.len() {
324 context_lines.insert(error_in_context_idx + 1, col_indicator);
325 }
326
327 tracing::error!(
328 "JSON parsing error in {filename}: Line {}, column {}:\n{}",
329 err.line(),
330 err.column(),
331 context_lines.join("\n")
332 );
333}
334
335impl Encoding {
336 pub fn get_hash(&self) -> u64 {
337 let mut hasher = DefaultHasher::new();
338 self.hash(&mut hasher);
339 hasher.finish()
340 }
341}
342
343#[derive(Debug, Clone, Copy, Default)]
347pub struct TokenizerOptions {
348 pub add_special_tokens: bool,
355}
356
357#[derive(Clone)]
359pub struct Tokenizer(Arc<dyn traits::Tokenizer>);
360
361impl Tokenizer {
362 pub fn from_file(file_path: &str) -> Result<Tokenizer> {
363 Ok(Tokenizer(create_tokenizer_from_file(file_path)?))
364 }
365
366 pub fn from_file_with_options(file_path: &str, options: TokenizerOptions) -> Result<Tokenizer> {
367 Ok(Tokenizer(create_tokenizer_from_file_with_options(
368 file_path, options,
369 )?))
370 }
371
372 pub fn decode_stream(
374 &self,
375 prompt_token_ids: &[TokenIdType],
376 skip_special_tokens: bool,
377 ) -> DecodeStream {
378 DecodeStream::new(self.0.clone(), prompt_token_ids, skip_special_tokens)
379 }
380}
381
382impl Deref for Tokenizer {
383 type Target = Arc<dyn traits::Tokenizer>;
384
385 fn deref(&self) -> &Self::Target {
386 &self.0
387 }
388}
389
390impl From<Arc<dyn traits::Tokenizer>> for Tokenizer {
391 fn from(tokenizer: Arc<dyn traits::Tokenizer>) -> Self {
392 Tokenizer(tokenizer)
393 }
394}
395
396impl<T> From<Arc<T>> for Tokenizer
397where
398 T: traits::Tokenizer + 'static, {
400 fn from(tokenizer: Arc<T>) -> Self {
401 Tokenizer(tokenizer)
402 }
403}
404
405pub fn create_tokenizer_from_file(file_path: &str) -> Result<Arc<dyn traits::Tokenizer>> {
412 create_tokenizer_from_file_with_options(file_path, Default::default())
413}
414
415pub fn create_tokenizer_from_file_with_options(
422 file_path: &str,
423 options: TokenizerOptions,
424) -> Result<Arc<dyn traits::Tokenizer>> {
425 use traits::Tokenizer as _;
426
427 let path = Path::new(file_path);
428 let extension = path
429 .extension()
430 .and_then(std::ffi::OsStr::to_str)
431 .ok_or_else(|| Error::msg("Failed to read file extension".to_string()))?;
432
433 match extension {
434 "json" => {
435 let tokenizer = HuggingFaceTokenizer::from_file(file_path)?.with_options(options);
436 Ok(Arc::new(tokenizer))
437 }
438 "model" | "tiktoken" => {
439 let tokenizer = TikTokenTokenizer::from_file_auto(file_path)?.with_options(options);
440 Ok(Arc::new(tokenizer))
441 }
442 _ => Err(Error::msg(format!(
443 "Unsupported tokenizer file type: .{extension}"
444 ))),
445 }
446}
447
448const INITIAL_INCREMENTAL_DETOKENIZATION_OFFSET: usize = 5;
454
455pub struct DecodeStream {
461 tokenizer: Arc<dyn traits::Tokenizer>,
463
464 skip_special_tokens: bool,
465 all_token_ids: Vec<u32>,
477
478 prefix_offset: usize,
479
480 read_offset: usize,
481
482 has_emitted: bool,
484}
485
486impl DecodeStream {
487 pub fn new(
488 tokenizer: Arc<dyn traits::Tokenizer>,
489 prompt_token_ids: &[TokenIdType],
490 skip_special_tokens: bool,
491 ) -> Self {
492 let num_input_tokens = prompt_token_ids.len();
493 let prompt_token_ids = prompt_token_ids.to_vec();
494 Self {
495 tokenizer,
496 skip_special_tokens,
497 all_token_ids: prompt_token_ids,
498 prefix_offset: num_input_tokens
499 .saturating_sub(INITIAL_INCREMENTAL_DETOKENIZATION_OFFSET),
500 read_offset: num_input_tokens,
501 has_emitted: false,
502 }
503 }
504
505 pub fn step(&mut self, id: u32) -> Result<Option<String>> {
518 self.all_token_ids.push(id);
519
520 let prefix_text: String = self
521 .tokenizer
522 .decode(
523 &self.all_token_ids[self.prefix_offset..self.read_offset],
524 self.skip_special_tokens,
525 )?
526 .into();
527
528 let new_result = self.tokenizer.decode(
529 &self.all_token_ids[self.prefix_offset..],
530 self.skip_special_tokens,
531 )?;
532
533 let new_text = new_result.as_str();
534 let is_partial = new_result.is_partial();
535
536 if self.has_emitted && !is_partial && !new_text.starts_with(prefix_text.as_str()) {
539 return Err(Error::msg(
540 "incremental decoding rewrote already emitted text",
541 ));
542 }
543
544 if new_text.len() > prefix_text.len() && !is_partial {
545 let requested_split = prefix_text.len();
546 let split = if self.has_emitted {
547 requested_split
550 } else {
551 new_text.floor_char_boundary(requested_split)
554 };
555
556 let emitted = new_text[split..].to_string();
557
558 self.prefix_offset = self.read_offset;
559 self.read_offset = self.all_token_ids.len();
560 self.has_emitted = true;
561
562 Ok(Some(emitted))
563 } else {
564 Ok(None)
565 }
566 }
567}
568
569#[cfg(test)]
570mod decode_stream_unicode_tests {
571 use super::{DecodeResult, DecodeStream, Encoding, Result, TokenIdType};
572 use std::sync::Arc;
573
574 struct RewritingTokenizer {
575 prefix_text: &'static str,
576 rewritten_text: &'static str,
577 prefix_is_partial: bool,
578 }
579
580 impl super::traits::Encoder for RewritingTokenizer {
581 fn encode(&self, _input: &str) -> Result<Encoding> {
582 Ok(Encoding::Sp(vec![]))
583 }
584
585 fn encode_batch(&self, _inputs: &[&str]) -> Result<Vec<Encoding>> {
586 Ok(vec![])
587 }
588 }
589
590 impl super::traits::Decoder for RewritingTokenizer {
591 fn decode(
592 &self,
593 token_ids: &[TokenIdType],
594 _skip_special_tokens: bool,
595 ) -> Result<DecodeResult> {
596 let result = match token_ids.len() {
597 1 if self.prefix_is_partial => DecodeResult::Partial(self.prefix_text.to_string()),
598 1 => DecodeResult::Complete(self.prefix_text.to_string()),
599 2 => DecodeResult::Complete(self.rewritten_text.to_string()),
600 _ => DecodeResult::Complete(String::new()),
601 };
602 Ok(result)
603 }
604 }
605
606 impl super::traits::Tokenizer for RewritingTokenizer {}
607
608 #[test]
609 fn allows_boundary_recovery_before_generated_text_is_emitted() {
610 for (prefix_text, rewritten_text, expected) in [
611 ("㺄馉凓鄗\u{FFFD}", "㺄馉凓鄗𫷲", "𫷲"),
612 (
613 "JUnitworkflow Completion intuition\u{FFFD}",
614 "JUnitworkflow Completion intuition𝟙",
615 "𝟙",
616 ),
617 ] {
618 let tokenizer: Arc<dyn super::traits::Tokenizer> = Arc::new(RewritingTokenizer {
619 prefix_text,
620 rewritten_text,
621 prefix_is_partial: true,
622 });
623 let mut stream = DecodeStream::new(tokenizer, &[1], false);
624
625 assert_eq!(stream.step(2).unwrap(), Some(expected.to_string()));
626 }
627 }
628
629 #[test]
630 fn errors_when_incremental_decode_rewrites_emitted_text() {
631 for (prefix_text, rewritten_text) in [
632 ("abcde", "abcdXY"),
633 ("abcde", "abcdX"),
634 ("abcde", "abcd"),
635 ("㺄馉凓鄗abc", "㺄馉凓鄗𫷲"),
636 (
637 "JUnitworkflow Completion intuitionabc",
638 "JUnitworkflow Completion intuition𝟙",
639 ),
640 ] {
641 let tokenizer: Arc<dyn super::traits::Tokenizer> = Arc::new(RewritingTokenizer {
642 prefix_text,
643 rewritten_text,
644 prefix_is_partial: false,
645 });
646 let mut stream = DecodeStream::new(tokenizer, &[], false);
647
648 assert_eq!(stream.step(1).unwrap(), Some(prefix_text.to_string()));
649
650 let error = stream.step(2).unwrap_err();
651 assert!(error.to_string().contains("already emitted text"));
652 }
653 }
654
655 #[test]
656 fn emits_suffix_when_incremental_decode_preserves_emitted_text() {
657 let tokenizer: Arc<dyn super::traits::Tokenizer> = Arc::new(RewritingTokenizer {
658 prefix_text: "abcde",
659 rewritten_text: "abcdeXY",
660 prefix_is_partial: false,
661 });
662 let mut stream = DecodeStream::new(tokenizer, &[], false);
663
664 assert_eq!(stream.step(1).unwrap(), Some("abcde".to_string()));
665 assert_eq!(stream.step(2).unwrap(), Some("XY".to_string()));
666 }
667}
668
669pub struct Sequence {
671 tokenizer: Tokenizer,
673
674 token_ids: Vec<TokenIdType>,
676
677 prefix_offset: usize,
679
680 read_offset: usize,
682}
683
684impl std::fmt::Debug for Sequence {
685 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
686 f.debug_struct("Sequence")
687 .field("tokenizer", &"Arc<dyn Tokenizer>")
688 .field(
689 "token_ids",
690 &format_args!("{}", {
691 let token_ids = self.token_ids();
692 if token_ids.len() <= 20 {
693 format!("{:?}", token_ids)
694 } else {
695 let first_ten = &token_ids[..10];
696 let last_ten = &token_ids[token_ids.len() - 10..];
697 format!("{:?} ... {:?}", first_ten, last_ten)
698 }
699 }),
700 )
701 .field("prefix_offset", &self.prefix_offset)
702 .field("read_offset", &self.read_offset)
703 .field("token count", &self.token_ids.len())
704 .finish()
705 }
706}
707
708impl Sequence {
709 pub fn new(tokenizer: Tokenizer) -> Self {
710 Self {
711 tokenizer,
712 token_ids: Vec::new(),
713 prefix_offset: 0,
714 read_offset: 0,
715 }
716 }
717
718 pub fn is_empty(&self) -> bool {
719 self.token_ids.is_empty()
720 }
721
722 pub fn len(&self) -> usize {
723 self.token_ids.len()
724 }
725
726 pub fn clear(&mut self) {
727 self.token_ids.clear();
728 self.prefix_offset = 0;
729 self.read_offset = 0;
730 }
731
732 pub fn append_text(&mut self, input: &str) -> Result<()> {
733 let encoding = self.tokenizer.encode(input)?;
738 self.token_ids.extend(encoding.token_ids());
739 Ok(())
740 }
741
742 pub fn append_token_id(&mut self, token_id: TokenIdType) -> Result<String> {
746 self.token_ids.push(token_id);
747 let prefix_text: String = self
750 .tokenizer
751 .decode(&self.token_ids[self.prefix_offset..self.read_offset], false)?
752 .into();
753
754 let new_result = self
755 .tokenizer
756 .decode(&self.token_ids[self.prefix_offset..], false)?;
757
758 let new_text = new_result.as_str();
759
760 let mut prefix_text_len = prefix_text.len();
764 while !new_text.is_char_boundary(prefix_text_len) && prefix_text_len > 0 {
765 prefix_text_len -= 1;
766 }
767 let prefix_text_len = prefix_text_len;
768
769 if new_text.len() > prefix_text.len() {
770 if new_result.is_partial() {
771 return Ok("".to_string());
772 } else {
773 let new_text = new_text[prefix_text_len..]
775 .to_string()
776 .replace('\u{FFFD}', "");
777 self.prefix_offset = self.read_offset;
778 self.read_offset = self.token_ids.len();
779 return Ok(new_text);
780 }
781 }
782
783 Ok("".to_string())
784 }
785
786 pub fn tokenizer(&self) -> Tokenizer {
787 self.tokenizer.clone()
788 }
789
790 pub fn token_ids(&self) -> &[TokenIdType] {
791 &self.token_ids
792 }
793
794 pub fn text(&self) -> Result<String> {
795 Ok(self.tokenizer.decode(&self.token_ids, false)?.into())
799 }
800}
801
802pub enum SequenceDecoderOutput {
805 Text(String),
807
808 Held,
811
812 Stopped,
815
816 StoppedWithText(String),
820}
821
822#[derive(Debug)]
828pub struct StopSequenceDecoder {
829 sequence: Sequence,
831
832 stop_token_ids_visible: Vec<TokenIdType>,
835
836 stop_token_ids_hidden: Vec<TokenIdType>,
839
840 #[allow(dead_code)]
843 stop_sequences_visible: Vec<String>,
844
845 stop_sequences_hidden: Vec<String>,
848
849 stopped: bool,
852
853 state: String,
856}
857
858impl StopSequenceDecoder {
859 pub fn builder(tokenizer: Tokenizer) -> StopSequenceDecoderBuilder {
861 StopSequenceDecoderBuilder::new(tokenizer)
862 }
863
864 pub fn append_token_id(&mut self, token_id: TokenIdType) -> Result<SequenceDecoderOutput> {
866 if self.stopped {
867 return Err(Error::msg("Decoder is stopped"));
868 }
869
870 let text = self.sequence.append_token_id(token_id)?;
872
873 self.state.push_str(text.as_str());
875
876 let mut stop: bool = false;
877 let mut visible: bool = false;
878
879 if self.stop_token_ids_visible.contains(&token_id) {
880 stop = true;
881 visible = true;
882 }
883
884 if self.stop_token_ids_hidden.contains(&token_id) {
885 stop = true;
886 visible = false;
887 }
888
889 if stop {
890 self.stopped = true;
891 let state = std::mem::take(&mut self.state);
892 if visible {
893 return Ok(SequenceDecoderOutput::StoppedWithText(state));
894 }
895 return Ok(SequenceDecoderOutput::Stopped);
896 }
897
898 for stop_sequence in self.stop_sequences_hidden.iter() {
900 if stop_sequence.starts_with(&self.state) {
901 if stop_sequence == &self.state {
902 self.stopped = true;
904 return Ok(SequenceDecoderOutput::Stopped);
905 } else {
906 return Ok(SequenceDecoderOutput::Held);
907 }
908 }
909 }
910
911 let state = std::mem::take(&mut self.state);
912 Ok(SequenceDecoderOutput::Text(state))
913 }
914
915 pub fn is_empty(&self) -> bool {
916 self.sequence.token_ids.is_empty()
917 }
918
919 pub fn len(&self) -> usize {
920 self.sequence.token_ids.len()
921 }
922
923 pub fn is_complete(&self) -> bool {
924 self.stopped
925 }
926
927 pub fn close(&mut self) {
928 self.stopped = true;
929 }
930}
931
932pub struct StopSequenceDecoderBuilder {
933 tokenizer: Tokenizer,
934 stop_token_ids_visible: Vec<TokenIdType>,
935 stop_token_ids_hidden: Vec<TokenIdType>,
936 stop_sequences_visible: Vec<String>,
937 stop_sequences_hidden: Vec<String>,
938}
939
940impl StopSequenceDecoderBuilder {
941 pub fn new(tokenizer: Tokenizer) -> Self {
942 Self {
943 tokenizer,
944 stop_token_ids_visible: Vec::new(),
945 stop_token_ids_hidden: Vec::new(),
946 stop_sequences_visible: Vec::new(),
947 stop_sequences_hidden: Vec::new(),
948 }
949 }
950
951 pub fn add_stop_token_id_visible(mut self, token_id: TokenIdType) -> Self {
953 self.stop_token_ids_visible.push(token_id);
954 self
955 }
956
957 pub fn add_stop_token_ids_visible(mut self, token_ids: &[TokenIdType]) -> Self {
960 self.stop_token_ids_visible.extend(token_ids);
961 self
962 }
963
964 pub fn add_stop_token_id_hidden(mut self, token_id: TokenIdType) -> Self {
966 self.stop_token_ids_hidden.push(token_id);
967 self
968 }
969
970 pub fn add_stop_token_ids_hidden(mut self, token_ids: &[TokenIdType]) -> Self {
973 self.stop_token_ids_hidden.extend(token_ids);
974 self
975 }
976
977 pub fn add_stop_sequence_visible(mut self, text: &str) -> Self {
978 self.stop_sequences_visible.push(text.to_string());
979 self
980 }
981
982 pub fn add_stop_sequences_visible(mut self, strings: &[&str]) -> Self {
983 self.stop_sequences_visible
984 .extend(strings.iter().map(|text| text.to_string()));
985 self
986 }
987
988 pub fn add_stop_sequence_hidden(mut self, text: &str) -> Self {
989 self.stop_sequences_hidden.push(text.to_string());
990 self
991 }
992
993 pub fn add_stop_sequences_hidden(mut self, strings: &[&str]) -> Self {
994 self.stop_sequences_hidden
995 .extend(strings.iter().map(|text| text.to_string()));
996 self
997 }
998
999 pub fn build(self) -> Result<StopSequenceDecoder> {
1000 Ok(StopSequenceDecoder {
1001 sequence: Sequence::new(self.tokenizer.clone()),
1002 stop_token_ids_visible: self.stop_token_ids_visible,
1003 stop_token_ids_hidden: self.stop_token_ids_hidden,
1004 stop_sequences_visible: self.stop_sequences_visible,
1005 stop_sequences_hidden: self.stop_sequences_hidden,
1006 stopped: false,
1007 state: String::new(),
1008 })
1009 }
1010}