# PR #53829 完整报告

- 仓库：`vllm-project/vllm`
- 标题：[Bugfix][Model] Fix CohereASR streaming audio-token estimate (unit + subsampling)
- 合并时间：2026-09-02 23:55
- 原文链接：http://prhub.com.cn/vllm-project/vllm/pull/53829

---

# 执行摘要

- 一句话：修复 CohereASR 流式转录使用统计中音频令牌估算的单位错误和子采样遗漏，导致 prompt_tokens 被夸大 5 个数量级。
- 推荐动作：这是一个质量很高的 bugfix PR，强烈建议精读。核心变更是一个小范围但至关重要的计算逻辑修复，直接解决了影响用户可见统计的严重错误。讨论中关于 `ceil/floor` 对齐、数据源一致性和错误处理风格的交流非常有价值，体现了对代码健壮性和一致性的关注。测试设计（使用具体数值断言）也值得借鉴。

# 功能与动机

根据 PR body 描述，此修复针对流式转录使用统计（streaming transcription usage stats）的严重计量错误。原始实现 `hop_length = window_stride`（约 0.01）被误用作 `audio_duration_s * sample_rate`（样本数）的除数，导致计算出的音频令牌数被夸大约 5 个数量级（例如 1 秒音频估算为 1,600,000 个令牌，而预期约 13 个）。这直接影响用户对资源使用情况的正确评估。

# 实现拆解

1. **核心逻辑修复**：修改 `vllm/model_executor/models/cohere_asr.py` 中的 `CohereAsrForConditionalGeneration.get_num_audio_tokens` 类方法。
 - **单位修正**：不再将 `preprocessor["window_stride"]`（秒）直接用作 `hop_length`。而是从预处理器字典读取 `sample_rate` 和 `window_stride`，计算 `hop_length = int(window_stride * sample_rate)`，得到正确的样本跨度（160 samples）。
 - **计算对齐**：将帧数计算从 `math.ceil(duration * sample_rate / hop_length)` 改为 `int(audio_duration_s * sample_rate) // hop_length`（floor 除法），以对齐 `get_seq_len` 路径。
 - **补充子采样**：新增除以 `hf_config.encoder["subsampling_factor"]`（值为 8），然后取 `math.ceil` 得到最终令牌数。
 - **数据源统一**：`sample_rate` 和 `window_stride` 均从 `model_config.hf_config.preprocessor` 字典直接索引读取，与 `get_hf_processor` 保持一致，避免了从 `stt_config` 读取可能的不同步问题。
2. **测试覆盖**：新增测试文件 `tests/models/multimodal/test_cohere_asr.py`。
 - 使用参数化测试验证不同音频时长（1.0s, 10.0s, 30.0s）下的估算结果。
 - 断言具体的、已知正确的令牌数（13, 125, 375），而非重新推导公式，有效防止回归。
 - 构造了包含 `preprocessor` 和 `encoder` 字典的 `model_config` 模拟对象。

关键文件：
- `vllm/model_executor/models/cohere_asr.py`（模块 模型执行器；类别 source；类型 core-logic；符号 CohereAsrForConditionalGeneration.get_num_audio_tokens）: 包含核心 bugfix，修改了 `get_num_audio_tokens` 类方法，修复了流式转录使用统计的令牌估算逻辑。
- `tests/models/multimodal/test_cohere_asr.py`（模块 模型测试；类别 test；类型 test-coverage；符号 test_get_num_audio_tokens_streaming_estimate）: 新增的测试文件，提供了对修复后逻辑的具体回归测试覆盖。

关键符号：CohereAsrForConditionalGeneration.get_num_audio_tokens

## 关键源码片段

### `vllm/model_executor/models/cohere_asr.py`

包含核心 bugfix，修改了 `get_num_audio_tokens` 类方法，修复了流式转录使用统计的令牌估算逻辑。

```python
# 修复后的 CohereASR.get_num_audio_tokens 类方法
@classmethod
def get_num_audio_tokens(
    cls,
    audio_duration_s: float,
    stt_config: SpeechToTextConfig,
    model_config: ModelConfig,
) -> int | None:
    hf_config = model_config.hf_config
    preprocessor = hf_config.preprocessor
    # 从预处理器字典读取 sample_rate 和 window_stride，与 get_hf_processor 保持一致
    sample_rate = preprocessor["sample_rate"]
    window_stride = preprocessor["window_stride"]
    # 将 window_stride（秒）转换为样本跨度（hop_length），单位为 samples
    hop_length = int(window_stride * sample_rate)
    # 使用 floor 除法计算帧数，与 get_seq_len 路径对齐
    num_frames = int(audio_duration_s * sample_rate) // hop_length
    # 除以编码器子采样因子，得到最终的令牌数估算
    subsampling_factor = hf_config.encoder["subsampling_factor"]
    return math.ceil(num_frames / subsampling_factor)

```

