diff --git a/src/ai/clip_client.rs b/src/ai/clip_client.rs index 9b38a2a..85c66a7 100644 --- a/src/ai/clip_client.rs +++ b/src/ai/clip_client.rs @@ -181,10 +181,7 @@ impl ClipClient { /// Encode a natural-language query to an embedding. Used by the /// search route to rank stored image embeddings by cosine sim. - pub async fn encode_text( - &self, - text: &str, - ) -> std::result::Result { + pub async fn encode_text(&self, text: &str) -> std::result::Result { let Some(base) = self.base_url.as_deref() else { return Err(ClipError::Disabled); }; @@ -206,9 +203,10 @@ impl ClipClient { }; let status = resp.status(); if status.is_success() { - let body: EncodeResponse = resp.json().await.map_err(|e| { - ClipError::Transient(anyhow::anyhow!("clip response decode: {e}")) - })?; + let body: EncodeResponse = resp + .json() + .await + .map_err(|e| ClipError::Transient(anyhow::anyhow!("clip response decode: {e}")))?; return Ok(body); } let body_text = resp.text().await.unwrap_or_default(); @@ -246,9 +244,10 @@ impl ClipClient { }; let status = resp.status(); if status.is_success() { - let body: EncodeResponse = resp.json().await.map_err(|e| { - ClipError::Transient(anyhow::anyhow!("clip response decode: {e}")) - })?; + let body: EncodeResponse = resp + .json() + .await + .map_err(|e| ClipError::Transient(anyhow::anyhow!("clip response decode: {e}")))?; return Ok(body); } let body_text = resp.text().await.unwrap_or_default(); diff --git a/src/backfill.rs b/src/backfill.rs index 3de017c..34baa41 100644 --- a/src/backfill.rs +++ b/src/backfill.rs @@ -273,10 +273,12 @@ pub fn process_clip_backlog( let candidates: Vec = rows .into_iter() - .map(|(rel_path, content_hash)| crate::clip_watch::ClipCandidate { - rel_path, - content_hash, - }) + .map( + |(rel_path, content_hash)| crate::clip_watch::ClipCandidate { + rel_path, + content_hash, + }, + ) .collect(); crate::clip_watch::run_clip_encoding_pass( diff --git a/src/bin/probe_clip_search.rs b/src/bin/probe_clip_search.rs index 45686ed..80d5e7f 100644 --- a/src/bin/probe_clip_search.rs +++ b/src/bin/probe_clip_search.rs @@ -122,7 +122,10 @@ async fn main() -> anyhow::Result<()> { .into_iter() .find(|l| l.id == args.library) .ok_or_else(|| anyhow::anyhow!("library id {} not found", args.library))?; - info!("probing library #{} ({}) at {}", lib.id, lib.name, lib.root_path); + info!( + "probing library #{} ({}) at {}", + lib.id, lib.name, lib.root_path + ); let dao: Arc>> = Arc::new(Mutex::new(Box::new(SqliteExifDao::new()))); let ctx = opentelemetry::Context::new(); @@ -136,9 +139,7 @@ async fn main() -> anyhow::Result<()> { let query_vec = decode_f32_vec(&query_resp.embedding)?; info!( "query encoded ({}d, {}ms): {:?}", - query_resp.embedding_dim, - query_resp.duration_ms, - args.query + query_resp.embedding_dim, query_resp.duration_ms, args.query ); // Page through (id, rel_path), filter to images on disk, encode up @@ -245,7 +246,11 @@ async fn main() -> anyhow::Result<()> { scores.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(std::cmp::Ordering::Equal)); let elapsed = started.elapsed(); println!(); - println!("── top {} for query: {:?} ──", args.top.min(scores.len()), args.query); + println!( + "── top {} for query: {:?} ──", + args.top.min(scores.len()), + args.query + ); for (i, (sim, path)) in scores.iter().take(args.top).enumerate() { println!("[{:>2}] sim={:.3} {}", i + 1, sim, path); } diff --git a/src/clip_search.rs b/src/clip_search.rs index a3e3f9b..d5a2368 100644 --- a/src/clip_search.rs +++ b/src/clip_search.rs @@ -121,7 +121,7 @@ pub async fn search_photos( })); } - let limit = query.limit.min(200).max(1); + let limit = query.limit.clamp(1, 200); let threshold = query.threshold.clamp(-1.0, 1.0); // 1. Encode the query text. Fast — Apollo's text encoder is ~50ms @@ -174,7 +174,10 @@ pub async fn search_photos( match dao.list_clip_index( &ctx, &library_ids, - query.model_version.as_deref().or(Some(&query_resp.model_version)), + query + .model_version + .as_deref() + .or(Some(&query_resp.model_version)), ) { Ok(r) => r, Err(e) => { @@ -257,7 +260,8 @@ pub async fn search_photos( Err(e) => { log::warn!( "clip_search: find_by_content_hash failed for {}: {:?}", - hash, e + hash, + e ); continue; } diff --git a/src/clip_watch.rs b/src/clip_watch.rs index a94e4ab..16a75fa 100644 --- a/src/clip_watch.rs +++ b/src/clip_watch.rs @@ -142,10 +142,7 @@ async fn process_one( let emb_bytes = match resp.decode_embedding() { Ok(b) => b, Err(e) => { - warn!( - "clip_watch: bad embedding for {}: {:?}", - cand.rel_path, e - ); + warn!("clip_watch: bad embedding for {}: {:?}", cand.rel_path, e); return; } }; diff --git a/src/state.rs b/src/state.rs index 572a83f..8f1bd4e 100644 --- a/src/state.rs +++ b/src/state.rs @@ -450,6 +450,7 @@ impl AppState { insight_chat, preview_dao, FaceClient::new(None), // disabled in test + ClipClient::new(None), // disabled in test ) } }