Prhub

#1908 Snapshot test the argv of all model scripts

原始 PR 作者 fzyzcjy 合并时间 2026-08-09 18:47 文件变更 68 提交数 15 评论 1 代码增减 +3592 / -0

执行摘要

为全部模型脚本 argv 增加快照测试

PR body 明确说明这是 #1837(重构与增强跟踪 issue)的一部分。提交信息进一步解释了动机:「The next ops rewrite all 62 scripts/models/*.sh into python. Once the shell versions are gone there is no source of truth left to prove the rewrite was faithful, so record the argv each of them expands to now.」也就是说,在 shell 脚本被删除之前必须先把当前展开结果固化为 golden 文件,否则重写后没有任何证据能证明新 Python 实现与旧 shell 行为一致。此外,这批快照还让 18 个此前没有任何 launcher 快照覆盖的模型获得了第一层基础覆盖。

值得精读。重点阅读 model_args_harness.py 中如何用最小化环境执行 shell 展开并抽取 argv,以及 test_model_args.pyTestDiscovery 的设计——用「集合一致性」防止快照文件失联的思路值得借鉴。此外,这组快照测试是理解后续 #1909、#1910、#1911 一系列重构的必读前提。

讨论亮点

该 PR 没有实质性的 review 讨论。仅有 gemini-code-assist[bot] 的一条系统通知,说明 Gemini Code Assist 的消费者版本已停用,所有代码审查活动正式终止,因此没有来自人类的评论或争议。值得关注的是 PR 作者同时也是合并者,单人完成了整个 op 链的推进,并依赖快照测试本身的失败结果来驱动后续调整。

