Commit Graph

55 Commits

Author SHA1 Message Date
Cameron
0b9528f61e feat(ai): chat continuation for photo insights (server v1)
Adds POST /insights/chat and GET /insights/chat/history. Replays the
stored agentic conversation through the same backend the insight was
generated with (or a per-turn override), runs a short tool-calling
loop, and persists the extended history in append or amend mode.

Backend switching: same-backend or hybrid->local replay verbatim;
local->hybrid is rejected in v1 (would require on-the-fly vision
description rewrite).

Per-(library, file) async mutex serialises concurrent turns. Soft
context budget drops oldest tool_call+result pairs when the
serialized history exceeds num_ctx - 2048 tokens.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 13:00:27 -04:00
Cameron
3ac0cd62eb feat(ai): hybrid backend mode for agentic insights
Adds a `backend` column to photo_insights (default 'local', migration
2026-04-20-000000) and a corresponding optional `backend` field on the
agentic request. When a request sets backend=hybrid:

- The local Ollama vision model is called once via describe_image to
  produce a text description.
- The description is inlined into the first user message as text —
  no base64 image is ever sent to the chat model.
- The agentic tool-calling loop and title generation route through an
  OpenRouterClient (dispatched via &dyn LlmClient), letting the user
  pick any tool-capable model from OpenRouter per request.
- describe_photo is removed from the offered tools since the description
  is already present.

Embeddings and vision stay on local Ollama regardless of backend.
Hybrid mode requires OPENROUTER_API_KEY; handlers return a clear error
when hybrid is requested without it, and also when the selected
OpenRouter model lacks tool-calling support.

AppState gains an optional openrouter client built from
OPENROUTER_API_KEY / OPENROUTER_BASE_URL / OPENROUTER_DEFAULT_MODEL /
OPENROUTER_EMBEDDING_MODEL / attribution headers. Default model is
anthropic/claude-sonnet-4.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 22:30:40 -04:00
Cameron
3027a3ffda perf: DB-backed recursive /photos + watcher reconciliation
Recursive listings now query image_exif instead of walking disk, taking
union-mode /photos from ~17s to sub-second on a 10k-file library. The
watcher's full scan prunes stale image_exif rows so the DB stays in
parity with the filesystem when files are deleted externally.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 01:55:07 +00:00
Cameron
4a775b5e9b test: cover resolve_library_param and per-library ExifDao filter
Adds 9 unit tests around the library plumbing:
- resolve_library_param branches (absent, empty/whitespace, numeric id,
  name, unknown id, unknown name)
- Library::resolve symmetry with strip_root
- ExifDao::get_all_with_date_taken in union and scoped modes

Introduces SqliteExifDao::from_connection test constructor mirroring the
existing preview_dao pattern so DAO tests can drive an in-memory SQLite.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 01:55:07 +00:00
Cameron
2c8de8dcc6 feat: union /photos and /memories across libraries
When `library` is omitted, both endpoints now walk every configured
library root, interleave the results, and tag each row with its source
library via the parallel `photo_libraries` / per-row `library_id`
arrays. Previously the handlers fell back to the primary library,
silently hiding the rest.

Threads a parallel `file_libraries: Vec<i32>` through the sort/paginate
helpers so library attribution survives sorting and pagination.
Directory names are de-duplicated across libraries.

`get_all_with_date_taken` grows an optional library filter so memories
can scope its EXIF query per-library during the union walk.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 01:55:07 +00:00
Cameron
c2ee3996be chore: apply cargo fmt + clippy cleanup across crate
Silence forward-looking dead_code on unused DAO modules, annotate
individual placeholder items, rewrite tautological assert!(true/false)
in token tests as panic! arms, and pick up fmt drift.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 01:55:07 +00:00
Cameron
2d942a9926 feat: content-hash-aware tag/insight sharing + library scoping
Tags and insights now follow content across libraries via content_hash
lookups on the read path, so the same file indexed at different rel_paths
in multiple libraries shares its annotations. Recursive tag search scopes
hits to the selected library by checking each tagged rel_path against
the library's disk (with a content-hash sibling fallback so tags attached
under one library's rel_path still match a content-equivalent file in
another). The /image and /image/metadata handlers fall back across
libraries when the file isn't under the resolved one, so union-mode
search results (which carry no library attribution in the response)
still serve correctly.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-21 01:55:07 +00:00
Cameron
0aaea91cc2 feat: add content_hash backfill + register every media file
Adds blake3 content hashing as the basis for derivative dedup
(thumbnails, HLS) across libraries. Computed inline by the watcher on
ingest and by a new `backfill_hashes` binary for historical rows.

