Skip to content

章节梗概 (5/9):缓存层 timeline 落盘与读回 - #19

Merged
zj1123581321 merged 2 commits into
mainfrom
stack/chapters-05-cache-timeline-io
Jul 21, 2026
Merged

zj1123581321 merged 2 commits into
mainfrom
stack/chapters-05-cache-timeline-io

Conversation

@zj1123581321

Copy link
Copy Markdown
Collaborator

范围

Stacked PR 系列第 5 个(依赖 #18)。单个原子提交「接通 CapsWriter/YouTube timeline 落盘与 get_cache 读回」(6210 行,单提交无法再拆,CI review 将自动分片):

  • cache_manager 支持 timeline/segments 落盘与 get_cache 读回,CapsWriter 与 YouTube 两路打通
  • 为 chapters_status/llm_chapters 分层缓存奠定存储基础

配置/Schema 触点

缓存存储格式扩展(timeline 字段);无配置文件变更。

验证证据

  • uv run pytest tests/unit 全绿(本地边界点已预验证)

Stacked 结构

  1. 章节梗概 (1/9):统一 segments 带时间片段读取适配器(T3) #15 T3 / 2. 章节梗概 (2/9):字幕时间戳保留(T2)+ ChaptersProcessor 核心(T5) #16 T2+T5 / 3. 章节梗概 (3/9):ChaptersProcessor gate 安全加固 r1–r14 #17 r1–r14 / 4. 章节梗概 (4/9):ChaptersProcessor gate 安全加固 r15–r28 #18 r15–r28 / 5. 本 PR(stack-04 ← stack-05)/ 6. 接线 / 7. T8 / 8. T9+T10 / 9. T11

- CapsWriter save_cache 传 funasr_json_data 写 transcript_capswriter.json
- YouTube 平台字幕改 get_subtitle_result,segments 作为 extra_json_data
- youtube-api fetch_for_transcription 解析 SRT 时保留 transcript_segments
- get_cache 经 load_segments 附带返回 segments
- get_subtitle 薄委托 get_subtitle_result,去掉平行分支
- capswriter _split_long_segment 对非有限时间诚实降级
@github-actions

github-actions Bot commented Jul 20, 2026 •

Copy link
Copy Markdown

ℹ️ PR 体积预检:将自动分片审查

当前审查 Patch 为 7367 行,超过单轮预算 4000 行,Codex 会覆盖所有分片并做跨模块整合。

  • 文件:8
  • 实际增删:6684 行(+3775 / -2909)
  • 审查 Patch:7367 行(包含上下文和 diff 元数据)
  • Reviewed commit: 4a385722f5992b48c5d1fe132b7e805b7a8c83dc

这次可以继续,但下次优先按单一功能拆成 small PR,以缩短反馈时间。

@github-actions

github-actions Bot commented Jul 20, 2026 •

Copy link
Copy Markdown

Reviewed commit: 4a385722f5992b48c5d1fe132b7e805b7a8c83dc

🤖 Review 门禁 — 判定: fail(主审: claude-glm)

最终集成审查通过。前一轮 codex-sub 报出的 4 个 major 正确性问题已全部在当前 head (4a38572) 修复并逐一核实:1) 旧 timeline 侧车覆盖清理(cache_manager.py:684-693);2) segments-only 字幕正文重建(transcription.py 两条路径);3) CapsWriter 无效时间统计不再 TypeError 崩溃(capswriter_client.py:794-798);4) 倒挂时间区间降级为 None(capswriter_client.py:237-242)。回归测试覆盖完整(test_cache_timeline_wiring / test_youtube_segments_text_fallback / test_capswriter_funasr_compat_persistence / test_youtube_get_subtitle_result)。

分片 1 报出的 correctness-empty-subtitle-still-saved 经逐路径追踪,其触发条件在当前代码库中不可达:三条 YouTube 字幕解析路径均不会产出 text 为空且所有 segment text 均为空的 SubtitleResult——本地路径在 _fetch_youtube_transcript_result 里已过滤空文本结果,TikHub XML 与 SRT 提取器在构建 segments 时均跳过空内容条目。因此 _rebuild_text_from_segments 在 segments 非空时永远返回非空正文,外层成功判定始终经 subtitle.strip() 通过。该发现降级为 minor(防御性缺口,非可触发的正确性 bug)。

分片 2 报出的 get_subtitle 对 segments-only 返回空串属刻意设计且已有测试断言,仅 docstring 与行为有轻微出入。无 blocker/major 级别问题。