实现拆解

  1. 新增 argv 提取工具:在 tests/fast/launch_scripts/model_args_harness.py 中实现 iter_model_types()(通过 glob 扫描 scripts/models/*.sh 得到模型类型列表)和 expand_model_args()(以最小化环境变量执行 source script && printf 展开 MODEL_ARGS 数组,逐行输出)。最小化环境(PATH/HOME/LANG/LC_ALL)保证快照结果不受宿主机环境干扰。
  2. 新增参数化快照测试tests/fast/launch_scripts/test_model_args.pyTestEveryModelType.test_model_args_match_snapshot 对每个模型类型执行展开并与 tests/snapshots/model_args/{model_type}.txt 比较;test_model_args_are_flags_and_values 校验首 token 以 -- 开头且所有 token 无空白,防止 argv 拼接后隐式分裂。
  3. 新增发现一致性测试TestDiscovery.test_every_model_is_discovered_and_snapshotted 断言 iter_model_types() 返回的模型集合与快照目录中的 .txt 文件集合完全一致,并断言模型数量 > 60,防止新模型脚本或快照文件被静默遗漏。
  4. 生成 60+ 个 golden 快照文件:为 deepseek-v4-prodeepseek-v4-flashdeepseek-v32moonlightglm4.7-flashglm5-744B-A40Bkimi-k2 等所有模型脚本逐一生成 tests/snapshots/model_args/*.txt,内容为 JSON 序列化后的逐 token argv。
  5. 配套后期演进:本 PR 的快照文件在后续 #1909、#1910、#1911 中被持续重新生成(如为 run.py 命令增加 --model_args 内联 base64 参数等),承担了「重构是否 faithful」的验证职责。
文件 模块 状态 重要度
tests/fast/launch_scripts/test_model_args.py 模型参数测试 added 7.53
tests/fast/launch_scripts/model_args_harness.py 模型参数工具 added 6.76
tests/snapshots/model_args/deepseek-v4-pro.txt 快照文件 added 4.6

关键符号

iter_model_types expand_model_args test_model_args_match_snapshot test_model_args_are_flags_and_values test_every_model_is_discovered_and_snapshotted

关键源码片段

tests/fast/launch_scripts/test_model_args.py test-coverage

核心测试文件:通过参数化测试将每个模型脚本的展开 argv 与 golden 文件比对,并用 TestDiscovery 保证模型发现与快照文件集合一致,防止重构后模型参数漂移。

import jsonimport pytestfrom tests.fast.launch_scripts.model_args_harness import expand_model_args, iter_model_types
from tests.fast.launch_scripts.sh_harness import REPO_ROOT, assert_matches_snapshot# 快照目录集中放在 tests/snapshots/model_args 下,便于统一管理
_SNAPSHOT_DIR = REPO_ROOT / "tests" / "snapshots" / "model_args"# 模块加载时一次性发现全部模型脚本,保证参数化用例集合固定
_MODEL_TYPES = iter_model_types()
​
​
class TestEveryModelType:
    @pytest.mark.parametrize("model_type", _MODEL_TYPES)
    def test_model_args_match_snapshot(self, model_type: str) -> None:
        """The golden argv of every model, so a later rewrite of the model definitions cannot drift."""
        # 逐 token JSON 序列化后按行拼接,保证快照可读且能精确还原 shell 展开结果
        actual = "\n".join(json.dumps(token) for token in expand_model_args(model_type)) + "\n"
​
        assert_matches_snapshot(_SNAPSHOT_DIR / f"{model_type}.txt", actual, model_type)
​
    @pytest.mark.parametrize("model_type", _MODEL_TYPES)
    def test_model_args_are_flags_and_values(self, model_type: str) -> None:
        """Consumers split the args on whitespace, so a token that contains any would silently become two."""
        tokens = expand_model_args(model_type)
​
        # 调用方按空白切分参数,因此任何含空白的 token 都会静默分裂,这里直接封死该可能
        assert tokens
        assert tokens[0].startswith("--")
        assert all(token == token.strip() and " " not in token for token in tokens)
​
​
class TestDiscovery:
    def test_every_model_is_discovered_and_snapshotted(self) -> None:
        """A model that stops matching the discovery glob would otherwise lose its golden file silently."""
        # 若模型脚本改名或新增,而快照文件未同步,此断言会立即失败,避免 golden 文件失联
        snapshotted = {path.stem for path in _SNAPSHOT_DIR.glob("*.txt")}
​
        assert set(_MODEL_TYPES) == snapshotted
        assert len(_MODEL_TYPES) > 60
tests/fast/launch_scripts/model_args_harness.py test-coverage

测试基础设施:定义了 iter_model_types 与 expand_model_args,是快照数据来源,后续 Python 化重写必须复现其输出。

import subprocessfrom tests.fast.launch_scripts.sh_harness import REPO_ROOT# 模型脚本统一放在 scripts/models 目录下
MODEL_SCRIPT_DIR = REPO_ROOT / "scripts" / "models"# 刻意剔除 LOGNAME/USER 等环境变量,避免宿主环境干扰快照展开结果
_ENV_WITHOUT_THE_MODEL_ARGS_KNOBS = {
    "PATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
    "HOME": "/root",
    "LANG": "C",
    "LC_ALL": "C",
}
​
​
def iter_model_types() -> list[str]:
    # 以文件名(不含 .sh)作为模型类型标识,例如 deepseek-v4-pro
    return sorted(path.stem for path in MODEL_SCRIPT_DIR.glob("*.sh"))
​
​
def expand_model_args(model_type: str) -> list[str]:
    """The golden files are taken from this shell expansion; whatever replaces it must reproduce them."""
    # source 脚本后按行打印 MODEL_ARGS 数组,得到展开后的 argv 逐 token 列表
    script = MODEL_SCRIPT_DIR / f"{model_type}.sh"
    result = subprocess.run(
        f'source "{script}" && printf "%s\\n" "${{MODEL_ARGS[@]}}"',
        shell=True,
        executable="/bin/bash",
        env=_ENV_WITHOUT_THE_MODEL_ARGS_KNOBS,
        capture_output=True,
        text=True,
        check=True,
    )
    return result.stdout.splitlines()

评论区精华

无人工 review 讨论 other

PR 没有收到任何人工 review 评论,仅有一个 gemini-code-assist[bot] 的系统留言说明其服务已停用。

结论:唯一评论是机器人通知,无实际技术讨论,PR 由作者自行合并。 · 已解决

风险与影响

  1. 快照更新纪律风险:60+ 个 golden 文件完全依赖「手动重新生成」来保持与脚本一致,若后续修改模型脚本而忘记更新快照,CI 会失败,这是设计使然,但也意味着每次模型参数调整都会产生大量快照文件变更,可能带来 review 噪音。
  2. 对宿主环境的敏感性expand_model_args() 通过 subprocess.run 执行 shell 脚本,虽然设置了最小化环境,但如果本地开发机的 bash 版本或环境变量处理与 CI 不一致,可能出现假阳性失败。快照文件本身与 shell 展开行为强绑定,后续 Python 化重写必须逐字节复现这些输出。
  3. 文件数量膨胀风险tests/snapshots/model_args/ 下新增 60+ 个文本文件,加上后续 #1909 对 shell 快照的补充,仓库内快照文件总数显著增加,需要留心快照文件的管理策略(如是否纳入 pre-commit 自动重生成)。
  4. 数量断言脆弱性assert len(_MODEL_TYPES) > 60 引入了对模型数量的硬编码下限,若未来清理或合并模型脚本导致总数降至 60 以下,测试会失败,但这种情况实际发生概率很低,风险可控。

对用户与开发者的影响:该 PR 不改变任何运行时行为,所有变更都在 tests/ 下,因此对线上训练、rollout 等功能无直接影响。对团队的影响:建立了模型脚本重构的「行为基准线」,任何对 scripts/models/*.sh 的修改都会在 CI 中被快照测试自动校验,能显著降低「改坏模型参数而无人察觉」的风险。对后续重构的影响:tests/snapshots/model_args/*.txt 成为 #1909、#1910、#1911 等 PR 的验收依据,实际推动了 launcher 快照、模型配置 Python 化等系列重构的落地。

快照文件更新依赖手动重生成 测试对 bash 环境敏感 快照文件数量大幅膨胀 数量断言硬编码下限

关联 Issue

#1837 Tracking issue for refactoring and enhancements

完整报告

参与讨论