feat: add entity-relationship knowledge memory to agentic insights
Implements persistent cross-photo knowledge memory so the agentic
insight loop can learn and recall facts about people, places, and
events across the photo collection.
Changes:
- photo_insights: drop UNIQUE(file_path) + INSERT OR REPLACE, replace
with append-only rows + is_current flag for insight history retention
- New tables: entities, entity_facts, entity_photo_links with FK
constraints and confidence scoring
- KnowledgeDao trait + SqliteKnowledgeDao with upsert, merge, and
corroboration (confidence +0.1 on duplicate fact detection)
- Four new agent tools: recall_entities, recall_facts_for_photo,
store_entity, store_fact (with object_entity_id FK support)
- Cameron entity auto-seeded with stable ID injected into system prompt
- Pre-run photo link clearing + post-loop source_insight_id backfill
- Audit REST API: GET/PATCH/DELETE /knowledge/entities/{id},
POST /knowledge/entities/merge, GET/PATCH/DELETE /knowledge/facts/{id},
GET /knowledge/recent
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,7 @@
|
||||
use crate::database::schema::{favorites, image_exif, photo_insights, users, video_preview_clips};
|
||||
use crate::database::schema::{
|
||||
entities, entity_facts, entity_photo_links, favorites, image_exif, photo_insights, users,
|
||||
video_preview_clips,
|
||||
};
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Insertable)]
|
||||
@@ -82,6 +85,7 @@ pub struct InsertPhotoInsight {
|
||||
pub summary: String,
|
||||
pub generated_at: i64,
|
||||
pub model_version: String,
|
||||
pub is_current: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Queryable, Clone, Debug)]
|
||||
@@ -92,6 +96,79 @@ pub struct PhotoInsight {
|
||||
pub summary: String,
|
||||
pub generated_at: i64,
|
||||
pub model_version: String,
|
||||
pub is_current: bool,
|
||||
}
|
||||
|
||||
// --- Knowledge memory models ---
|
||||
|
||||
#[derive(Insertable)]
|
||||
#[diesel(table_name = entities)]
|
||||
pub struct InsertEntity {
|
||||
pub name: String,
|
||||
pub entity_type: String,
|
||||
pub description: String,
|
||||
pub embedding: Option<Vec<u8>>,
|
||||
pub confidence: f32,
|
||||
pub status: String,
|
||||
pub created_at: i64,
|
||||
pub updated_at: i64,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Queryable, Clone, Debug)]
|
||||
pub struct Entity {
|
||||
pub id: i32,
|
||||
pub name: String,
|
||||
pub entity_type: String,
|
||||
pub description: String,
|
||||
pub embedding: Option<Vec<u8>>,
|
||||
pub confidence: f32,
|
||||
pub status: String,
|
||||
pub created_at: i64,
|
||||
pub updated_at: i64,
|
||||
}
|
||||
|
||||
#[derive(Insertable)]
|
||||
#[diesel(table_name = entity_facts)]
|
||||
pub struct InsertEntityFact {
|
||||
pub subject_entity_id: i32,
|
||||
pub predicate: String,
|
||||
pub object_entity_id: Option<i32>,
|
||||
pub object_value: Option<String>,
|
||||
pub source_photo: Option<String>,
|
||||
pub source_insight_id: Option<i32>,
|
||||
pub confidence: f32,
|
||||
pub status: String,
|
||||
pub created_at: i64,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Queryable, Clone, Debug)]
|
||||
pub struct EntityFact {
|
||||
pub id: i32,
|
||||
pub subject_entity_id: i32,
|
||||
pub predicate: String,
|
||||
pub object_entity_id: Option<i32>,
|
||||
pub object_value: Option<String>,
|
||||
pub source_photo: Option<String>,
|
||||
pub source_insight_id: Option<i32>,
|
||||
pub confidence: f32,
|
||||
pub status: String,
|
||||
pub created_at: i64,
|
||||
}
|
||||
|
||||
#[derive(Insertable)]
|
||||
#[diesel(table_name = entity_photo_links)]
|
||||
pub struct InsertEntityPhotoLink {
|
||||
pub entity_id: i32,
|
||||
pub file_path: String,
|
||||
pub role: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Queryable, Clone, Debug)]
|
||||
pub struct EntityPhotoLink {
|
||||
pub id: i32,
|
||||
pub entity_id: i32,
|
||||
pub file_path: String,
|
||||
pub role: String,
|
||||
}
|
||||
|
||||
#[derive(Insertable)]
|
||||
|
||||
Reference in New Issue
Block a user