Phase 3: Integrate Google Takeout context into InsightGenerator

- Updated InsightGenerator struct with calendar, location, and search DAOs
- Implemented hybrid context gathering methods:
  * gather_calendar_context(): ±7 days with semantic ranking
  * gather_location_context(): ±30 min with GPS proximity check
  * gather_search_context(): ±30 days semantic search
- Added haversine_distance() utility for GPS calculations
- Updated generate_insight_for_photo_with_model() to use multi-source context
- Combined all context sources (SMS + Calendar + Location + Search) with equal weight
- Initialized new DAOs in AppState (both default and test implementations)
- All contexts are optional (graceful degradation if data missing)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Cameron
2026-01-05 14:57:31 -05:00
parent d86b2c3746
commit cd66521c17
3 changed files with 327 additions and 11 deletions

View File

@@ -1,6 +1,8 @@
use crate::ai::{InsightGenerator, OllamaClient, SmsApiClient};
use crate::database::{
DailySummaryDao, ExifDao, InsightDao, SqliteDailySummaryDao, SqliteExifDao, SqliteInsightDao,
CalendarEventDao, DailySummaryDao, ExifDao, InsightDao, LocationHistoryDao, SearchHistoryDao,
SqliteCalendarEventDao, SqliteDailySummaryDao, SqliteExifDao, SqliteInsightDao,
SqliteLocationHistoryDao, SqliteSearchHistoryDao,
};
use crate::video::actors::{PlaylistGenerator, StreamActor, VideoPlaylistManager};
use actix::{Actor, Addr};
@@ -96,16 +98,27 @@ impl Default for AppState {
let daily_summary_dao: Arc<Mutex<Box<dyn DailySummaryDao>>> =
Arc::new(Mutex::new(Box::new(SqliteDailySummaryDao::new())));
// Initialize Google Takeout DAOs
let calendar_dao: Arc<Mutex<Box<dyn CalendarEventDao>>> =
Arc::new(Mutex::new(Box::new(SqliteCalendarEventDao::new())));
let location_dao: Arc<Mutex<Box<dyn LocationHistoryDao>>> =
Arc::new(Mutex::new(Box::new(SqliteLocationHistoryDao::new())));
let search_dao: Arc<Mutex<Box<dyn SearchHistoryDao>>> =
Arc::new(Mutex::new(Box::new(SqliteSearchHistoryDao::new())));
// Load base path
let base_path = env::var("BASE_PATH").expect("BASE_PATH was not set in the env");
// Initialize InsightGenerator
// Initialize InsightGenerator with all data sources
let insight_generator = InsightGenerator::new(
ollama.clone(),
sms_client.clone(),
insight_dao.clone(),
exif_dao.clone(),
daily_summary_dao.clone(),
calendar_dao.clone(),
location_dao.clone(),
search_dao.clone(),
base_path.clone(),
);
@@ -155,7 +168,15 @@ impl AppState {
let daily_summary_dao: Arc<Mutex<Box<dyn DailySummaryDao>>> =
Arc::new(Mutex::new(Box::new(SqliteDailySummaryDao::new())));
// Initialize test InsightGenerator
// Initialize test Google Takeout DAOs
let calendar_dao: Arc<Mutex<Box<dyn CalendarEventDao>>> =
Arc::new(Mutex::new(Box::new(SqliteCalendarEventDao::new())));
let location_dao: Arc<Mutex<Box<dyn LocationHistoryDao>>> =
Arc::new(Mutex::new(Box::new(SqliteLocationHistoryDao::new())));
let search_dao: Arc<Mutex<Box<dyn SearchHistoryDao>>> =
Arc::new(Mutex::new(Box::new(SqliteSearchHistoryDao::new())));
// Initialize test InsightGenerator with all data sources
let base_path_str = base_path.to_string_lossy().to_string();
let insight_generator = InsightGenerator::new(
ollama.clone(),
@@ -163,6 +184,9 @@ impl AppState {
insight_dao.clone(),
exif_dao.clone(),
daily_summary_dao.clone(),
calendar_dao.clone(),
location_dao.clone(),
search_dao.clone(),
base_path_str.clone(),
);