Key changes:
- `content_hash` and `size_bytes` are now populated on new image_exif
  rows; a new ExifDao surface (`get_rows_missing_hash`,
  `backfill_content_hash`, `find_by_content_hash`) supports backfill and
  future hash-keyed lookups.
- The watcher now registers every image/video in image_exif, not just
  files with parseable EXIF. EXIF becomes optional enrichment; videos
  and other non-EXIF files still get a hashed row. This also makes
  DB-indexed sort/filter cover the full library.
- `/image` thumbnail serve dual-looks up hash-keyed path first, then
  falls back to the legacy mirrored layout.
- Upload flow accepts `?library=` query param + hashes uploaded files.
- Store_exif logs the underlying Diesel error on insert failure so
  constraint violations surface instead of hiding behind a generic
  InsertError.
- New migration normalizes rel_path separators to forward slash across
  all tables, deduplicating any rows that collide after normalization.
  Fixes spurious UNIQUE violations from mixed backslash/forward-slash
  paths on Windows ingest.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-21 01:55:07 +00:00
Cameron
ffcddbb843 feat: multi-library foundation (schema + libraries module)
Adds a `libraries` registry table and threads library_id through
per-instance metadata tables (image_exif, photo_insights,
entity_photo_links, video_preview_clips). File-path columns renamed to
rel_path to make the relative-to-root semantics explicit. Adds
content_hash + size_bytes on image_exif to support future hash-keyed
thumbnail/HLS dedup. Tags and favorites stay library-agnostic so they
share across libraries by rel_path.

Behavior is unchanged: a single primary library (id=1) is seeded from
BASE_PATH on first boot; all handlers and DAOs route through it as a
transitional shim until the API gains a library query param.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-21 01:55:07 +00:00
Cameron
c703a47f17 Add the ability to rate insights to curate training data 2026-04-13 09:23:40 -04:00
Cameron
65e938035f fix: reduce duplicate entities from weak model inconsistency
Adds normalize_entity_type() which lowercases and canonicalises synonyms
(location→place, human→person, etc.) before every upsert. The SQL lookup
now uses lower(entity_type) on both sides so existing dirty rows (Person,
Location) correctly deduplicate against normalised writes without a migration.

Adds a pre-flight similarity check in tool_store_entity: before upserting,
searches active entities of the same type using the first name token. Any
non-exact matches are appended to the tool response so the agentic loop
can choose to reuse an existing entity ID rather than create a duplicate.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 18:27:09 -04:00
Cameron
da039bbc49 fix: include files without EXIF when sorting by date
Date sorting previously used a DB-level query that acted as an inner join,
silently dropping files with no image_exif row. Replace it with the existing
in-memory sort which already falls back to filename-extracted and filesystem
dates, so all files appear in sorted results.

