use std::path::Path;
use std::sync::Arc;
use crate::config::Config;
use crate::engine::ContextEngine;
use crate::lexicon::{CompositeLexiconScorer, DefaultEnglishScorer, LexiconScorer};
use crate::scrub::ScrubConfig;
use crate::storage::open_storage;
use crate::traits::Result;
use crate::ContextForge;
pub struct ContextForgeBuilder {
config: Config,
persona_scorer: Option<Arc<dyn LexiconScorer>>,
#[cfg(feature = "semantic")]
embedding_cache_dir: Option<std::path::PathBuf>,
}
impl ContextForgeBuilder {
#[must_use]
pub fn new(config: Config) -> Self {
Self {
config,
persona_scorer: None,
#[cfg(feature = "semantic")]
embedding_cache_dir: None,
}
}
#[must_use]
pub fn with_persona_scorer(mut self, scorer: impl LexiconScorer + 'static) -> Self {
self.persona_scorer = Some(Arc::new(scorer));
self
}
#[cfg(feature = "semantic")]
#[must_use]
pub fn with_embedding_model(mut self, cache_dir: impl AsRef<std::path::Path>) -> Self {
self.embedding_cache_dir = Some(cache_dir.as_ref().to_path_buf());
self
}
pub async fn build(self) -> Result<ContextForge> {
let db_path = self.config.db_path.clone();
let max_entries = self.config.max_entries;
let scrub_config: ScrubConfig = self.config.scrub.clone();
let (storage, searcher) = open_storage(Path::new(&db_path), max_entries).await?;
let english: Arc<dyn LexiconScorer> = Arc::new(DefaultEnglishScorer::default());
let scorer: Arc<dyn LexiconScorer> = match self.persona_scorer {
Some(persona) => Arc::new(CompositeLexiconScorer::new(vec![english, persona])),
None => english,
};
#[cfg_attr(not(feature = "semantic"), allow(unused_mut))]
let mut engine = ContextEngine::new(Box::new(storage), Box::new(searcher), self.config)
.with_scorer(scorer);
#[cfg(feature = "semantic")]
if let Some(cache_dir) = self.embedding_cache_dir {
let embedder = crate::semantic::FasEmbedder::new(&cache_dir)?;
engine = engine.with_embedder(Arc::new(embedder));
}
Ok(ContextForge::from_parts(engine, scrub_config))
}
}