From cf4d8a550c796b38d6ab3e224a87ee7bdd757e63 Mon Sep 17 00:00:00 2001 From: zj1123581321 Date: Sat, 10 Oct 2026 22:49:18 +0800 Subject: [PATCH] =?UTF-8?q?chapters=20segments=20=E7=A7=8D=E5=AD=90?= =?UTF-8?q?=E6=94=B9=E7=94=A8=20load=5Fsegments=20=E5=90=8C=E6=BA=90?= =?UTF-8?q?=E5=8A=A0=E8=BD=BD=EF=BC=88=E4=BF=AE=20notes=20=E6=8C=87?= =?UTF-8?q?=E7=BA=B9=E9=94=99=E9=85=8D=EF=BC=8C#227=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit transcription.py 分层缓存补种逻辑的 segments 分支不再取运行态 cache_data["segments"],改用与 notes reader 同源的 transcriber.segments.load_segments(file_path),使 chapters 存储指纹 永远可从磁盘复现,修复 notes 生成因指纹错配而确定性失败的问题。 Agent-Executor: agy Agent-Model: gemini-3.8-flash-high Agent-Effort: unknown Dispatch-Id: dlg-20261010-142849-1cebf3 Task-Id: VideoTranscriptAPI-20261015-05 Fixes-Issue: #227 --- .../api/services/transcription.py | 2 - tests/integration/test_layered_cache.py | 56 +++++++++++++++++++ 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/src/video_transcript_api/api/services/transcription.py b/src/video_transcript_api/api/services/transcription.py index b9052d15..42c30a7e 100644 --- a/src/video_transcript_api/api/services/transcription.py +++ b/src/video_transcript_api/api/services/transcription.py @@ -1705,8 +1705,6 @@ def _fail_task_and_notify( processed.get("dialogs"), list ) and processed["dialogs"]: timeline_segments_seed = processed["dialogs"] - elif isinstance(cache_data.get("segments"), list) and cache_data["segments"]: - timeline_segments_seed = cache_data["segments"] else: file_path = cache_data.get("file_path") if file_path: diff --git a/tests/integration/test_layered_cache.py b/tests/integration/test_layered_cache.py index c07b8b5a..5d4e7a00 100644 --- a/tests/integration/test_layered_cache.py +++ b/tests/integration/test_layered_cache.py @@ -35,6 +35,10 @@ ChaptersStatus, SummaryStatus, ) +from video_transcript_api.llm.processors.notes_processor import ( + compute_notes_anchor_fingerprint, +) +from video_transcript_api.transcriber.segments import load_segments class DummyQueue: @@ -943,6 +947,58 @@ def test_missing_llm_status_does_not_trust_existing_calibrated_file( # 重新校对必须喂原始转录文本,而不是那份未经确认的 calibrated 产物。 assert task["transcript"] == "RAW uncalibrated transcript" + def test_chapters_seed_uses_load_segments_isomorphic_with_notes_reader( + self, monkeypatch, patch_runtime, tmp_path + ): + """Issue #227: chapters timeline seed for segments backfill must use + transcriber.segments.load_segments(cache_dir) rather than in-memory + cache_data['segments'], ensuring anchor fingerprint isomorphism.""" + cache_dir = tmp_path / "cache_entry" + cache_dir.mkdir(parents=True) + disk_raw = { + "segments": [ + {"start_time": 0.0, "end_time": 1.5, "text": "seg 1", "speaker": "0"}, + {"start_time": 1.5, "end_time": 3.0, "text": "seg 2", "speaker": "1"}, + ] + } + (cache_dir / "transcript_funasr.json").write_text(json.dumps(disk_raw), encoding="utf-8") + expected_disk_segments = load_segments(cache_dir) + expected_fingerprint = compute_notes_anchor_fingerprint(expected_disk_segments) + + divergent_segments = [ + {"start_time": 0.0, "end_time": 1.5, "text": "seg 1", "speaker": "Speaker 0"}, + {"start_time": 1.5, "end_time": 3.0, "text": "seg 2", "speaker": "Speaker 1"}, + ] + assert compute_notes_anchor_fingerprint(divergent_segments) != expected_fingerprint + + cache_data = { + **BASE_CACHE_DATA, + "file_path": str(cache_dir), + "use_speaker_recognition": True, + "transcript_type": "funasr", + "transcript_data": disk_raw, + "segments": divergent_segments, + "llm_calibrated": "calibrated text", + "llm_summary": "summary text", + "llm_status": { + "calibration_status": CalibrationStatus.FULL, + "summary_status": SummaryStatus.GENERATED, + "chapters_status": ChaptersStatus.FAILED, + }, + } + + result, queued = _run( + monkeypatch, patch_runtime, cache_data, + {"calibrate": False, "summarize": False, "chapters": True, "infer_speaker_names": False}, + ) + + assert result["status"] == "success" + assert len(queued) == 1 + seed_segments = queued[0].get("timeline_segments") + assert seed_segments == expected_disk_segments + assert compute_notes_anchor_fingerprint(seed_segments) == expected_fingerprint + + class TestFullHitMirrorsCacheStatusOnTaskRow: """Regression for codex-review R2 item 2: a full cache hit takes no