use crate::prelude::{Chunk, Chunker, Document, Embedder};
use srx::SRX;
use std::cell::OnceCell;
use std::future::Future;
use std::rc::Rc;
use std::str::FromStr;
#[derive(Debug, Clone)]
pub struct SentenceChunker {
srx: Rc<SRX>,
}
impl SentenceChunker {
pub fn new(rules: &str) -> Self {
Self {
srx: SRX::from_str(rules)
.expect("the rules file is valid")
.into(),
}
}
pub fn load(reader: impl std::io::Read) -> Result<Self, srx::Error> {
Ok(Self {
srx: SRX::from_reader(reader)?.into(),
})
}
pub fn split_sentences(&self, string: &str) -> Vec<std::ops::Range<usize>> {
let language = whatlang::detect_lang(string)
.map(|lang_code| lang_code.code())
.unwrap_or("en");
let rules = self.srx.language_rules(language);
rules.split_ranges(string)
}
}
impl Default for SentenceChunker {
fn default() -> Self {
thread_local! {
static DEFAULT_RULES: OnceCell<Rc<SRX>> = const { OnceCell::new() };
}
let rules = DEFAULT_RULES.with(|default| {
default
.get_or_init(|| {
let rules = SRX::from_str(include_str!("./assets/segment.srx"))
.expect("the rules file is valid");
Rc::new(rules)
})
.clone()
});
Self { srx: rules }
}
}
impl Chunker for SentenceChunker {
fn chunk<E: Embedder + Send>(
&self,
document: &Document,
embedder: &E,
) -> impl Future<Output = anyhow::Result<Vec<Chunk<E::VectorSpace>>>> + Send {
let mut initial_chunks = Vec::new();
let body = document.body();
let ranges = self.split_sentences(document.body());
for chunk in &ranges {
initial_chunks.push(body[chunk.clone()].to_string());
}
async move {
let embeddings = embedder.embed_vec(initial_chunks).await?;
let mut chunks = Vec::new();
for (embedding, chunk) in embeddings.into_iter().zip(ranges) {
let chunk = Chunk {
byte_range: chunk,
embeddings: vec![embedding],
};
chunks.push(chunk);
}
Ok(chunks)
}
}
}