refactor: drop AI/vision pipeline + plain Postgres + full-refresh script
Removes the OpenCLIP-on-ONNX classifier and everything that fed or
consumed it:
- backend: app/services/vision/, app/tasks/vision.py,
app/services/feature_flags.py, app/routers/features.py — all
deleted; admin AI/feature-flag endpoints and the worker-vision
bootstrap call gone. Photo.needs_review and its index dropped.
- frontend: AI Settings tab, useFeaturesQuery hook, FeatureFlag
types, "Needs Review" sidebar entry + filter, needs_review filter
URL param all gone.
- infra: worker-vision compose service + models_data volume deleted;
worker-light command no longer runs bootstrap_models; the db
image switches from pgvector/pgvector:pg16 to postgres:16; backend
Dockerfile drops the dedicated torch RUN layer; requirements.txt
drops torch/torchvision/open-clip-torch/onnxruntime.
Alembic 0019_drop_ai_remnants:
- drops photos.needs_review + ix_photos_needs_review
- DROP EXTENSION IF EXISTS vector (must run before the image swap;
the new postgres:16 doesn't ship pgvector)
New scripts/full_refresh.py: one-shot DB ↔ filesystem reconciliation.
Runs cleanup_data_integrity, scans every active SourceRoot inline
(no celery dependency so the worker can be stopped), hard-prunes
photo + folder rows for files that are gone, removes orphan
/data/thumbs/{user}/{photo}/ directories. New helper
prune_orphan_thumbnails in cleanup.py.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -18,7 +18,7 @@ import type { MemoriesResponse, MemoryPhoto } from '../services/api'
|
||||
* per-photo cache so rating stars / color swatches flip instantly.
|
||||
* - Rolls back the patch on error and surfaces a toast.
|
||||
* - Invalidates the photo/library queries on success so server-side
|
||||
* derived fields (needs_review, date_warning, etc.) reconcile.
|
||||
* derived fields (date_warning, etc.) reconcile.
|
||||
*
|
||||
* Discard lives elsewhere — its two call sites have deliberately
|
||||
* different semantics (keep-in-place for the X hotkey; strip-from-
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
import { useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { features, type FeaturesMap } from '../services/api'
|
||||
|
||||
export const FEATURES_QUERY_KEY = ['features'] as const
|
||||
|
||||
/** Read the effective feature-flag state (admin override or YAML
|
||||
* default). Powers conditional rendering of pipeline-dependent UI —
|
||||
* People view, Tags view, OCR snippets, etc. */
|
||||
export function useFeaturesQuery() {
|
||||
return useQuery<FeaturesMap>({
|
||||
queryKey: FEATURES_QUERY_KEY,
|
||||
queryFn: features.list,
|
||||
// Re-read every minute so admin toggles reflect without a page
|
||||
// reload. The admin tab also invalidates this key on write so the
|
||||
// refresh can be immediate for the admin who just flipped it.
|
||||
staleTime: 60_000,
|
||||
refetchInterval: 60_000,
|
||||
})
|
||||
}
|
||||
|
||||
export function useIsFeatureEnabled(name: 'vision.enabled'): boolean {
|
||||
const { data } = useFeaturesQuery()
|
||||
// Default to enabled while loading so we don't flash "feature off"
|
||||
// during a first-paint fetch. The backend is the source of truth;
|
||||
// any gated UI that slipped through just returns empty data anyway.
|
||||
if (!data) return true
|
||||
return !!data[name]
|
||||
}
|
||||
|
||||
export function invalidateFeaturesQuery(queryClient: ReturnType<typeof useQueryClient>) {
|
||||
queryClient.invalidateQueries({ queryKey: FEATURES_QUERY_KEY })
|
||||
}
|
||||
@@ -86,7 +86,6 @@ function parseUrl(): HydratePayload {
|
||||
}
|
||||
|
||||
if (sp.get('duplicates') === 'true') out.duplicates = true
|
||||
if (sp.get('needs_review') === 'true') out.needsReview = true
|
||||
|
||||
const groupBy = sp.get('group')
|
||||
if (groupBy === 'date' || groupBy === 'tag') out.groupBy = groupBy
|
||||
@@ -120,7 +119,6 @@ function writeUrl(f: FilterState & { currentSection?: string }) {
|
||||
if (f.folderId) sp.set('folder_id', f.folderId)
|
||||
if (f.tagIds.length > 0) sp.set('tag_ids', f.tagIds.join(','))
|
||||
if (f.duplicates) sp.set('duplicates', 'true')
|
||||
if (f.needsReview) sp.set('needs_review', 'true')
|
||||
if (f.groupBy !== 'date') sp.set('group', f.groupBy)
|
||||
if (f.currentSection && f.currentSection !== 'all-photos')
|
||||
sp.set('section', f.currentSection)
|
||||
|
||||
@@ -65,7 +65,6 @@ export function usePhotosQuery() {
|
||||
folderId: s.folderId,
|
||||
tagIds: s.tagIds,
|
||||
duplicates: s.duplicates,
|
||||
needsReview: s.needsReview,
|
||||
groupBy: s.groupBy,
|
||||
sortBy: s.sortBy,
|
||||
sortOrder: s.sortOrder,
|
||||
|
||||
Reference in New Issue
Block a user