Add CLIP embeddings and similar-image search
- add Candle + HF Hub CLIP image embedding pipeline with background embedding worker - write image embeddings into sqlite-vec and expose similar-image lookup through a new backend command - surface embedding progress and recovery in the UI, including retries for failed embeddings - improve gallery/lightbox embedding UX and make similar-image actions directly accessible Refs: #3, #4
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
use crate::db::{self, DbPool, Folder, ImageRecord};
|
||||
use crate::db::{self, DbPool, Folder, FolderJobProgress, ImageRecord};
|
||||
use crate::indexer;
|
||||
use crate::vector;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::PathBuf;
|
||||
use tauri::{AppHandle, State};
|
||||
@@ -32,6 +33,17 @@ pub struct UpdateImageDetailsParams {
|
||||
pub rating: Option<i64>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct FindSimilarImagesParams {
|
||||
pub image_id: i64,
|
||||
pub limit: Option<usize>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct RetryFailedEmbeddingsParams {
|
||||
pub folder_id: i64,
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn add_folder(
|
||||
app: AppHandle,
|
||||
@@ -75,6 +87,14 @@ pub async fn get_folders(db: State<'_, DbState>) -> Result<Vec<Folder>, String>
|
||||
db::get_folders(&conn).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_background_job_progress(
|
||||
db: State<'_, DbState>,
|
||||
) -> Result<Vec<FolderJobProgress>, String> {
|
||||
let conn = db.get().map_err(|e| e.to_string())?;
|
||||
db::get_all_folder_job_progress(&conn).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn remove_folder(db: State<'_, DbState>, folder_id: i64) -> Result<(), String> {
|
||||
let conn = db.get().map_err(|e| e.to_string())?;
|
||||
@@ -147,3 +167,24 @@ pub async fn reindex_folder(
|
||||
indexer::index_folder(app, db.inner().clone(), folder_id, folder_path);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn find_similar_images(
|
||||
db: State<'_, DbState>,
|
||||
params: FindSimilarImagesParams,
|
||||
) -> Result<Vec<ImageRecord>, String> {
|
||||
let conn = db.get().map_err(|e| e.to_string())?;
|
||||
let limit = params.limit.unwrap_or(32);
|
||||
let image_ids = vector::find_similar_image_ids(&conn, params.image_id, limit)
|
||||
.map_err(|e| e.to_string())?;
|
||||
db::get_images_by_ids(&conn, &image_ids).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn retry_failed_embeddings(
|
||||
db: State<'_, DbState>,
|
||||
params: RetryFailedEmbeddingsParams,
|
||||
) -> Result<usize, String> {
|
||||
let conn = db.get().map_err(|e| e.to_string())?;
|
||||
db::retry_failed_embedding_jobs(&conn, params.folder_id).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user