fix: vision tasks inherit user_id, admin owns mount root
- detect_objects, classify_content, recluster_faces now look up the photo's user_id and set it on created Tag rows — fixes tags being invisible to the owning user due to NULL user_id - Initial admin setup creates source root at the mount root (/photos) instead of a subdirectory, since the admin owns the entire library - Revert to OpenCLIP ViT-B/32 (512-d) as default embedder — SigLIP requires transformers version alignment not yet available in the Docker image. SigLIP2 code remains for future enablement. - Add transformers to requirements for future SigLIP support Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -163,6 +163,12 @@ def detect_objects(photo_id: str):
|
||||
|
||||
session = _get_sync_session()
|
||||
try:
|
||||
# Get the photo's user_id so tags inherit ownership.
|
||||
photo = session.execute(
|
||||
select(Photo).where(Photo.id == photo_id)
|
||||
).scalar_one_or_none()
|
||||
owner_id = photo.user_id if photo else None
|
||||
|
||||
# Wipe previous detection results for this photo from this model
|
||||
session.execute(
|
||||
delete(photo_tags).where(
|
||||
@@ -178,13 +184,13 @@ def detect_objects(photo_id: str):
|
||||
best_per_label[det.label] = (det.confidence, det.bbox)
|
||||
|
||||
for label, (confidence, bbox) in best_per_label.items():
|
||||
# Find or create the object tag
|
||||
# Find or create the object tag (scoped to user)
|
||||
tag = session.execute(
|
||||
select(Tag).where(Tag.name == label, Tag.kind == 'object')
|
||||
select(Tag).where(Tag.name == label, Tag.kind == 'object', Tag.user_id == owner_id)
|
||||
).scalar_one_or_none()
|
||||
|
||||
if not tag:
|
||||
tag = Tag(name=label, kind='object', source=source_name)
|
||||
tag = Tag(name=label, kind='object', source=source_name, user_id=owner_id)
|
||||
session.add(tag)
|
||||
session.flush() # get tag.id
|
||||
|
||||
@@ -234,6 +240,12 @@ def classify_content(photo_id: str):
|
||||
|
||||
session = _get_sync_session()
|
||||
try:
|
||||
# Get the photo's user_id so tags inherit ownership.
|
||||
photo = session.execute(
|
||||
select(Photo).where(Photo.id == photo_id)
|
||||
).scalar_one_or_none()
|
||||
owner_id = photo.user_id if photo else None
|
||||
|
||||
# Wipe previous classification for this photo
|
||||
session.execute(
|
||||
delete(photo_tags).where(
|
||||
@@ -242,13 +254,13 @@ def classify_content(photo_id: str):
|
||||
)
|
||||
)
|
||||
|
||||
# Find or create content_type tag
|
||||
# Find or create content_type tag (scoped to user)
|
||||
tag = session.execute(
|
||||
select(Tag).where(Tag.name == best.label, Tag.kind == 'content_type')
|
||||
select(Tag).where(Tag.name == best.label, Tag.kind == 'content_type', Tag.user_id == owner_id)
|
||||
).scalar_one_or_none()
|
||||
|
||||
if not tag:
|
||||
tag = Tag(name=best.label, kind='content_type', source=source_name)
|
||||
tag = Tag(name=best.label, kind='content_type', source=source_name, user_id=owner_id)
|
||||
session.add(tag)
|
||||
session.flush()
|
||||
|
||||
@@ -434,11 +446,16 @@ def recluster_faces():
|
||||
|
||||
if label not in cluster_tag_map:
|
||||
cluster_name = f"Person {label + 1}"
|
||||
# Inherit user_id from the representative photo.
|
||||
rep_photo = session.execute(
|
||||
select(Photo.user_id).where(Photo.id == face_rows[i].photo_id)
|
||||
).scalar_one_or_none()
|
||||
tag = Tag(
|
||||
name=cluster_name,
|
||||
kind='face_cluster',
|
||||
source=source_name,
|
||||
representative_photo_id=face_rows[i].photo_id,
|
||||
user_id=rep_photo,
|
||||
)
|
||||
session.add(tag)
|
||||
session.flush()
|
||||
|
||||
Reference in New Issue
Block a user