# 评论区精华

讨论集中在几个实现细节的权衡上：
1. **ceil vs. floor**：`aaron-seq` 指出原始实现使用 `ceil` 计算帧数，而 `get_seq_len` 使用 `floor`，导致约 11% 的时长会多报一个令牌。作者随后修正为 `//`（floor 除法）。
2. **数据源一致性**：`aaron-seq` 建议从 `preprocessor` 字典读取 `sample_rate`，而非 `stt_config.sample_rate`，以与 `get_hf_processor` 对齐并避免未来不同步。作者采纳了此建议。
3. **错误处理风格**：讨论了 `window_stride` 和 `subsampling_factor` 是使用带默认值的 `.get()` 还是直接索引。
 - `aaron-seq` 和 `ekagra-ranjan` 建议使用直接索引，让配置错误更早暴露（KeyError）。
 - `hungnnvidia` 在初版对 `window_stride` 使用了 `.get("window_stride", 0.01)`，但最终根据反馈改为直接索引（`preprocessor["window_stride"]`）。
 - 对于 `subsampling_factor`，作者坚持使用直接索引（`hf_config.encoder["subsampling_factor"]`），认为设置默认值（如 1）会静默导致估算值乘以 8，而抛出 `KeyError` 是更安全的选择，并指出 per-request 路径 `CohereASRProcessingInfo.get_num_audio_tokens` 也使用直接索引。
4. **测试策略**：`aaron-seq` 指出初始测试可能基于与代码相同的公式推导预期值，无法真正检测回归。最终测试断言了具体的数值结果。
5. **文件命名**：`DarkLight1337` 建议简化测试文件名，从 `test_cohere_asr_audio_tokens.py` 改为 `test_cohere_asr.py`，作者执行了此变更。

- 帧数计算的取整策略（ceil vs floor） (correctness): 采用 floor 除法，与 `get_seq_len` 保持一致，消除了不一致性。
- sample_rate 和 window_stride 的数据来源 (design): 统一从 `model_config.hf_config.preprocessor` 读取，增强了代码健壮性。
- 配置键的错误处理风格（默认值 vs 直接索引） (design): 采用直接索引访问配置键，优先让配置错误快速失败（fail-fast），而非提供可能误导的默认值。
- 测试用例的预期值来源 (testing): 测试断言了已知的具体数值结果，增强了测试的防护能力。
- 测试文件命名简化 (style): 采纳建议，重命名测试文件以保持简洁。

# 风险与影响

- 风险：
 1. **逻辑风险（低）**：修复逻辑清晰，对齐了已验证的 per-request 路径（`get_seq_len` + `subsampling_factor`）。主要风险在于修改了流式使用统计的计算逻辑，如果实现有误，会影响 `prompt_tokens` 计数。
 2. **兼容性风险（低）**：变更仅影响 `CohereASR` 模型的流式转录使用统计计算，不改变模型推理的核心计算或输出。
 3. **回归风险（低）**：新增了针对此核心计算路径的单元测试，且使用了具体数值进行断言，能有效防止回归。测试覆盖了不同时长（包括非 hop 倍数时长）。
 4. **配置依赖风险（中）**：修复后的代码直接索引 `hf_config.preprocessor` 和 `hf_config.encoder` 字典。如果模型配置缺少这些键，会抛出 `KeyError`。这是一种有意的设计选择，旨在让配置问题更早暴露，但需要确保目标模型（如 CohereLabs/cohere-transcribe-03-2026）的配置始终包含这些字段。
 - 影响：**用户影响**：修复了 CohereASR 模型在流式转录场景下向用户报告的 `prompt_tokens` 使用统计的严重错误（夸大 ~5 个数量级）。这有助于用户正确理解资源消耗。
**系统影响**：仅影响 CohereASR 模型的特定计算路径（`get_num_audio_tokens` 类方法），不涉及核心调度、内核或分布式通信逻辑。变更范围小且隔离。
**团队影响**：此修复是一个高质量的 bugfix PR，展示了清晰的根因分析、与 reviewer 的有效沟通和代码改进。它也为多模态模型类似的使用统计计算提供了一个良好的实践范例。

- 风险标记：配置依赖

# 关联脉络

- PR #39259 Fix Cohere ASR failing to load when librosa is not installed: 同属 Cohere ASR 模型的修复，涉及 `processors/cohere_asr.py` 的依赖导入问题，与本 PR 修改的模型文件相关。
- PR #54918 [Bugfix][Multimodal] Scope cache hash kwargs by modality: 同属 `multi-modality` 领域的 bugfix PR，修复了多模态处理中的性能问题，与本 PR 在模块标签（multi-modality）和变更性质（bugfix）上具有关联性。