feat: nightly agentic pre-generation of memory reels

Implement end-to-end nightly pre-generation of memory reels with agentic
scripting that grounds narration in calendar, location, messages, and RAG.

Sections A-E from the plan:

A. Extract produce_reel pipeline core from run_reel_job with
   ScripterMode::Fast/Agentic and progress callbacks.

B. Agentic scripter: factor run_readonly_tool_loop from the insight
   generator, build read-only tool gate, prompt builder with GPS, and
   generate_script_agentic with fallback to fast path.

C. Precomputed reels ledger (SQLite table + DAO), GET /reels/precomputed
   handler with validity gate, GET /reels/by-key/{key}/video streaming,
   and normalize_library_key helper.

D. Nightly scheduler: spawn_pregen_scheduler with configurable hour,
   run_pregen_batch (day/week/month spans), pregen_one with dedup and
   disk-check, secs_until_next_run_hour time math.

E. user_ai_prefs passive mirror table + DAO for param capture in
   create_reel_handler and replay in the scheduler.

Also fixes resolve_library_param signature to take &[Library] and adds
resolve_library_param_state wrapper for AppState callers.

New files: migrations/2026-06-13-000000_add_precomputed_reels/,
  migrations/2026-06-13-000010_add_user_ai_prefs/,
  src/database/precomputed_reel_dao.rs,
  src/database/user_ai_prefs_dao.rs
This commit is contained in:
Cameron Cordes
2026-06-13 14:29:34 -04:00
parent b30c8c16d0
commit f707353807
26 changed files with 1825 additions and 153 deletions
+28
View File
@@ -266,6 +266,16 @@ diesel::table! {
}
}
diesel::table! {
user_ai_prefs (id) {
id -> Integer,
voice -> Nullable<Text>,
tz_offset_minutes -> Nullable<Integer>,
library -> Nullable<Text>,
updated_at -> BigInt,
}
}
diesel::table! {
video_preview_clips (id) {
id -> Integer,
@@ -294,6 +304,22 @@ diesel::table! {
}
}
diesel::table! {
precomputed_reels (id) {
id -> Integer,
span -> Text,
library_key -> Text,
cache_key -> Text,
output_path -> Text,
title -> Text,
media_count -> Integer,
render_version -> Integer,
tz_offset_minutes -> Integer,
voice -> Nullable<Text>,
generated_at -> BigInt,
}
}
diesel::joinable!(entity_facts -> photo_insights (source_insight_id));
diesel::joinable!(entity_photo_links -> entities (entity_id));
diesel::joinable!(entity_photo_links -> libraries (library_id));
@@ -322,9 +348,11 @@ diesel::allow_tables_to_appear_in_same_query!(
personas,
persons,
photo_insights,
precomputed_reels,
search_history,
tagged_photo,
tags,
user_ai_prefs,
users,
video_preview_clips,
);