Skip to content
Open
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
119 commits
Select commit Hold shift + click to select a range
e506141
Add pet clustering engine with agglomerative clustering
Amrithesh-Kakkoth Mar 11, 2026
c4ae159
Wire pet clustering into ML pipeline
Amrithesh-Kakkoth Mar 11, 2026
096b952
Add FP16 model support, lazy session loading, and pet recognition types
Amrithesh-Kakkoth Mar 2, 2026
dc69aa3
Add pet face and body detection, alignment, and embedding pipeline
Amrithesh-Kakkoth Mar 2, 2026
ed5889e
Expose pet recognition pipeline through Rust API bridge
Amrithesh-Kakkoth Mar 2, 2026
5834bab
Integrate pet recognition into ML indexing pipeline with feature flag
Amrithesh-Kakkoth Mar 2, 2026
064e1a9
feat: Implement pet detection and display pet faces in file details.
Amrithesh-Kakkoth Mar 3, 2026
82724a2
Restore hardware acceleration and optimization for face/CLIP models
Amrithesh-Kakkoth Mar 3, 2026
c8cb1fa
Remove libc crate dependency, use raw extern C declaration for dlsym
Amrithesh-Kakkoth Mar 3, 2026
3205d5b
Remove per-image session unloading from ML pipeline
Amrithesh-Kakkoth Mar 3, 2026
1d0fd11
Remove debugPetOnlyMode and forceAll debug scaffolding
Amrithesh-Kakkoth Mar 3, 2026
364b81e
Address PR review feedback for pet recognition pipeline
Amrithesh-Kakkoth Mar 3, 2026
dc82c55
Fix pet detection never running on previously-indexed files
Amrithesh-Kakkoth Mar 3, 2026
da72e7c
Fix pet recognition: Mutex to RwLock, cat face classification, and mi…
Amrithesh-Kakkoth Mar 5, 2026
b9c0bbe
Rename detected_objects schema to pet_bodies and remove unused embedd…
Amrithesh-Kakkoth Mar 6, 2026
545a779
Retrigger CI checks
Amrithesh-Kakkoth Mar 13, 2026
d76e3f1
Add pet cluster feedback, naming, and search integration
Amrithesh-Kakkoth Mar 15, 2026
a3b74d4
Fix mutable runtime API after rebase
Amrithesh-Kakkoth Mar 15, 2026
2def4bb
Fix review issues: parameterize SQL, guard clustering memory, restore…
Amrithesh-Kakkoth Mar 15, 2026
b5db278
Add Rust agglomerative clustering fallback for human faces
Amrithesh-Kakkoth Mar 15, 2026
f198e7d
Fix clippy lints: collapsible ifs, is_multiple_of, doc formatting
Amrithesh-Kakkoth Mar 15, 2026
1f9c592
Remove unused deletePetName method
Amrithesh-Kakkoth Mar 15, 2026
258797e
Fix incremental pet clustering: stale summary deletion, centroid refr…
Amrithesh-Kakkoth Mar 15, 2026
3850b07
Fix post-feedback summary recomputation, offline pet thumbnails, and …
Amrithesh-Kakkoth Mar 15, 2026
f2453c4
Normalize merged pet centroid, route pet thumbnails in view-all, pin …
Amrithesh-Kakkoth Mar 16, 2026
153049f
Create singleton summaries after pet cluster split, map offline pet f…
Amrithesh-Kakkoth Mar 16, 2026
27f6af4
Recompute summaries after move/split, use offline IDs for pet reassig…
Amrithesh-Kakkoth Mar 16, 2026
6fdca79
Guard empty fileIds in SQL IN clause, use member face ID for cluster …
Amrithesh-Kakkoth Mar 16, 2026
8aae3af
Recompute centroids for all clusters in incremental face clustering, …
Amrithesh-Kakkoth Mar 16, 2026
5c606e8
Wire PetsChangedEvent into provider reload, fix faceVectorId test exp…
Amrithesh-Kakkoth Mar 16, 2026
21d01b7
Query DB for active cluster IDs when pruning stale pet summaries
Amrithesh-Kakkoth Mar 16, 2026
d909dfe
Weighted centroid merge for bucketed face clustering, fix people/pets…
Amrithesh-Kakkoth Mar 16, 2026
d44ef6b
Build pet summaries from DB membership, remove double-counted histori…
Amrithesh-Kakkoth Mar 16, 2026
2cc545f
Move pet cluster centroids from SQLite BLOB to vector DB, simplify in…
Amrithesh-Kakkoth Mar 17, 2026
17da4a5
Revert face pipeline and face clustering changes per review feedback
Amrithesh-Kakkoth Mar 17, 2026
f638be6
Fix stale HDBSCAN comments in pet clustering to reflect agglomerative…
Amrithesh-Kakkoth Mar 17, 2026
8a99a02
Fix centroid DB using wrong offline flag, read source summary before …
Amrithesh-Kakkoth Mar 17, 2026
a7dc882
Remove unused isOfflineMode import
Amrithesh-Kakkoth Mar 17, 2026
3fa7b24
Replace pet_names table with PetEntity in separate DB, revert face cl…
Amrithesh-Kakkoth Mar 17, 2026
dc3e699
Make mergePetClusters map both clusters to the same PetEntity
Amrithesh-Kakkoth Mar 17, 2026
546b3ee
Fix clippy warnings in Rust clustering test files
Amrithesh-Kakkoth Mar 17, 2026
f788c06
Add tests for PetEntity model and pet_cluster_pet mapping table
Amrithesh-Kakkoth Mar 17, 2026
c4d9118
Add PetEntity syncing via entity sync service using person type
Amrithesh-Kakkoth Mar 23, 2026
b5fcc3f
Align PetData with PersonData structure
Amrithesh-Kakkoth Mar 23, 2026
65d4f50
Fix stale pet cluster centroids, missing body centroids, and stale ga…
Amrithesh-Kakkoth Mar 23, 2026
fd79f56
Merge main into ml/pet-clustering
Amrithesh-Kakkoth Mar 23, 2026
9ae6d70
Fix Float64List type mismatch in body centroid computation
Amrithesh-Kakkoth Mar 23, 2026
984c261
Run cargo fmt on shared Rust crate
Amrithesh-Kakkoth Mar 23, 2026
a74d038
Simplify pet clustering to face-only, remove body pipeline
Amrithesh-Kakkoth Mar 23, 2026
507498d
Merge upstream main into ml/pet-clustering
Amrithesh-Kakkoth Mar 24, 2026
d0dd9c4
Fix unused import and clippy warnings in shared Rust crate
Amrithesh-Kakkoth Mar 24, 2026
1b8318a
Fix review issues: centroid mismatch, N+1 queries, bulk operations
Amrithesh-Kakkoth Mar 24, 2026
ada5dec
Implement body rescue, body-only clustering, and cross-cluster merge
Amrithesh-Kakkoth Mar 24, 2026
01a9aeb
Fix await on void PersonService.init in ML compute path
Amrithesh-Kakkoth Mar 24, 2026
cbe6d2f
Only fire PetsChangedEvent when clustering changes, simplify pet save…
Amrithesh-Kakkoth Mar 24, 2026
ed64d4c
Fire PetsChangedEvent instead of PeopleChangedEvent on pet cluster reset
Amrithesh-Kakkoth Mar 24, 2026
8c6ce6f
Clear conflicting target rejections when manually moving pet faces
Amrithesh-Kakkoth Mar 24, 2026
11d2c4c
Filter exported cluster assignments by species in dumpEmbeddingsJson
Amrithesh-Kakkoth Mar 24, 2026
e5bb4a9
Use localized species labels for autogenerated pet cluster names
Amrithesh-Kakkoth Mar 24, 2026
9f56c59
Add real-image clustering tests, fix import ordering, add docs
Amrithesh-Kakkoth Mar 24, 2026
d202982
Merge upstream main into ml/pet-clustering
Amrithesh-Kakkoth Mar 24, 2026
88eda09
Load merged pet cluster files and fix Rust clustering phases
Amrithesh-Kakkoth Mar 24, 2026
5376e91
Add pet cluster unmerge (revert merge)
Amrithesh-Kakkoth Mar 24, 2026
56acf5c
Fix setState during build in PetClusterPage event listener
Amrithesh-Kakkoth Mar 24, 2026
3401008
Fix duplicate keys by deduplicating files in merged pet clusters
Amrithesh-Kakkoth Mar 24, 2026
7368bfe
Fix PetClustersPage to reload after unmerge and show remove text
Amrithesh-Kakkoth Mar 24, 2026
eb8e9cc
Remove outdated pet clustering docs
Amrithesh-Kakkoth Mar 24, 2026
efef51a
Require named pet for cluster merge, remove empty PetEntity creation
Amrithesh-Kakkoth Mar 24, 2026
d76295a
Add pet cluster reconciliation for cross-device sync
Amrithesh-Kakkoth Mar 24, 2026
d034076
Only allow merging clusters into named pets
Amrithesh-Kakkoth Mar 24, 2026
d2bad9f
Regenerate Flutter Rust Bridge bindings and update deps
Amrithesh-Kakkoth Mar 24, 2026
33d81c0
Run cargo fmt on shared Rust crate
Amrithesh-Kakkoth Mar 24, 2026
381d130
Add species ambiguity filter and fix clippy warnings
Amrithesh-Kakkoth Mar 25, 2026
ba96f83
Remove HDBSCAN code and benchmark test files
Amrithesh-Kakkoth Mar 25, 2026
5d28d3c
Remove unused PetService and PetEntity imports from feedback service
Amrithesh-Kakkoth Mar 25, 2026
c3884be
Add re-index pet faces button to ML debug settings
Amrithesh-Kakkoth Mar 25, 2026
a4b8fd1
Show 'People & Pets' title on all-faces page when pets enabled
Amrithesh-Kakkoth Mar 27, 2026
3582c7e
Clear pet clustering tables and centroid VDBs during full ML reset
Amrithesh-Kakkoth Mar 27, 2026
605fa3d
Fix stale pet cluster mappings not removed during remote sync
Amrithesh-Kakkoth Mar 27, 2026
d762000
Add multi-exemplar incremental clustering
Amrithesh-Kakkoth Mar 27, 2026
ccea472
Fix trailing comma and doc comment lint issues
Amrithesh-Kakkoth Mar 27, 2026
4095166
Fix unused variable warning in Rust clustering API
Amrithesh-Kakkoth Mar 27, 2026
46d6c59
Resolve merge conflicts in detail_page, pubspec, and inline_text_dete…
Amrithesh-Kakkoth Mar 31, 2026
9386266
Suppress human faces misdetected as pets via cross-model filtering
Amrithesh-Kakkoth Mar 31, 2026
7a30c62
Merge remote-tracking branch 'origin/main' into ml/pet-clustering
Amrithesh-Kakkoth Apr 2, 2026
5b2d733
Refactor pet detection filtering and hide default pet labels
Amrithesh-Kakkoth Apr 2, 2026
ab64ba6
Merge main into ml/pet-clustering
Amrithesh-Kakkoth Apr 2, 2026
f62338c
Fix rustfmt formatting in pet clustering
Amrithesh-Kakkoth Apr 2, 2026
70ddeb6
Fix rustfmt formatting in indexing.rs
Amrithesh-Kakkoth Apr 2, 2026
d35aa8b
Fix clippy warnings in pet clustering
Amrithesh-Kakkoth Apr 2, 2026
bd3b029
Remove redundant null checks on LatLng from latlngAsync
Amrithesh-Kakkoth Apr 2, 2026
49feb77
Update pet clustering and service layer after main merge
Amrithesh-Kakkoth Apr 2, 2026
82457e2
Merge upstream main, resolve import conflict in ml_service.dart
Amrithesh-Kakkoth Apr 2, 2026
310c936
Fix pet sync race: push local before stale cleanup, clear empty pets
Amrithesh-Kakkoth Apr 2, 2026
2fa25d0
Optimize agglomerative clustering
Amrithesh-Kakkoth Apr 6, 2026
2e1a93b
Format cluster.rs
Amrithesh-Kakkoth Apr 6, 2026
8301f6f
Fix clippy lint in cluster loop
Amrithesh-Kakkoth Apr 6, 2026
39ba3e0
fix: Guard offline mode from user-feedback tables and edit actions
Amrithesh-Kakkoth Apr 7, 2026
7fafbd4
chore: Apply dart format
Amrithesh-Kakkoth Apr 7, 2026
0fe3d34
refactor: remove pet human-face cross-check
Amrithesh-Kakkoth Apr 7, 2026
90636cd
refactor: remove redundant pet search params
Amrithesh-Kakkoth Apr 7, 2026
3ee48e1
refactor: remove pet cluster move flow
Amrithesh-Kakkoth Apr 7, 2026
5ceff27
refactor: drop pet cluster merge helper
Amrithesh-Kakkoth Apr 7, 2026
c8c06f9
refactor: remove incomplete pet merge flows
Amrithesh-Kakkoth Apr 7, 2026
f3d16a5
Merge remote-tracking branch 'origin/main' into ml/pet-clustering
Amrithesh-Kakkoth Apr 7, 2026
d94c576
build: drop photos rust rlib crate type
Amrithesh-Kakkoth Apr 7, 2026
bc52511
fix: await pet assignment sync updates
Amrithesh-Kakkoth Apr 7, 2026
0db2a23
fix: Add missing banner actions and clean up pet deletion
Amrithesh-Kakkoth Apr 8, 2026
b5f151a
refactor: remove pet cluster summary table
Amrithesh-Kakkoth Apr 8, 2026
4480790
refactor: Remove unused migration tables, handle version downgrade
Amrithesh-Kakkoth Apr 8, 2026
0af538f
refactor: clean up pet clustering leftovers
Amrithesh-Kakkoth Apr 8, 2026
420ec49
feat: gate pet clustering behind internal release
Amrithesh-Kakkoth Apr 8, 2026
58be078
revert: restore pet feature flag behavior
Amrithesh-Kakkoth Apr 8, 2026
7029dda
feat: Add pet interaction parity with person flow
Amrithesh-Kakkoth Apr 8, 2026
f1ea3d8
fix: assert on db version downgrade
Amrithesh-Kakkoth Apr 8, 2026
619fde8
refactor: Remove unused pet cluster centroid VDB
Amrithesh-Kakkoth Apr 8, 2026
ffdd668
fix: Restore createPetBodyVectorIdMappingTable in migration lists
Amrithesh-Kakkoth Apr 8, 2026
d384b74
refactor: Use spread for offline migration scripts
Amrithesh-Kakkoth Apr 8, 2026
9121f8c
chore: Remove petClusterCentroidVectorIdMappingTable references
Amrithesh-Kakkoth Apr 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions mobile/apps/photos/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ PODS:
- Flutter
- ente_photos_rust (0.0.1):
- Flutter
- ente_qr (0.0.1):
- Flutter
- ente_rust (0.0.1):
- Flutter
- ffmpeg_kit_custom (6.0.3)
Expand Down Expand Up @@ -271,6 +273,7 @@ DEPENDENCIES:
- dart_ui_isolate (from `.symlinks/plugins/dart_ui_isolate/ios`)
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
- ente_photos_rust (from `.symlinks/plugins/ente_photos_rust/ios`)
- ente_qr (from `.symlinks/plugins/ente_qr/ios`)
- ente_rust (from `.symlinks/plugins/ente_rust/ios`)
- ffmpeg_kit_flutter (from `.symlinks/plugins/ffmpeg_kit_flutter/ios`)
- file_saver (from `.symlinks/plugins/file_saver/ios`)
Expand Down Expand Up @@ -369,6 +372,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/device_info_plus/ios"
ente_photos_rust:
:path: ".symlinks/plugins/ente_photos_rust/ios"
ente_qr:
:path: ".symlinks/plugins/ente_qr/ios"
ente_rust:
:path: ".symlinks/plugins/ente_rust/ios"
ffmpeg_kit_flutter:
Expand Down Expand Up @@ -490,6 +495,7 @@ SPEC CHECKSUMS:
dart_ui_isolate: 46f6714abe6891313267153ef6f9748d8ecfcab1
device_info_plus: 21fcca2080fbcd348be798aa36c3e5ed849eefbe
ente_photos_rust: 79732f3ba67df7b0fb22089627afb80d692c2b2d
ente_qr: 2a30f91f9e938c229ebcbc98a6f8f018d1ac665d
ente_rust: 9c976e3d083f0ce90f31693deb99629b28d8fe4c
ffmpeg_kit_custom: 682b4f2f1ff1f8abae5a92f6c3540f2441d5be99
ffmpeg_kit_flutter: 915b345acc97d4142e8a9a8549d177ff10f043f5
Expand Down
2 changes: 2 additions & 0 deletions mobile/apps/photos/ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -637,6 +637,7 @@
"${BUILT_PRODUCTS_DIR}/dart_ui_isolate/dart_ui_isolate.framework",
"${BUILT_PRODUCTS_DIR}/device_info_plus/device_info_plus.framework",
"${BUILT_PRODUCTS_DIR}/ente_photos_rust/ente_photos_rust.framework",
"${BUILT_PRODUCTS_DIR}/ente_qr/ente_qr.framework",
"${BUILT_PRODUCTS_DIR}/ente_rust/ente_rust.framework",
"${BUILT_PRODUCTS_DIR}/file_saver/file_saver.framework",
"${BUILT_PRODUCTS_DIR}/flutter_email_sender/flutter_email_sender.framework",
Expand Down Expand Up @@ -739,6 +740,7 @@
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/dart_ui_isolate.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/device_info_plus.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ente_photos_rust.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ente_qr.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ente_rust.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/file_saver.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_email_sender.framework",
Expand Down
36 changes: 31 additions & 5 deletions mobile/apps/photos/lib/db/ml/db.dart
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,19 @@ class MLDataDB with SqlDbBase implements IMLDataDB<int> {
createPetBodiesTable,
createPetFaceVectorIdMappingTable,
createPetBodyVectorIdMappingTable,
createPetIndexedFilesTable,
dropPetIndexedFilesTable,
dropPetFaceClustersTable,
dropPetClusterSummaryTable,
dropPetFacesTable, // 23: recreate with UNIQUE faceVectorId
createPetFacesTable,
dropPetBodiesTable, // 25: recreate with UNIQUE bodyVectorId
createPetBodiesTable,
createPetFaceClustersTable, // 26: recreate (dropped in step 20)
petFcClusterIDIndex, // 27
createPetClusterSummaryTable, // 28: recreate (dropped in step 21)
createPetNamesTable, // 29: pet name persistence
createNotPetFeedbackTable, // 30: not-pet feedback for manual reassignment
];
static const List<String> _offlineMigrationScripts = [
..._defaultMigrationScripts,
Expand Down Expand Up @@ -2460,21 +2473,28 @@ class MLDataDB with SqlDbBase implements IMLDataDB<int> {
}
}

