1use crate::error::Result;
14use crate::json_compat::dumps_unicode;
15use crate::question::Question;
16use crate::tokenizer::Tokenizer;
17use serde_json::Value;
18
19const MAX_OPTION_TOKENS: usize = 48;
21const MIN_HEAD_TOKENS: usize = 8;
23const MIN_OPTION_BUDGET: usize = 16;
25
26#[derive(Clone, Debug, PartialEq, Eq)]
28pub struct Prefix {
29 pub ids: Vec<u32>,
30 pub markers: Vec<usize>,
31}
32
33#[derive(Clone, Debug, PartialEq, Eq)]
35pub struct Sequence {
36 pub ids: Vec<u32>,
37 pub markers: Vec<usize>,
38 pub truncated: bool,
44}
45
46pub fn serialize_state(state: &Value) -> String {
49 match state {
50 Value::String(text) => text.clone(),
51 other => dumps_unicode(other),
52 }
53}
54
55pub fn build_prefix(
57 tokenizer: &Tokenizer,
58 question: &Question,
59 head_max_len: usize,
60) -> Result<Prefix> {
61 let mask = tokenizer.mask_token.as_str();
62 let options = question.render_options();
63
64 let instructions = question.instructions.replace(mask, " ");
66 let head_text = format!("{} question: {}", question.kind.name(), instructions);
67 let mut head_ids = tokenizer.encode(&head_text)?;
68
69 let mut option_ids: Vec<Vec<u32>> = Vec::with_capacity(options.len());
70 for option in &options {
71 let text = format!(" {}", option.replace(mask, " "));
72 let mut ids = tokenizer.encode(&text)?;
73 ids.truncate(MAX_OPTION_TOKENS);
74 let mut with_marker = Vec::with_capacity(ids.len() + 1);
75 with_marker.push(tokenizer.mask_token_id);
76 with_marker.extend_from_slice(&ids);
77 option_ids.push(with_marker);
78 }
79
80 let used: usize = option_ids.iter().map(Vec::len).sum();
81 let mut option_budget = head_max_len.saturating_sub(used);
84 if option_budget < MIN_OPTION_BUDGET {
85 let per = std::cmp::max(
86 4,
87 head_max_len.saturating_sub(MIN_OPTION_BUDGET) / std::cmp::max(1, option_ids.len()),
88 );
89 for ids in &mut option_ids {
90 ids.truncate(per);
91 }
92 let used: usize = option_ids.iter().map(Vec::len).sum();
93 option_budget = head_max_len.saturating_sub(used);
94 }
95 head_ids.truncate(std::cmp::max(MIN_HEAD_TOKENS, option_budget));
96
97 let mut ids = Vec::with_capacity(head_ids.len() + used + 3);
98 ids.push(tokenizer.cls_token_id);
99 ids.extend_from_slice(&head_ids);
100 ids.push(tokenizer.sep_token_id);
101
102 let mut markers = Vec::with_capacity(option_ids.len());
103 for option in &option_ids {
104 markers.push(ids.len());
105 ids.extend_from_slice(option);
106 }
107 ids.push(tokenizer.sep_token_id);
108
109 Ok(Prefix { ids, markers })
110}
111
112pub fn extend_with_state(
114 tokenizer: &Tokenizer,
115 prefix: &Prefix,
116 state_ids: &[u32],
117 max_len: usize,
118 truncate_left: bool,
119) -> Sequence {
120 let room = max_len.saturating_sub(prefix.ids.len() + 1);
121 let state = if truncate_left {
122 if room == 0 {
126 state_ids
127 } else {
128 &state_ids[state_ids.len().saturating_sub(room)..]
129 }
130 } else {
131 &state_ids[..std::cmp::min(room, state_ids.len())]
132 };
133
134 let mut ids = Vec::with_capacity(prefix.ids.len() + state.len() + 1);
135 ids.extend_from_slice(&prefix.ids);
136 ids.extend_from_slice(state);
137 ids.push(tokenizer.sep_token_id);
138
139 let truncated = state.len() < state_ids.len() || ids.len() > max_len;
142 ids.truncate(max_len);
143
144 let markers = prefix
145 .markers
146 .iter()
147 .copied()
148 .filter(|marker| *marker < max_len)
149 .collect();
150
151 Sequence {
152 ids,
153 markers,
154 truncated,
155 }
156}
157
158pub fn build_sequence(
160 tokenizer: &Tokenizer,
161 state: &Value,
162 question: &Question,
163 max_len: usize,
164 head_max_len: usize,
165) -> Result<Sequence> {
166 let prefix = build_prefix(tokenizer, question, head_max_len)?;
167 let state_text = serialize_state(state).replace(tokenizer.mask_token.as_str(), " ");
168 let state_ids = tokenizer.encode(&state_text)?;
169 Ok(extend_with_state(
170 tokenizer, &prefix, &state_ids, max_len, false,
171 ))
172}