Also removes the now-unused get_files_sorted_by_date trait method and its
SqliteExifDao implementation and test mock.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 14:43:26 -04:00
Cameron
191ccc0d77 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>
2026-04-03 17:27:49 -04:00
Cameron
c1b6013412 chore: cargo fmt + clippy fix for collapsed if-let chain (T017)
- cargo fmt applied across all modified source files
- Collapse nested if let Some / if !is_empty into a single let-chain (clippy::collapsible_match)
- All other warnings are pre-existing dead-code lint on unused trait methods

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 23:09:58 -04:00
Cameron
0d05033b38 Add comprehensive testing for preview clip and status handling
- Implement unit tests for PreviewClipRequest/PreviewStatusRequest serialization and deserialization.
- Add tests for PreviewDao (insert, update, batch retrieval, and status-based queries).
- Extend Actix-web integration tests for `/video/preview/status` endpoint scenarios.
- Introduce in-memory TestPreviewDao for mock database interactions.
- Update README with new config parameters for preview clips.
2026-02-26 10:06:21 -05:00
Cameron
19c099360e Add VideoWall feature: server-side preview clip generation and mobile grid view
Backend (Rust/Actix-web):
- Add video_preview_clips table and PreviewDao for tracking preview generation
- Add ffmpeg preview clip generator: 10 equally-spaced 1s segments at 480p with CUDA NVENC auto-detection
- Add PreviewClipGenerator actor with semaphore-limited concurrent processing
- Add GET /video/preview and POST /video/preview/status endpoints
- Extend file watcher to detect and queue previews for new videos
- Use relative paths consistently for DB storage (matching EXIF convention)

Frontend (React Native/Expo):
- Add VideoWall grid view with 2-3 column layout of looping preview clips
- Add VideoWallItem component with ActiveVideoPlayer sub-component for lifecycle management
- Add useVideoWall hook for batch status polling with 5s refresh
- Add navigation button in grid header (visible when videos exist)
- Use TextureView surface type to fix Android z-ordering issues
- Optimize memory: players only mount while visible via FlatList windowSize
- Configure ExoPlayer buffer options and caching for short clips
- Tap to toggle audio focus, long press to open in full viewer

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 19:40:17 -05:00
Cameron
1efdd02eda Add GPS summary sorting
Run cargo fmt/clippy
2026-01-28 10:52:17 -05:00
Cameron
073b5ed418 Added gps-summary endpoint for Map integration 2026-01-26 11:58:24 -05:00
Cameron
be483c9c1a Add database optimizations for photo search and pagination
Implement database-level sorting with composite indexes for efficient date and tag queries. Add pagination metadata support and optimize tag count queries using batch processing.
2026-01-18 19:17:10 -05:00
Cameron
af35a996a3 Cleanup unused message embedding code
Fixup some warnings
2026-01-14 13:33:36 -05:00
Cameron
e2d6cd7258 Run clippy fix 2026-01-14 13:17:58 -05:00
Cameron
084994e0b5 Daily Summary Embedding Testing 2026-01-08 13:41:32 -05:00
Cameron
61e10f7678 Improve Exif Query path handling 2026-01-08 13:41:08 -05:00
Cameron
cd66521c17 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>
2026-01-05 14:57:31 -05:00
Cameron
d86b2c3746 Add Google Takeout data import infrastructure
Implements Phase 1 & 2 of Google Takeout RAG integration:
- Database migrations for calendar_events, location_history, search_history
- DAO implementations with hybrid time + semantic search
- Parsers for .ics, JSON, and HTML Google Takeout formats
- Import utilities with batch insert optimization

Features:
- CalendarEventDao: Hybrid time-range + semantic search for events
- LocationHistoryDao: GPS proximity with Haversine distance calculation
- SearchHistoryDao: Semantic-first search (queries are embedding-rich)
- Batch inserts for performance (1M+ records in minutes vs hours)
- OpenTelemetry tracing for all database operations

Import utilities:
- import_calendar: Parse .ics with optional embedding generation
- import_location_history: High-volume GPS data with batch inserts
- import_search_history: Always generates embeddings for semantic search

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-05 14:50:49 -05:00
Cameron
bb23e6bb25 Cargo fix 2026-01-05 10:31:34 -05:00
Cameron
11e725c443 Enhanced Insights with daily summary embeddings
Bump to 0.5.0. Added daily summary generation job
2026-01-05 09:13:16 -05:00
Cameron
1171f19845 Create Insight Generation Feature
Added integration with Messages API and Ollama
2026-01-03 10:30:37 -05:00
Cameron
c035678162 Add tracing to EXIF DAO methods 2025-12-23 22:57:24 -05:00
Cameron
6dbac6f22f Run cargo fmt/fix 2025-12-23 22:07:50 -05:00
Cameron
aaf9cc64be Add Cleanup binary for fixing broken DB/file relations 2025-12-18 16:02:15 -05:00
Cameron
eb8e08b9ff Add EXIF search infrastructure (Phase 1 & 2)
Implements foundation for EXIF-based photo search capabilities:

- Add geo.rs module with GPS distance calculations (Haversine + bounding box)
- Extend FilesRequest with EXIF search parameters (camera, GPS, date, media type)
- Add MediaType enum and DateTakenAsc/DateTakenDesc sort options
- Create date_taken index migration for efficient date queries
- Implement ExifDao methods: get_exif_batch, query_by_exif, get_camera_makes
- Add FileWithMetadata struct for date-aware sorting
- Implement date sorting with filename extraction fallback
- Make extract_date_from_filename public for reuse

Next: Integrate EXIF filtering into list_photos() and enhance get_all_tags()
2025-12-18 09:34:07 -05:00
Cameron
c7fd328925 Check Exif DB for memory collection 2025-12-17 22:10:23 -05:00
Cameron
4082f1fdb8 Add Exif storing and update to Metadata endpoint 2025-12-17 16:55:48 -05:00
Cameron
f02a858368 Bump to 0.3.1 and format/clippy 2025-12-01 13:04:55 -05:00
Cameron
273b877e16 Update to Rust 2024 edition
Formatted code.
2025-09-01 13:36:27 -04:00
Cameron
3ce1b84604 Sort on recursive search
Run clippy
2024-12-06 11:21:42 -05:00
Cameron Cordes
ae2642a544 Fix warnings, and general code cleanup 2023-03-25 20:52:20 -04:00
Cameron Cordes
4ded708911 Extract FileSystem to a trait for better testability
Added some tests around filtering and searching by Tags. Added the
ability to use an in-memory Sqlite DB for more integration tests.
2023-03-22 15:17:33 -04:00
Cameron Cordes
de4041bd17 Refactor tags services and added tests
Implemented some functionality which will allow the service
configuration of the app to be split among the features for readability
and testability.
2023-03-19 15:05:20 -04:00
Cameron Cordes
68bfcbf85f Update and Migrate Diesel to 2.0
Almost have tag support working, still figuring out how to get photo
tags.
2023-03-18 14:43:41 -04:00
Cameron Cordes
40c79d13db Move tags to their own module
All checks were successful
Core Repos/ImageApi/pipeline/pr-master This commit looks good
2022-03-17 22:07:33 -04:00
Cameron Cordes
2e5ac8861c Add created timestamps for tags 2021-10-11 21:52:06 -04:00
Cameron Cordes
8939ffbaf5 Create Tag tables and Add Tag endpoint 2021-10-11 21:52:06 -04:00
Cameron Cordes
2c50b4ae2f Add anyhow, Improve Auth token code
Moved test helper code to its own module.
2021-10-07 20:32:36 -04:00
Cameron Cordes
9d823fdc51 Create file metadata endpoint
This allows retrieving create/modify date as well as file size for any
file in the BASE_PATH.
2021-05-19 08:53:20 -04:00
Cameron Cordes
2e97086751 FavoritesDao for querying, adding and removing favorites
Some checks failed
Core Repos/ImageApi/pipeline/head Something is wrong with the build of this commit
Core Repos/ImageApi/pipeline/pr-master This commit looks good
2021-03-27 16:33:45 -04:00
Cameron Cordes
31e95dc158 Fix some lint warning and simplify some code 2021-03-25 13:17:58 -04:00
Cameron Cordes
a2a9c27f12 Use Actix worker thread for database operations 2021-03-17 22:30:02 -04:00
Cameron Cordes
3611f46004 Fix test name and simplify helper 2021-03-04 16:37:19 -05:00