// Delete mapping table entries and detection rows atomically.
// Delete mapping table entries, cluster assignments, and detection rows
// atomically. Also clean up cluster summaries that become empty.
await db.writeTransaction((tx) async {
if (faceIdsToRemove.isNotEmpty) {
final placeholders = List.filled(faceIdsToRemove.length, '?').join(',');
final fpH = List.filled(faceIdsToRemove.length, '?').join(',');
await tx.execute(
'DELETE FROM $petFaceVectorIdMappingTable '
'WHERE $petFaceIDColumn IN ($placeholders)',
'WHERE $petFaceIDColumn IN ($fpH)',
faceIdsToRemove,
);
// Remove cluster assignments for deleted faces
await tx.execute(
'DELETE FROM $petFaceClustersTable '
'WHERE $petFaceIDColumn IN ($fpH)',
faceIdsToRemove,
);
}
if (bodyIdsToRemove.isNotEmpty) {
final placeholders = List.filled(bodyIdsToRemove.length, '?').join(',');
final bpH = List.filled(bodyIdsToRemove.length, '?').join(',');
await tx.execute(
'DELETE FROM $petBodyVectorIdMappingTable '
'WHERE $petBodyIDColumn IN ($placeholders)',
'WHERE $petBodyIDColumn IN ($bpH)',
bodyIdsToRemove,
);
}
Expand All @@ -2486,6 +2506,12 @@ class MLDataDB with SqlDbBase implements IMLDataDB<int> {
'DELETE FROM $petBodiesTable WHERE $fileIDColumn IN ($placeholders)',
fileIDs,
);
// Clean up cluster summaries that no longer have any face assignments
await tx.execute(
'DELETE FROM $petClusterSummaryTable '
'WHERE $clusterIDColumn NOT IN '
'(SELECT DISTINCT $clusterIDColumn FROM $petFaceClustersTable)',
);
});
}

