Reject vision matches with action phase mismatches

This commit is contained in:
Melbar
2026-05-02 16:49:47 +02:00
parent d9e470c877
commit 1a177d6b89
3 changed files with 177 additions and 3 deletions
+61
View File
@@ -632,6 +632,66 @@ def _merge_best_results(existing: list, candidates: list, cfg) -> list:
return sorted(by_id.values(), key=lambda r: r.beat_id)
def _filter_semantically_invalid_vision_matches(results: list, beats: list, cfg) -> list:
"""Drop vision-enabled matches whose final action phase contradicts the beat."""
if not cfg.vision.enabled or not results:
return results
from dataclasses import replace
from src.llm.vision_cache import validate_match_window_with_vision
logger = logging.getLogger(__name__)
beats_by_id = {beat.beat_id: beat for beat in beats}
kept = []
for result in results:
beat = beats_by_id.get(result.beat_id)
if beat is None:
kept.append(result)
continue
windows = []
if getattr(result, "segments", ()):
for segment in result.segments:
segment_beat = replace(
beat,
start_s=beat.start_s + segment.trailer_offset_s,
end_s=beat.start_s + segment.trailer_offset_s + segment.duration_s,
)
windows.append((
segment_beat,
segment.scene_id,
segment.in_point_s,
segment.out_point_s,
))
else:
windows.append((beat, result.scene_id, result.in_point_s, result.out_point_s))
valid = True
reasons: list[str] = []
for check_beat, scene_id, in_point_s, out_point_s in windows:
ok, reason = validate_match_window_with_vision(
check_beat,
source_path=result.source_path,
scene_id=scene_id,
in_point_s=in_point_s,
out_point_s=out_point_s,
cfg=cfg,
)
reasons.append(reason)
if not ok:
valid = False
break
if valid:
kept.append(result)
else:
logger.warning(
"Beat %d: rejected by vision action-phase verification (%s)",
result.beat_id,
"; ".join(reasons),
)
return kept
def _attach_visual_segments(results: list, beats: list, cfg) -> list:
"""Attach automatic sub-shot matches for multi-island trailer beats."""
from dataclasses import replace
@@ -976,6 +1036,7 @@ def cmd_match(args: argparse.Namespace, cfg) -> list:
skip_global_segment_scan_for=set(single_island_trims),
)
results = _attach_visual_segments(results, beats, cfg)
results = _filter_semantically_invalid_vision_matches(results, beats, cfg)
# A targeted one-beat match should improve the cache without deleting
# automatic matches for other beats.