use anyhow::{Context, Result};
use rusqlite::Connection;
use std::path::{Path, PathBuf};
pub const ATTACH_ALIAS: &str = "emb";
const DB_EXT: &str = "db";
pub fn model_slug(model_id: &str) -> String {
model_id.replace('/', "--")
}
pub fn model_from_slug(slug: &str) -> String {
slug.replacen("--", "/", 1)
}
fn validate_context_storage(ctx: &crate::library::LibraryContext) -> Result<()> {
ctx.ensure_root_identity()?;
crate::library_locks::reject_dir_redirect(&ctx.paths.embeddings, "the embeddings directory")?;
Ok(())
}
pub fn db_path_in(ctx: &crate::library::LibraryContext, model_id: &str) -> Result<PathBuf> {
crate::embeddings::validate_model_id(model_id)?;
validate_context_storage(ctx)?;
let path = ctx
.paths
.embeddings
.join(format!("{}.{DB_EXT}", model_slug(model_id)));
crate::library_locks::reject_redirect(&path, "the model database")?;
Ok(path)
}
pub const PAGE_SIZE: i64 = 16384;
fn init_model_db(path: &Path) -> Result<()> {
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent).with_context(|| format!("create {}", parent.display()))?;
}
let conn = Connection::open(path).with_context(|| format!("create {}", path.display()))?;
conn.pragma_update(None, "page_size", PAGE_SIZE)
.context("set page_size")?;
conn.pragma_update(None, "journal_mode", "WAL")
.context("set journal_mode")?;
conn.execute_batch(
"CREATE TABLE IF NOT EXISTS embeddings (
hash TEXT PRIMARY KEY NOT NULL,
model_id TEXT NOT NULL,
embedding BLOB NOT NULL,
embedded_at TEXT NOT NULL
);",
)
.with_context(|| format!("create embeddings table in {}", path.display()))?;
Ok(())
}
pub fn attach_in(
conn: &Connection,
ctx: &crate::library::LibraryContext,
model_id: &str,
create: bool,
) -> Result<()> {
crate::library_locks::verify_state(ctx)?;
let path = db_path_in(ctx, model_id)?;
if !path.exists() {
if !create {
let available = list_models_in(ctx).unwrap_or_default();
let available = if available.is_empty() {
"(none)".to_string()
} else {
available.join(", ")
};
anyhow::bail!(
"no embeddings for {model_id} in this library\n \
expected: {}\n available: {available}\n \
run: videre embed --model {model_id}",
path.display()
);
}
std::fs::create_dir_all(&ctx.paths.embeddings)
.with_context(|| format!("create {}", ctx.paths.embeddings.display()))?;
validate_context_storage(ctx)?;
init_model_db(&path)?;
}
crate::library_locks::reject_redirect(&path, "the model database")?;
let meta =
std::fs::symlink_metadata(&path).with_context(|| format!("inspect {}", path.display()))?;
anyhow::ensure!(meta.is_file(), "{} is not a regular file", path.display());
conn.execute(
&format!("ATTACH DATABASE ?1 AS {ATTACH_ALIAS}"),
[path.to_string_lossy().as_ref()],
)
.with_context(|| format!("attach {}", path.display()))?;
if create {
conn.execute_batch(
"CREATE TABLE IF NOT EXISTS emb.embeddings (
hash TEXT PRIMARY KEY NOT NULL,
model_id TEXT NOT NULL,
embedding BLOB NOT NULL,
embedded_at TEXT NOT NULL
);",
)
.with_context(|| format!("ensure schema in {}", path.display()))?;
}
Ok(())
}
pub fn list_models_in(ctx: &crate::library::LibraryContext) -> Result<Vec<String>> {
validate_context_storage(ctx)?;
let entries = match std::fs::read_dir(&ctx.paths.embeddings) {
Ok(entries) => entries,
Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(Vec::new()),
Err(error) => {
return Err(error).with_context(|| format!("read {}", ctx.paths.embeddings.display()))
}
};
let mut models = Vec::new();
for entry in entries {
let entry = entry.with_context(|| format!("read {}", ctx.paths.embeddings.display()))?;
let path = entry.path();
if path.extension().is_none_or(|extension| extension != DB_EXT) {
continue;
}
crate::library_locks::reject_redirect(&path, "the model database")?;
let meta = std::fs::symlink_metadata(&path)
.with_context(|| format!("inspect {}", path.display()))?;
anyhow::ensure!(meta.is_file(), "{} is not a regular file", path.display());
if let Some(stem) = path.file_stem() {
models.push(model_from_slug(&stem.to_string_lossy()));
}
}
models.sort();
Ok(models)
}
#[derive(Debug, Clone, PartialEq, serde::Serialize)]
pub struct ModelEmbeddingCount {
pub model_id: String,
pub count: i64,
pub dims: i64,
pub size_bytes: i64,
}
pub fn counts_by_model_in(
ctx: &crate::library::LibraryContext,
) -> Result<Vec<ModelEmbeddingCount>> {
let mut out = Vec::new();
for model_id in list_models_in(ctx)? {
let path = db_path_in(ctx, &model_id)?;
let size_bytes = std::fs::metadata(&path)
.with_context(|| format!("inspect {}", path.display()))?
.len() as i64;
let conn = Connection::open(&path).with_context(|| format!("open {}", path.display()))?;
let count: i64 = conn
.query_row("SELECT COUNT(*) FROM embeddings", [], |row| row.get(0))
.unwrap_or(0);
let dims: i64 = conn
.query_row(
"SELECT LENGTH(embedding) / 2 FROM embeddings LIMIT 1",
[],
|row| row.get(0),
)
.unwrap_or(0);
out.push(ModelEmbeddingCount {
model_id,
count,
dims,
size_bytes,
});
}
Ok(out)
}
pub fn attach_for_read_in(
conn: &Connection,
ctx: &crate::library::LibraryContext,
model_id: &str,
) -> Result<()> {
attach_in(conn, ctx, model_id, false)
}
pub fn attach_for_read_or_placeholder_in(
conn: &Connection,
ctx: &crate::library::LibraryContext,
model_id: &str,
) -> Result<()> {
if attach_for_read_in(conn, ctx, model_id).is_ok() {
return Ok(());
}
conn.execute("ATTACH DATABASE ':memory:' AS emb", [])
.context("attach placeholder embeddings database")?;
conn.execute_batch(
"CREATE TABLE emb.embeddings (
hash TEXT PRIMARY KEY NOT NULL,
model_id TEXT NOT NULL,
embedding BLOB NOT NULL,
embedded_at TEXT NOT NULL
);",
)
.context("create placeholder embeddings table")?;
Ok(())
}
pub fn detach(conn: &Connection) -> Result<()> {
conn.execute(&format!("DETACH DATABASE {ATTACH_ALIAS}"), [])
.context("detach embeddings database")?;
Ok(())
}
#[cfg(test)]
pub(crate) fn test_context(tag: &str) -> crate::library::LibraryContext {
let base = std::env::temp_dir().join(format!("videre-embdb-{}-{}", tag, std::process::id()));
let _ = std::fs::remove_dir_all(&base);
let root = base.join("lib");
std::fs::create_dir_all(&root).unwrap();
let ctx = crate::library::LibraryContext::new(&root, &base.join("cache")).unwrap();
std::fs::create_dir_all(&ctx.paths.state).unwrap();
ctx
}
#[cfg(test)]
mod tests {
use super::*;
fn explicit_context(parent: &Path, name: &str) -> crate::library::LibraryContext {
let root = parent.join(name);
std::fs::create_dir(&root).unwrap();
crate::library::LibraryContext::new(&root, &parent.join("cache")).unwrap()
}
#[test]
fn explicit_model_stores_are_isolated_and_keep_f16_dimensions() {
let temp = tempfile::tempdir().unwrap();
let a = explicit_context(temp.path(), "a");
let b = explicit_context(temp.path(), "b");
let a_conn = crate::library_db::initialize(&a).unwrap();
let b_conn = crate::library_db::initialize(&b).unwrap();
for model in ["owner/model-a", "owner/model-b"] {
attach_in(&a_conn, &a, model, true).unwrap();
a_conn
.execute(
"INSERT INTO emb.embeddings (hash, model_id, embedding, embedded_at)
VALUES ('shared', ?1, zeroblob(1536), 'now')",
[model],
)
.unwrap();
detach(&a_conn).unwrap();
}
attach_in(&b_conn, &b, "owner/model-a", true).unwrap();
b_conn
.execute(
"INSERT INTO emb.embeddings (hash, model_id, embedding, embedded_at)
VALUES ('shared', ?1, zeroblob(8), 'now')",
["owner/model-a"],
)
.unwrap();
detach(&b_conn).unwrap();
assert_eq!(
list_models_in(&a).unwrap(),
vec!["owner/model-a", "owner/model-b"]
);
let a_counts = counts_by_model_in(&a).unwrap();
assert_eq!(a_counts.len(), 2);
assert!(a_counts
.iter()
.all(|count| count.count == 1 && count.dims == 768));
let b_counts = counts_by_model_in(&b).unwrap();
assert_eq!(b_counts[0].count, 1);
assert_eq!(b_counts[0].dims, 4);
assert_ne!(
db_path_in(&a, "owner/model-a").unwrap(),
db_path_in(&b, "owner/model-a").unwrap()
);
}
#[test]
fn explicit_read_attachment_does_not_create_a_missing_store() {
let temp = tempfile::tempdir().unwrap();
let ctx = explicit_context(temp.path(), "library");
let conn = crate::library_db::initialize(&ctx).unwrap();
let expected = db_path_in(&ctx, "owner/missing").unwrap();
let error = attach_for_read_in(&conn, &ctx, "owner/missing").unwrap_err();
assert!(format!("{error:#}").contains("no embeddings for owner/missing"));
assert!(!expected.exists());
assert!(!ctx.paths.embeddings.exists());
}
#[test]
fn model_slug_replaces_the_owner_separator() {
assert_eq!(
model_slug("google/siglip2-base-patch16-384"),
"google--siglip2-base-patch16-384"
);
}
#[test]
fn model_slug_round_trips_through_model_from_slug() {
for id in [
"google/siglip2-base-patch16-384",
"google/siglip-so400m-patch14-384",
"google/siglip-base-patch16-224",
] {
assert_eq!(model_from_slug(&model_slug(id)), id);
}
}
#[test]
fn model_slug_contains_no_path_separator() {
assert!(!model_slug("google/siglip2-base-patch16-384").contains('/'));
}
#[test]
fn db_path_in_joins_the_embeddings_dir_and_model_slug() {
let ctx = test_context("dbpath");
let p = db_path_in(&ctx, "google/siglip2-base-patch16-384").unwrap();
assert_eq!(
p.file_name().unwrap(),
"google--siglip2-base-patch16-384.db"
);
assert_eq!(p.parent().unwrap(), ctx.paths.embeddings);
}
#[test]
fn attach_with_create_makes_a_database_with_the_chosen_page_size() {
let ctx = test_context("create");
let conn = Connection::open_in_memory().unwrap();
attach_in(&conn, &ctx, "google/siglip2-base-patch16-384", true).unwrap();
let ps: i64 = conn
.query_row("PRAGMA emb.page_size", [], |r| r.get(0))
.unwrap();
assert_eq!(ps, PAGE_SIZE);
}
#[test]
fn attach_with_create_is_idempotent_and_preserves_rows() {
let ctx = test_context("idem");
let model = "google/siglip2-base-patch16-384";
let c1 = Connection::open_in_memory().unwrap();
attach_in(&c1, &ctx, model, true).unwrap();
c1.execute(
"INSERT INTO emb.embeddings (hash, model_id, embedding, embedded_at)
VALUES ('h1', ?1, X'0102', '2026-08-05T00:00:00')",
[model],
)
.unwrap();
detach(&c1).unwrap();
drop(c1);
let c2 = Connection::open_in_memory().unwrap();
attach_in(&c2, &ctx, model, true).unwrap();
let n: i64 = c2
.query_row("SELECT COUNT(*) FROM emb.embeddings", [], |r| r.get(0))
.unwrap();
assert_eq!(n, 1, "re-attaching must not clobber existing rows");
}
#[test]
fn attach_with_create_repairs_a_file_that_exists_without_the_table() {
let ctx = test_context("repair");
let model = "google/siglip2-base-patch16-384";
let path = db_path_in(&ctx, model).unwrap();
std::fs::create_dir_all(path.parent().unwrap()).unwrap();
std::fs::write(&path, b"").unwrap();
let conn = Connection::open_in_memory().unwrap();
attach_in(&conn, &ctx, model, true).unwrap();
let n: i64 = conn
.query_row("SELECT COUNT(*) FROM emb.embeddings", [], |r| r.get(0))
.expect("the table must exist after attach_in(create: true)");
assert_eq!(n, 0);
}
#[test]
fn attach_without_create_errors_and_names_available_models() {
let ctx = test_context("missing");
let conn = Connection::open_in_memory().unwrap();
attach_in(&conn, &ctx, "google/siglip2-base-patch16-384", true).unwrap();
detach(&conn).unwrap();
let err = attach_in(&conn, &ctx, "google/siglip-base-patch16-224", false).unwrap_err();
let msg = format!("{err:#}");
assert!(
msg.contains("no embeddings for google/siglip-base-patch16-224"),
"{msg}"
);
assert!(
msg.contains("google/siglip2-base-patch16-384"),
"error must list what IS available: {msg}"
);
assert!(msg.contains("videre embed --model"), "{msg}");
}
#[test]
fn two_models_do_not_see_each_others_rows() {
let ctx = test_context("isolate");
let a = "google/siglip2-base-patch16-384";
let b = "google/siglip-base-patch16-224";
let conn = Connection::open_in_memory().unwrap();
attach_in(&conn, &ctx, a, true).unwrap();
conn.execute(
"INSERT INTO emb.embeddings (hash, model_id, embedding, embedded_at)
VALUES ('h1', ?1, X'0102', 'now')",
[a],
)
.unwrap();
detach(&conn).unwrap();
attach_in(&conn, &ctx, b, true).unwrap();
let n: i64 = conn
.query_row("SELECT COUNT(*) FROM emb.embeddings", [], |r| r.get(0))
.unwrap();
assert_eq!(n, 0, "model b must not see model a's rows");
}
#[test]
fn attached_table_is_visible_through_emb_sqlite_master() {
let ctx = test_context("master");
let conn = Connection::open_in_memory().unwrap();
attach_in(&conn, &ctx, "google/siglip2-base-patch16-384", true).unwrap();
let found: i64 = conn
.query_row(
"SELECT COUNT(*) FROM emb.sqlite_master
WHERE type='table' AND name='embeddings'",
[],
|r| r.get(0),
)
.unwrap();
assert_eq!(found, 1);
let unqualified: i64 = conn
.query_row(
"SELECT COUNT(*) FROM sqlite_master
WHERE type='table' AND name='embeddings'",
[],
|r| r.get(0),
)
.unwrap();
assert_eq!(unqualified, 0, "documents exactly why emb. is required");
}
#[test]
fn detach_allows_attaching_a_different_model_on_the_same_connection() {
let ctx = test_context("reattach");
let conn = Connection::open_in_memory().unwrap();
attach_in(&conn, &ctx, "google/siglip2-base-patch16-384", true).unwrap();
detach(&conn).unwrap();
attach_in(&conn, &ctx, "google/siglip-base-patch16-224", true).unwrap();
detach(&conn).unwrap();
}
#[test]
fn list_models_returns_sorted_ids_and_ignores_unrelated_files() {
let ctx = test_context("list");
let conn = Connection::open_in_memory().unwrap();
for m in [
"google/siglip2-base-patch16-384",
"google/siglip-base-patch16-224",
] {
attach_in(&conn, &ctx, m, true).unwrap();
detach(&conn).unwrap();
}
std::fs::write(ctx.paths.embeddings.join("notes.txt"), b"x").unwrap();
let models = list_models_in(&ctx).unwrap();
assert_eq!(
models,
vec![
"google/siglip-base-patch16-224".to_string(),
"google/siglip2-base-patch16-384".to_string(),
]
);
}
#[test]
fn list_models_on_a_library_with_no_embeddings_is_empty_not_an_error() {
let ctx = test_context("listempty");
assert!(list_models_in(&ctx).unwrap().is_empty());
}
#[test]
fn counts_by_model_reports_rows_dims_and_size() {
let ctx = test_context("counts");
let model = "google/siglip2-base-patch16-384";
let conn = Connection::open_in_memory().unwrap();
attach_in(&conn, &ctx, model, true).unwrap();
conn.execute(
"INSERT INTO emb.embeddings (hash, model_id, embedding, embedded_at)
VALUES ('h1', ?1, zeroblob(1536), 'now')",
[model],
)
.unwrap();
detach(&conn).unwrap();
let counts = counts_by_model_in(&ctx).unwrap();
assert_eq!(counts.len(), 1);
assert_eq!(counts[0].model_id, model);
assert_eq!(counts[0].count, 1);
assert_eq!(
counts[0].dims, 768,
"dims derive from blob length, not a table"
);
assert!(counts[0].size_bytes > 0);
}
#[test]
fn counts_by_model_reports_zero_dims_for_an_empty_model_database() {
let ctx = test_context("countsempty");
let conn = Connection::open_in_memory().unwrap();
attach_in(&conn, &ctx, "google/siglip2-base-patch16-384", true).unwrap();
detach(&conn).unwrap();
let counts = counts_by_model_in(&ctx).unwrap();
assert_eq!(counts.len(), 1);
assert_eq!(counts[0].count, 0);
assert_eq!(counts[0].dims, 0);
}
#[test]
fn path_computation_creates_nothing() {
let ctx = test_context("nocreate");
let _ = db_path_in(&ctx, "google/siglip2-base-patch16-384").unwrap();
assert!(
!ctx.paths.embeddings.exists(),
"path computation must not create {:?}",
ctx.paths.embeddings
);
}
}