Expand Down
72 changes: 72 additions & 0 deletions mobile/apps/photos/lib/db/ml/schema.dart
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ const createPetFacesTable = '''CREATE TABLE IF NOT EXISTS $petFacesTable (
''';

const deletePetFacesTable = 'DELETE FROM $petFacesTable';
const dropPetFacesTable = 'DROP TABLE IF EXISTS $petFacesTable';

// ── Pet Bodies Table ──

Expand All @@ -235,6 +236,7 @@ const createPetBodiesTable = '''CREATE TABLE IF NOT EXISTS $petBodiesTable (
''';

const deletePetBodiesTable = 'DELETE FROM $petBodiesTable';
const dropPetBodiesTable = 'DROP TABLE IF EXISTS $petBodiesTable';

// ── Vector ID Mapping Tables ──

Expand Down Expand Up @@ -265,3 +267,73 @@ CREATE TABLE IF NOT EXISTS $petBodyVectorIdMappingTable (

const deletePetBodyVectorIdMappingTable =
'DELETE FROM $petBodyVectorIdMappingTable';

// ── Pet Face Clusters Table ──

const petFaceClustersTable = 'pet_face_clusters';

const createPetFaceClustersTable = '''
CREATE TABLE IF NOT EXISTS $petFaceClustersTable (
$petFaceIDColumn TEXT NOT NULL PRIMARY KEY,
$clusterIDColumn TEXT NOT NULL
);
''';

const dropPetFaceClustersTable = 'DROP TABLE IF EXISTS $petFaceClustersTable';

const petFcClusterIDIndex =
'CREATE INDEX IF NOT EXISTS idx_pet_fc_cluster ON $petFaceClustersTable ($clusterIDColumn)';

// ── Pet Cluster Summary Table ──

const petClusterSummaryTable = 'pet_cluster_summary';

const createPetClusterSummaryTable = '''
CREATE TABLE IF NOT EXISTS $petClusterSummaryTable (
$clusterIDColumn TEXT NOT NULL PRIMARY KEY,
$avgColumn BLOB NOT NULL,
$countColumn INTEGER NOT NULL DEFAULT 0,
$speciesColumn INTEGER NOT NULL DEFAULT -1
);
''';

const dropPetClusterSummaryTable =
'DROP TABLE IF EXISTS $petClusterSummaryTable';

// ── Pet Names Table ──

const petNamesTable = 'pet_names';
const petNameColumn = 'name';

const createPetNamesTable = '''
CREATE TABLE IF NOT EXISTS $petNamesTable (
$clusterIDColumn TEXT NOT NULL PRIMARY KEY,
$petNameColumn TEXT NOT NULL DEFAULT '',
$speciesColumn INTEGER NOT NULL DEFAULT -1
);
''';

// ── Not-Pet Feedback Table ──

const notPetFeedbackTable = 'not_pet_feedback';

const createNotPetFeedbackTable = '''
CREATE TABLE IF NOT EXISTS $notPetFeedbackTable (
$clusterIDColumn TEXT NOT NULL,
$petFaceIDColumn TEXT NOT NULL,
PRIMARY KEY($clusterIDColumn, $petFaceIDColumn)
);
''';

// ── Pet Indexing Tracking Table ──

const petIndexedFilesTable = 'pet_indexed_files';

const createPetIndexedFilesTable = '''
CREATE TABLE IF NOT EXISTS $petIndexedFilesTable (
$fileIDColumn INTEGER NOT NULL PRIMARY KEY,
$mlVersionColumn INTEGER NOT NULL DEFAULT -1
);
''';

const dropPetIndexedFilesTable = 'DROP TABLE IF EXISTS $petIndexedFilesTable';
10 changes: 10 additions & 0 deletions mobile/apps/photos/lib/events/pets_changed_event.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import "package:photos/events/event.dart";

class PetsChangedEvent extends Event {
final String source;

PetsChangedEvent({this.source = ""});

@override
String get reason => '$runtimeType{"via": $source}';
}
6 changes: 5 additions & 1 deletion mobile/apps/photos/lib/l10n/intl_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -1508,6 +1508,7 @@
},
"faces": "Faces",
"people": "People",
"peopleAndPets": "People & Pets",
"contents": "Contents",
"addNew": "Add new",
"@addNew": {
Expand Down Expand Up @@ -2904,5 +2905,8 @@
"offlineNameFaceBannerSubtitle": "Sign up to tag them and easily find their photos later.",
"signUp": "Sign up",
"dog": "Dog",
"cat": "Cat"
"cat": "Cat",
"pet": "Pet",
"moveTo": "Move to",
"notThisPet": "Not this pet"
}
3 changes: 3 additions & 0 deletions mobile/apps/photos/lib/models/search/search_constants.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,8 @@ const kPersonWidgetKey = 'person_widget_key';
const kPersonPinned = 'person_pinned';
const kClusterParamId = 'cluster_id';
const kFileID = 'file_id';
const kPetClusterParamId = 'pet_cluster_id';
const kPetSpecies = 'pet_species';
const kPetHasCustomName = 'pet_has_custom_name';
const kContactEmail = 'contact_email';
const kContactCollections = 'contact_collections';
16 changes: 8 additions & 8 deletions mobile/apps/photos/lib/models/search/search_types.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ import "package:photos/events/event.dart";
import "package:photos/events/location_tag_updated_event.dart";
import "package:photos/events/magic_cache_updated_event.dart";
import "package:photos/events/people_changed_event.dart";
import "package:photos/events/pets_changed_event.dart";
import "package:photos/generated/l10n.dart";
import "package:photos/models/collection/collection.dart";
import "package:photos/models/collection/collection_items.dart";
import "package:photos/models/search/search_result.dart";
import "package:photos/models/typedefs.dart";
import "package:photos/services/collections_service.dart";
import "package:photos/services/machine_learning/face_ml/face_filtering/face_filtering_constants.dart";
import "package:photos/services/search_service.dart";
import "package:photos/ui/viewer/gallery/collection_page.dart";
import "package:photos/ui/viewer/location/add_location_sheet.dart";
Expand Down Expand Up @@ -252,12 +252,7 @@ extension SectionTypeExtensions on SectionType {
}) {
switch (this) {
case SectionType.face:
return SearchService.instance.getAllFace(
limit,
minClusterSize: limit == null
? kMinimumClusterSizeAllFaces
: kMinimumClusterSizeSearchResult,
);
return SearchService.instance.getAllPeopleAndPets(limit);
case SectionType.magic:
return SearchService.instance.getMagicSectionResults(context!);
case SectionType.wrapped:
Expand Down Expand Up @@ -290,7 +285,10 @@ extension SectionTypeExtensions on SectionType {
case SectionType.album:
return [Bus.instance.on<CollectionUpdatedEvent>()];
case SectionType.face:
return [Bus.instance.on<PeopleChangedEvent>()];
return [
Bus.instance.on<PeopleChangedEvent>(),
Bus.instance.on<PetsChangedEvent>(),
];
case SectionType.contacts:
return [Bus.instance.on<PeopleChangedEvent>()];
default:
Expand All @@ -302,6 +300,8 @@ extension SectionTypeExtensions on SectionType {
///events listened to in AllSectionsExampleState.
List<Stream<Event>> sectionUpdateEvents() {
switch (this) {
case SectionType.face:
return [Bus.instance.on<PetsChangedEvent>()];
case SectionType.location:
return [Bus.instance.on<LocationTagUpdatedEvent>()];
case SectionType.magic:
Expand Down
29 changes: 29 additions & 0 deletions mobile/apps/photos/lib/services/machine_learning/ml_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import "package:photos/db/ml/db_pet_model_mappers.dart";
import "package:photos/db/offline_files_db.dart";
import "package:photos/events/compute_control_event.dart";
import "package:photos/events/people_changed_event.dart";
import "package:photos/events/pets_changed_event.dart";
import "package:photos/models/ml/clip.dart";
import "package:photos/models/ml/face/face.dart";
import "package:photos/models/ml/ml_versions.dart";
Expand All @@ -24,6 +25,7 @@ import "package:photos/services/machine_learning/face_ml/face_detection/detectio
import "package:photos/services/machine_learning/face_ml/person/person_service.dart";
import "package:photos/services/machine_learning/ml_indexing_isolate.dart";
import 'package:photos/services/machine_learning/ml_result.dart';
import "package:photos/services/machine_learning/pet_ml/pet_clustering_service.dart";
import "package:photos/services/machine_learning/semantic_search/semantic_search_service.dart";
import "package:photos/services/search_service.dart";
import "package:photos/services/video_preview_service.dart";
Expand Down Expand Up @@ -215,6 +217,14 @@ class MLService {
if ((await mlDataDB.getUnclusteredFaceCount()) > 0) {
await clusterAllImages();
}
if (_hasModeChanged(mode)) {
_logger.info("App mode changed during ML run, stopping");
return;
}
// Pet clustering (internal users only)
if (flagService.petEnabled && localSettings.petRecognitionEnabled) {
await _clusterPets(mlDataDB, mode);
}
if (_mlControllerStatus == true) {
if (_hasModeChanged(mode)) {
_logger.info("App mode changed during ML run, stopping");
Expand Down Expand Up @@ -533,6 +543,19 @@ class MLService {
}
}

Future<void> _clusterPets(MLDataDB mlDataDB, MLMode mode) async {
if (_shouldPauseIndexingAndClustering) return;
try {
await PetClusteringService.instance.clusterPets(
mlDataDB: mlDataDB,
isOffline: mode == MLMode.offline,
);
Bus.instance.fire(PetsChangedEvent(source: "clustering"));
} catch (e, s) {
_logger.severe("Pet clustering failed", e, s);
}
}

Future<bool> processImage(FileMLInstruction instruction) async {
bool actuallyRanML = false;

Expand Down Expand Up @@ -692,6 +715,9 @@ class MLService {
);
}
}
if (instruction.shouldRunPets) {
await mlDataDB.markPetIndexed(result.fileId, petMlVersion);
}
_logger.info("ML result for fileID ${result.fileId} stored remote+local");
return actuallyRanML;
} catch (e, s) {
Expand Down Expand Up @@ -730,6 +756,9 @@ class MLService {
[DBPetFace.empty(instruction.fileKey, error: true)],
);
}
if (instruction.shouldRunPets) {
await mlDataDB.markPetIndexed(instruction.fileKey, petMlVersion);
}
return true;
}
_logger.severe(
Expand Down
Loading
Loading