use rustc_hash::FxHashMap;
use serde_json::Value;
use super::super::normalizer::NormOp;
use super::super::precompiled::Precompiled;
use super::super::tokenizer::{GPT2_PATTERN, SENTENCEPIECE_PATTERN};
use super::HfJsonError;
#[derive(Debug, Clone)]
pub(super) struct PreTokenization {
pub byte_level: bool,
pub pattern: String,
pub add_prefix_space: bool,
pub anchored: bool,
pub unknown: Vec<String>,
}
pub(super) fn parse_pre_tokenizer(pre: Option<&Value>) -> PreTokenization {
let mut byte_level = false;
let mut split_regex: Option<String> = None;
let mut metaspace = false;
let mut add_prefix_space: Option<bool> = None;
let mut unknown: Vec<String> = Vec::new();
fn walk(
v: &Value,
byte_level: &mut bool,
split_regex: &mut Option<String>,
metaspace: &mut bool,
add_prefix_space: &mut Option<bool>,
unknown: &mut Vec<String>,
) {
match v.get("type").and_then(Value::as_str) {
Some("ByteLevel") => {
*byte_level = true;
if let Some(b) = v.get("add_prefix_space").and_then(Value::as_bool) {
*add_prefix_space = Some(b);
}
}
Some("Metaspace") => {
*metaspace = true;
if let Some(scheme) = v.get("prepend_scheme").and_then(Value::as_str) {
*add_prefix_space = Some(scheme != "never");
} else if let Some(b) = v.get("add_prefix_space").and_then(Value::as_bool) {
*add_prefix_space = Some(b);
}
}
Some("Split") if split_regex.is_none() => {
if let Some(re) = v.get("pattern").and_then(|p| {
p.get("Regex")
.and_then(Value::as_str)
.or_else(|| p.get("String").and_then(Value::as_str))
}) {
*split_regex = Some(re.to_string());
}
}
Some("Split") | Some("Whitespace") | Some("WhitespaceSplit") => {}
Some("Sequence") => {
if let Some(list) = v.get("pretokenizers").and_then(Value::as_array) {
for item in list {
walk(
item,
byte_level,
split_regex,
metaspace,
add_prefix_space,
unknown,
);
}
}
}
Some(other) => unknown.push(other.to_string()),
None => {}
}
}
if let Some(pre) = pre {
walk(
pre,
&mut byte_level,
&mut split_regex,
&mut metaspace,
&mut add_prefix_space,
&mut unknown,
);
}
let pattern = match (&split_regex, metaspace) {
(Some(re), _) => re.clone(),
(None, true) => SENTENCEPIECE_PATTERN.to_string(),
(None, false) => GPT2_PATTERN.to_string(),
};
PreTokenization {
byte_level,
pattern,
add_prefix_space: add_prefix_space.unwrap_or(metaspace || byte_level),
anchored: byte_level || metaspace || split_regex.is_some(),
unknown,
}
}
#[derive(Debug, Clone)]
pub(super) struct BertNorm {
pub lowercase: bool,
pub handle_chinese_chars: bool,
pub clean_text: bool,
}
pub(super) fn parse_bert_norm(norm: Option<&Value>) -> BertNorm {
let mut lowercase = false;
let mut handle_chinese_chars = true;
let mut clean_text = true;
fn walk(v: &Value, lc: &mut bool, cjk: &mut bool, clean: &mut bool) {
match v.get("type").and_then(Value::as_str) {
Some("Lowercase") => *lc = true,
Some("BertNormalizer") => {
if v.get("lowercase").and_then(Value::as_bool).unwrap_or(false) {
*lc = true;
}
*cjk = v
.get("handle_chinese_chars")
.and_then(Value::as_bool)
.unwrap_or(true);
*clean = v.get("clean_text").and_then(Value::as_bool).unwrap_or(true);
}
Some("Sequence") => {
if let Some(list) = v.get("normalizers").and_then(Value::as_array) {
for item in list {
walk(item, lc, cjk, clean);
}
}
}
_ => {}
}
}
if let Some(norm) = norm {
walk(
norm,
&mut lowercase,
&mut handle_chinese_chars,
&mut clean_text,
);
}
BertNorm {
lowercase,
handle_chinese_chars,
clean_text,
}
}
pub(super) fn parse_norm_ops(norm: Option<&Value>) -> Result<Vec<NormOp>, HfJsonError> {
let mut ops = Vec::new();
let mut unknown: Vec<String> = Vec::new();
let mut bad_regex: Vec<String> = Vec::new();
fn walk(
v: &Value,
ops: &mut Vec<NormOp>,
unknown: &mut Vec<String>,
bad_regex: &mut Vec<String>,
) {
match v.get("type").and_then(Value::as_str) {
Some("NFC") => ops.push(NormOp::Nfc),
Some("NFD") => ops.push(NormOp::Nfd),
Some("NFKC") => ops.push(NormOp::Nfkc),
Some("NFKD") => ops.push(NormOp::Nfkd),
Some("Lowercase") => ops.push(NormOp::Lowercase),
Some("StripAccents") => ops.push(NormOp::StripAccents),
Some("Nmt") => ops.push(NormOp::Nmt),
Some("Prepend") => {
if let Some(p) = v.get("prepend").and_then(Value::as_str) {
ops.push(NormOp::Prepend(p.to_string()));
}
}
Some("Strip") => ops.push(NormOp::Strip {
left: v.get("strip_left").and_then(Value::as_bool).unwrap_or(true),
right: v
.get("strip_right")
.and_then(Value::as_bool)
.unwrap_or(true),
}),
Some("Replace") => {
let content = v
.get("content")
.and_then(Value::as_str)
.unwrap_or("")
.to_string();
if let Some(p) = v.get("pattern") {
if let Some(s) = p.get("String").and_then(Value::as_str) {
ops.push(NormOp::ReplaceStr {
from: s.to_string(),
to: content,
});
} else if let Some(re) = p.get("Regex").and_then(Value::as_str) {
match NormOp::replace_regex(re, content) {
Some(op) => ops.push(op),
None => bad_regex.push(re.to_string()),
}
}
}
}
Some("Precompiled") => {
if let Some(b64) = v.get("precompiled_charsmap").and_then(Value::as_str) {
use base64::Engine;
if let Ok(bytes) = base64::engine::general_purpose::STANDARD.decode(b64) {
if let Some(pc) = Precompiled::from_bytes(&bytes) {
ops.push(NormOp::Precompiled(pc));
}
}
}
}
Some("BertNormalizer") => {
let lc = v.get("lowercase").and_then(Value::as_bool).unwrap_or(false);
let strip = match v.get("strip_accents").and_then(Value::as_bool) {
Some(b) => b,
None => lc,
};
if strip {
ops.push(NormOp::Nfd);
ops.push(NormOp::StripAccents);
}
if lc {
ops.push(NormOp::Lowercase);
}
}
Some("Sequence") => {
if let Some(list) = v.get("normalizers").and_then(Value::as_array) {
for item in list {
walk(item, ops, unknown, bad_regex);
}
}
}
Some(other) => unknown.push(other.to_string()),
None => {}
}
}
if let Some(norm) = norm {
walk(norm, &mut ops, &mut unknown, &mut bad_regex);
}
if !unknown.is_empty() {
return Err(HfJsonError::UnsupportedNormalizer(unknown.join(", ")));
}
if !bad_regex.is_empty() {
return Err(HfJsonError::InvalidNormalizerRegex(bad_regex.join(", ")));
}
Ok(ops)
}
pub(super) fn parse_special_tokens(root: &Value) -> FxHashMap<String, u32> {
let mut specials = FxHashMap::default();
if let Some(list) = root.get("added_tokens").and_then(Value::as_array) {
for t in list {
if let (Some(content), Some(id)) = (
t.get("content").and_then(Value::as_str),
t.get("id").and_then(Value::as_u64),
) {
specials.insert(content.to_string(), id as u32);
}
}
}
specials
}
pub(super) fn parse_special_decode_ids(root: &Value) -> rustc_hash::FxHashSet<u32> {
let mut ids = rustc_hash::FxHashSet::default();
if let Some(list) = root.get("added_tokens").and_then(Value::as_array) {
for t in list {
if t.get("special").and_then(Value::as_bool).unwrap_or(true) {
if let Some(id) = t.get("id").and_then(Value::as_u64) {
ids.insert(id as u32);
}
}
}
}
ids
}
pub(super) fn find_added_token(root: &Value, candidates: &[&str]) -> Option<u32> {
let list = root.get("added_tokens").and_then(Value::as_array)?;
for cand in candidates {
for t in list {
if t.get("content").and_then(Value::as_str) == Some(cand) {
return t.get("id").and_then(Value::as_u64).map(|n| n as u32);
}
}
}
None
}