pub(crate) mod db;
pub(crate) mod embedding;
pub(crate) mod external;
pub(crate) mod external_sweep;
pub mod index;
pub(crate) mod local_engine;
pub(crate) mod search;
pub(crate) mod store;
pub use embedding::{
embed_content, embed_content_api, embed_query_api, embed_via_api, engine_if_ready, get_engine,
};
pub(crate) mod chunk_fts;
pub(crate) mod chunker;
pub mod vector_search;
pub mod backfill_sweep;
pub mod freshness;
pub mod health_report;
pub use db::Store;
pub use index::{BRAIN_FILES, index_file, index_file_fts_only, reindex};
pub use search::{RrfResult, hybrid_search_rrf, search, search_brain};
pub(crate) use search::{search_external, search_memory};
pub use store::get_store;
fn vector_enabled() -> bool {
let config = read_memory_config();
config.vector_enabled
}
fn read_memory_config() -> crate::config::MemoryConfig {
let mut cfg = read_memory_section().unwrap_or_default();
if let Some(ref mut emb) = cfg.embedding
&& needs_embedding_key(emb.api_key.as_deref())
&& let Some(key) = memory_embedding_key_from_keys_file()
{
emb.api_key = Some(key);
}
cfg
}
pub(crate) fn needs_embedding_key(configured: Option<&str>) -> bool {
configured.is_none_or(|k| k.trim().is_empty())
}
fn read_memory_section() -> Option<crate::config::MemoryConfig> {
let config_path = crate::config::opencrabs_home().join("config.toml");
let content = std::fs::read_to_string(&config_path).ok()?;
let table = content.parse::<toml::Table>().ok()?;
let memory = table.get("memory")?;
toml::from_str::<crate::config::MemoryConfig>(&toml::to_string(memory).ok()?).ok()
}
fn memory_embedding_key_from_keys_file() -> Option<String> {
let keys_path = crate::config::opencrabs_home().join("keys.toml");
let content = std::fs::read_to_string(&keys_path).ok()?;
memory_embedding_key_in(&content)
}
pub(crate) fn memory_embedding_key_in(keys_toml: &str) -> Option<String> {
let table = keys_toml.parse::<toml::Table>().ok()?;
let key = table
.get("providers")?
.get("memory_embedding")?
.get("api_key")?
.as_str()?;
crate::config::stored_key::real_key(key).map(str::to_string)
}
pub(crate) fn embedding_api_configured() -> bool {
let cfg = read_memory_config();
cfg.vector_enabled
&& cfg
.embedding
.as_ref()
.is_some_and(|e| e.url.is_some() && e.model.is_some())
}
fn embedding_api_config() -> Option<crate::config::EmbeddingConfig> {
read_memory_config().embedding
}
pub fn doctor_lines() -> Vec<String> {
let cfg = read_memory_config();
let key = key_source(&cfg);
let stats = get_store()
.ok()
.and_then(|s| s.lock().ok())
.and_then(|s| s.vector_stats().ok());
let health = crate::config::health::get_health(health_report::EMBEDDING_HEALTH_KEY);
health_report::health_lines(
&cfg,
key,
stats.as_ref(),
health.as_ref(),
chrono::Utc::now(),
)
}
fn key_source(cfg: &crate::config::MemoryConfig) -> health_report::KeySource {
use health_report::KeySource;
let Some(emb) = cfg.embedding.as_ref().filter(|e| e.url.is_some()) else {
return KeySource::NotApplicable;
};
let in_config_toml = read_memory_section()
.and_then(|c| c.embedding)
.is_some_and(|e| !needs_embedding_key(e.api_key.as_deref()));
if in_config_toml {
return KeySource::ConfigToml;
}
if !needs_embedding_key(emb.api_key.as_deref()) {
return KeySource::KeysToml;
}
KeySource::Missing
}
fn embedding_dimensions() -> usize {
let cfg = read_memory_config();
if let Some(ref emb) = cfg.embedding
&& let Some(dims) = emb.dimensions
{
return dims;
}
768 }
pub(crate) fn extra_paths_config() -> Vec<crate::config::ExtraPath> {
read_memory_config().extra_paths
}
pub(crate) fn external_excludes() -> Vec<String> {
read_memory_config().exclude
}
pub(crate) fn external_allowed_in_shared() -> bool {
read_memory_config().external_allowed_in_shared
}
pub(crate) fn sweep_interval_secs() -> u64 {
read_memory_config().sweep_interval_secs
}
static SHARED_SESSIONS: std::sync::LazyLock<
std::sync::Mutex<std::collections::HashSet<uuid::Uuid>>,
> = std::sync::LazyLock::new(|| std::sync::Mutex::new(std::collections::HashSet::new()));
pub fn mark_session_shared(session_id: uuid::Uuid) {
if let Ok(mut g) = SHARED_SESSIONS.lock() {
g.insert(session_id);
}
}
pub fn is_session_shared(session_id: uuid::Uuid) -> bool {
SHARED_SESSIONS
.lock()
.map(|g| g.contains(&session_id))
.unwrap_or(false)
}
#[derive(Debug, Clone)]
pub struct MemoryResult {
pub path: String,
pub snippet: String,
pub rank: f64,
}
pub(crate) const COLLECTION_MEMORY: &str = "memory";
pub(crate) const COLLECTION_BRAIN: &str = "brain";
pub(crate) const COLLECTION_EXTERNAL: &str = "external";