Repository navigation
Cameras whose names share a prefix read each other's stored captions #706
Description
What happens
Stored camera captions are read by namespace ("video_analysis", <camera name>), and the LangGraph Postgres store matches a namespace as a prefix (store.prefix LIKE 'video_analysis.side%'). A read for camera.side therefore also returns rows for camera.side_gate, camera.side_yard and so on. _ in a camera name is itself a LIKE wildcard, so front_door can match other names too.
#705 (3.43.0) made caption deduplication drop the other cameras' rows after the read. That stops one camera's caption from suppressing another's, but the store's row limit is still applied before the filter:
- Similarity search (
_search_own_captions,core/video_analyzer.py): asks for 50 rows. If a prefix-sharing camera has 50 or more closer matches, none of the camera's own captions come back. The result isno_matchorscore_below_threshold, the notification is sent, and the recent-caption scan is never reached from those branches. A static scene ("parked car") on such a camera can notify repeatedly. - Recent-caption scan (
_recent_captions): its 4 x 50 pages count notified rows from every prefix-sharing camera, so a busy sibling can use up the budget before the camera's own notified caption is read. - Camera activity for the agent (
agent/camera_activity.py,get_recent_camera_activity):store.asearch(("video_analysis", camera), limit=1)takes the newest row under the prefix, so "last activity" forsidecan beside_gate's caption. This one was not touched by Caption dedup: suppress on any recent notified match, not only the closest (#704) #705.
All three fail toward an extra notification or a wrong label, never a missed alert, and only affect installs where one camera's entity name is a prefix of (or wildcard-matches) another's.
Expected
A read for one camera returns that camera's rows only, and the row limit applies to them.
Fix ideas
- Store the camera name in each caption's value and pass it as a store
filter(value->'camera' = ...), so the database applies it before the limit. Rows stored before the change lack the field; decide whether to backfill them or accept that old captions stop matching (static scenes would notify once more each). - Or write new captions under a namespace that cannot prefix-match another camera's (an extra terminating segment), and read both shapes for a while.
- Escape
_and%if the namespace stays as it is; the store does not.
Tests to add
Fifty foreign rows ahead of an own match in the similarity search; foreign notified rows filling the scan's pages; get_recent_camera_activity with side and side_gate.
Activity
goruck commentedon Oct 26, 2026
Progress update: #867 fixes two of the three readers. This issue stays open for the third, the similarity search.
Fixed in #867 (bundled for 3.45.0, once merged):
side_gatecan no longer fill the scan's 200 rows with its own notifications. Captions stored earlier get the field from a background task after startup.get_recent_camera_activityhad no caller since 1f68e4f, so it was removed rather than fixed.Still open: the similarity search (
_search_own_captions). It still reads cameras that share a name prefix together. A sibling camera with 50 or more closer matches can push this camera's own matches out, which givesno_match, and a static scene can notify again.Both database-side fixes trade one problem for another. Measured on pg17 with pgvector 0.8.7, on 100k synthetic captions with 1024-dimension vectors and an HNSW index:
filter={"camera": …}value->'camera', so Postgres scores every caption of the cameraCREATE STATISTICS ON (value -> 'camera')ef_search)hnsw.iterative_scan = relaxed_orderPrepared statements did not undo the statistics; timings stayed steady over 40 runs per connection.
Options for this part:
("video_analysis", camera, "captions"), read in both shapes for a transition period.EXPLAIN ANALYZEon a live store. That overlaps with Check whether the store's vector search uses the HNSW index with post-filtering #707's question.Merge pull request #867 from goruck/fix/camera-caption-namespace-prefix
goruck commentedon Oct 27, 2026
Fixed by #867 (the recent-caption scan reads only the camera's own captions; older captions get their camera in the background) and #868 (when another camera's closer captions take every similarity-search row, the camera-filtered recent-caption scan runs before notifying). Both are merged and ship in the 3.45.0 bundle.
One limit remains, documented in
docs/camera-entities.md: on a camera whose name another camera's starts with (side/side_gate), a crowded-out match older than 30 minutes is still not found, so a static scene there can notify once per half hour. The live-store measurements behind this approach are on #707.