覆盖:sharded+cross-module integration;3 个审查单元;7367 行 diff。

  • minor · correctness · correctness-empty-subtitle-still-saved src/video_transcript_api/api/services/transcription.py:1918 — 代码存在防御性缺口:当 subtitle 为空串时,仅凭 subtitle_extra_json 非空仍会进入成功分支。该缺口在当前字幕解析器实现下不可触发(segments 不可能全空文本),但逻辑上不够严谨,未来若新增不经上述三条解析器的字幕来源可能被激活。
    • 触发:YouTube 非 API Server 字幕路径返回 subtitle_result,其 text 为空且 segments 非空但所有 segment text 也为空——该触发条件在当前三条字幕解析路径中均不可达
    • 证据:第 1918-1921 行外层成功判定允许 subtitle 为空但 subtitle_extra_json 非空时通过。但经逐路径核实:本地 youtube-transcript-api 路径在 _fetch_youtube_transcript_result 中对 text 为空的结果直接过滤(youtube.py:655,677,test_youtube_get_subtitle_result.py:152-156 注释确认 in practice never produces this shape);TikHub XML 解析在 youtube.py:933-934 跳过空 content 条目;API Server SRT 提取器在 youtube_api_client.py:971-972 仅添加 segment_text 非空的分段。三条路径产出的 segments 只要非空就必定含有非空文本,故 _rebuild_text_from_segments 在被调用时永远返回非空,外层判定始终经 subtitle.strip() 通过而非依赖 subtitle_extra_json。
    • 验收:将外层进入条件改为以 subtitle.strip() 为必要条件(不再仅凭 subtitle_extra_json 非空放行),或在 _rebuild_text_from_segments 返回空后不设置 subtitle_extra_json、不进入成功分支。保持现有 segments-only 正文重建行为不变即可,无需大改。
  • minor · compatibility · compatibility-get-subtitle-empty-string src/video_transcript_api/downloaders/youtube.py:423 — 向后兼容入口 get_subtitle 的返回契约从 无字幕返回 None 变为 segments-only 返回空字符串。当前主转录路径已切换到 get_subtitle_result 且有 _rebuild_text_from_segments 兜底,不会将空串写入缓存或送入 LLM,实际影响有限;但任何仍通过 get_subtitle 获取字幕的外部调用方会拿到空串而非 None。
    • 触发:get_subtitle 薄委托 get_subtitle_result 后,对 text 为空但 segments 非空的 segments-only 字幕返回空字符串而非 None
    • 证据:get_subtitle 主体为 result = self.get_subtitle_result(url); return result.text if result else None(:422-423)。segments-only 时 get_subtitle_result 返回 SubtitleResult(text 空串, segments 非空),get_subtitle 因此返回空串。BaseDownloader.get_subtitle 抽象方法声明返回值为 str|None(base.py:128);docstring 写 无字幕时返回 None(youtube.py:420),与 segments-only 返回空串不一致。PR 已在 test_youtube_get_subtitle_result.py:257-267 显式断言此行为并注释 callers that need timeline should use get_subtitle_result directly,属刻意设计。
    • 验收:在 get_subtitle 中对 result.text 做与旧版一致的空串检查:当 result.text 为空(或纯空白)时返回 None 而非空串,保持 docstring 与实际行为一致。或更新 docstring 明确说明 segments-only 场景返回空串的设计意图。
  • nit · design · nit-get-cache-absolute-import src/video_transcript_api/cache/cache_manager.py:905 — 风格不一致;相对导入 from ..transcriber.segments import load_segments 更健壮(不依赖顶层包名在 sys.path 上可解析),且与本文件惯例统一。transcriber/segments.py 仅依赖 utils.logging,不存在循环导入,无需延迟到函数体内。
    • 触发:get_cache 内 load_segments 的导入使用绝对路径 from video_transcript_api.transcriber.segments import load_segments,与本文件其余相对导入风格不一致
    • 证据:cache_manager.py 全文其余导入均为相对形式(如 from ..utils.logging import setup_logger),唯独此处用绝对包名导入。该导入在 try/except 内,失败时静默降级,当前运行环境下可正常工作。
    • 验收:将该导入改为相对形式 from ..transcriber.segments import load_segments 并提升到文件顶部与其余导入并列(或如需保留惰性导入则同样改用相对路径)。

@github-actions

github-actions Bot commented Jul 20, 2026 •

Copy link
Copy Markdown

📒 Review ledger state

  • Commit: 4a385722f5992b48c5d1fe132b7e805b7a8c83dc
  • Round: 3
  • Status / findings: waived / 0
  • Comparison: same_head_rerun; stable/missing/appeared = 0/3/0

完整数据保存在 codex-review-ledger artifact;此 sticky comment 仅保存跨 rerun 的连续游标。

@zj1123581321 zj1123581321 added the codex-review-waived Codex review 由维护者带书面理由显式豁免 label Jul 20, 2026
@zj1123581321

Copy link
Copy Markdown
Collaborator Author

Codex review waiver: 本 PR 的 agent review 再次产出内容干净但 verdict 字段错填的判定(run 详见 Actions,head 4a38572,主审 claude-glm):summary 明确「最终集成审查通过」,前一轮 codex-sub 报出的 4 条 major 全部核实已修复(侧车覆盖清理 cache_manager.py:684-693、两条 YouTube 路径 segments 正文重建、None 时间统计 TypeError 修复、倒挂区间降级 None),回归测试覆盖完整;剩余 findings 为 2 minor(transcription.py:1918 不可触发的防御性缺口、youtube.py:423 get_subtitle 兼容性边角)+ 1 nit(导入风格),无任何 blocker/major——但 verdict 字段仍错填 fail,与 PR #18 同型的 verdict/findings 自相矛盾故障(本次 stack 第 5 次)。lint 与 tests 门槛真实通过(2457 单测全绿),2 条 minor 作为已接受 backlog 记录,故对 review 步骤行使审计豁免。豁免仅针对 head 4a38572。validate_verdict 一致性校验的修复建议将汇总提交给 gate-hub。

@zj1123581321 zj1123581321 reopened this Jul 20, 2026
@zj1123581321
zj1123581321 changed the base branch from stack/chapters-04-gate-fixes-2 to main July 21, 2026 02:22
@zj1123581321
zj1123581321 merged commit 03bf450 into main Jul 21, 2026
3 of 4 checks passed
@zj1123581321
zj1123581321 deleted the stack/chapters-05-cache-timeline-io branch July 21, 2026 02:24
zj1123581321 added a commit that referenced this pull request Oct 15, 2026
章节梗概 (5/9):缓存层 timeline 落盘与读回
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

codex-review-waived Codex review 由维护者带书面理由显式豁免

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant