feature/insight-chat-improvements #83

Merged
cameron merged 19 commits from feature/insight-chat-improvements into master 2026-05-07 22:19:13 +00:00
4 changed files with 17 additions and 15 deletions
Showing only changes of commit 6f0c15d0c5 - Show all commits

View File

@@ -119,8 +119,8 @@ impl InsightGenerator {
location_dao: Arc<Mutex<Box<dyn LocationHistoryDao>>>, location_dao: Arc<Mutex<Box<dyn LocationHistoryDao>>>,
search_dao: Arc<Mutex<Box<dyn SearchHistoryDao>>>, search_dao: Arc<Mutex<Box<dyn SearchHistoryDao>>>,
tag_dao: Arc<Mutex<Box<dyn TagDao>>>, tag_dao: Arc<Mutex<Box<dyn TagDao>>>,
knowledge_dao: Arc<Mutex<Box<dyn KnowledgeDao>>>,
face_dao: Arc<Mutex<Box<dyn crate::faces::FaceDao>>>, face_dao: Arc<Mutex<Box<dyn crate::faces::FaceDao>>>,
knowledge_dao: Arc<Mutex<Box<dyn KnowledgeDao>>>,
libraries: Vec<Library>, libraries: Vec<Library>,
) -> Self { ) -> Self {
Self { Self {
@@ -135,8 +135,8 @@ impl InsightGenerator {
location_dao, location_dao,
search_dao, search_dao,
tag_dao, tag_dao,
knowledge_dao,
face_dao, face_dao,
knowledge_dao,
libraries, libraries,
} }
} }
@@ -2180,15 +2180,16 @@ Return ONLY the summary, nothing else."#,
log::info!("tool_get_faces_in_photo: file_path='{}'", file_path); log::info!("tool_get_faces_in_photo: file_path='{}'", file_path);
// Resolve content_hash from any library that has this rel_path. // Resolve content_hash from any library that has this rel_path.
// Walk libraries in their declared order and take the first hit. // Hold the FaceDao lock once across all libraries — resolve_content_hash
let mut content_hash: Option<String> = None; // is synchronous and there's no await in the loop body.
for lib in &self.libraries { let content_hash: Option<String> = {
let mut dao = self.face_dao.lock().expect("Unable to lock FaceDao"); let mut dao = self.face_dao.lock().expect("Unable to lock FaceDao");
if let Ok(Some(h)) = dao.resolve_content_hash(cx, lib.id, &file_path) { self.libraries.iter().find_map(|lib| {
content_hash = Some(h); dao.resolve_content_hash(cx, lib.id, &file_path)
break; .ok()
} .flatten()
} })
};
let Some(content_hash) = content_hash else { let Some(content_hash) = content_hash else {
return "No content_hash found for that file path (the photo may not be indexed yet, \ return "No content_hash found for that file path (the photo may not be indexed yet, \
or the path doesn't match any library)." or the path doesn't match any library)."
@@ -2215,9 +2216,11 @@ Return ONLY the summary, nothing else."#,
let mut out = format!("Found {} face(s) in this photo:\n", faces.len()); let mut out = format!("Found {} face(s) in this photo:\n", faces.len());
for f in &bound { for f in &bound {
// Invariant: `bound` is filtered on `person_name.is_some()` above.
let name = f.person_name.as_deref().expect("bound face must have a name");
out.push_str(&format!( out.push_str(&format!(
"- {} (confidence {:.2}, bbox x={:.2} y={:.2} w={:.2} h={:.2}, source: {})\n", "- {} (confidence {:.2}, bbox x={:.2} y={:.2} w={:.2} h={:.2}, source: {})\n",
f.person_name.as_deref().unwrap_or("?"), name,
f.confidence, f.confidence,
f.bbox_x, f.bbox_x,
f.bbox_y, f.bbox_y,

View File

@@ -201,8 +201,8 @@ async fn main() -> anyhow::Result<()> {
location_dao, location_dao,
search_dao, search_dao,
tag_dao, tag_dao,
knowledge_dao,
face_dao, face_dao,
knowledge_dao,
all_libs.clone(), all_libs.clone(),
); );

View File

@@ -1437,7 +1437,6 @@ impl FaceDao for SqliteFaceDao {
} }
fn has_any_faces(&mut self, ctx: &opentelemetry::Context) -> anyhow::Result<bool> { fn has_any_faces(&mut self, ctx: &opentelemetry::Context) -> anyhow::Result<bool> {
use anyhow::Context;
let mut conn = self.connection.lock().expect("face dao lock"); let mut conn = self.connection.lock().expect("face dao lock");
trace_db_call(ctx, "query", "has_any_faces", |_span| { trace_db_call(ctx, "query", "has_any_faces", |_span| {
face_detections::table face_detections::table

View File

@@ -234,8 +234,8 @@ impl Default for AppState {
location_dao.clone(), location_dao.clone(),
search_dao.clone(), search_dao.clone(),
tag_dao.clone(), tag_dao.clone(),
knowledge_dao,
face_dao.clone(), face_dao.clone(),
knowledge_dao,
libraries_vec.clone(), libraries_vec.clone(),
); );
@@ -376,8 +376,8 @@ impl AppState {
location_dao.clone(), location_dao.clone(),
search_dao.clone(), search_dao.clone(),
tag_dao.clone(), tag_dao.clone(),
knowledge_dao,
face_dao.clone(), face_dao.clone(),
knowledge_dao,
vec![test_lib], vec![test_lib],
); );